本人最近一直在作一些算法方面的学习,最近也刷了一些力扣题目,我将我作过的题目分享到了个人GitHub上:算法题解能够供你们参考。最近在刷题的过程中,我发现我总是在二分法的边界条件上出问题,常常是出现栈溢出的状况。因此想写一篇文章,记录一下个人学习心得与体会。java
二分法每每是对一个有序的数据形式,进行查找特定值target的算法。git
该算法的优点是时间复杂度仅为O(log n),相较于顺序查找O(n)的时间复杂度有着明显的提高。github
二分法有4个重要的值:target,start, end, mid.算法
我将二分法的区间划分,分为3钟类型:数组
划分为[start,mid]和[mid+1,end]这两个区间函数
划分为[start,mid-1]和[mid,end]这两个区间学习
划分为[start,mid-1]和mid和[mid+1,end]这三个区间code
这三种分法的区别就是:mid该放在哪里排序
该如何划分区间,是得视具体状况进行的。有的经典二分彻底可使用第三种分法(我的最喜欢的,由于边界条件最简单),可是有的时候,必须得用第一种和第二种形式,如:力扣第35题 。这题须要将mid归到左区间当中。(start<target <= mid)。leetcode
状况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]这样两个区间。
之后碰到这种状况,牢记分开元素准则,咱们只须要当场复盘一下这个过程便可避免栈溢出的产生!