2020/7/18
总结
总结:二分查找算法以及
leetcode
体型
算法总结
算法思想
减而治之,即将大规模问题转换为小规模问题。减而治之就是分而治之的特例,将大问题划分为若干个子问题以后,最终答案只在其中一个子问题里。
不断排除不存在结果的区间或者说不断锁定目标区间
生活中的二分查找
- 猜数字大小
- 程序员定位bug,经常在一些逻辑关键点做一些变量的打印输出,以逐渐缩小查找范围,最终定位出问题的代码行(或者块)
二分查找模板(在循环体中排除一定不存在目标元素的区间)
基本思想
从考虑哪些元素一定不是目标元素开始考虑,根据看到的mid位置的元素,排除掉一定不可能存在目标元素的区间,而下一轮可能存在目标的子区间里继续查找
具体做法
int left=;
int right = ;
while(left<right){
//这里取mid的方式可能需要进行调整,当left=mid时需要进行向上进行调整使得mid=(left+mid+1)>>>1
mid=(left+right)>>>1;
if(check(mid)){
right = mid+1;
}else{
left=mid
}
}
-
先把循环可以继续写成
while(left<right)
,表示循环推出的时候,[left,right]
这个区间里只有一个元素,这个元素有可能时目标元素; -
写
if
和else
语句的时候,思考当nums[mid]
满足什么性质的时候,nums[mid]
不是解,进而判断mid
的左边有没有可能是解,mid的右边有没有可能是解(本质上就是不断缩小区间,确定最终的解可能在哪个区间)- 做题的经验告诉我们,思考什么么时候不是解是比较好想,如果一个数要满足多个条件,只要堆其中一个条件取反,就可以达到缩小搜索范围的目的`
此时mid作为待查找数组的分界,就把它分为两个区间:一个部分可能存在目标元素,一个部分一定不存在目标元素
-
当选取的left=mid;时候需要调整middle的选取,向上调整 mid=(left+righ+1)>>>1
-
、退出循环的时候,一定有 left == right 成立。有些时候可以直接返回 left (或者 right,由于它们相等,后面都省略括弧)或者与 left 相关的数值,有些时候还须要再做一次判断,判断 left 与 right 是否是我们需要查找的元素,这一步叫「后处理」。
题型
在数组中查找符合条件的元素的下标
一般而言这个数组是有序的,也可能是半有序的(旋转有序数组)
-
简单的二分查找:最简单的二分查找,套模板即可
-
搜索插入位置:搜索排序数组中,元素的插入位置,就是在数组中搜索第一个大于插入元素的位置,当target>nums[middle]时一定不存在结果 在[left,middle]一定不存在结
-
在排序数组中查找元素的第一个和最后一个位置:
关键是减治法,如何不断判断缩减空间
-
搜索旋转排序数组中是否存在某个值:当
nums[midddle]>nums[left]
的时候可以发现在[left,midle]
中数组是增序的,因此若此时nums[left]<=target<=nums[rightt]
则targe一定落在[left,middle]
的区间,否则落在另外的一个区间;同理若nums[middle]<nums[left]
,则在[middle,right]
数组是递增的,若此时``nums[middle]<=target<=nums[right],则可以确定结果在[middle,right]反之 -
搜索旋转排序数组中是否存在某个值(数组中存在重复元素):
在上体的基础上关键是如何处理重复元素,若重复元素等于待查找的值则直接返回,否则排除
-
旋转排序数组中的最小值(数组中不含重复元素):若中间数比右边数大
nums[middle]>nums[right]
,则[middle,right]
区间一定不是有序的,在[left,middle]区间数字有序,因此最小值一定不会再[left,midddle]
区间,可能会在[middle+1,right]
反之 -
旋转排序数组中的最小值(数组中含重复元素):
关键重复的元素如何解决
在一个有范围的区间里搜索一个整数
定位一个有范围的整数,这件事情也叫「二分答案」或者叫「二分结果」。如果题目要求的是一个整数,这个整数有明确的范围,可以考虑使用二分查找。