参考
细节详解
二分查找
相关问题
704.二分查找
34.在排序数组中查找元素的第一个和最后一个位置
- 875.爱吃香蕉的珂珂
- 1011 .在 D 天内送达包裹的能力
二分查找-basic
时间复杂度 :O(logn);
要求:排序数组。
1. caution:
循环不变量的定义 :// 在nums[l…r]之中查找target
循环继续条件: l <= r ,因为循环不变量定义了是闭区间,所以要加上等号,使得相等的时候还继续循环,不会漏掉l==r时那个数。
template<typename T>
int binarySearch(T nums[], T target) {
int left = 0;
int right = nums.size() - 1; // 注意
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid - 1; // 注意
}
return -1;
}
二分查找-左边界
1. caution:
循环不变量的定义 :// 在nums[l…r)之中查找target的左边界
循环继续条件: l < r ,因为循环不变量定义了是开区间,所以不用加上等号,使得相等的时候break。
template<typename T>
int binarySearch(T nums[], T target) {
int left = 0;
int right = nums.size() ; // 注意
if (nums.size() == 0 || nums[left]>target) return -1;//排除不在范围内的target
while(left < right) {// 注意
int mid = left + (right - left) / 2;
if(nums[mid] == target)
right = mid; // 缩小右边界
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid; // 因为是开区间,不需要mid-1
}
// target 比所有数都大
if (left == nums.length) return -1;
// 类似之前算法的处理方式
return nums[left] == target ? left : -1;
}
二分查找-右边界
1. caution:
循环不变量的定义 :// 在nums[l…r)之中查找右边界
循环继续条件: l < r ,因为循环不变量定义了是开区间,所以不用加上等号,使得相等的时候break。
因为r=n,l=mid+1,所以最后的时候lrmid+1,所以return r-1。
template<typename T>
int binarySearch(T nums[], T target) {
int left = 0;
int right = nums.size(); // 注意
if (nums.size() == 0 || nums[right-1]<target) return -1;//排除不在范围内的target
while(left < right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target)
left = mid + 1; //缩小左边界
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid; // 注意
}
//
if (left == 0) return -1;
// 类似之前算法的处理方式
return nums[right-1 ] == target ? (right-1) : -1;//这里注意是right-1
}
逻辑统一
来梳理一下这些细节差异的因果逻辑:
第一个,最基本的二分查找算法:
因为我们初始化 right = nums.length - 1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid+1 和 right = mid-1
因为我们只需找到一个 target 的索引即可
所以当 nums[mid] == target 时可以立即返回
第二个,寻找左侧边界的二分查找:
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要立即返回
而要收紧右侧边界以锁定左侧边界
第三个,寻找右侧边界的二分查找:
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要立即返回
而要收紧左侧边界以锁定右侧边界
又因为收紧左侧边界时必须 left = mid + 1
所以最后无论返回 left 还是 right,必须减一