如何写一个正确的二分法代码
程序员文章站
2024-03-20 17:33:22
...
二分原理简单,但是实现起来经常出错,下面一些经验很有用:
- 跨步移动(left=mid+1,right=mid-1)是惯例,跨步移动保证每次搜索范围至少缩小1。如果不跨步,不管left<=right还是left<right都可能死循环,除非能保证提前退出的情况下才可以不跨步移动。
- 跨步移动的前提下,假设搜索范围是闭区间[0,n-1],left退出范围是[0,n],right是[-1,n-1]
- left<=right情况下,如果依靠循环退出,left刚好在right右边一个,left<right退出刚好两个是相等的。选择<还是<=主要是等一下思考一下codes在相等情况下要不要再走一遍。left<=right更常用一些
- 分的时候,重点考虑一下if里面等于的情况!!!
有了跨步移动和left<=right两个惯例,二分不确定性降低很多,只要思考if的时候甩哪半和if里面等于的情况就好,这取决于数据单调性、重复性、是否一定有解、退出范围等各种问题。在这两个惯例的前提下,二分代码简洁而且有深刻含义,具体如下:
变种一:lower_bound和upper_bound (https://blog.csdn.net/taoqick/article/details/23260157)
- lower_bound,重复数据里大于等于target的第一个元素,插入target的第一个恰当位置:
int searchFirstEqualOrLarger(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = left+(right-left)/2; if(arr[mid] >= key) right = mid-1; else if (arr[mid] < key) left = mid+1; } return left; }
- upper_bound,重复数据里大于target的第一个元素,插入target的最后一个恰当位置:
int searchFirstLarger(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = left+(right-left)/2; if(arr[mid] > key) right = mid-1; else if (arr[mid] <= key) left = mid+1; } return left; }
变种二:
- 找出第一个与key相等的元素
int searchFirstEqual(int *arr, int n, int key) { int left = 0, right = n-1; while(left<=right) { int mid = left+(right-left)/2; if(arr[mid] >= key) right = mid - 1; else if(arr[mid] < key) left = mid + 1; } if( left < n && arr[left] == key) return left; return -1; }
- 找出最后一个与key相等的元素
int searchLastEqual(int *arr, int n, int key) { int left = 0, right = n-1; while(left<=right) { int mid = (left+right)/2; if(arr[mid] > key) right = mid - 1; else if(arr[mid] <= key) left = mid + 1; } if( right>=0 && arr[right] == key) return right; return -1; }
变种三:
- 查找最后一个等于或者小于key的元素
int searchLastEqualOrSmaller(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = left+(right-left)/2; if(arr[m] > key) right = m-1; else if (arr[m] <= key) left = m+1; } return right; }
- 查找最后一个小于key的元素
int searchLastSmaller(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = left+(right-left)/2; if(arr[mid] >= key) right = mid-1; else if (arr[mid] < key) left = mid+1; } return right; }
上一篇: BZOJ4590: [Shoi2015]自动刷题机
下一篇: 创建一个最简单的服务器