二分法是算法題里面一個比較基礎但是很容易錯的概念,一開始練習的時候由於不熟悉二分法的套路,反復出現死循環或者目標值找錯,非常影響做題心情。我總結了如下幾個模板。原則上這里的模板無論你使用哪一個,都可以解決二分法類型的問題,只不過有一些題目,比如尋找一個最大值/最小值的,可能某一個模板更適合,需要判斷的條件較少。
如下模板是用Java實現的
模板一,找有序數組中是否存在一個目標值。注意 right 指針一開始定義是在數組下標范圍內的,所以while的條件才能寫成 <=。
1 class Solution { 2 public int binarySearch2(int[] nums, int target) { 3 // left和right都在數組下標范圍內 4 // [left, right] 5 int left = 0; 6 int right = nums.length - 1; 7 // while循環跳出的條件是left < right 8 // 所以如果沒找到target的話,也不需要特判了 9 while (left <= right) { 10 int mid = left + (right - left) / 2; 11 if (nums[mid] == target) { 12 return mid; 13 } else if (nums[mid] < target) { 14 left = mid + 1; 15 } else { 16 right = mid - 1; 17 } 18 } 19 // 如果沒找到就只能返回-1 20 return -1; 21 } 22 }
模板二,適合判斷當前 index 和 index + 1 之間的關系。right 指針一開始的定義是在數組下標范圍外的,[left, right),所以在需要移動 right 指針的時候不能寫成 right = mid。這樣會遺漏掉一些下標的判斷。
1 class Solution { 2 public int binarySearch3(int[] nums, int target) { 3 // right不在下標范圍內 4 // [left, right) 5 int left = 0; 6 int right = nums.length; 7 // while循環跳出的條件是left == right 8 // 這個模板比較適合判斷當前index和index + 1之間的關系 9 // left < right, example, left = 0, right = 1 10 while (left < right) { 11 int mid = left + (right - left) / 2; 12 if (nums[mid] == target) { 13 return mid; 14 } else if (nums[mid] < target) { 15 left = mid + 1; 16 } else { 17 // 因為搜索范圍是左閉右開所以這里不能-1 18 right = mid; 19 } 20 } 21 // 最后的特判 22 if (left != nums.length && nums[left] == target) { 23 return left; 24 } 25 return -1; 26 } 27 }
模板三,適用於查找有序數組中某個元素是否存在。若不存在,往往題目要求返回 -1。注意 right 指針一開始定義是在數組下標范圍內的,while條件不滿足的時候,left + 1 == right,兩下標應該指向某個下標 i 和 i + 1。這樣如果有什么特殊的值需要判斷,應該不是 left 就是 right 了。
1 class Solution { 2 public int binarySearch1(int[] nums, int target) { 3 // left和right都在數組下標范圍內 4 // [left, right] 5 int left = 0; 6 int right = nums.length - 1; 7 // 舉例,start - 0, end = 3 8 // 中間隔了起碼有start + 1和start + 2兩個下標 9 // 這樣跳出while循環的時候,start + 1 == end 10 // 才有了最后的兩個判斷 11 while (left + 1 < right) { 12 int mid = left + (right - left) / 2; 13 if (nums[mid] == target) { 14 return mid; 15 } else if (nums[mid] < target) { 16 left = mid; 17 } else { 18 right = mid; 19 } 20 } 21 // 特判 22 if (nums[left] == target) { 23 return left; 24 } 25 if (nums[right] == target) { 26 return right; 27 } 28 // 如果沒找到就只能返回-1 29 return -1; 30 } 31 }
如上三個模板的比較如下圖

這 3 個模板的不同之處在於:
左、中、右索引的分配。
循環或遞歸終止條件。
后處理的必要性。
模板 #1 和 #3 是最常用的,幾乎所有二分查找問題都可以用其中之一輕松實現。模板 #2 更 高級一些,用於解決某些類型的問題。
這 3 個模板中的每一個都提供了一個特定的用例
模板 #1 (left <= right)
二分查找的最基礎和最基本的形式。
查找條件可以在不與元素的兩側進行比較的情況下確定(或使用它周圍的特定元素)。
不需要后處理,因為每一步中,你都在檢查是否找到了元素。如果到達末尾,則知道未找到該元素。
模板 #2 (left < right)
一種實現二分查找的高級方法。
查找條件需要訪問元素的直接右鄰居。
使用元素的右鄰居來確定是否滿足條件,並決定是向左還是向右。
保證查找空間在每一步中至少有 2 個元素。
需要進行后處理。 當你剩下 1 個元素時,循環 / 遞歸結束。 需要評估剩余元素是否符合條件。
模板 #3 (left + 1 < right)
實現二分查找的另一種方法。
搜索條件需要訪問元素的直接左右鄰居。
使用元素的鄰居來確定它是向右還是向左。
保證查找空間在每個步驟中至少有 3 個元素。
需要進行后處理。 當剩下 2 個元素時,循環 / 遞歸結束。 需要評估其余元素是否符合條件。
作者:力扣 (LeetCode)
鏈接:https://leetcode-cn.com/leetbook/read/binary-search/xewjg7/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
