二分只能用來查詢元素嗎?

2022-01-10 03:14:42 字數 2827 閱讀 9851

在電腦科學中,二分搜尋(binary search)也稱折半搜尋(half-interval search)、對數搜尋(logarithmic search),是在有序陣列中查詢某一特定元素的搜尋演算法。

其基本思想是通過逐次比較陣列特定範圍的中間元素與目標元素的大小,每次縮小一半的搜尋範圍,來提高搜尋效率。

二分搜尋的時間複雜度是 \(o(log n)\),空間複雜度為 \(o(1)\)。

二分查詢到底能運用在**?

最常見的就是教科書上的例子,也就是上文介紹的一般。在有序陣列中搜尋給定的某個目標值的索引。再推廣一點,如果目標值存在重複,修改版的二分查詢可以返回目標值的左側邊界索引或者右側邊界索引。

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;

}

剩下的輔助函式也很簡單,可以一步步拆解實現:

// 時間複雜度 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)

類似剛才的問題,我們要求最小載重,可以用 for 迴圈從小到大遍歷,那麼就可以用搜尋左側邊界的二分查找演算法優化線性搜尋:

// 尋找左側邊界的二分查詢int shipwithindays(int weights, int d)  else 

}return left;

}// 如果載重為 cap,是否能在 d 天內運完貨物?boolean canfinish(int w, int d, int cap)

}return false;

}

通過這兩個例子,你是否明白了二分查詢在實際問題中的應用呢?

首先思考使用 for 迴圈暴力解決問題,觀察**是否如下形式:

for (int i = 0; i < n; i++)

if (isok(i))

return answer;

如果是,那麼就可以使用二分搜尋優化搜尋空間:如果要求最小值就是搜尋左側邊界的二分,如果要求最大值就用搜尋右側邊界的二分。

很多人覺得二分搜尋很簡單,實際上二分搜尋也可以出比較難的題。甚至有些題目,你不一定能想到用二分法來解決。

同時在不同的資料結構和不同的應用場景中,都可以使用二分搜尋的思想。

這裡 是 leetcode 中和二分搜尋有關的習題。

leetcode 上還有個二分查詢的 專題練習卡片

二分查詢目標元素索引

package com.zhangry.search public class binarysearch int start 0 int end arrayforsearch.length 1 int target 3 system.out.println target 在此陣列中的索引為 bina...

你真的會二分查詢嗎?

看到這個標題無論你是處於怎樣的心理進來看了,我覺得都是值得的。因為這個問題太簡單,任何乙個開始接觸 真正 演算法基本都是從二分查詢開始的。至於二分查詢都不知道是什麼的可以先去找別的資料看下,再來看這篇文章。既然很簡單,那麼我們開始一起寫乙個吧,要求是對num 不減序列在區間 0,7 進行查詢,當然我...

你真的會二分查詢嗎?

引用請註明出處 看到這個標題無論你是處於怎樣的心理進來看了,我覺得都是值得的。因為這個問題太簡單,任何乙個開始接觸 真正 演算法基本都是從二分查詢開始的。至於二分查詢都不知道是什麼的可以先去找別的資料看下,再來看這篇文章。既然很簡單,那麼我們開始一起寫乙個吧,要求是對num 不減序列在區間 0,7 ...