版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
二分查找的前提:
查找的序列需要是有序的,若是无序的,则需要先给序列排序,然后再进行二分查找。
二分查找成功的三个步骤:
- 1)预处理:如果序列未排序,则先进行排序
- 2)二分查找:使用循环或递归将中间值元素与目标元素进行比较,将区间划分为两个子区间,然后再符合条件的其中一个子区间内进行寻找,直至循环或递归结束。
- 3)后处理:在循环或递归完成后,需要对剩余区间的元素中确定符合条件的元素
模板1:
int binarySearch(vector<int>& nums, int target)
{
if(nums.size() == 0) return -1;
int left = 0, right = nums.size() - 1;
while(left <= right)
{
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] == target){ return mid; }
else if(nums[mid] < target) { left = mid + 1; }
else { right = mid - 1; }
}
// End Condition: left > right即left=right+1
return -1;
}
模板1的关键属性:
- 1)模板1是二分查找最基本和最基础的形式
- 2)查找元素可以不与元素两侧进行比较下确定,即向左查找为right=mid-1,向右查找为left=mid+1
- 3)不需要进行后处理,因为在循环结束后,区间的元素数量为0
区别语法:
- 1)二分区间为[0,size-1]
- 2)向左查找right=mid-1;向右查找left=mid+1
- 3)循环结束条件是:left>right即left=right+1
模板2:
int binarySearch(vector<int>& nums, int target)
{
if(nums.size() == 0)
return -1;
int left = 0, right = nums.size();
while(left < right)
{
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] == target){ return mid; }
else if(nums[mid] < target) { left = mid + 1; }
else { right = mid; }
}
// Post-processing:
// End Condition: left == right
if(left != nums.size() && nums[left] == target) return left;
return -1;
}
模板2的关键属性:
- 1)模板2是二分查找的的高级方法,它用于查找需要访问数组中
当前索引
及其直接右邻居索引
的元素或条件(mid为中间元素,我们不仅需要判断mid指向的元素是否满足条件,我们还需要判断mid的右邻居元素是否满足条件:若mid指向的元素和右邻居元素mid+1满足条件时,我们只需要判断mid左边的元素是否满足条件就好了,所以下一次查找子区间的右边界right就是mid所代表的元素了,即right=mid;若mid指向的元素不满足条件时,那我们还需要判断右邻居元素mid+1是否满足条件,则我们向右查找left=mid+1即left为mid的右邻居元素)- 2)查找的区间范围至少两个元素
- 3)需要进行后处理,循环结束的条件是left=right即左右边界指向同一个元素,我们需要判断这个元素是否符合题目规定
区别语法:
- 1)二分区间为[0,size)
- 2)向左查找:right=mid,向右查找:left=mid+1
- 3)循环结束的条件是:left=right
模板3:
int binarySearch(vector<int>& nums, int target){
if (nums.size() == 0)
return -1;
int left = 0, right = nums.size() - 1;
while (left + 1 < right)
{
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if (nums[mid] == target) {return mid;}
else if (nums[mid] < target) {left = mid;}
else {right = mid;}
}
// Post-processing:
// End Condition: left + 1 == right
if(nums[left] == target) return left;
if(nums[right] == target) return right;
return -1;
}
模板3的关键属性:
- 1)模板3是二分查找的另一种独特方式,它用于查找需要访问当前索引及其在数组中的直接左右邻居索引的元素或条件(mid为中间元素,我们不仅需要判断mid是否满足条件而且还需要判断mid的左右邻居是否满足条件:若mid满足条件,我们还需要判断mid的左邻居元素是否满足条件,即right=mid;若mid不满足条件,则我们还需要判断mid的右邻居元素是否满足条件,即left=mid)
- 2)查找的区间中至少含有三个元素
- 3)需要进行后处理,循环结束的条件是left+1=right,我们需要判断left和right指向的元素是否为目标值
区分语法:
- 1)二分区间为[0,size-1]
- 2)向左查找:right=mid,向右查找:left=mid
- 3)循环结束的条件是:left+1=right
二分法的练习题:
题目 | 题解 | 难度 |
---|---|---|
4. 寻找两个有序数组的中位数 | C++ | hard |
33. 搜索旋转排序数组 | C++ | medium |
34. 在排序数组中查找元素的第一个和最后一个位置 | C++ | medium |
50. Pow(x, n) | C++ | mdium |
69. x 的平方根 | C++ | easy |
153. 寻找旋转排序数组中的最小值 | C++ | medium |
154. 寻找旋转排序数组中的最小值 II | C++ | hard |
162. 寻找峰值 | C++ | medium |
278. 第一个错误的版本 | C++ | easy |
287. 寻找重复数 | C++ | medium |
209. 长度最小的子数组 | C++ | medium |
349. 两个数组的交集 | C++ | easy |
350. 两个数组的交集 II | C++ | easy |
367. 有效的完全平方数 | C++ | easy |
374. 猜数字大小 | C++ | easy |
410. 分割数组的最大值 | C++ | hard |
454. 四数相加 II | C++ | medium |
658. 找到 K 个最接近的元素 | C++ | medium |
704. 二分查找 | C++ | easy |
719. 找出第 k 小的距离对 | C++ | hard |