觀感度:?????
口味:孜然牛肉
烹飪時間: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 真題
假設按照升序排序的陣列在預先未知的某個點上進行了旋轉。( 例如,陣列 [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
是 黑色的 3
。mid
在右邊也就是 [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)
只需要常量級別的空間存放變數
❤️愛心三連擊
1.看到這裡了就點個贊支援下吧,你的贊是我創作的動力。
2.關注公眾號前端食堂,你的前端食堂,記得按時吃飯!
3.本文已收錄在前端食堂Github
github.com/Geekhyt,求個小星星,感謝Star。