经典二分查找的分析

前言

对于二分查找,也可以叫折半查找,你一定不会陌生,以前应该也有玩过猜价格的有戏,让你在某个价格区间内,猜某个商品的价格,如果没猜到就告诉你是猜多了还是猜少了,然后让你继续猜,这其实就可以运用到二分查找的方式来最快的猜到那个价格。

高效的查找方式

二分查找是一种非常高效的查找方式,对于一个2的32次方的数来说,大约有42亿之多,但由于二分查找拥有logn的时间复杂度,所以也就最多只需要32次就能查找出结果。

简单但不容易

二分查找虽然思路非常简单,但是要想写出正确的二分查找,其实并没有那么容易,这点我相信只要写过的都应该能够体会到,主要问题就在于边界的处理。

代码实现

    public int binarySearch(int[] nums, int target) {
    
    
        int l = 0;
        int r = nums.length - 1;
        while (l <= r) {
    
    
            //找到中点,这样写l + ((r - l) >> 1) 等同于 (l+r)/2,但是可以防止l+r溢出
            int mid = l + ((r - l) >> 1);
            //如果等于目标值直接返回
            if (nums[mid] == target) {
    
    
                return mid;
            }
            //如果中间点大于目标值,则找左半边,否则找右半边
            if (nums[mid] > target) {
    
    
                r = mid - 1;
            } else {
    
    
                l = mid + 1;
            }
        }
        //找不到返回-1
        return -1;
    }

容易出问题的地方

  • 问题1:找中点,l + ((r - l) >> 1)对于奇数来说,找的刚好就是中间的位置,对于偶数来说,找的是靠左边的位置。
    这样写可以防止(l+r)/2时,l+r可能会溢出的问题。

  • 问题2:while (l <= r),注意这里是 l<=r,而不是l<r
    如果写成l<r,就会把第一个和最后一个位置的元素漏掉。

  • 问题3:r和l的更新,应该是mid-1或者是mid+1
    如果直接更新为mid,则可能会发生死循环,比如把r = mid-1,直接写成r = mid,然后在1,2,3,5,6,7,8,查找4,就会发生死循环。因为最后会算出l==r(都等于3),mid等于3,并且nums[mid](5)) > target(4),然后r被无限赋值为3。

限制条件

虽然二分查找非常的快,但也是有一定的局限性的,其中最大的问题就是要求待查找的数据是有序的,其次要求支持高效的随机访问,如果每一次定位中间位置都需要花费额外的时间去查找,那就不能说其整个时间复杂度是logn的了,最后既然要求有序还要支持随机访问,那能想到的数据结构就是数组了,所以一般对于二分查找来说,还需要连续的内存空间,如果二分查找的数据量非常的大,可能对内存的要求也比较苛刻。

Guess you like

Origin blog.csdn.net/CSDN_WYL2016/article/details/120320978