题目来源:LeetCode 374:猜数字大小
问题抽象: 设计一个猜数字游戏,目标是通过调用预定义的 guess
API 接口,高效找出一个在 [1, n]
范围内的目标整数 target
,满足以下核心需求:
-
API 交互规则:
- 预定义函数
guess(int num)
返回整型结果:-1
:所猜数字num
大于target
;1
:所猜数字num
小于target
;0
:num
等于target
(成功)。
- 需实现函数
guessNumber(int n)
,返回目标值target
。
- 预定义函数
-
优化目标:
- 最小化 API 调用次数:利用 二分查找 策略(时间复杂度 O(log n));
- 禁止遍历所有数字(避免
O(n)
调用)。
-
边界处理:
- 最小范围:
n=1
时直接返回1
(无需调用 API); - 整数溢出:二分查找中点计算需用
left + (right - left) / 2
避免溢出; - 无解情况:输入保证目标值必在
[1, n]
内(无需额外检查)。
- 最小范围:
-
计算约束:
- 时间复杂度 O(log n):二分查找最多调用
log₂(n)
次guess
; - 空间复杂度 O(1):仅需存储左右边界及中点;
- 输入范围:
1 ≤ n ≤ 2^31 - 1
(需处理大整数)。
- 时间复杂度 O(log n):二分查找最多调用
输入:整数 n
(目标值范围上限)
输出:目标整数 target
(1 ≤ target ≤ n
)
函数签名:
public class Solution extends GuessGame {
public int guessNumber(int n) {
// 二分查找实现
}
}
预定义接口:
class GuessGame {
int guess(int num) { /* 由系统实现 */ }
}
解题思路
题目要求在 1
到 n
之间猜一个数字,通过调用预定义的 guess
接口获得反馈。解题关键在于高效缩小搜索范围,使用二分查找算法可以将时间复杂度优化至
O
(
log
n
)
O(\log n)
O(logn),空间复杂度为
O
(
1
)
O(1)
O(1)。
算法步骤:
- 初始化边界:左边界
left = 1
,右边界right = n
。 - 二分查找:
- 计算中间值
mid = left + (right - left) / 2
(避免整数溢出)。 - 调用
guess(mid)
获取结果:- 若返回
0
,直接返回mid
(找到目标)。 - 若返回
-1
,说明mid
偏大,调整右边界right = mid - 1
。 - 若返回
1
,说明mid
偏小,调整左边界left = mid + 1
。
- 若返回
- 计算中间值
- 循环终止:当
left <= right
时持续搜索(题目保证解存在,无需额外边界判断)。
代码实现(Java版)🔥点击下载源码
public class Solution extends GuessGame {
public int guessNumber(int n) {
int left = 1;
int right = n;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止计算溢出
int res = guess(mid); // 调用预定义API
if (res == 0) {
return mid; // 猜中目标,直接返回
} else if (res == -1) {
right = mid - 1; // 数字过大,缩小右边界
} else {
left = mid + 1; // 数字过小,缩小左边界
}
}
return -1; // 理论不会执行(题目保证解存在)
}
}
代码说明
- 二分查找优化:
- 使用
left + (right - left) / 2
计算中间值,避免(left + right)
可能导致的整数溢出。 - 每次迭代将搜索范围减半,确保 O ( log n ) O(\log n) O(logn) 时间复杂度。
- 使用
- 边界调整:
guess(mid) = -1
:目标小于mid
,将右边界设为mid - 1
。guess(mid) = 1
:目标大于mid
,将左边界设为mid + 1
。
- 终止条件:
left <= right
保证搜索区间有效性,找到目标时立即返回。 - 返回值:循环内必会命中目标(题目数据保证),最后的
return -1
仅为语法需要。