来玩二分查找
二分查找是典型的看起来很普通,当时细节却很复杂的算法,可以理解为:思路很简单,细节是魔鬼,各种边界情况复杂,如果想不通不妨在纸上模拟计算。
本篇博客就开始来探寻一下二分查找,常用的几个二分查找,寻找一个数,寻找左侧边界,寻找右侧边界等情况,循环结束条件是什么,索引到底是该加一还是减一等情况,分析细节及细节的差异。
先来一个简单的二分查找代码
public static void getTwoNum4(int[] arr, int num) {
int left = 0;
// 数组索引从0开始,长度从1开始
int right = arr.length - 1;
// 为什么要小于等于,必须以left或right为标的,大于覆盖一边,否则如果是相等,则相等的索引就会被漏掉
// 这里可以带入(3<=2) 只有当left+1>right循环终止,且区间为空
while (left <= right) {
int index = (left + right) / 2;
if (num == arr[index]) {
System.out.println("找到符合要求的数:" + arr[index]);
break;
} else if (arr[index] > num) {
// 传统做法right--相当于从右向左完整遍历,当索引值大于目标数,则右侧索引减一
// right--;
// 根据索引值大于目标数,从索引处减一,排除索引右侧大于目标数,复杂度更小
right = index - 1;
} else if (arr[index] < num) {
// 传统做法left++相当于从左向右完整遍历,当索引值小于目标数,则左侧索引加一
// left++;
// 根据索引值小于目标数,从索引处加一,排除索引左侧小于目标数,复杂度更小
left = index + 1;
}
}
}
细节问题
-
right索引为什么是 arr.length-1?
答:数组索引下标从0开始,数组的长度从1开始,右侧数组最大索引是长度-1,否则会越界 -
为什么while的循环条件是<=而不是<
从图上可以看出来,必须以left或right为标的,大于覆盖一边,否则如果是相等,则相等的索引就会被漏掉,如上图第一列数组,如果循环条件是while(left<rigth)带入(3,3),循环结束则会漏掉索引3,如上图第二列数组,如果循环条件是while(left<=rigth)带入(3,2),没有数字比3大比2小,则while循环完整结束。
- 循环判断条件arr[index]>num为什么right=arr[index]-1
答:传统做法是当索引值大于目标值,右侧索引减减,相当于从右向左完整遍历查找,二分查找是当(left+right)/2,获取数组中间索引值,比较如果大于目标数值,则代表目标数值在于数组的左侧,排除索引右侧大于目标数,所以right索引为index-1;同理如果数组中间索引值,比较小于目标数值,则代表目标数值在于数组右侧,排除索引左侧小于目标数,所以left的索引为index+1,查找复杂度更小。
左侧边界和右侧边界二分查找后续更新!