從酒桌遊戲看二分查詢演算法

童歐巴發表於2020-07-10

圖怪獸_a333ddb7d5a4c52a6b2d361d2a272c35_92006.png
觀感度:?????

口味:孜然牛肉

烹飪時間:5min

本文已收錄在Github github.com/Geekhyt,感謝Star。

酒桌上曾經玩過這樣一個小遊戲,遊戲規則是:主持人每次隨機從 1-1000 中選擇一個數字,比如是 171。只有主持人自己知道並事先寫在紙條上留存,然後分別讓大家來猜,能夠用最少次數猜到的人獲勝並擁有指定一個人罰酒的權利。

  • 童歐巴:500
  • 主持人:大了
  • 童歐巴:250
  • 主持人:大了
  • 童歐巴:125
  • 主持人:小了
  • 童歐巴:187
  • 主持人:大了
  • 童歐巴:156
  • 主持人:小了
  • 童歐巴:171

主持人再次挑選數字,讓扒蒜小妹去猜...

最後,童歐巴用的次數最少,童歐巴獲勝!指定扒蒜小妹罰酒。

這個遊戲就是看誰能使用最少的次數猜到主持人選的數字,誰就獲勝。這種在有序資料集合中的查詢用二分查詢再合適不過了。

二分查詢 Binary Search

二分查詢,顧名思義。(看上文歐巴熟練的灌酒操作也可以知道)每次的查詢都是和區間的中間元素對比,將待查詢的區間縮小為一半,直到找到目標元素,或者區間被縮小為 0 (沒找到)。二分查詢的時間複雜度是 O(logn)。對比常量級時間複雜度,當常量很大時 O(999999),就會比 O(1) 的演算法要高效。

二分演算法雖然高效,但也存在一定的侷限性。想要使二分查詢發揮威力,需要滿足幾個前置條件才行。

  • 有序(單調遞增/遞減)
  • 陣列(能夠通過索引訪問)
  • 資料量不能太大(陣列記憶體空間連續,對記憶體要求嚴格)也不能太小(遍歷即可)

LeetCode 真題

33. 搜尋旋轉排序陣列

假設按照升序排序的陣列在預先未知的某個點上進行了旋轉。

( 例如,陣列 [0,1,2,4,5,6,7] 可能變為 [4,5,6,7,0,1,2] )。

搜尋一個給定的目標值,如果陣列中存在這個目標值,則返回它的索引,否則返回 -1 。

你可以假設陣列中不存在重複的元素。

你的演算法時間複雜度必須是 O(log n) 級別。

示例 1:

輸入: nums = [4,5,6,7,0,1,2], target = 0
輸出: 4

示例 2:

輸入: nums = [4,5,6,7,0,1,2], target = 3
輸出: -1

關鍵點

進行旋轉後的陣列一定有一部分是有序的。而且,題目要求時間複雜度為 O(logn),暗示我們使用二分搜尋。

如上圖中的兩種情況,觀察旋轉後的陣列:

  • nums[mid] >= nums[start] 時,mid 在左邊且左邊有序 5 >= 2
  • nums[mid] < nums[start] 時,mid 在右邊且右邊有序 2 < 6

接著我們來判斷 target 在哪一個部分,捨棄另一部分即可。如上圖的第二種情況,我們假設 target黑色的 3mid 在右邊也就是 [mid, end]target > nums[mid] && target <= nums[end],所以捨棄左邊,start = mid + 1

const search = function(nums, target) {
    let start = 0;
    let end = nums.length - 1;
    
    while (start <= end) {
        const mid = start + ((end - start) >> 1);
        if (nums[mid] === target) {
            return mid;
        }
        // 左側有序
        if (nums[mid] >= nums[start]) {
            // target 在 [start, mid] 之間
            if (target >= nums[start] && target < nums[mid]) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        } else { // 右側有序
            // target 在 [mid, end] 之間
            if (target > nums[mid] && target <= nums[end]) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
    }
    return -1;
}

複雜度分析

  • 時間複雜度:O(logn)
  • 空間複雜度:O(1) 只需要常量級別的空間存放變數

相關文章