[LeetCode] 二分查找模板 binary search


二分法是算法題里面一個比較基礎但是很容易錯的概念,一開始練習的時候由於不熟悉二分法的套路,反復出現死循環或者目標值找錯,非常影響做題心情。我總結了如下幾個模板。原則上這里的模板無論你使用哪一個,都可以解決二分法類型的問題,只不過有一些題目,比如尋找一個最大值/最小值的,可能某一個模板更適合,需要判斷的條件較少。

如下模板是用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)
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

LeetCode 題目總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM