1. 程式人生 > >對於二分法的一些感想

對於二分法的一些感想

## 前言 ​ 本人最近一直在做一些演算法方面的學習,最近也刷了一些力扣題目,我將我做過的題目分享到了我的GitHub上:[演算法題解](https://github.com/Spicy-Jelly/algorithm)可以供大家參考。最近在刷題的過程當中,我發現我老是在二分法的**邊界條件**上出問題,經常是出現**棧溢位**的情況。所以想寫一篇文章,記錄一下我的學習心得與體會。 ## 二分法用來幹嘛 ​ **二分法往往是對一個有序的資料形式,進行查詢特定值**target**的演算法。** ​ 該演算法的優勢是時間複雜度僅為O(log n),相較於順序查詢O(n)的時間複雜度有著明顯的提升。 ## 二分法的分類 ​ 二分法有4個重要的值:target,start, end, mid. ​ 我將二分法的區間劃分,分為3鍾型別: 1. 劃分為**[start,mid]和[mid+1,end]**這兩個區間 ![情況1](https://img2020.cnblogs.com/blog/2267611/202102/2267611-20210215203201611-393008356.png) 2. 劃分為**[start,mid-1]和[mid,end]**這兩個區間 ![情況2](https://img2020.cnblogs.com/blog/2267611/202102/2267611-20210215203235762-1726322559.png) 3. 劃分為**[start,mid-1]和mid和[mid+1,end]**這三個區間 ![情況3](https://img2020.cnblogs.com/blog/2267611/202102/2267611-20210215203254472-1527171688.png) ​ 這三種分法的區別就是:**mid該放在哪裡** ​ 該如何劃分區間,是得視具體情況進行的。有的經典二分完全可以使用第三種分法(個人最喜歡的,因為邊界條件最簡單),但是有的時候,必須得用第一種和第二種形式,如:[力扣第35題](https://leetcode-cn.com/problems/search-insert-position/) 。這題需要將mid歸到左區間當中。(startend)的時候跳出迴圈即可,不會造成死迴圈或者棧溢位。但是情況2和情況3往往會造成邊界條件分析不清晰導致產生死迴圈! ​ 為什麼說邊界條件難呢?如果mid值取得不對,容易造成死迴圈。且造成死迴圈往往是在剩下兩個值的時候產生。這裡舉個例子: 在條件2的時候,**下面的程式碼就會造成死迴圈!!!!** ```java //在情況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); } ``` 主函式為: ```java public static void main(String[] args) { int[] array = {0,2}; int re = BinarySearch(array,0,0,1); System.out.println(re); } ``` 報錯為: ```java 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) ``` 但是我們將程式碼換成第一種情況,即:將第一個條件的 **<=** 變為 **<** 並將區間變化修改成第一種情況,將會是正確的! ```java //在情況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); } ``` 同樣用第一個主函式跑,結果是正確的! ```java 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]這樣兩個區間。 ​ 以後碰到這種情況,牢記分開元素準則,我們只需要當場覆盤一下這個過程即可避免棧溢位的