二分查找算法

1 定义

       折半查找算法也称二分查找算法或折半搜索算法,是一种在有序数组(即前提必须是数组是已经排好序的)中查找某一特定元素的搜索算法。搜素过程是

1)计算中间元素mid

从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;

2)比较左边元素left, 比较右边元素right

如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。

       这种搜索算法每一次比较都使搜索范围缩小一半。如下图,lowerBound代表查找起始范围,upperBound代表查找终止位置,mid是中间元素。我们需要考虑的是算法何时结束?

2 java代码实现

我们定义如下的一个函数来实现二分查找

[java]  view plain copy
  1.       
  2.       / * @param a 待查找的数组  
  3.      * @param fromIndex  查找的起始位置  
  4.      * @param toIndex    查找的终止位置是(toIndex-1)  
  5.      * @param key  要查找的数据  
  6.      * @return  查找数据在数组中的位置  
  7.      * 如果查找的数据不存在,则返回 - 1  
  8.      * insertion point是查找数据如果插入数组时它插入的位置  
  9.      */  
  10.      public static int search(int[] a,int fromIndex,int toIndex,int key){  
  11.     //add  your code here  
  12. }  
1) 代码第一次实现如下


观察上述代码,你能发现几个问题呢?

1)输入控制

如果数组为null,那么a[i]势必会抛出异常;

如果输入的fromIndex>toIndex,函数本身就没有意义;而且fromIndex和toIndex如果不在数组长度范围(0~length-1)内怎么办?

上述可能出现的异常,都源于没有对输入参数做控制处理。

[java]  view plain copy
  1. //input control  
  2. if(a==null)  //if a is null  
  3.     return -1;  
  4. if(fromIndex<0 || toIndex>a.length-1 || fromIndex>toIndex)  
  5.     throw new Exception("illegal input!");  
2)注意到while循环里面mid没有?在key<a[mid]和key>a[mid]之前,a[mid]已和key比较过,能进行 key<a[mid]或key>a[mid],说明a[mid]肯定是和key不相等,所以后面没有必要再比较mid了,修改如下


3)注意到while循环没有循环结束的条件,这是致命的。那么什么时候二分查找结束呢?

注意到进入下一次二分查找改变的地方是upperBound=mid-1和lowerBound=mid+1,所以在这里我们考虑临界条件发生的情况。如果二分后还有2个元素时,我们记为i,i+1,那么mid=(2i+1)/2=i,那么分为2半(i,upperBound=mid-1=i-1)(lowerBound=mid+1=i+1,i+1)

如果是(i,upperBound=mid-1=i-1),那么再做一次划分就会出现lowerBound>upperBound的异常;

如果是(lowerBound=mid+1=i+1,i+1),那么再做一次划分也会出现lowerBound>upperBound;

所以循环结束的条件是数组中只剩2个元素时,直接折半查找每次把查找范围砍掉一半,最后当只有一个元素时(此时lowerBound=upperBound),经过语句upperBound=mid-1或lowerBound=mid+1后,lowerBound>upperBound,此时循环结束。所以临界条件(base case)是lowerBound>upperBound,此时停止查找。

