二分查找(上):如何用最省内存的方式实现快速查找功能?

二分查找算法:一种针对有序数据集合的查找算法,也叫折半查找算法。

思考题:

       假设有1000万个整数数据,每个数据占8字节,如何设计数据结构和算法,快速判断某个整数是否出现在这1000万数据中?希望实现这个功能所需要的内存空间不超过100M。

1. 理解二分查找思想

         二分查找是非常简单易懂的快速查找算法,在生活中也是比较常见。举一个猜字游戏为例,进行说明。随机写一个0-99的数字,猜写的到底是哪个数。假设我写的是23,大概会按照下面这个步骤进行猜测:

       

        实际的开发场景:

        假设这里有1000条订单数据,已经按照订单金额从小到大排序了,每个订单金额都不同,并且最小单位是元。我们现在想知道是否存在订单金额为19元的订单。如果存在,就返回订单数据,如果不存在则返回null。

        传统的方法可能就是依次遍历这1000条数据,直到找到金额等于19元的订单为止。但是这样的查找会比较慢,最坏情况下,可能需要遍历1000条记录才能找到。那用二分查找能不能快速解决这个问题呢?

        为了方便,这里假设只有10个订单,订单金额分别为:8,11,19,23,27,33,45,55,67,98。利用二分查找思想,每次都与区间的中间数据比较大小,缩小查找的范围。参看下图进行理解,其中low和high表示待查找区间的下标,mid表示待查找区间的中间元素的下标。

        

        总结: 二分查找针对的是一个有序的数据集合,查找思想有点类似于分治思想。每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或是区间被缩小为0。

2. 二分查找的时间复杂度

        假设数据的大小是n,每次查找数据都会缩小为原来的一半,最坏的情况下,直到查找空间被缩小为空,才停止。

       

        其中n/{2^{k}}=1时,k值就是总共缩小的次数,而每一次缩小操作都只涉及两个数据的大小比较,所以经过k次区间缩小操作,时间复杂度是O(k)。由于n/{2^{k}}=1,可以求得k = log_{2}n,所以时间复杂度是O(logn)。

        这种对数时间复杂度,是一种极其高效的时间复杂度,有时候甚至会比复杂度为O(1)的算法还要高效。比如n等于2的32次方,大约42亿,也就是说42亿数据用二分查找只需要比较32次。而O(1)有可能会表示一个非常大的常量数据,所以有时候算法的执行效率没有O(logn)高。

3. 二分查找的递归和非递归实现

        最简单的二分查找情况:有序数组中不存在重复元素。下面是一个最简单的二分查找算法的Java代码:

public int bsearch(int[] a, int n, int value){
    int low = 0;
    int high = n-1;
    
    while(low <= high){
       int mid = (low + high)/2;
       if (a[mid] == value){
         return mid;
       } else if {a[mid] < value){
         low = mid + 1;
       } else {
         high = mid - 1;
       } 
   }
   return -1;

}

   容易出错的三个地方:

  1. 循环退出条件:low<=high,而不是low<high。
  2. mid取值。mid = (low+high)/2这种写法有问题,low与high较大时,和可能会溢出。改进方法写成:low+(high-low)/2。
  3. low和high的更新。low = mid+1,high = mid-1。如果直接写成是low = mid,high = mid,可能会导致死循环。

    二分查找的递归实现:

//二分查找的递归实现
public int bsearch(int[] a, int n, int value){
     return bsearchInternally(a, 0,n-1,value);

}

public int bsearchInternally(int[] a, int low, int high, int value){
  if (low > high) return -1;
  
  int mid = low + ((high - low)>>1);
  if (a[mid] == value){
     return mid;
  } else if (a[mid] < value){
    return bsearchInternally(a, mid+1, high, value);
  } else { 
    return bsearchInternally(a, low, mid-1, value);
  }

}

4. 二分查找的应用局限性

  • 二分查找依赖的是顺序表结构,简单点说就是数组(二分查找是根据下标随机访问元素)
  • 二分查找针对的是有序数据
  • 二分查找不太适合数据量太小的查找(数据量小的时候,二分查找跟顺序遍历差不多)
  • 二分查找不适合数据量太大的查找(数据量大的时候,数据很难用数组进行存储,所以就不适合用二分查找)

5. 解答思考题

        将数据存储在数组中,内存占用差不多80M,符合内存限制要求。将这1000万数据从小到大排序,然后再利用二分查找算法,就可以快速地查找到想要的数据了。

       

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值