來玩二分查找
二分查找是典型的看起來很普通,當時細節卻很複雜的算法,可以理解爲:思路很簡單,細節是魔鬼,各種邊界情況複雜,如果想不通不妨在紙上模擬計算。
本篇博客就開始來探尋一下二分查找,常用的幾個二分查找,尋找一個數,尋找左側邊界,尋找右側邊界等情況,循環結束條件是什麼,索引到底是該加一還是減一等情況,分析細節及細節的差異。
先來一個簡單的二分查找代碼
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,查找複雜度更小。
左側邊界和右側邊界二分查找後續更新!