[java]  view plain copy
  1. package zyang.binaryInsertSort;  
  2.   
  3. /**  
  4.  * @version 1.0 
  5.  * @date    2012-11-14 上午10:03:59  
  6.  * @fuction  binary search 
  7.  * binary search 
  8.  * 折半搜索,也称二分查找算法、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。 
  9.  * 搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束; 
  10.  * 如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。 
  11.  * 如果在某一步骤查找的数组的下界大于上届,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。 
  12.  * 时间复杂度:二分搜索每次把搜索区域砍掉一半,很明显时间复杂度为O(logn)。(n代表集合中元素的个数) 
  13.  * 空间复杂度:O(1) 
  14.  */  
  15.   
  16. public class BinarySearch   
  17. {  
  18.     private BinarySearch()  
  19.     {}  
  20.           
  21.     /** 
  22.      * 根据给定数组下标的范围,用折半查找算法查找指定数据 
  23.      * 数组必须是已排好序的,否则查找结果没有意义。如果数组中有相等的元素,则会找到其中之一,至于是哪一个,是不确定的 
  24.      * @param a 待查找的数组 
  25.      * @param fromIndex  查找的起始位置 
  26.      * @param toIndex    查找的终止位置 
  27.      * @param key  要查找的数据 
  28.      * @return  查找数据在数组中的位置 
  29.      * 如果查找的数据不存在,则返回(- 1) 
  30.      * insertion point是查找数据如果插入数组时它插入的位置 
  31.      * @throws Exception  
  32.      */  
  33.      //implement by loop  
  34.      public static int search(int[] a,int fromIndex,int toIndex,int key) throws Exception  
  35.         {    
  36.              /* 
  37.               * input control:specail case 
  38.               * 1)a is null 
  39.               * 2)[fromIndex,toIndex]not in range [0, a.length-1] 
  40.               * 3) fromIndex>toIndex 
  41.               * step: 
  42.               * 1) get mid 
  43.               * 2) compare key with mid 
  44.               * just equal:key==mid,return mid 
  45.               * in left:key<mid, toIndex=mid-1 
  46.               * in right:key>mid, fromIndex=mid+1 
  47.               * 3) do the loop of step2 
  48.               * base case: fromIndex > toIndex 
  49.               */  
  50.                
  51.            
  52.             //input control  
  53.             if(a==null)  //if a is null  
  54.                 return -1;  
  55.             if(fromIndex>toIndex || fromIndex<0 || toIndex>a.length-1)  
  56.                 throw new Exception("illegal input!");  
  57.               
  58.             int mid;  //mid  
  59.             while(fromIndex <= toIndex){  //base case:lowerBound>upperBound  
  60.                 mid =(fromIndex+toIndex)/2;  
  61.                 //found it  
  62.                 if(key==a[mid])  
  63.                     return mid;  
  64.                 //left  
  65.                 if(key<a[mid])  
  66.                     toIndex=mid-1;  
  67.                 //right  
  68.                 else  
  69.                     fromIndex=mid+1;  
  70.             }//end while  
  71.             return -1//not found  
  72.         }//end search()  
  73.        
  74.      //implement by recursion  
  75.      public static int searchRecursion(int[] a,int fromIndex, int toIndex, int key) throws Exception{  
  76.             //input control  
  77.             if(a==null)  //if a is null  
  78.                 return -1;  
  79.             if(fromIndex>toIndex || fromIndex<0 || toIndex>a.length-1)  
  80.                 throw new Exception("illegal input!");  
  81.               
  82.             int mid; //mid  
  83.   
  84.                 //base case  
  85.                 if(fromIndex>toIndex)  
  86.                     return -1//not found  
  87.                 else{  
  88.                     mid=(fromIndex+toIndex)/2;  
  89.                     if(key==a[mid])  
  90.                         return mid;  
  91.                     if(key<a[mid]) //left  
  92.                         searchRecursion(a, fromIndex, mid-1, key);  
  93.                     else  
  94.                         searchRecursion(a, mid+1, toIndex, key);  
  95.                 }//end else  
  96.      }//end searchRecursion()  
  97.               
  98.      public static int search(int[] a,int key) throws Exception  
  99.      {  
  100.         return  search(a, 0, a.length-1, key);  
  101.      }  
  102.       
  103.     /** 
  104.      * @param args 
  105.      * @throws Exception  
  106.      */  
  107.     public static void main(String[] args) throws Exception {  
  108.         //test case  
  109.           int[] a={1,2,3,4,5,6};  //test array  
  110.           //specail input   
  111.           //a==null  
  112.           System.out.println("a==null");  
  113.           System.out.println(BinarySearch.search(null,2,5,4));  
  114.           //fromIndex>toIndex  
  115.           System.out.println("fromIndex>toIndex");  
  116.           System.out.println(BinarySearch.search(a,5,2,4));  
  117.           // out of array bound:fromIndex<0 toIndex<0 toIndex>a.length-1  
  118.           System.out.println("out of array bound");  
  119.           System.out.println(BinarySearch.search(a,-1,2,4));  
  120.           System.out.println(BinarySearch.search(a,1,-2,4));  
  121.           System.out.println(BinarySearch.search(a,1,7,4));  
  122.           //logic test      
  123.           System.out.println("key in the arrary:"+BinarySearch.search(a, 4));  
  124.           System.out.println("key not in the array:"+BinarySearch.search(a, 4));  
  125.           char[] test={'2','3'};  
  126.     }  
  127. }  

折半查找的边界条件

      折半查找每次把查找范围砍掉一半,最后当只有一个元素时(此时lowerBound=upperBound),经过语句upperBound=mid-1或lowerBound=mid+1后,lowerBound>upperBound,此时循环结束。所以临界条件(base case)是lowerBound>upperBound,此时停止查找。

3 复杂度分析

时间复杂度

折半查找每次把搜索区域砍掉一半,很明显时间复杂度为O\left( \log n  \right)。(n代表集合中元素的个数)

空间复杂度

O\left(  1  \right)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值