二分查找的应用
二分查找的應用
文章目錄
- 二分查找的應用
- 一、題目描述:愛吃香蕉的珂珂
- 二、分析
- 三、完整代碼
- 四、問題描述:在 D 天內送達包裹的能力
- 五、分析
- 六、代碼
點擊: 二分詳解
一、題目描述:愛吃香蕉的珂珂
二、分析
- 也就是說,Koko 每小時最多吃一堆香蕉,如果吃不下的話留到下一小時再吃;
- 如果吃完了這一堆還有胃口,也只會等到下一小時才會吃下一堆。在這個條件下,讓我們確定 Koko 吃香蕉的最小速度(根/小時)。
- 如果直接給你這個情景,你能想到哪里能用到二分查找算法嗎?如果沒有見過類似的問題,恐怕是很難把這個問題和二分查找聯系起來的。
- 那么我們先拋開二分查找技巧,想想如何暴力解決這個問題呢?
- 首先,算法要求的是「H 小時內吃完香蕉的最小速度」,我們不妨稱為 speed,請問 speed 最大可能為多少,最少可能為多少呢?
- 顯然最少為 1,最大為 max(piles),因為一小時最多只能吃一堆香蕉。那么暴力解法就很簡單了,只要從 1 開始窮舉到 max(piles),一旦發現發現某個值可以在 H 小時內吃完所有香蕉,這個值就是最小速度:
- 注意這個 for循環,就是在連續的空間線性搜索,這就是二分查找可以發揮作用的標志。由于我們要求的是最小速度,所以可以用一個搜索左側邊界的二分查找來代替線性搜索,提升效率:
剩下的輔助函數也很簡單,可以一步步拆解實現:
// 時間復雜度 O(N) boolean canFinish(int[] piles, int speed, int H) {int time = 0;for (int n : piles) {time += timeOf(n, speed);}return time <= H; }int timeOf(int n, int speed) {return (n / speed) + ((n % speed > 0) ? 1 : 0); }int getMax(int[] piles) {int max = 0;for (int n : piles)max = Math.max(n, max);return max; }至此,借助二分查找技巧,算法的時間復雜度為 O(NlogN)。
三、完整代碼
class Solution { public:// 時間復雜度 O(N) bool canFinish(vector<int> piles, int speed, int H) {int time = 0;for (int n : piles) {time += timeOf(n, speed);}return time <= H; }int timeOf(int n, int speed) {return (n / speed) + ((n % speed > 0) ? 1 : 0); }int getMax(vector<int> piles) {int maxx = 0;for (int n : piles)maxx = std::max(n, maxx);return maxx; }int minEatingSpeed(vector<int>& piles, int H) {// 套用搜索左側邊界的算法框架int left = 1, right = getMax(piles) + 1;while (left < right) {// 防止溢出int mid = left + (right - left) / 2;if (canFinish(piles, mid, H)) {right = mid;} else {left = mid + 1;}}return left;} };四、問題描述:在 D 天內送達包裹的能力
五、分析
- 要在 D 天內運輸完所有貨物,貨物不可分割,如何確定運輸的最小載重呢(下文稱為 cap)?
- 其實本質上和 Koko 吃香蕉的問題一樣的,首先確定 cap 的最小值和最大值分別為 max(weights) 和 sum(weights)。
- 我們要求最小載重,所以可以用搜索左側邊界的二分查找算法優化線性搜索:
六、代碼
// 尋找左側邊界的二分查找 int shipWithinDays(int[] weights, int D) {// 載重可能的最小值int left = getMax(weights);// 載重可能的最大值 + 1int right = getSum(weights) + 1;while (left < right) {int mid = left + (right - left) / 2;if (canFinish(weights, D, mid)) {right = mid;} else {left = mid + 1;}}return left; }// 如果載重為 cap,是否能在 D 天內運完貨物? boolean canFinish(int[] w, int D, int cap) {int i = 0;for (int day = 0; day < D; day++) {int maxCap = cap;while ((maxCap -= w[i]) >= 0) {i++;if (i == w.length)return true;}}return false; } 超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
- 上一篇: 高效进行模幂运算
- 下一篇: 去除有序数组/链表的重复元素--双指针原