对二分查找的几点思考
Posted CBer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对二分查找的几点思考相关的知识,希望对你有一定的参考价值。
二分查找是一种常见的类型题,最简单的就是从一个排序数组里面找到某个给定元素的位置。根据自己笔试和面试的经验,面试里面出现过几次,题目都不是很难;笔试的话一般是在不用二分查找之前,会通过一些测试用例,另一些会超时,这个时候使用二分查找降一下时间复杂度可能就通过了。总之还是一种比较重要的类型题。
先看一个简单的例子
Given a sorted array and a target value, return the index if the
target is found. If not, return the index where it would be if it
were inserted in order.
You may assume no duplicates in the array.
Example 1:
Input: [1,3,5,6], 5
Output: 2
Example 2:
Input: [1,3,5,6], 2
Output: 1
Example 3:
Input: [1,3,5,6], 7
Output: 4
Example 4:
Input: [1,3,5,6], 0
0 :
这个题是LeetCode上的easy难度,大概的目标就是从排序数组中找到这个元素应该放在哪个位置。写出来的能通过所有测试用例的代码,基本上是这样的:
class Solution {
public int searchInsert(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return 0;
}
if (nums[nums.length - 1] < target) {
return nums.length;
} else if (nums[0] > target) {
return 0;
}
int startIndex = 0, endIndex = nums.length - 1;
while (startIndex < endIndex) {
int middleIndex = startIndex + (endIndex - startIndex) / 2;
if (nums[middleIndex] == target) {
return middleIndex;
} else if (nums[middleIndex] < target) {
startIndex = middleIndex + 1;
} else {
endIndex = middleIndex;
}
}
return startIndex;
}
}
这个题的思路很简单,前面几行先参数检查,然后把特殊的情况先return掉,接下来进入正式的二分查找环节。
二分查找环节有几个关键点,接下来依次说明
1. **确定好搜索区间**
我自己习惯的写法是 [startIndex, endIndex], 两侧都作为搜索的闭区间
2. **求middleIndex的正确写法**
一种常见的写法是这样的
int middleIndex = (startIndex + endIndex) / 2;
但是这样写有问题,因为 startIndex 和 endIndex 都是正整数,它们直接相加再除以2,可能会出现相加的时候就**溢出**了;
所以上面的示例代码写法是
int middleIndex = startIndex + (endIndex - startIndex) / 2;
这样不会出现溢出;
另外除2也可以用右移1位(>>1)来代替
3. **进行判断以后的正确操作**
判断指的就是 nums[middleIndex] 和 value 的大小对比,这个要根据具体的逻辑来,但是要按照自己的习惯(第1点),startIndex和middleIndex到底应该如何更新(我自己的习惯是startIndex和middleIndex总是代表搜索两段的闭区间端点,按照这个原则去更新)
4. **while的退出条件**
二分查找最容易写错的地方是while的退出条件,写错了容易出现死循环,有的时候是小于,有的时候是小于等于,有的时候是小于右侧减1;
有个基本的判断方法是:将startIndex和endIndex指向间隔为1的位置(例如 startIndex = i-1, endIndex = i+1),然后类似于单步调试,按照代码的逻辑走一遍while循环;一般走一遍之后会退出while循环或者startIndex和endIndex相邻,然后再走一遍看看会不会有死循环
5. **出while循环以后最后的return值**
具体情况具体分析,有的时候如果能确保在while中一定会返回,也可以return -1这种特殊值
6. **递归写法**
递归写法也是先根据第1点确定一下范围,这个示例由于比较简单,递归写法和非递归写法都比较容易;有的题目稍微难一点,可以先用递归写出来,然后看需要决定是否转成非递归。
这道题的递归写法可以这么写,用一个helper函数作为二分查找的递归函数:
class Solution {
public int searchInsert(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return 0;
}
if (nums[nums.length - 1] < target) {
return nums.length;
} else if (nums[0] > target) {
return 0;
}
return searchInsertHelper(nums, target, 0, nums.length - 1);
}
private int searchInsertHelper(int[] nums, int target, int startIndex, int endIndex) {
if (startIndex == endIndex) {
return startIndex;
}
int middleIndex = startIndex + (endIndex - startIndex) / 2;
if (nums[middleIndex] == target) {
return middleIndex;
}
return (nums[middleIndex] < target ?
searchInsertHelper(nums, target, middleIndex + 1, endIndex) :
searchInsertHelper(nums, target, startIndex, middleIndex));
}
}
以上是关于对二分查找的几点思考的主要内容,如果未能解决你的问题,请参考以下文章