二分查找算法

来源:互联网 发布:平板淘宝怎么开店 编辑:程序博客网 时间:2024/06/18 14:23

1.必须针对有序数组

2.查找时间为logn

3.如何取中间值以及边界判断

二分查找又称折半查找,它是一种效率较高的查找方法。

折半查找的算法思想是将数列按有序化(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,如果要找的元素值小 于该中点元素,则将待查序列缩小为左半部分,否则为右半部分。通过一次比较,将查找区间缩小一半。 折半查找是一种高效的查找方法。它可以明显减少比较次数,提高查找效率。但是,折半查找的先决条件是查找表中的数据元素必须有序。


折半查找法的优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。


二分算法步骤描述
① 首先确定整个查找区间的中间位置 mid = ( left + right )/ 2
② 用待查关键字值与中间位置的关键字值进行比较;
若相等,则查找成功
若大于,则在后(右)半个区域继续进行折半查找
若小于,则在前(左)半个区域继续进行折半查找
③ 对确定的缩小区域再按折半公式,重复上述步骤。
最后,得到结果:要么查找成功, 要么查找失败。折半查找的存储结构采用一维数组存放。 折半查找算法举例
对给定数列(有序){ 3,5,11,17,21,23,28,30,32,50,64,78,81,95,101},按折半查找算法,查找关键字值为81的数据元素。


二分查找算法讨论:
优点:ASL≤log2n,即每经过一次比较,查找范围就缩小一半。经log2n 次计较就可以完成查找过程。
缺点:因要求有序,所以要求查找数列必须有序,而对所有数据元素按大小排序是非常费时的操作。另外,顺序存储结构的插入、删除操作不便利。
考虑:能否通过一次比较抛弃更多的部分(即经过一次比较,使查找范围缩得更小),以达到提高效率的目的。
可以考虑把两种方法(顺序查找和折半查找)结合起来,即取顺序查找简单和折半查找高效之所长,来达到提高效率的目的?实际上这就是分块查找的算法思想。


转载自http://blog.csdn.net/yefengzhichen/article/details/52372407#comments

三种基本版本:

1.1 二分查找原始版--查找某个数的下标(任意一个)

在有序数组中查找某个数,找到返回数的下标,存在多个返回任意一个即可,没有返回-1。所有程序采用左右均为闭区间,即函数中n为最后一个元素下标,而不是元素个数。典型代码如下:

[java] view plain copy
 print?
  1. public int binarySearch(int[] a, int n, int key){  
  2.         //n + 1 个数  
  3.         int low = 0;  
  4.         int high = n;  
  5.         int mid = 0;  
  6.         while(low <= high) {  
  7.             mid = low + ((high-low) >> 1);  
  8.             if(key == a[mid]) {  
  9.                 return mid;  
  10.             } else if(key < a[mid]) {  
  11.                 high = mid - 1;  
  12.             } else {  
  13.                 low = mid + 1;  
  14.             }  
  15.         }  
  16.         return -1;  
  17.     }  

1.2 查找第一个大于等于某个数的下标

例:int[] a = {1,2,2,2,4,8,10},查找2,返回第一个2的下标1;查找3,返回4的下标4;查找4,返回4的下标4。如果没有大于等于key的元素,返回-1。

下面是代码,改动只有两处:

[java] view plain copy
 print?
  1. public int firstGreatOrEqual(int[] a, int n, int key){  
  2.         //n + 1 个数  
  3.         int low = 0;  
  4.         int high = n;  
  5.         int mid = 0;  
  6.         while(low <= high) {  
  7.             mid = low + ((high-low) >> 1);  
  8.             if(key <= a[mid]) {  
  9.                 high = mid - 1;  
  10.             } else {  
  11.                 low = mid + 1;  
  12.             }  
  13.         }  
  14.         return low <= n ? low : -1;  
  15.     }  


解释:

1、条件为key<=a[mid],意思是key小于等于中间值,则往左半区域查找。如在 {1,2,2,2,4,8,10}查找2,第一步,low=0, high=6, 得mid=3, key <= a[3],往下标{1,2,2}中继续查找。

2、终止前一步为: low=high,得mid = low,此时如果key <= a[mid],则high会改变,而low指向当前元素,即为满足要求的元素。如果key > a[mid],则low会改变,而low指向mid下一个元素。

3、如果key大于数组最后一个元素,low最后变为n+1,即没有元素大于key,需要返回 -1。

1.3 查找第一个大于某个数的下标

例:int[] a = {1,2,2,2,4,8,10},查找2,返回4的下标4;查找3,返回4的下标4;查找4,返回8的下标5。如果没有大于key的元素,返回-1。

如下是代码,与上面大于等于某个数仅判断一个符号不同:

[java] view plain copy
 print?
  1. public int firstGreat(int[] a, int n, int key){  
  2.         //n + 1 个数  
  3.         int low = 0;  
  4.         int high = n;  
  5.         int mid = 0;  
  6.         while(low <= high) {  
  7.             mid = low + ((high-low) >> 1);  
  8.             if(key < a[mid]) {  
  9.                 high = mid - 1;  
  10.             } else {  
  11.                 low = mid + 1;  
  12.             }  
  13.         }  
  14.         return low <= n ? low : -1;  
  15.     }  


以上原型的扩展应用

2.1 查找数组中某个数的位置的最小下标,没有返回-1

直接用上面1.2,但需要处理一下,即当返回的low位置不等于key时,也返回-1。如下:

[java] view plain copy
 print?
  1. public int firstIndex(int[] a, int n, int key){  
  2.         //n + 1 个数  
  3.         int low = 0;  
  4.         int high = n;  
  5.         int mid = 0;  
  6.         while(low <= high) {  
  7.             mid = low + ((high-low) >> 1);  
  8.             if(key <= a[mid]) {  
  9.                 high = mid - 1;  
  10.             } else {  
  11.                 low = mid + 1;  
  12.             }  
  13.         }  
  14.         return (low <= n) && (a[low] == key) ? low : -1;  
  15.     }  


2.2 查找数组中某个数的位置的最大下标,没有返回-1

直接用上面1.3,得到的下标 - 1即可,但也需要处理一下,即不用判断low <= n,而是判断low-1>=0,且返回的low-1位置等于key时,才返回位置low - 1。如下:

[java] view plain copy
 print?
  1. public int lastIndex(int[] a, int n, int key){  
  2.         //n + 1 个数  
  3.         int low = 0;  
  4.         int high = n;  
  5.         int mid = 0;  
  6.         while(low <= high) {  
  7.             mid = low + ((high-low) >> 1);  
  8.             if(key < a[mid]) {  
  9.                 high = mid - 1;  
  10.             } else {  
  11.                 low = mid + 1;  
  12.             }  
  13.         }  
  14.         return (low - 1 >= 0 && (a[low - 1] == key))? low - 1: -1;  
  15.     }  


2.3 查找数组中小于某个数的最大下标,没有返回-1

直接用上面1.2,返回的low-1位置即为可能的位置。也需要处理一下,需要low-1>=0。

[java] view plain copy
 print?
  1. public int firstLess(int[] a, int n, int key) {  
  2.         // n + 1 个数  
  3.         int low = 0;  
  4.         int high = n;  
  5.         int mid = 0;  
  6.         while (low <= high) {  
  7.             mid = low + ((high - low) >> 1);  
  8.             if (key <= a[mid]) {  
  9.                 high = mid - 1;  
  10.             } else {  
  11.                 low = mid + 1;  
  12.             }  
  13.         }  
  14.         return (low - 1 >= 0) ? low - 1 : -1;  
  15.     }  

2.4 查找数组中某个数的出现次数

直接使用1.2,1.3即可,但需要修改1.2,1.3的返回条件,当low>n时,直接返回low。举例说明:int[] a = {1,2,2,2,4,8,10},查找10的个数,根据1.2程序得到low = 6,而1.3程序返回值 = -1,不符合。因此需要将1.3得到的low = n + 1值返回即可。同理,如果查找15,此时1.2程序返回值为-1,也得改为直接返回low = n+1。

使用1.2,1.3,分别求得下界first和上界last,两个相减即(last - first)是key出现次数。如下:

[java] view plain copy
 print?
  1. public int getCount(int[] a, int n, int key) {  
  2.     // n + 1 个数  
  3.     int first = firstGreatOrEqual2(a, n, key);  
  4.     int last = firstGreat2(a, n, key);  
  5.     return last - first;  
  6. }  
  7.   
  8. public int firstGreatOrEqual2(int[] a, int n, int key) {  
  9.     // n + 1 个数  
  10.     int low = 0;  
  11.     int high = n;  
  12.     int mid = 0;  
  13.     while (low <= high) {  
  14.         mid = low + ((high - low) >> 1);  
  15.         if (key <= a[mid]) {  
  16.             high = mid - 1;  
  17.         } else {  
  18.             low = mid + 1;  
  19.         }  
  20.     }  
  21.     return low;  
  22. }  
  23.   
  24. public int firstGreat2(int[] a, int n, int key) {  
  25.     // n + 1 个数  
  26.     int low = 0;  
  27.     int high = n;  
  28.     int mid = 0;  
  29.     while (low <= high) {  
  30.         mid = low + ((high - low) >> 1);  
  31.         if (key < a[mid]) {  
  32.             high = mid - 1;  
  33.         } else {  
  34.             low = mid + 1;  
  35.         }  
  36.     }  
  37.     return low;  
  38. }