二分查找(总结)
来源:互联网 发布:尚品宅配圆方软件下载 编辑:程序博客网 时间:2024/06/09 20:27
二分查找是基本算法,但是据统计只有10%的程序员可以写出没有bug的代码。出错的原因主要集中在对边界值得选择和判定条件上,一不小心就可能导致超出边界或者死循环的情况。这里作如下总结:
标准版二分查找:
在有序(非降序)数组中查找一个target值,数组中元素没有重复,找到target则返回该元素对应的index,找不到则返回-1。
注意1:如果是start < end,那么当target等于num[num.length-1]时,会找不到该值。 注意2:因为num[mid] > target, 所以如果有num[index] == target, index一定小于mid,能不能写成end = mid呢?举例来说:num = {1, 2, 5, 7, 9}; 如果写成end = mid,当循环到start = 0, end = 0时(即num[start] = 1, num[end] = 1时),mid将永远等于0,此时end也将永远等于0,陷入死循环。也就是说寻找target = -2时,程序将死循环。
注意3:因为num[mid] < target, 所以如果有num[index] == target, index一定大于mid,能不能写成start = mid呢?举例来说:num = {1, 2, 5, 7, 9}; 如果写成start = mid,当循环到start = 3, end = 4时(即num[start] = 7, num[end] = 9时),mid将永远等于3,此时start也将永远等于3,陷入死循环。也就是说寻找target = 9时,程序将死循环。
public int binarySearchStandard(int[] num, int target){ int start = 0; int end = num.length - 1; while(start <= end){ //注意1 int mid = start + ((end - start) >> 1); if(num[mid] == target) return mid; else if(num[mid] > target){ end = mid - 1; //注意2 } else{ start = mid + 1; //注意3 } } return -1;}
二分查找变形一:
在有序(非降序)数组中查找一个target值,数组中元素可能有重复,找到target在数组中对应的index的最小值,找不到则返回-1。
注意1:为了对左右相邻的两个数进行对比,循环结束的时候,start和end应该相隔1。
注意2:a). 当num[mid] == target时,此时并不能直接返回mid,因为有可能num[mid-1]或者num[mid-2]…也等于target,所以此时应该将end = mid,然后继续循环。b). 当num[mid] > target时,如果有num[index] == target,那么index一定小于mid,本来按此道理应该令end = mid – 1,但是为什么此处可以end = mid呢?回头去看上一题(标准版)的注意2,只有当start = end时,才会出现死循环的情况。这里因为while循环的限制条件使得start和end永远不可能相等,所以end = mid也是正确的。由此,我们可将a) 和 b) 两种情况合并。
注意3:这里与上题一致,不用有变化。但是在这里写成start = mid也是成立的,为啥呢?回头看上题的注意3,同样的例子num = {1, 2, 5, 7, 9}; 在上题中,如果写成start = mid,当循环到start = 3, end = 4时(即num[start] = 7, num[end] = 9时),mid将永远等于3,此时start也将永远等于3,陷入死循环。但在此题中由于while中的限制条件的变化,避免了这种死循环的发生。
注意4:此处为什么要分别检验num[start]和num[end]是否等于target呢?因为这两个值都有可能等于target,取决于中间二分时start和end的更新过程。这里随便用几个简单的例子即可验证。如果循环结束时num[start] == num[end] == target,根据题意应该返回start,所以我们先验证start。如果两个值都不是target,则target不存在,返回-1。
public int binarySearchMinimumIndex(int[] num, int target){ int start = 0; int end = num.length - 1; while(start + 1 < end){ //注意1 int mid = start + ((end - start) >> 1); if(num[mid] >= target){ //注意2 end = mid; } else{ start = mid + 1; //注意3 } } if(num[start] == target) //注意4 return start; else if(num[end] == target) return end; else return -1;}
二分查找变形二:
在有序(非降序)数组中查找一个target值,数组中元素可能有重复,找到target在数组中对应的index的最大值,找不到则返回-1。
道理与上一题相同,只要在注意2和注意3处与上题反过来就行了,原理不重复了,拿不准的时候可以用几个简单的test case测试一下。
public int binarySearchMaximumIndex(int[] num, int target){ int start = 0; int end = num.length - 1; while(start + 1 < end){ //注意1 int mid = start + ((end - start) >> 1); if(num[mid] <= target){ //注意2 start = mid; } else{ end = mid - 1; //注意3 } } if(num[end] == target) return end; else if(num[start] == target) //注意4 return start; else return -1;}
二分查找变形三:
在有序(非降序)数组中查找一个可能的”最大”index,使得num[index] < target,数组中元素可能有重复,找不到则返回-1。
注意1:我们可以对此步分开考虑,当num[mid] == target时,因为我们要求num[index] < target,所以index必须在mid左边。当num[mid] > target时,target肯定在num[mid]的左边,所以index肯定也要在mid的左边。那么再多想一步,假如num[mid] >= target时,我们进行了end = mid的操作,会产生什么后果呢?经我的有限测试,好像并没有什么问题,因为while中的限制条件导致了每当start + 1 == end的时候,循环就结束了,所以标准版二分查找中出现的死循环的情况并不存在。
注意2:当num[mid] < target时,为什么此处用start = mid而不是start = mid + 1?因为有可能num[mid+1] == target,此时如果start = mid + 1, 则num[start] == target,明显错过了正解。
public int binarySearchClosestFromLeft(int[] num, int target){ int start = 0; int end = num.length - 1; while(start + 1 < end){ int mid = start + ((end - start) >> 1); if(num[mid] >= target){ //注意1 end = mid - 1; } else{ //注意2 start = mid; } } if(num[end] < target){ return end; } else if(num[start] < target){ return start; } else{ return -1; }}
二分查找变形四:
在有序(非降序)数组中查找一个可能的”最小”index,使得num[index] > target,数组中元素可能有重复,找不到则返回-1。
道理与上题完全相同,稍作调整即可。
注意1:此处同上题注意1原理相同,写成start = mid也应该没有问题。
public int binarySearchClosestFromRight(int[] num, int target){ int start = 0; int end = num.length - 1; while(start + 1 < end){ int mid = start + ((end - start) >> 1); if(num[mid] <= target){ start = mid + 1; //注意1 } else{ end = mid; } } if(num[start] > target){ return start; } else if(num[end] > target){ return end; } else{ return -1; }}
综上所述,总结规律:
1. 当数组中没有重复元素时:while循环判定条件是(start <= end),每次start更新为mid + 1,end更新为mid – 1。
2. 当数组中含有重复元素时或者要找的值是target的相邻数时,判定条件是(start + 1 < end),当num[mid] == target时,并不返回mid,而是根据情况跟新start或者end。每次start更新为mid,end也更新为mid即可。
原网址链接:
https://simpleandstupid.com/2014/12/23/%E5%85%B3%E4%BA%8E%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E7%9A%84%E6%80%BB%E7%BB%93/
- 二分查找(总结)
- 查找(二):二分查找----<学习总结>
- 【专题总结】二分查找(持续更新)
- 二分查找总结
- 二分查找的总结
- 【二分查找】学习总结
- 二分查找分类总结
- 二分查找小小总结
- 二分查找总结
- 二分查找总结
- 二分查找总结
- 二分查找自我总结
- 二分查找分类总结
- LeetCode二分查找总结
- 二分查找总结
- 二分查找总结
- 二分查找总结
- binarySearch二分查找总结
- Freemark静态化怎么用?
- 顺序表的查找
- 晶晶实验六自己动手构造CR块
- "Yes, yes! Guiseppe! Guiseppe!"
- Perceptual Losses for Real-Time Style Transfer and Super-Resolution----论文笔记
- 二分查找(总结)
- PAT
- 1128. N Queens Puzzle (20)
- visual studio(vs)初始化
- [unity3d]保存文件到本地and加载本地文件
- 给phpstorm 设置代码片段 快捷方式
- HDU 1690---Bus System(Floyd模板题+合理定义INF)
- maven项目不能添加到tomcat容器中
- windwos下编写php扩展的一些注意事项