前言
本人最近一直在做一些演算法方面的學習,最近也刷了一些力扣題目,我將我做過的題目分享到了我的GitHub上:演算法題解可以供大家參考。最近在刷題的過程當中,我發現我老是在二分法的邊界條件上出問題,經常是出現棧溢位的情況。所以想寫一篇文章,記錄一下我的學習心得與體會。
二分法用來幹嘛
二分法往往是對一個有序的資料形式,進行查詢特定值target的演算法。
該演算法的優勢是時間複雜度僅為O(log n),相較於順序查詢O(n)的時間複雜度有著明顯的提升。
二分法的分類
二分法有4個重要的值:target,start, end, mid.
我將二分法的區間劃分,分為3鍾型別:
-
劃分為[start,mid]和[mid+1,end]這兩個區間
-
劃分為[start,mid-1]和[mid,end]這兩個區間
-
劃分為[start,mid-1]和mid和[mid+1,end]這三個區間
這三種分法的區別就是:mid該放在哪裡
該如何劃分割槽間,是得視具體情況進行的。有的經典二分完全可以使用第三種分法(個人最喜歡的,因為邊界條件最簡單),但是有的時候,必須得用第一種和第二種形式,如:力扣第35題 。這題需要將mid歸到左區間當中。(start<target <= mid)。
邊界條件的難點
情況3邊界條件比較簡單,當(start>end)的時候跳出迴圈即可,不會造成死迴圈或者棧溢位。但是情況2和情況3往往會造成邊界條件分析不清晰導致產生死迴圈!
為什麼說邊界條件難呢?如果mid值取得不對,容易造成死迴圈。且造成死迴圈往往是在剩下兩個值的時候產生。這裡舉個例子:
在條件2的時候,下面的程式碼就會造成死迴圈!!!!
//在情況2的時候,該程式碼會造成死迴圈!!!
//假設array是從小到大排序的有序陣列
static int BinarySearch(int[] array,int target,int start,int end){
if(target < array[start] || target > array[end]) return -1;
if(start == end){
if(array[start] == target) return start;
else return -1;
}
int mid = (start + end) / 2;
if(array[mid] <= target)
return BinarySearch(array,target,mid,end);
else
return BinarySearch(array,target,start,mid-1);
}
主函式為:
public static void main(String[] args) {
int[] array = {0,2};
int re = BinarySearch(array,0,0,1);
System.out.println(re);
}
報錯為:
Exception in thread "main" java.lang.StackOverflowError
at demo.BinarySearch(demo.java:14)
at demo.BinarySearch(demo.java:14)
at demo.BinarySearch(demo.java:14)
at demo.BinarySearch(demo.java:14)
但是我們將程式碼換成第一種情況,即:將第一個條件的 <= 變為 < 並將區間變化修改成第一種情況,將會是正確的!
//在情況1的時候,程式碼會成功執行!!!
//假設array是從小到大排序的有序陣列
static int BinarySearch(int[] array,int target,int start,int end){
if(target < array[start] || target > array[end]) return -1;
if(start == end){
if(array[start] == target) return start;
else return -1;
}
int mid = (start + end) / 2;
//將這裡的<=該成<,並修改區間
if(array[mid] < target)
return BinarySearch(array,target,mid+1,end);
else
return BinarySearch(array,target,start,mid);
}
同樣用第一個主函式跑,結果是正確的!
0
Process finished with exit code 0
我之前在這裡老是理解很糊塗,走了不少彎路。但是,現在我再也不會在這裡栽跟頭了!!!!
解決邊界條件
大家也看見了,我陣列會造成棧溢位,說明,這裡的邊界條件導致的棧溢位就是在陣列剩下兩個元素的時候發現。為什麼會發生這樣的情況其實可以這樣區分:當只剩下兩個元素的時候,用mid能不能使得這兩個元素分開!
看第一個會造成死迴圈的例子:
當陣列是[left,left+1]這種情況的時候,mid的值為left,這時候,劃分為[left,left-1]和[left,left+1]這兩個區間,這樣兩個區間,沒辦法將left和left+1這兩個元素分開,說明這樣的mid是沒有意義的。
這時候我們需要將mid = mid+1即mid = left + 1可分開!因為這樣,區間可以劃分為[left,left]和[left+1,left+1]這樣兩個區間。這樣才能將兩個元素分開。
而針對情況1,mid的值為left就可以分開,這時候,劃分為[left,left]和[left+1,left+1]這樣兩個區間。
以後碰到這種情況,牢記分開元素準則,我們只需要當場覆盤一下這個過程即可避免棧溢位的產生!