二分查詢到底有能運用在**?
最常見的就是教科書上的例子,在有序陣列中搜尋給定的某個目標值的索引。再推廣一點,如果目標值存在重複,修改版的二分查詢可以返回目標值的左側邊界索引或者右側邊界索引。
ps:以上提到的三種二分查詢演算法形式在前文「二分查詢詳解」有**詳解,如果沒看過強烈建議看看。
拋開有序陣列這個枯燥的資料結構,二分查詢如何運用到實際的演算法問題中呢?當搜尋空間有序的時候,就可以通過二分搜尋「剪枝」,大幅提公升效率。
說起來玄乎得很,本文先用乙個具體的「koko 吃香蕉」的問題來舉個例子。
也就是說,koko 每小時最多吃一堆香蕉,如果吃不下的話留到下一小時再吃;如果吃完了這一堆還有胃口,也只會等到下一小時才會吃下一堆。在這個條件下,讓我們確定 koko 吃香蕉的最小速度(根/小時)。
如果直接給你這個情景,你能想到**能用到二分查詢演算法嗎?如果沒有見過類似的問題,恐怕是很難把這個問題和二分查詢聯絡起來的。
那麼我們先拋開二分查詢技巧,想想如何暴力解決這個問題呢?
首先,演算法要求的是「h
小時內吃完香蕉的最小速度」,我們不妨稱為speed
,請問speed
最大可能為多少,最少可能為多少呢?
顯然最少為 1,最大為max(piles)
,因為一小時最多只能吃一堆香蕉。那麼暴力解法就很簡單了,只要從 1 開始窮舉到max(piles)
,一旦發現發現某個值可以在h
小時內吃完所有香蕉,這個值就是最小速度:
int mineatingspeed(int piles, int h)
return max;
}
注意這個 for 迴圈,就是在連續的空間線性搜尋,這就是二分查詢可以發揮作用的標誌。由於我們要求的是最小速度,所以可以用乙個搜尋左側邊界的二分查詢來代替線性搜尋,提公升效率:
int mineatingspeed(int piles, int h) else
}return left;
}
ps:如果對於這個二分查詢演算法的細節問題有疑問,建議看下前文「二分查詢詳解」搜尋左側邊界的演算法模板,這裡不展開了。
剩下的輔助函式也很簡單,可以一步步拆解實現:
// 時間複雜度 o(n)
boolean canfinish(int piles, int speed, int h)
return time <= h;
}int timeof(int n, int speed)
int getmax(int piles)
至此,借助二分查詢技巧,演算法的時間複雜度為 o(nlogn)。
類似的,再看一道運輸問題:
要在d
天內運輸完所有貨物,貨物不可分割,如何確定運輸的最小載重呢(下文稱為cap
)?
其實本質上和 koko 吃香蕉的問題一樣的,首先確定cap
的最小值和最大值分別為max(weights)
和sum(weights)
。
我們要求最小載重,所以可以用搜尋左側邊界的二分查詢演算法優化線性搜尋:
// 尋找左側邊界的二分查詢
int shipwithindays(int weights, int d) else
}return left;
}// 如果載重為 cap,是否能在 d 天內運完貨物?
boolean canfinish(int w, int d, int cap)
}return false;
}
通過這兩個例子,你是否明白了二分查詢在實際問題中的應用?
for (int i = 0; i < n; i++)
if (isok(i))
return ans;
如何運用二分查詢演算法
875.愛吃香蕉的珂珂 1011.在d天內送達包裹的能力 二分查詢到底有能運用在 最常見的就是教科書上的例子,在有序陣列中搜尋給定的某個目標值的索引。再推廣一點,如果目標值存在重複,修改版的二分查詢可以返回目標值的左側邊界索引或者右側邊界索引。ps 以上提到的三種二分查詢演算法形式在前文 二分查詢詳...
查詢演算法 二分查詢
利用二分查詢演算法查詢某乙個元素,前提條件是該被查詢的元素是乙個已經有序的陣列。二分查詢的思想是將陣列元素的最高位 high 和最低位 low 進行標記,取陣列元素的中間 mid 和和要查詢的值 key 進行比較,如果目標值比中間值要大,則將最低位設定為mid 1,繼續進行查詢。如果目標值小於中間值...
查詢演算法 二分查詢
二分查詢的思路是很簡單的,前提是這組資料是有順序的。思路是從中間找乙個數,判斷大小,如果數比中間數大,說明在中間數到結尾的數中,如果小於,則說明在開始和中間數之間,經過多次相同操作,就可以得到我們想查詢的數時間複雜度就是 o logn 非遞迴的實現 const testarr let i 0whil...