代码改变世界

二分查找排序

2013-10-30 22:08  夜与周公  阅读(557)  评论(0编辑  收藏  举报

  二分查找算法,是在大学阶段学习的基础性算法。算法思想简单,但要写出一个正确的二分查找算法并不简单,从1946年提出,到1962年才完成一个正确的二分查找排序算法。二分查找排序算法的变种,也常常出现在各个IT公司的面试题中。本文总结资料尝试给出一个“正确”的二分查找算法,最后给出一个二分查找排序的变种问题。

  1. 经典二分查找排序的算法:

 1 int BinarySearch(int* array, int n, int x)
 2 {
 3     assert((array != NULL) && (0 <= n));
 4 
 5     int low = 0;
 6     int high = n -1;
 7 
 8     while (low <= high)
 9     {
10         int mid = low + ((high - low) >> 1);
11         if (array[mid] == x)
12         {
13             return mid;
14         }
15         if (x > array[mid])
16         {
17             low = mid + 1;
18         }
19         else
20         {
21             high = mid -1;
22         }
23     }
24     return -1;
25 }

  可能需要关注细节问题:

  1)循环条件

  在第8行,循环的条件设置成 low <=high。如果设置成low<high,是存在问题的:当只有数组只有一个元素,并且该元素就是需要查找的元素时,算法返回错误的值-1,查找失败。

  2)求中间元素

  在第10行,求中间元素使用的代码:mid = low + ((high - low) >> 1)。一些书籍中给出的是:mid = (high + low) >> 1,如果high+low很大时,会造成溢出。这种情况出现的概率较小,但是为了程序适应比较恶劣条件,使用第10行代码求中间元素就显得有必要了。

(ps:上面的代码可能还存在带完善的地方,欢迎拍砖。。。)

  2. 二分查找排序算法的变种

  问题描述:已知一个有序的数组a[N],循环左移k位(k<N)后得到新的数组b[N],在这个新的数组中查找元素x。例如:数组 a[N] = {1, 3, 5, 7, 9, 10},循环左移2位后变成数组b[N]= {5, 7, 9, 10, 1, 3},在这个新的数组上查找某个元素x。

  上题是我在面试中问道的问题,比较庆幸的是我在《剑指offer》一书中碰到了类似的一题,书中需要找出数组b[N]最小的最小元素,因此能比较迅速的解决了这个问题。解决问题的思路和《剑指offer》提供的思路一致。

  解题思路:数组b[N]中存在着两个部分有序的子数组,b1:{5, 7, 9, 10}和b2:{1, 3},其中b1中的元素均大于b2中的元素。在使用二分查找算法时,中间元素mid必定落入b1或者b2某个递增序列中,具体解决步骤如下:

  Init:    low = 0, high = N-1, mid = b[low + (high - low) >> 1]

  step1: 求出数组b的中间元素mid, 如果mid == x,则返回成功,否则进入步骤step2。

  step2: if(a[low]<mid),mid落入b1数组,此时:

         1) if (a[low]<x<mid), x在有序区间[low, mid),则将high = mid -1

         2) 否则,x落入区间(mid, high],则将low = mid +1

       else mid落入b2数组,此时:

         1)if (mid< x< b[high]),x在有序区间(mid, high],则将low = mid + 1

         2)否则, x落入区间[low, mid),则将hign = mid -1

  step3: if(low <= hign) 继续步骤step1,否则返回查找失败

 具体的代码如下:

 1 int rotate_array_search(int a[], int n, int x)
 2 {
 3   int low = 0;
 4   int high = n-1; 
 5   while(low <= high) 
 6   {
 7     int mid = low + ((high - low) >> 1); 
8 if(a[mid] == x) 9 return mid;
10 if(a[mid] >= a[low]) 11 { 12 if(x < a[mid] && x >= a[low]) 13 { 14 high = mid -1; 15 } 16 else 17 { 18 low = mid + 1; 19 } 20 } 21 else 22 { 23 if(x > a[mid] && x <= a[high]) 24 { 25 low = mid + 1; 26 }
     else 27 { 28 high = mid -1; 29 } 30 } 31 } 32 return -1; // 查找失败,则返回-1 33 }