常见的二分查找变形问题(以数据是从小到大排列为前提且集合中存在重复的数据为例):
1、查找第一个值等于给定值的元素。
(1)、思路:
a[mid]跟要查找的value的大小关系有三种情况:大于、小于、等于。
对于a[mid]>value的情况,需要更新high= mid-1;
对于a[mid]<value的情况,需要更新low=mid+1。
当a[mid]=value的时候, 如果查找的是任意一个值等于给定值的元素,当a[mid]等于要查找的值时, a[mid]就是我们要找的元素。如果求解的是第一个值等于给定值的元素,当a[mid]等于要查找的值时,我们就需要确认一下这个a[mid]是不是第一个等于给定值的元素。如果mid等于0,那这个元素已经是数组的第一个元素,那它肯定是我们要找的;如果mid不等于0,但a[mid]的前一个元素a[mid-1]不等于value,那也说明a[mid]就是我们要找的第一个等于给定值的元素。如果经过检查之后发现a[mid]前面的一个元素a[mid-1]也等于value,那说明此时的a[mid]肯定不是要查找的第一个等于给定值的元素。那就更新high=mid-1,因为要找的元素肯定出现在[low, mid-1]之间。
(2)、源码:
public int bsearch(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else if (a[mid] < value) {
low = mid + 1;
} else {
if ((mid == 0) || (a[mid - 1] != value)) return mid;
else high = mid - 1;
}
}
return -1;
}
2、查找最后一个值等于给定值的元素。
(1)、思路:
a[mid]跟要查找的value的大小关系有三种情况:大于、小于、等于。
对于a[mid]>value的情况,需要更新high= mid-1;
对于a[mid]<value的情况,需要更新low=mid+1。
当a[mid]=value的时候, 如果查找的是任意一个值等于给定值的元素,当a[mid]等于要查找的值时, a[mid]就是我们要找的元素。如果求解的是最后一个值等于给定值的元素,当a[mid]等于要查找的值时,我们就需要确认一下这个a[mid]是不是最后一个等于给定值的元素。如果mid等于a.length - 1,那这个元素已经是数组的最后一个元素,那它肯定是我们要找的;如果mid不等于0,但a[mid]的后一个元素a[mid+1]不等于value,那也说明a[mid]就是我们要找的最后一个等于给定值的元素。如果经过检查之后发现a[mid]后面的一个元素a[mid+1]也等于value,那说明此时的a[mid]肯定不是要查找的最后一个值等于给定值的元素。那就更新low=mid+1,因为要找的元素肯定出现在[mid+1, high]之间。
(2)、源码:
public int bsearch(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else if (a[mid] < value) {
low = mid + 1;
} else {
if ((mid == n - 1) || (a[mid + 1] != value)) return mid;
else low = mid + 1;
}
}
return -1;
}
3、查找第一个大于等于给定值的元素。
(1)、思路:
如果a[mid]小于要查找的值value,那要查找的值肯定在[mid+1, high]之间,所以,我们更新low=mid+1。
对于a[mid]大于等于给定值value的情况,我们要先看下这个a[mid]是不是我们要找的第一个值大于等于给定值的元素。如果a[mid]前面已经没有元素,或者前面一个元素小于要查找的值value,那a[mid]就是我们要找的元素。如果a[mid-1]也大于等于要查找的值value,那说明要查找的元素在[low, mid-1]之间,所以,我们将high更新为mid-1。
(2)、源码:
public int bsearch(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] >= value) {
if ((mid == 0) || (a[mid - 1] < value)) return mid;
else high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
4、查找最后一个小于等于给定值的元素。
(1)、思路:
如果a[mid]大于要查找的值value,那要查找的值肯定在[mid+1, high]之间,所以,我们更新low=mid+1。
对于a[mid]小于等于给定值value的情况,我们要先看下这个a[mid]是不是我们要找的最后一个值小于等于给定值的元素。如果a[mid]后面已经没有元素,或者后面一个元素大于要查找的值value,那a[mid]就是我们要找的元素。如果a[mid-1]也小于等于要查找的值value,那说明要查找的元素在[mid + 1, high]之间,所以,我们将low更新为mid+1。
(2)、源码:
public int bsearch7(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else {
if ((mid == n - 1) || (a[mid + 1] > value)) return mid;
else low = mid + 1;
}
}
return -1;
}