leetcode 704 二分查找。链接:https://leetcode.cn/problems/binary-search
题目描述:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。
解题思路:
设定数组的起始位置是start为0,末尾位置是length-1。
主要逻辑:将target 和数组的中间数mid进行比较,如果nums[mid]<target, 就将start赋值为mid+1, 如果nums[mid]>target,就将end赋值为mid-1, 如果都不是那就是nums[mid]和target的值相等,返回mid的值就是所需要的数组下标,往后start和end的更新值将会进入下一轮循环,直到找到或者说找不到跳出循环。
边界处理:如果一开始target就比数组nums的最大值大或者比最小值小那么就没有必要进入循环进行比较了。
代码及分析与优化
之前写的代码:
function search(nums: number[], target: number): number {
let start =0;//起始位置
let end =nums.length-1;//终点位置
if(target>nums[end]||target<nums[start]){//特殊情况判断
return -1;
}
while(start<=end ){//主要逻辑
let mid=start+ Math.floor((end-start)/2);//解法多样,需要注意别溢出
if(nums[mid]<target){
start=mid+1;
}else if(nums[mid]>target){
end=mid-1;
}
else if(nums[mid]===target){
return mid;
}
}
return -1;
};
这一版主要是最初按照自己的逻辑写的,稍微需要留意和判断的地方就是,为什么nums[mid]<target的时候将start赋值为mid +1 而不是mid, 其实是一方面因为num[mid]已经确定不是target了,我们为了尽量缩小范围直接将原本的mid值进行剔除,另一方面反向思考:如果说将start的值赋值为mid,那么可能会存在死循环的情况,比如如果进入某一个循环需要处理的就只剩数组中的两数的时候。(理解之后,可以直接简单这么记:跟target比较过的mid的值是不会进入下一次循环的)。
我出过错的地方:
mid的取值问题
我之前是这么写的:let mid=(end + start)/2;。这样写可能会存在数组越界的情况。end+start的值有可能超过javascript能表示的数字类型的最大值。如果超过的话,JavaScript 会按照 IEEE 754 标准进行舍入,导致结果不准确。
解决办法:
使用 start + Math.floor((end - start) / 2) 可以避免精度问题。
因为 start + Math.floor((end - start) / 2) 不会超过最大值, 因为 end - start 一定小于等于原来的数组长度,这样就不会导致精度问题了
代码虽然能够跑通,但是不够精简,下面是优化之后的代码:
function search(nums: number[], target: number): number {
let start = 0;
let end = nums.length - 1;
while (start < end) {
let mid = start + Math.floor((end - start) / 2);
if (nums[mid] < target) {
start = mid + 1;
} else if (nums[mid] > target) {
end = mid;
} else {
return mid;
}
}
return nums[start] === target ? start : -1;
}
优化点:
使用start<end代替start<=end,这样可以保证每次循环都能够缩小查找范围,节省查找时间。
将边界处理融入一个nums[start] === target ? start : -1中。我们仔细思考在循环条件是start<end时我们的边界处理逻辑和循环跳出的时候。不会进入循环的情况就只有单个数字的数组,如果nums[start]也就是nums[0],刚好start就是所求的值,如果不是那么就是返回-1。如果进入循环没能找到符合条件的值,那么nums[start]和target也肯定不会相等,直接返回-1即可。