文章目录
思想: 有序吗?找值吗?都满足=》二分 两个条件缺一不可
如果无序但是想不到别的方法了,非要二分?可以 =》 手动排序
两个模板
1 while(left<=right)
在循环体内部查找元素
循环体内部的判断通常有三个
1、 nums[mid] = = target
2、nums[mid] < target
3 、nums[mid] > target
当循环退出时,left一定在right的右边,需要根据情况判断最终的返回值
当正常的顺序数组时很适合,变形之后的比如旋转数组就不适合了
2 while(left<right)
循环中基本两次判断
不在循环中返回,退出循环再返回。
优点是当退出循环时,一定有left==right ,不用纠结返回什么,可以少考虑很多细节
但是此法有一个问题:
就是当如图:
此时 int mid=left+(right-left)/2;得到的mid就是left
但是如果check(mid)返回false则会执行left=mid(只要有left=mid就要注意)。从此陷入死循环
解决办法:
int mid=left+(right-left+1)/2
向上取整即可避免此情况。
思路爆炸的一题
在排序数组中查找元素的第一个和最后一个位置
思路:不要怕,题目怎么要求就怎么做,要找左右边界,那就先找左边界,再找右边界。
一 、找左边界
因为是顺序数组,所以无论怎样,left<=right
因为left<=right 所以while中三次判断,严格执行就完事了
1 、nums[mid] == target 我要找左边界,所以 right = mid - 1;
2、nums[mid] < target left=mid+1
3 、nums[mid] > target right = mid - 1
循环内没有返回 那就在循环外返回 ,返回谁?
left right -1 就这三个没跑,试一下就知道left还是right,此时是left
什么时候返回left?当然是 nums[left] = = target的时候,否则就是-1
但是报错数组下标越界,只可能是nums[left] = = target这里越界啊,left为啥会越界? 很容易想到当target很大时啊,试一下果然是的,那就判断呗,当
left != nums.length/left<nums.length都可以。
二、找右边界
照着左边界写就完事了。
##########################################
山脉数组的峰顶索引
思路:
由于是找最大值的下标,且最大值左右两边都有数,所以采用nums[mid]和nums[mid+1]比较的方式,如果右边比我大,就继续往右边找,否则往左边找。
关键:
2
由1的思想进一步解决山脉数组中查找目标值
思路:先找到峰值索引,由峰值索引将数组分为左边升序,右边降序。
寻找峰值
多个峰值中找出其中一个。多看下官方题解理解一下。
3 旋转数组 有别于峰值数组
详细题解旋转数组
注意:不能用题1的解法找到峰值点然后+1得到最小值点。
因为峰值点可能就是第一个元素,此时是找不到峰值点的,返回的一定是最后一个元素,+1之后数组下标越界
思路:
1 左值 < 中值, 中值 < 右值 :没有旋转,最小值在最左边,可以收缩右边界
右
中
左
2 左值 > 中值, 中值 < 右值 :有旋转,最小值在左半边,可以收缩右边界
左
右
中
3 左值 < 中值, 中值 > 右值 :有旋转,最小值在右半边,可以收缩左边界
中
左
右
4 左值 > 中值, 中值 > 右值 :单调递减,不可能出现
左
中
右
分析前面三种可能的情况,会发现情况1、2是一类,情况3是另一类。
如果中值 < 右值,则最小值在左半边,可以收缩右边界。
如果中值 > 右值,则最小值在右半边,可以收缩左边界。
通过比较中值与右值,可以确定最小值的位置范围,从而决定边界收缩的方向。
而情况1与情况3都是左值 < 中值,但是最小值位置范围却不同,这说明,如果比较左值与中值,不能确定最小值的位置范围。
所以需要通过比较中值与右值来确定最小值的位置范围,进而确定边界收缩的方向。
4
搜索旋转排序数组
思路:先找出最小值的位置以此分割出左右两个有序数组,然后由target和 nums[0] 的大小关系判断出target在那边,再在相应的这边做二分
5
搜索旋转排序数组2
思路:相较于第4题,数据可以重复,那就当重复时向右移动,一直移到最后一个值即可
//可重复的 就多了这一步而已
if(nums[left]==nums[mid]){
left++;
continue;
}
#############################################3
6
面试题 08.03. 魔术索引
就用一次遍历算了,二分法太难理解了,想到就更不可能了。
7
搜索插入位置
思路:由于是顺序数组,所以 left<=right ,偏向左边所以最后返回left
8
x 的平方根
思路:二分没问题,right=x/2没问题。相乘就一定要考虑溢出。向下取整(偏向左边),所以返回right
9
0~n-1中缺失的数字
思路: 当A[n]=n时,下标一定在右边,否则目标可能就是n,也可能在左边。
class Solution {
//最后一定是 left指向右子数组的首位元素,right指向左子数组的末位元素
public int missingNumber(int[] nums) {
// if(nums.length==1) return nums[0]==0? 1:0;
int left=0,right=nums.length-1,mid;
while (left<=right){
mid=(left+right)/2;
if(nums[mid]==mid) left=mid+1;
else right=mid-1;
}
return left;
}
}
但是下面这样不行:
while (left<right){
mid=(left+right)/2;
if(nums[mid]==mid) left=mid+1;
else right=mid;//按理说应该是=mid而不是mid-1
//因为mid可能就是第一个不匹配的位置
}
###################################
二维数组找值 官方题解 没啥好说的 顺序数组没重复找值==二分
row= length//n
col=length%n
二维数组找值2
自己:每一行二分 O(nlogm)
官方法四很好
有序矩阵中第K小的元素,和二维数组找值类似
官方法三,题解还没完全搞懂。
##############################################