最近又重新拾起了一直很凌乱的二分查找,各种版本,有时候总是很难调对,今天就整理了一下。
最基础的二分查找是从非递减序的数组中查找某个元素是否在数组中,如果在的话,随意返回一个位置,如果没有这个元素,返回-1。那么就有了下面这个基础的算法
//查找目标元素在数组中的位置,任意一个都可以,如果没有返回-1 int Binary_search(int *arr, int n, int val) { int l = 0; int r = n - 1; while (l <= r) { int mid = (l + r) >> 1; if (arr[mid] == val) return mid; if (arr[mid] < val) l = mid + 1; else r = mid - 1; } return -1; }
然后下面关于二分查找的变种比较多了,下面主要列出来一些常用的变种
1.查找一个大于等于目标元素的位置,等同于lower_bound()函数
//查找第一个大于等于目标元素的位置, 等同于lower_bound(); int Binary_search_first_greater_or_equal(int *arr, int n, int val) { int l = 0; int r = n - 1; while (l <= r) { int mid = (l + r) >> 1; if (arr[mid] < val) l = mid + 1; else r = mid - 1; } return l; }
这个主要是去掉了等于的时候直接返回,之所以用小于号而不是小于等于是因为如果如果arr[mid]比要找的元素小的话,大于等于他的元素一定在他的右边,而如果是等于他的话,可能在他的左边,大于他自然在左边。
2. 查找一个大于目标元素的位置,等同于upper_bound()
//查找第一个大于目标元素的位置, 等同于upper_bound(); int Binary_search_first_greater(int *arr, int n, int val) { int l = 0; int r = n - 1; while (l <= r) { int mid = (l + r) >> 1; if (arr[mid] <= val) l = mid + 1; else r = mid - 1; } return l; }
因为要返回大于他的,那么当arr[mid] == val的时候,继续往右找。
3.查找目标元素的第一个位置
//查找目标元素的第一个位置,左闭右开区间 int Binary_search_first_pos(int *arr, int n, int val) { int l = 0; int r = n - 1; while (l <= r) { int mid = (l + r) >> 1; if (arr[mid] < val) l = mid + 1; else r = mid - 1; } return (l < n && arr[l] == val) ? l : -1; }
基本上就是查找大于等于元素的程序,只不过在最后加了一个判断,看看是否等于要找到那个值
4.查找目标元素的最后一个位置
int Binary_search_last_positon(int *arr, int n, int val)//n is the size of arr { int l = 0; int r = n - 1; while (l <= r) { int mid = (l + r) >> 1; if (arr[mid] > val) r = mid - 1; else l = mid + 1; } if (l >= n && arr[l - 1] != val) return -1;//若找到最后,并且最后元素的值与目标元素不同,返回-1 return (l - 1 >= 0 && arr[l - 1] == val) ? l - 1 : -1; }
若arr[mid] > val,那么自然一定在左边,如果等于val,有可能在右边,因为我是找最右的,所以,还是要继续往右找
以上所有的返回都是l,也就是做区间端点。