所有的题型目录在下面的链接
LeetCode相关典型题解合集(两百多道题)
二分查找的三个模板
在这里主要写二分查找的和新模板
注意:对于
int mid = left + (right - left) / 2
可以写成
int mid = left + (right - left) >>1;
为什么不能写成?
int mid = (left + right) / 2
因为left+right会造成最大值溢出
模板1
int left = 0, right = nums.length - 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; }
}
- 模板1是二分查找的最基本的模板
- 向左查找是right=mid-1
- 向右查找是left=mid+1
模板2
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; }
}
- 对于left是赋值为mid+1
- 对于right是赋值为mid
这个模板是最常用的,退出的时候一定时left=right,所以把答案留到退出循环以后在判断,尤其是边界问题
模板3
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;
}
- 向左查找:right = mid
- 向右查找:left = mid
二分查找
69. Sqrt(x) (Easy)
思路:二分查找,初始化的界限就是0到x,然后根据二分查找的方法继续往下做就行
二分查找记好这种写法,记住。这是模板
写法一
int mySqrt(int x) {
int l=0;
int r=x;
int temp=-1;
while(l<=r){
int mid=(l+r)/2;
//因为只保留整数的部分而忽略小数部分,因此要返回的值肯定是小于等于
if((long int)mid*mid<=x){
temp=mid;
l=mid+1;
}else{
r=mid-1;
}
}
return temp;
}
744. Find Smallest Letter Greater Than Target (Easy)
这道题用线性扫描就行,但是用二分查找效率会高点
char nextGreatestLetter(vector<char>& letters, char target) {
int l=0;
int r=letters.size()-1;
if(target>=letters[r]||target<letters[0]){
return letters[0];
}
while(l<r){
int mid=l+((r-l)>>2);
if(letters[mid]>target){
r=mid;
}else{
l=mid+1;
}
}
return letters[l];
}
540. Single Element in a Sorted Array (Medium)
思路:这道题因为限定了logn的时间复杂度,所以用二分查找来做
- 首先确定mid,然后判断是mid左边和mid相等还是mid的右边和mid相等
- 然后判断是往左还是往右。这里面举个例子自己就知道了
- 注意一点,就是判断中出现了mid+1和mid-1,但是不会越界,因为边界(left或者right)是1和2变化的
做这道题时衍生出来一个问题,困扰了我很久。
当left=0,right=2时。mid应该为1,但
int mid=left+((right-left)>>2)=0
详细解释看这个:c++的左移和右移运算符
int singleNonDuplicate(vector<int>& nums) {
int left=0;
int right=nums.size()-1;
while(left<right){
int mid=left+((right-left)/2);
//只算一边是否为偶数就行,另一边自动就知道了
bool isDouble=(mid-left)%2==0;
if(nums[mid]==nums[mid+1]){
if(isDouble){
left=mid+2;
}else{
right=mid-1;
}
}
else if(nums[mid]==nums[mid-1]){
if(isDouble){
right=mid-2;
}else{
left=mid+1;
}
}else{
return nums[mid];
}
}
return nums[left];
}
278. First Bad Version (Easy)
二分法,最后循环结束得到一个left值,第一个错误的肯定在left的左边或者右边
int firstBadVersion(int n) {
int left=1;
int right=n;
while(left<right){
int mid=left+(right-left)/2;
if(isBadVersion(mid)){
right=mid;
}else{
left=mid+1;
}
}
//二分法之后如果这个索引是错误,就要--
if(isBadVersion(left)){
while(isBadVersion(left)){
left--;
}
}else{
while(!isBadVersion(left)){
left++;
}
}
return left+1;
}
153. Find Minimum in Rotated Sorted Array (Medium)
看代码应该知道,我们确定往左还是往右搜索的时候是用mid和最右边的值比较的
首先这道题是一个升序的数组,左边的数小而右边的数大,这道题要找最小值,肯定是往左边找(同理,若改成最大值我们可以往右边找,比较最左边的值)
为什么比较mid和right的值呢?因为目标值右边的情况容易区分
这样子想:比如mid值小于最右边的,比如7123456,3<6,这在升序数组中时百分百肯定的,证明我只有mid的左边才会有问题。比如23451,4<1,按照升序数组这是不可能的,我就能确定最小值在mid的右边
int findMin(vector<int>& nums) {
int left=0;
int right=nums.size()-1;
while(left<right){
int mid=left+(right-left)/2;
while(left<right){
int mid=left+(right-left)/2;
if(nums[mid]>nums[right]){
left=mid+1;
}else{
right=mid;
}
}
}
return nums[left];
}
34. Find First and Last Position of Element in Sorted Array(重点!)
下面两个链接是将二分法的细节的
二分查找的几种写法和区别
每次遇到二分法,都是一看就会,一写就废
这道题用二分法有点绕,当有序数组中出现两个或多个相同的值是,可以用两次二分法找到重复值出现的边界对应的索引下标,思想如下:
利用二分思想先找其左边界,再找其右边界即可,注意找左边界的时候,由右侧逼近;找右边界的时候,由左侧逼近,即可。
当往左找的时候,right=mid,当带上=号时,也要继续right=mid,这样就可以确定左侧边界。
往右找的时候同理。但是因为两次查找的方向不一致,为了保证边界条件,第二次不能使用n-1。
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res(2,-1);
if(nums.empty()){
return res;
}
int left=0;
int right=nums.size()-1;
while(left<right){
int mid=left+(right-left)/2;
if(nums[mid]>=target){
right=mid;
}else{
left=mid+1;
}
}
if(nums[left]!=target){
return res;
}
res[0]=left;
//left和right在上一个循环时值已经发生改变,在第二次时right就不减一了
right=nums.size();
left=0;
while(left<right){
//这个mid的计算有问题,不然会进入死循环
int mid=left+(right-left)/2;
if(nums[mid]<=target){
left=mid+1;
}else{
right=mid;
}
}
res[1]=left-1;
return res;
}
总结
一张图彻底理解二分查找!