二分查找其实是很难的问题,主要是边界问题难以解决,下面就我自己的理解说一说,我们应该如何进行思考。
二分法主要面向两类问题,
一类是无重复元素的按升序排序的数组
另一类是面向有重复元素的数组。
而每一类都必须面临两个难点,对应如下
1:如何将问题的规模减小,因为二分就是分治的应用,我们必须要保证问题在逐步减小,不会出现死循环的情况。
2:while循环终止的条件,是选用low<high 还是选择low<=high(其实有这两种选择就够了,不需要有其他的写法,其余的写法其实是冗余的,可以简化)
好,下面来结合这两个难点,我们想一下如何解决如下几个问题
1:传统的二分法,求一个按升序排列的数组中是否存在某一个key
这个问题肯定是没有重复元素的问题,先上一段代码
int binary(int Key,vector<int> nums){
int low=0,high=nums.size()-1,mid;
while(low<=high){
mid=low+((high-low)>>2);
if(nums[mid]==Key){
return mid;
}
else if(nums[mid]<Key){
low=mid+1;
}
else if(nums[mid]>Key){
high=mid-1;
}
}
return -1;
}
对于问题一:我们可以知道,上面对应的是三段式,当它等于的时候,就直接return这个下标,然后没找到的话,就需要进行丢弃一半的元素,例如Key>nums[mid],那么就会丢弃掉原来的low到mid之间的所有元素,总的丢掉的是mid-low+1个元素,这是大于1的,而另外一种情况,Key<nums[mid],要丢弃掉原来的mid到high之间的所有元素,也是要丢掉high-mid+1个元素,也是大于1的,这样就解决了我们的第一个问题,不会死循环,即问题的每一步都在逐渐减小。那么我们就思考,while中循环的条件,到底是选择low<=high,还是选择low<high,我们假定这样想,如果选择了第二种,那么我们最后一次循环的时候必然是low=high
那么很自然上一个的mid也等于low=high,那么这个时候,我们想,那么上一步的high不就没有判断吗?因为我们判断的时候就是[low,high],左闭右闭,最后一步,mid=low,此时high决对是没有判断的,下一步就会退出,那么就会误判。好了这个问题就到此结束
2:有重复元素的数组,求Key的最左边的那个值的下标,