二分查找笔记
不会就学,学了才会。
参考资料:
https://www.jianshu.com/p/0f823fbd4d20
基本二分模版以及注意事项
int binarySearch(int a[], int n, int target) {
int low = 0, high = n, mid;
while(low <= high) {
mid = low + ((high - low) >> 1);
if(a[mid] == target) {
return mid;
}
else if(a[mid] > target) {
high = mid - 1;
}
else if(a[mid] < target) {
low = mid + 1;
}
}
return -1;
}
注意事项:
1、循环的判定条件是 low <= high
2、为了防止数据溢出,使用 low + ((hight - low) >> 1)
而不是
(hight + low) / 2
。
3、当 a[mid] != target
时,根据情况 high = mid - 1
或者 low = mid + 1
各种二分变形:
1、查找目标值区域的左边界/查找与目标值相等的第一个位置/查找第一个不小于目标值数的位置(要求目标值存在)
int binarySearch(int a[], int n, int target) {
int low = 0, high = n, mid;
while(low <= high) {
mid = low + ((high - low) >> 1);
if(target <= a[mid]) {
high = mid - 1;
}
else if(target > a[mid]) {
low = mid + 1;
}
}
if(low < a.length && a[low] == target)
return low;
else
return -1;
}
样例:
A = [1,3,3,5, 7 ,7,7,7,8,14,14]
target = 7
return 4
target = 6
return -1
2、查找目标值区域的右边界/查找与目标值相等的最后一个位置/查找最后一个不大于目标值数的位置(要求目标值存在)
int binarySearch(int a[], int n, int target) {
int low = 0, high = n, mid;
while(low <= high) {
mid = low + ((high - low) >> 1);
if(target >= a[mid]) {
low = mid + 1;
}
else if(target < a[mid]) {
high = mid - 1;
}
}
if(high >= 0 && a[high] == target)
return high;
else
return -1;
}
样例:
A = [1,3,3,5, 7 ,7,7,7,8,14,14]
target = 7
return 7
该函数可以变形为 查找第一个大于目标值的数/查找比目标值大但是最接近目标值的数
。因为我们已经找到了最后的一个目标值,只要 a[high+1] 存在
,那么 a[high+1] 就是比目标值大的第一个值。
3、查找最后一个小于目标值的数/查找比目标值小但是最接近目标值的数
int binarySearch(int a[], int n, int target) {
int low = 0, high = n, mid;
while(low <= high) {
mid = low + ((high - low) >> 1);
if(target <= a[mid]) {
high = mid - 1;
}
else if(target > a[mid]) {
low = mid + 1;
}
}
return high >= 0 ? high : -1;
}
可以由1变形而来,1可以找到目标值的下(左边界),那么只要low-1
就可以找到最后一个小于目标值的数,但其实 high恰好等于low-1
,所以我们只要判断一下high是否越界,然后 return high
即可
样例:
A = [1,3,3, 5 ,7,7,7,7,8,14,14]
target = 7
return 3
4、查找第一个大于目标值的数/查找比目标值大但是最接近目标值的数
由2变形而来,2恰好找到的是目标值的上(右)边界值,那么只要往右进一位(high + 1)就能找到第一个大于目标值的数。循环完之后hight + 1 恰好等于 low,所以判断 low 是否越界然后返回 low 值即可。
int binarySearch(int a[], int n, int target) {
int low = 0, high = n, mid;
while(low <= high) {
mid = low + ((high - low) >> 1);
if(target >= a[mid]) {
low = mid + 1;
}
else if(target < a[mid]) {
high = mid - 1;
}
}
return low < a.length ? low : -1;
}
样例:
A = [1,3,3, 5 ,7,7,7,7,8,14,14]
target = 7
return 8
5、旋转数组返回最小元素(不存在重复项)
int binarySearch(int a[], int lena) {
if(lena == 0)
return -1;
int low = 0, high = lena-1, mid;
while(low < high) {
mid = low + ((high - low) >> 1);
if(a[mid] > a[high])
low = mid + 1;
else if(a[mid] < a[high])
high = mid; //注意这里是high == mid 而不是 mid - 1
}
return a[low];
}
样例:
A= [3,4,5,1,2]
return 1
注意:
1、循环判定条件是 low < high
2、函数中是通过判断 a[high] 和 a[mid]
的值来判断 mid 所在的位置的。
如果 a[mid] > a[high]
说明 mid 前半部分是有序的,最小值在 mid 后半部分,所以令 low = mid +
1。
如果 a[mid] < a[high]
说明最小值在前半部分,令 high = mid
。
最终 low 会指向最小值
6、旋转数组返回最小元素(存在重复项)
int binarySearch(int a[], int lena) {
if(lena == 0)
return -1;
int low = 0, high = lena-1, mid;
while(low < high) {
mid = low + ((high - low) >> 1);
if(a[mid] > a[high])
low = mid + 1;
else if(a[mid] < a[high])
high = mid;
else if(a[mid] == a[high])
high--;
}
return a[low];
}
和5的区别在于多判断了一下相等的情况,如果相等则让上(右)边界减一即可。
7、在旋转排序数组中二分搜索(不存在重复项)
方法1:
先用5找到最小值的位置,然后将旋转数组当作偏移,计算出 realmid 的位置 (realmid = (offset + mid) % len)
。然后二分查找目标值。
int binarySearch(int a[], int lena, int target) {
if(lena == 0)
return -1;
int low = 0, high = lena-1, mid;
while(low < high) {
mid = low + ((high - low) >> 1);
if(a[mid] > a[high])
low = mid + 1;
else if(a[mid] < a[high])
high = mid;
}
int offset = low;
low = 0, high = lena - 1;
while(low <= high) {
mid = low + ((high - low) >> 1);
int realmid = (mid + offset) % lena;
if(a[realmid] == target)
return realmid;
else if(a[realmid] < target)
low = mid + 1;
else if(a[realmid] > target)
high = mid - 1;
}
return -1;
}
方法2:
不需要找出分界点,直接根据 high 和 mid 的值判定搜索左侧还是右侧,然后根据 target 值,二分搜索即可。
int binarySearch(int a[], int lena, int target) {
if(lena == 0)
return -1;
int low = 0, high = lena-1, mid;
while(low <= high) {
mid = low + ((high - low) >> 2);
if(a[mid] == target)
return mid;
else if(a[mid] >= a[low]) {
if(target < a[mid] && target >= a[low])
high = mid - 1;
else
low = mid + 1;
}else if(a[mid] <= a[high]) {
if(target > a[mid] && target <= a[high])
low = mid + 1;
else
high = mid - 1;
}
}
return -1;
}
8、在旋转排序数组中二分搜索(存在重复项)
参考7的方法2和6写即可。
int binarySearch(int a[], int lena, int target) {
if(lena == 0)
return -1;
int low = 0, high = lena-1, mid;
while(low <= high) {
mid = low + ((high - low) >> 2);
if(a[mid] == target)
return mid;
else if(a[mid] > a[high]) {
if(target < a[mid] && target >= a[low])
high = mid;
else
low = mid + 1;
}
else if(a[mid] < a[high]) {
if(target > a[mid] && target <= a[high])
low = mid + 1;
else
high = mid;
}
else
high--;
}
return -1;
}