先看看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,則在前乙個資料集合中查...