什么是左侧(右侧)边界二分查找
上一篇博客,细诉了二分查找的细节和实现逻辑,当时存在局限性,比如有一个数组int[] arr={1,2,2,2,3,5,7},目标num=2,返回索引是3,假如需要返回最左侧索引(1)或最右侧的索引(3),这样的话普通二分查找是无法实现的
来一个左侧边界二分查找:
public static int getTwoNum5(int[] arr, int num) {
int left = 0;
int right = arr.length;
// while(left < right) 终止的条件是 left == right,此时搜索区间 [left, left)
// 为空,所以可以正确终止
while (left < right) {
int index = (left + right) / 2;
System.out.println("当前索引位置:" + index + "索引值为:" + arr[index]);
if (num == arr[index]) {
// 当查询索引index查询到标的值,不立即返回而是缩小right边界[left,index]继续往左侧边界查找
right = index;
} else if (arr[index] > num) {
// 初始二分索引值比标的num值大,查询范围[0,index]
right = index;
} else if (arr[index] < num) {
// 初始二分索引值比标的num小,查询范围[index+1,right)
left = index + 1;
}
}
return arr[left] == num ? left : -1;
}
细节问题:
-
为什么right=arr.length而不是arr.length-1
答:因为搜索区间是左闭右开的,当arr[index]=num或者arr[index]>num时,循环范围都是[left,index),这样不断搜索区间靠左查询。 -
为什么while的循环条件是<而不是<=
答:当查询最左侧或者最右侧边界成立时即arr[index]=num,右侧边界会赋值为right=index,如果循环结束条件leth<=right,最左侧情况(0,0)或最右侧情况(arr.lenght-1,arr.lenght-1)出现死循环无法改出。 -
为什么right=index;left=index+1
答:是为了切割二分查询后左右两侧边界,当初次二分索引查询值比标的num大时,查询范围切割为[left,index),当比标的num小时,查询范围切割为[index+1,right)。 -
为什么numarr[index]时right=index
答:当查询索引index查询到标的值num时,不立即返回而是缩小right边界[left,index]继续往左侧边界查找,可以一直查到最左侧,如果出现[index+1,right],当numarr[index],则查询直接返回 -
如果是右侧边界的二分搜索
答:只需要在num==arr[index]成立时,不立即返回而是缩小left边界,赋值left=index+1继续向右侧查找[index+1,right].