4.4 KiB
練習
知識鞏固
二分搜尋怎樣縮小搜尋區間
在有序陣列 [2, 5, 8, 12, 16, 23, 38] 中查詢 16。
使用雙閉區間 [i, j],中點為
$m=i+(j-i)/2$(向下取整)。
請寫出每輪的 (i, j, m)、中點元素,以及下一步如何縮小區間,
直到找到目標。
??? success "參考答案"
每輪查詢過程如下:
| 輪次 | `(i, j, m)` | 中點元素 | 下一步 |
| --- | --- | --- | --- |
| 1 | `(0, 6, 3)` | 12 | `12 < 16`,令 `i = 4` |
| 2 | `(4, 6, 5)` | 23 | `23 > 16`,令 `j = 4` |
| 3 | `(4, 4, 4)` | 16 | 找到目標,返回索引 4 |
陣列有序,所以中點值小於目標時可以排除中點及其左側;
中點值大於目標時可以排除中點及其右側。
重複元素的左右邊界
在陣列 [1, 2, 2, 2, 4, 6] 中查詢數字 2。
一名同學用二分搜尋在索引 2 找到目標後立即返回,並說:
“索引 2 就是數字 2 的左邊界。”
- 這名同學的說法是否正確?數字 2 的左、右邊界分別是多少?請說明理由。
- 查詢左邊界時,若中點元素等於目標,接下來應繼續搜尋哪一側?
- 查詢右邊界時又應繼續搜尋哪一側?只需說明方向,無須寫出完整的查詢過程。
??? success "參考答案"
1. 這名同學的說法不正確。找到一個 2 後立即返回,只能保證找到某一個 2,不能保證找到最左或最右的 2。
本題的左邊界為索引 1,右邊界為索引 3。
2. 查詢左邊界時,即使中點元素等於 2,也要繼續搜尋左側,
例如在雙閉區間寫法中令 `j = m - 1`。
3. 查詢右邊界時,中點元素等於 2 後應繼續搜尋右側,
例如令 `i = m + 1`。
不同資料該選哪種搜尋方法
請在“順序查詢、二分搜尋、雜湊表”中,為下面三個場景選擇合適的方法,並說明理由:
- 在
10^7個已經有序且不再變動的整數中反覆查詢,不額外建立其他資料結構; - 在頻繁插入、刪除的資料集中反覆判斷某個鍵是否存在,不要求保持有序,也不進行範圍查詢;
- 在無序陣列中只查詢一次某個值。
??? success "參考答案"
1. 二分搜尋:資料有序且靜態,$O(\log n)$ 且無須額外空間。
2. 雜湊表:當雜湊函式能把鍵較均勻地分散到各桶時,插入、刪除和按鍵查詢的平均時間複雜度都是 $O(1)$。
3. 直接從頭到尾走訪:只查詢一次時,排序或建立雜湊表都要先處理整個陣列,
並不會減少這一次任務的總工作量。
選擇取決於資料是否有序、是否允許建立額外結構、查詢次數以及需要執行哪些操作。
程式設計練習
有序陣列的二分搜尋
給定一個按嚴格遞增順序排列的整數陣列 nums 和目標值 target。請使用二分搜尋找到 target:若存在,返回它的陣列索引;若不存在,返回 -1。
??? tip "解題提示"
1. 初始區間是 left = 0、right = n - 1,非空條件是 left <= right
2. 用 mid = left + (right - left) // 2 計算中點
3. 若 `nums[mid]` 小於 `target`,就把左邊界移到 `mid + 1`;若 `nums[mid]` 大於 `target`,就把右邊界移到 `mid - 1`;相等時立即返回
LeetCode{ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } 題目解析{ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }
有序陣列的插入位置
給定一個按嚴格遞增順序排列的整數陣列 nums 和目標值 target。
若 target 已在陣列中,返回它的索引;否則,返回把 target 插入陣列後仍能保持嚴格遞增順序的位置。答案可能是 0,也可能等於陣列長度。請使用二分搜尋。
??? tip "解題提示"
1. 答案可能是 0,也可能是陣列長度 n
2. 使用雙閉區間時,若 `nums[mid]` 大於等於 `target`,就令 `right = mid - 1`,繼續檢查更靠左的位置;否則令 `left = mid + 1`
3. 迴圈結束時 left 就是插入位置
LeetCode{ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }