二分查詢的思想

2021-10-11 03:13:41 字數 3835 閱讀 5844

先看看leetcode兩道相關的題目,都是二分思想的應用。

leetcode 35

leetcode 34

二分思想用一句話概況就是「一看就懂,一寫就廢」,花了很長時間都沒有弄得懂,每個人都是不同的寫法,新手很難從中發現規律。

其實二分查詢最重要的一句話就是「查詢範圍」,如何確定查詢範圍?只要確定了查詢範圍,二分查詢其實都是乙個套路。而二分查詢大致分為以下三種:

①普通二分,也就是給定乙個排序陣列nums和目標值target,在nums中查詢target,如果找到就返回下標。寫法也非常簡單:

int left =0;

int right = nums.

size()

-1;while

(left <= right)

else

if(nums[mid]

> target)

else

if(nums[mid]

== target)

}return-1

;

初學建議不要寫else,一步一步else if分析會更容易理解其中的「查詢範圍」如何思考。

下面兩種是二分查詢的變種,比基本二分要難一點。由於需要確定邊界,所以不能一找到nums[mid] == target就返回,而應該繼續縮小查詢範圍,不斷逼近邊界。

②確定左側邊界的二分查詢

需求:給定乙個排序陣列nums和乙個目標值target,在nums中找到target出現的最左側位置,返回該下標。先給出**再作解釋:

int

left_bound

(vector<

int> nums,

int target)

int left =0;

int right = nums.

size()

;while

(left < right)

else

if(nums[mid]

< target)

else

if(nums[mid]

== target)}if

(left == nums.

size()

|| nums[left]

!= target)

return left;

//否則此時left就是左邊界

}

注意到上面的**,首先確定查詢範圍!這篇文章我全部採用左閉右開區間的查詢範圍,當然也可以左閉右閉。

而right一開始為什麼是nums.size()而不是nums.size() - 1呢?這是因為查詢範圍為[left, right)左閉右開,為了確保所有的數都能被用到,所以right = nums.size(); 這樣就可以取到nums[nums.size() - 1],也即最後乙個數了。

好了,上面說完查詢範圍的確定,那確定左邊界就是不斷往左逼近了。簡單地說,就是區間盡量往左靠,不斷往左邊縮小區間,具體的體現就是當 nums[mid] == target的時候,並不是直接返回,而是把right = mid; 把查詢區間往左邊縮小為[left, mid),因為mid已經被我們用過了(也就是已經和target比較過了),而右區間又是開區間,所以right = mid而非mid - 1!

那為什麼left = mid + 1呢?這是因為我們左區間是閉區間,當mid被用過之後,下乙個區間就是mid + 1開始了。

到了這裡,相信大家對思路已經明白了,最後就是迴圈退出的時候,進行異常情況的判斷,迴圈退出的時候,left == right。

此時,有兩種情況:1. 找到了左邊界。 2.找不到左邊界。

找不到左邊界是什麼情況呢?要麼left = nums.size()超出陣列下標(target比所有nums的數都大),要麼nums[left] != target,至此結束。

③確定右側邊界的二分查詢

需求:給定乙個排序陣列nums和乙個目標值target,在nums中找到target出現的最右側位置,返回該下標。

還是先給出**:

int

right_bound

(vector<

int> nums,

int target)

int left =0;

int right = nums.

size()

;while

(left < right)

else

if(nums[mid]

< target)

else

if(nums[mid]

== target)

}return right -1;

//注意這裡的返回值

}

分析思路同上:既然要找最右邊界,那麼left就要不斷往右靠,查詢範圍同樣是[left, right),相信大家已經可以自己分析出來了。

那麼最終的返回值是什麼呢?我們知道,迴圈退出的時候left ==rigiht,由於有可能出現在最後乙個數,所以我們返回right - 1(left - 1也可以)。這裡是乙個小細節,因為上面找到target並縮小區間時,是這樣寫的

else

if(nums[mid]

== target)

所以真正的mid = left - 1(mid就是我們找到的目標值下標),這也是上面返回left的原因。

34. 在排序陣列中查詢元素的第乙個和最後乙個位置

class

solution;}

vector<

int>

res(2,

-1);

int left =0;

int right = nums.

size()

;while

(left < right)

else

if(nums[mid]

< target)

else

if(nums[mid]

== target)

}//異常情況判斷: 比陣列所有數都大 / 不存在target

if(left == nums.

size()

|| nums[left]

!= target)

res[0]

= left;

//確定左邊界

left =0;

right = nums.

size()

;while

(left < right)

else

if(nums[mid]

< target)

else}if

(nums[left -1]

!= target)

res[1]

= left -1;

//注意, 因為搜尋到mid的時候, left = mid + 1

return res;}}

;

35. 搜尋插入位置

class

solution

int left =0;

int right = nums.

size()

;while

(left < right)

else

if(nums[mid]

> target)

else

}return left;}}

;

二分查詢思想

二分查詢思想應用於對有序的陣列進行查詢操作。時間複雜度 二分查詢也稱為折半查詢,每次都能將查詢區間減半,這種折半特性演算法時間複雜度為o logn mid計算 有兩種計算中值mid的方式 l h可能出現加法溢位,也就是說加法的結果大於整形能夠表示的範圍。但是l和h都為正數,因此h l不會出現加法溢位...

二分查詢的核心思想

以題目為例,說明的更透徹一些 看到題目我們的第一反應 使用dfs或者是搜尋進行查詢,因為洛谷新手村中的這一道題 選數 p1036,但是需要注意時間,n2的複雜度再加上輸入的時間會超時,洛谷中的題用搜尋寫的,時間複雜度太高,所以我們轉而使用二分進行查詢,其實是看了郭煒老師的課程才想到二分 我們用一層迴...

二分查詢的思想及實現

1.二分查詢 又稱為 折半查詢,二分查詢,適合對已經排序好的資料集合進行查詢,時間複雜度o log2n 效率高。假設有一公升序的資料集合,先找出公升序集合中最中間的元素,將資料集合劃分為兩個子集,將最中間的元素和關鍵字key進行比較,如果等於key則返回,如果大於關鍵字key,則在前乙個資料集合中查...