前言说明
算法学习,日常刷题记录。
题目连接
题目内容
猜数字游戏的规则如下:
每轮游戏,我都会从1到n随机选择一个数字,请你猜选出的是哪个数字。
如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了。
你可以通过调用一个预先定义好的接口int guess(int num)来获取猜测结果,返回值一共有3种可能的情况(-1,1或0):
-1:我选出的数字比你猜的数字小pick < num
1:我选出的数字比你猜的数字大pick > num
0:我选出的数字和你猜的数字一样。恭喜!你猜对了!pick == num
返回我选出的数字。
示例1:
输入:n = 10, pick = 6
输出:6
示例2:
输入:n = 1, pick = 1
输出:1
示例3:
输入:n = 2, pick = 1
输出:1
示例4:
输入:n = 2, pick = 2
输出:2
提示:
1 <= n <= 2^31 - 1
1 <= pick <= n
分析过程
看题可以得知用二分法查找,取1-n的中间值,调用guess函数和pick比较。
如果guess结果大于0,那么pick > num,选出的数字大于猜测的数字,所以选出的数字在区间[mid + 1, right]中。
如果guess结果小于0,那么pick < num,选出的数字小于猜测的数字,所以选出的数字在区间[left, mid - 1]中。
注意:这里的guess函数是两数相反了,因为拿了pick作为比较基准,而不是传递进去num参数作为比较基准,所以和我们平时的认知是刚好相反的。
所以如下代码:
/**
* Forward declaration of guess API.
* @param num your guess
* @return -1 if num is lower than the guess number
* 1 if num is higher than the guess number
* otherwise return 0
* int guess(int num);
*/
public class Solution extends GuessGame {
public int guessNumber(int n) {
// 定义左右指标,左指标初始为1,右指标初始为n
int left = 1, right = n;
// 计算中间指标
int center = (left + right) / 2;
// 计算猜测结果
int result = guess(center);
// 循环直至guess的结果为0
while (result != 0) {
if (result > 0) {
// 若是大于0,那么pick > num,选出的数字大于猜测的数字,所以选出的数字在区间[mid + 1, right]中
left = center + 1;
} else {
// 若是小于0,那么pick < num,选出的数字小于猜测的数字,所以选出的数字在区间[left, mid - 1]中
right = center - 1;
}
// 计算中间指标
center = (left + right) / 2;;
// 计算猜测结果
result = guess(center);
}
// 当猜测结果为0时,即为二分查找到的数字
return center;
}
}
但是提交后提示运行时间超时:
是什么原因呢?我们可以看一下提示中n的范围:
1 <= n <= 2^31 - 1
其实这里如果输入2^31 - 1和1,一开始就已经整数溢出了,所以可以想象,还可能会出现2^31 - 1和一个二分查找时的中间指标相加再除以2,2^31 - 1已经是整数最大值了,若再加一个数,肯定就整数溢出了,导致二分查找时中间指标发生了跳跃变化,最终导致运行时间超时,我们计算中间指标如下:
// 计算中间指标
int center = (left + right) / 2;
错误的地方就在这里,这里可能会导致整数溢出,我们需要用其他方法来计算中间指标,可以改成如下:
// 计算中间指标,不用center = (left + right) / 2来计算,防止计算时溢出
int center = left + (right - left) / 2;
这样一来,就不会出现整数溢出了。
解答代码
所以需要考虑整数溢出的问题,解答代码如下:
/**
* Forward declaration of guess API.
* @param num your guess
* @return -1 if num is lower than the guess number
* 1 if num is higher than the guess number
* otherwise return 0
* int guess(int num);
*/
public class Solution extends GuessGame {
public int guessNumber(int n) {
// 定义左右指标,左指标初始为1,右指标初始为n
int left = 1, right = n;
// 计算中间指标,不用center = (left + right) / 2来计算,防止计算时溢出
int center = left + (right - left) / 2;
// 计算猜测结果
int result = guess(center);
// 循环直至guess的结果为0
while (result != 0) {
if (result > 0) {
// 若是大于0,那么pick > num,选出的数字大于猜测的数字,所以选出的数字在区间[mid + 1, right]中
left = center + 1;
} else {
// 若是小于0,那么pick < num,选出的数字小于猜测的数字,所以选出的数字在区间[left, mid - 1]中
right = center - 1;
}
// 计算中间指标,不用center = (left + right) / 2来计算,防止计算时溢出
center = left + (right - left) / 2;
// 计算猜测结果
result = guess(center);
}
// 当猜测结果为0时,即为二分查找到的数字
return center;
}
}
提交结果
执行用时0ms,时间击败100.00%的用户,内存消耗35MB,空间击败84.51%的用户。
原文链接
原文链接:猜数字大小