【转】二分

原创:http://blog.chinaunix.net/uid-1844931-id-3337784.html

前几天在论坛上看到有统计说有80%的程序员不能够写对简单的二分法。二分法不是很简单的吗? 这难道不是耸人听闻? 

其实,二分法真的不那么简单,尤其是二分法的各个变种。 最最简单的二分法,就是从一个排好序的数组之查找一个key值。 如下面的程序:

 1 int search(int *arr, int n, int key){
 2    int left=0, right=n-1;
 3      //首先要把握下面几个要点:  
 4      //right=n-1 => while(left <= right) => right=mid-1; 
 5      //right=n   => while(left <  right) => right=mid; 
 6      while(left<=right){
 7       int mid=left+((right-left)<<1);//防止溢出,移位也更高效。
 8       if(arr[mid]>key) right=mid-1;
 9       else if (arr[mid]<key) left=mid+1;
10       else return mid;
11         //数组中不相等的情况更多
12       //如果每次循环都判断一下是否相等,将耗费时间
13    }
14    return -1;
15 }

 

这个程序,相信只要是一个合格的程序员应该都会写。 稍微注意一点, 每次移动left和right指针的时候,需要在mid的基础上+1或者-1, 防止出现死循环, 程序也就能够正确的运行。

但如果条件稍微变化一下, 你还会写吗?如,数组之中的数据可能可以重复,要求返回匹配的数据的最小(或最大)的下标;更近一步, 需要找出数组中第一个大于key的元素(也就是最小的大于key的元素的)下标,等等。 这些,虽然只有一点点的变化,实现的时候确实要更加的细心。 下面列出了这些二分检索变种的实现。

1. 找出第一个与key相等的元素

 1 int searchFirstEqual(int *arr, int n, int key){
 2   int left=0,right=n-1;
 3   while(left<=right) {
 4   int mid=(left+right)/2;
 5   if(arr[mid]>=key) right=mid-1;
 6   else if(arr[mid]<key) left=mid+1;
 7   }
 8   if(left<n&&arr[left]==key) return left;
 9   return -1;
10 }

2. 找出最后一个与key相等的元素

 1 int searchLastEqual(int *arr, int n, int key){
 2   int left=0,right=n-1;
 3   while(left<=right) {
 4   int mid=(left+right)/2;
 5   if(arr[mid]>key) right=mid-1;
 6   else if(arr[mid]<=key) left=mid+1; 
 7   }
 8   if(right>=0&&arr[right]==key) return right;
 9   return -1;
10 }

3. 查找第一个等于或者大于Key的元素

1 int searchFirstEqualOrLarger(int *arr,int n,int key){
2   int left=0, right=n-1;
3   while(left<=right) {
4     int mid=(left+right)/2;
5     if(arr[mid]>=key) right=mid-1;
6     else if (arr[mid]<key) left=mid+1;
7   }
8   return left;
9 }

4. 查找第一个大于key的元素

1 int searchFirstLarger(int *arr, int n, int key){
2   int left=0, right=n-1;
3   while(left<=right) {
4     int mid=(left+right)/2;
5     if(arr[mid]>key) right=mid-1;
6     else if (arr[mid]<=key) left=mid+1;
7   }
8   return left;
9 }

5. 查找最后一个等于或者小于key的元素

1 int searchLastEqualOrSmaller(int *arr,int n,int key){
2   int left=0,right=n-1;
3   while(left<=right) {
4     int m=(left+right)/2;
5     if(arr[m]>key) right=m-1;
6     else if (arr[m]<=key) left=m+1;
7   }
8   return right;
9 }

6. 查找最后一个小于key的元素

1 int searchLastSmaller(int *arr,int n,int key){
2   int left=0, right=n-1;
3   while(left<=right) {
4     int mid=(left+right)/2;
5     if(arr[mid]>=key) right=mid-1;
6     else if(arr[mid]<key) left=mid+1;
7   }
8   return right;
9 }

下面是一个测试的例子:

 1 int main(){
 2   int arr[17] = {1, 
 3           2, 2, 5, 5, 5, 
 4           5, 5, 5, 5, 5, 
 5           5, 5, 6, 6, 7};
 6   printf("First Equal : %2d \n", searchFirstEqual(arr, 16, 5));
 7   printf("Last Equal : %2d \n", searchLastEqual(arr, 16, 5));
 8   printf("First Equal or Larger : %2d \n", searchFirstEqualOrLarger(arr, 16, 5));
 9   printf("First Larger : %2d \n", searchFirstLarger(arr, 16, 5));
10   printf("Last Equal or Smaller : %2d \n", searchLastEqualOrSmaller(arr, 16, 5));
11   printf("Last Smaller : %2d \n", searchLastSmaller(arr, 16, 5));
12   return 0;
13 }

最后输出结果是:

First Equal : 3
Last Equal : 12
First Equal or Larger : 3
First Larger : 13
Last Equal or Smaller : 12
Last Smaller : 2

很多的时候,应用二分检索的地方都不是直接的查找和key相等的元素,而是使用上面提到的二分检索的各个变种,熟练掌握了这些变种,当你再次使用二分检索的检索的时候就会感觉的更加的得心应手了。

 搜狗还有一道面试题目:

有一个数组,该数组的特点是:前一部分递增有序,后一部分递减有序,求数组的峰值位置。(严格先增后减)

View Code
 1 #include <stdio.h>
 2 //数组是先增后减数组。
 3 int findMax(int *a ,int n){
 4     int low = 0,high = n-1,mid;
 5     while(low <= high){
 6         mid = low + ((high-low)>>1);
 7         if((mid + 1)<=high && (mid-1)>=low){
 8             if(a[mid] >= a[mid-1] && a[mid] >= a[mid + 1]){
 9                 return mid;
10             }else if(a[mid] >= a[mid-1] && a[mid] <=a[mid + 1]){
11                 low = mid + 1;
12             }else{
13                 high = mid - 1;
14             }
15         }else if((mid+1) <= high){
16             if(a[mid] <= a[mid+1]){
17                 return mid+1;
18             }else{
19                 return mid;
20             }
21         }else{
22             if(a[mid] <= a[mid-1]){
23                 return mid-1;
24             }else{
25                 return mid;
26             }
27         }
28 
29     }
30 }
31 
32 int main(){
33     int a[] = {1,2,7,6,5,4,3,1};
34     printf("%d \n",findMax(a,8));
35 }

 

posted @ 2013-02-15 23:38  _sunshine  阅读(387)  评论(0编辑  收藏  举报