如何在一堆東西裡面尋找我想要的東西呢?相向大家都有相似的疑問吧?
這章節會介紹兩個常用的搜尋演算法
找東西最直觀的方法就是把所有東西掃過一遍,這樣就可以找到想要的內容了
而這種方法就叫做線性搜尋法
線性搜尋法很簡單,對於一個陣列,只需要一個一個比較是否相同即可
時間複雜度:
但是這樣就太慢了!!
二分搜尋法的精神就是把一個序列剖半、比較、再剖半、再比較…直到找到答案為止
在以下影片中,目標是尋找一區間中的紅線,黑線代表中線
那們會執行以下流程:
假設有一陣列 ,目標是希望找到
在這其中,我們可以使用三個指針 來維護
二分搜尋法需要先準備好一個「有序」的序列與一個目標值
最後會回傳目標值在序列中的位置
詳細流程如下:
PS : 實作時如果遇到無限迴圈,可以檢查是否維護好邊界或是終止條件
二分搜尋法基本上符合單調性的任何東西都可以使用
單調性基本上就是對於一個函數 :
當 越大 也會越大 或者 當 越大 也會越小
圖源: https://manabitimes.jp/math/1289
想當然一個排序好的陣列,或是說一個序列也會具有單調性
除此之外,像是坐標系、時間軸、走訪起來有序的樹狀資料結構…等等,也可能具有單調性,可以試試二分搜尋法
如果發現要搜尋的東西不是具有單調性的函數,而是一個具有「極值」的拋物線函數,那麼就可以使用三分搜尋法
C++ 其實有內建的二分搜尋法,分別是 lower_bound
與 upper_bound
在比賽時也比較推薦使用這兩個函數,可以省去很多 debug 時間
lower_bound() 函數可以找到一有序陣列中第一個「大於或等於」目標值的記憶體位置
如果要找的數大於所有的數字,則回傳陣列末尾
由於是找記憶體位置,所以實作時要記得檢掉原本陣列 的位置
upper_bound() 函數可以找到一有序陣列中第一個「大於」目標值的記憶體位置
用法與 lower_bound()
雷同
來源: 2019-2020 ACM-ICPC Brazil Subregional Programming Contest Problem M
這題要吃爆米花,共 人分享 桶爆米花,每人每一秒只能吃 粒爆米花
告訴你第 桶有 粒爆米花,並且要符合以下規則:
問他們最快要花多少時間才能吃完所有爆米花
首先,由於每個人要選擇「連續」的爆米花桶
因此可以發現這其實就是把這 桶爆米花分割成 個區間
測資一就是這樣切割:
我們可以來觀察一下:
根據輸出結果,因為每秒吃 粒爆米花,如果花 秒的時間,每個人各自可以吃 粒
這樣分割區間的話,三個人分別要吃 、、 粒爆米花,時間絕對來的及
試著想想看,如果今天給這三人 秒的時間,可以怎麼分割區間呢?
因為每秒吃 粒爆米花,如果花 秒的時間,每個人各自可以吃 粒
這樣分割區間的話三個人分別要吃 、、 顆
會發現這樣有人沒分到,所以是 個區間
試著想想看,如果今天給這三人 秒的時間,可以怎麼分割區間呢?
因為每秒吃 粒爆米花,如果花 秒的時間,每個人各自可以吃 粒
這樣分割區間的話三個人分別要吃 、、 顆,剩下 顆吃不完
會發現這樣會有 個區間(不是我要的答案)
可以歸納出三點:
可以發現子結論 具有單調性!!!
因此只要對時間做二分搜尋
每次搜尋確定可以分割的塊數小於或等於人數 (小於的話就是有人分 粒),就記錄答案
PS : 根據經驗法則,有時候二分搜尋也有可能是一種「逼近」答案的技巧
二分搜尋法是 APCS 的常客喔
Zerojudge c575. APCS 2017-0304-4基地台
Zerojudge d732. 二分搜尋法 (裸題)
ABC 231_C Counting 2 (可以想成裸題)
Zerojudge f581. APCS 3. 圓環出口 (前綴和 + 二分搜)
Zerojudge i401. APCS 3. 雷射測試
CSES Concert Tickets (資料結構 + 二分搜)
LeetCode First Bad Version (猜數字裸題)
LeetCode Find the Duplicate Number (其實可以不需要二分搜,但是題目要)
CSES Factory Machines (對時間二分搜)
ABC 319_D Minimum Width (可以轉化成例題的區間分割問題)
CSES Array Division
CSES Sum of Three Values (排序 + 窮舉 + 二分搜)
LeetCode Koko Eating Bananas (用二分搜猜速率)
ShanC 程式競賽筆記
作者: ShanC
更新: 2024/10/4 (颱風天寫的)