1. 程式人生 > 其它 >演算法總結-二分查詢(兩種模板方法總結)

演算法總結-二分查詢(兩種模板方法總結)

【二分查詢】

定義:

二分查詢也稱折半查詢(Binary Search),是一種在有序陣列中查詢某一特定元素的搜尋演算法。我們可以從定義可知,運用二分搜尋的前提是陣列必須是有序的,這裡需要注意的是,我們的輸入不一定是陣列,也可以是陣列中某一區間的起始位置和終止位置。

前言:

二分查詢的思路不難理解,但是裡面有許多細節問題需要注意,比如邊界條件,迴圈結束條件中 left 和 right 的關係,更新 left 和 right 位置時要不要加 1 減 1。下面的三種模板,是從其他部落格整理到的,但是實際上掌握一種就行,主要需要注意細節問題,不管哪種模板一定要判斷的是 下一輪「向左找」還是「向右找」,還有 mid 是不是有可能是問題的答案,這一點應該從題目中分析得到,所以一定要認真審題~

二分查詢法兩種模板寫法:

模板一:

while (left <= right) ,這種寫法裡面的 left 和 right 都要加 1 或者減 1,還要判斷 mid 位置的值有可能是解的時候,把 mid 的值儲存下來,退出迴圈以後 left 在右,right 在左,即left == right + 1,寫成區間 [left..right] ==> [right+1, right]為空區間,表示沒找到,直接返回-1即可;

 1 class Solution {
 2     public int searchInsert(int[] nums, int target) {
 3         int
left = 0, right = nums.length - 1; 4 while(left <= right) { // 注意 5 int mid = left + (right - left) / 2; 6 if(nums[mid] == target) {
7 return mid; 8 } else if(nums[mid] < target) { 9 left = mid + 1; // 注意 10 } else
{ 11 right = mid - 1; // 注意 12 } 13 } 14 // 相關返回值 15 return -1; 16 } 17 }

模板二:

while (left < right) ,這種寫法的好處是在迴圈結束的時候不用判斷應該返回left還是right。因為迴圈結束的條件是 left ==  right,區間 [left..right] 有 1 個元素,如果可以確定區間[left, right]一定有解,直接返回left即可,否則需要對left這個位置的元素單獨判斷一次。

搜尋區間[left, right]是配對形式存在的:

  • 要麼是[left, mid-1]和[mid, right]即left = mid 與 right = mid - 1,mid需要向上取整:mid = left + (right - left) / 2 ;
  • 要麼是[left, mid]和[mid+1, right]即left = mid + 1 與 right = mid ;

小提醒:在寫相關邏輯語句的時候,可以在註釋裡寫上「下一輪搜尋區間是什麼」。如果下一輪搜尋區間是 [mid..right] ,這個時候就設定 left = mid,這種情況的反面區間就是 [left..mid - 1] ,那麼 else 就設定 right = mid - 1,所以就不用記憶配對關係了。

 1  class Solution {
 2      public int searchInsert(int[] nums, int target) {
 3          int left = 0, right = nums.length - 1; 
 4          while(left < right) { // 注意
 5             int mid = (left + right) / 2; // 注意
 6             if(nums[mid] <= target) {
 7                // 相關邏輯
 8                left = mid + 1
 9             } else {
10                right = mid; // 注意
11             }
12          }
13          // 相關返回值
14         return nums[left] == target ? left : -1;
15     }
16  }

 【向上取整】與【向下取整】

向上取整與向下取整主要是為了處理當只剩下兩個元素的情況,這時 left 和 right 相差1。

向上取整:把整個區間劃分成[left, mid -1] 和 [mid, right],把mid劃分給了右區間,如果操作是選擇了包含mid的這個區間,那麼就需要mid值來更新left,如果向下取整,left和right值算出來的mid就與left相等(例如:(4+5) / 2 == 4),再將left更新成mid,相當於沒有更新left,故需要向上取整,算出來的mid值與right相等,這樣left向右移動,得到更新,和right指向相同的值。

向下取整:把整個區間劃分成[left, mid] 和 [mid + 1, right],把mid劃分給了左區間,如果操作是選擇了包含mid的這個區間,那麼就需要mid值來更新right,如果向上取整,left和right值算出來的mid就與right相等(例如:(5 + 4) / 2 +1 == 5),再將right更新成mid,相當於沒有更新right,故需要向下取整,算出來的mid值與left相等,這樣right向左移動,得到更新,和left指向相同的值。

總結:

mid如果被分給了右區間,需要向上取整,保證left能夠被新值更新。

mid如果被分給了左區間,需要向上取整,保證left能夠被新值更新。