二分基础入门

来源:互联网 发布:一个皇冠什么软件 编辑:程序博客网 时间:2024/06/17 02:52

转自:http://blog.csdn.net/luoweifu/article/details/16656737

简要描述

二分查找又称折半查找,对排好序的数组,每次取这个数和数组中间的数进行比较,复杂度是O(logn):设数组为a[n],查找的数x,

如果x==a[n/2],则返回n/2;

如果x < a[n/2],则在a[0]a[n/2-1]中进行查找;

如果x > a[n/2],则在a[n/2+1]a[n-1]中进行查找;

优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。

条件:查找的数组必须要为有序数组。

 

二种实现方式

 

1.递归

[cpp] view plain copy
  1. /* 
  2. 归的二分查找 
  3. arrat:数组 , low:上界;  high:下界;  target:查找的数据; 返回target所在数组的下标  
  4. */  
  5. int binarySearch(int array[], int low, int high, int target) {  
  6.     int middle = (low + high)/2;  
  7.     if(low > high) {  
  8.         return -1;  
  9.     }  
  10.     if(target == array[middle]) {  
  11.         return middle;  
  12.     }  
  13.     if(target < array[middle]) {  
  14.         return binarySearch(array, low, middle-1, target);  
  15.     }  
  16.     if(target > array[middle]) {  
  17.         return binarySearch(array, middle+1, high, target);  
  18.     }   
  19. }  


2.非递归(循环)

[cpp] view plain copy
  1. /* 
  2. 非递归的二分查找  
  3. arrat:数组 , n:数组的大小;  target:查找的数据; 返回target所在数组的下标  
  4. */  
  5. int binarySearch2(int array[], int n, int target) {  
  6.     int low = 0, high = n, middle = 0;  
  7.     while(low < high) {  
  8.         middle = (low + high)/2;  
  9.         if(target == array[middle]) {  
  10.             return middle;  
  11.         } else if(target < array[middle]) {  
  12.             high = middle;  
  13.         } else if(target > array[middle]) {  
  14.             low = middle + 1;  
  15.         }  
  16.     }  
  17.     return -1;  
  18. }  


推荐使用非递归的方式,因为递归每次调用递归时有用堆栈保存函数数据和结果。能用循环的尽量不用递归。

 

二分查找的应用

还是对上一篇博文《C++如何跳出多层循环》中提到的抽签问题进行分析。

上一篇博文中是进行了四重循环的嵌套,基时间复杂度是O(n4),数据大时其计算量会大的惊人。为便于分析,将之前代码帖至如下:

[cpp] view plain copy
  1. **  
  2. 抽签问题  
  3. 解决方案,复杂度n^4   
  4. */   
  5. void  drawLots() {  
  6.    //从标准输入读入  
  7.    int numOfCard, sum;  
  8.    int k[MAX_N];  
  9.    cout<<"输入numOfCard和sum"<<endl;  
  10.    cin>>numOfCard>>sum;   
  11.    cout<<"请输入这sum张卡片的数字"<<endl;  
  12.    for(int i=0; i<numOfCard; i++) {  
  13. cin>>k[i];  
  14.    }  
  15.    bool result = false;  
  16.    bool isBreakLoop = true;  
  17.    int _sum = 0;  
  18.    for(int a = 0; a < numOfCard && isBreakLoop; a ++) {  
  19.       for(int b = 0; b < numOfCard && isBreakLoop; b ++) {  
  20.           for(int c = 0; c < numOfCard && isBreakLoop; c++) {  
  21.               for(int d = 0; d < numOfCard && isBreakLoop; d ++) {  
  22.                 _sum = k[a] + k[b] + k[c] + k[d];  
  23.                   if(_sum == sum) {  
  24. result = true;  
  25. isBreakLoop = false;  
  26.                   }    
  27.               }  
  28.           }  
  29.       }  
  30.    }  
  31.    cout << "_sum:" << _sum << "  " << "sum:" << sum << endl;  
  32.    if(result){  
  33.     cout<<"Yes"<<endl;  
  34.    } else  
  35.     cout<<"No"<<endl;  
  36. }  

最内层循环所做事如下:

K+ kb + kc + kd = m

移项后如下:

Kd = m - (K+ kb + kc)

到第四层循环时,其实Kkbkc已经知道,那问题也就变成了对kd的查找,我们可用上面讲的二分查找,复杂度就降为O(n3logn).实现如下:

降低复杂度的实现

[cpp] view plain copy
  1. /** 
  2. 抽签问题  
  3. 解决方案,复杂度n^3 * log(n) 
  4. */   
  5. void drawLots2() {  
  6. int numOfCard, sum;  
  7. int k[MAX_N];  
  8. cout<<"输入numOfCard和sum"<<endl;  
  9.     cin>>numOfCard>>sum;   
  10.     cout<<"请输入这sum张卡片的数字"<<endl;  
  11.     for(int i=0; i<numOfCard; i++) {  
  12. cin>>k[i];  
  13.     }  
  14.     //对数组进行排序   
  15.     sort(k, k + numOfCard);  
  16. int index = -1;  
  17.     bool isBreakLoop = true;  
  18.     for(int a = 0; a < numOfCard && isBreakLoop; a ++) {  
  19.         for(int b = 0; b < numOfCard && isBreakLoop; b ++) {  
  20.             for(int c = 0; c < numOfCard && isBreakLoop; c++) {  
  21.             index = binarySearch2(k, numOfCard, sum - (k[a] + k[b] + k[c]));  
  22.                 if(index >= 0) {  
  23. isBreakLoop = false;  
  24.                 }  
  25.             }  
  26.         }  
  27. }  
  28.    if(index >= 0){  
  29.     cout<<"Yes"<<endl;  
  30.    } else  
  31.     cout<<"No"<<endl;  
  32. }  

进一步优化[O(n2logn)]

根据上一步的优化方式,我们可以进一步对内侧两层循环(也就是第三层和第四层)进行思考:

Kc+ kd = m - (K+ kb )

我们不能直接对Kc+ kd进行查找,但是可以预先枚举出K+ kb 的n2种数值并排序,再对Kc+ kd进行十分查找。列出枚举O(n2),排序O(n2logn), 循环O(n2logn),所以总的复杂度降为O(n2logn),实现如下:

[cpp] view plain copy
  1. /** 
  2. 抽签问题  
  3. 解决方案,复杂度n^2 * log(n) 
  4. */   
  5. void drawLots3() {  
  6. int numOfCard, sum;  
  7. int k[MAX_N];  
  8. cout<<"输入numOfCard和sum"<<endl;  
  9.     cin>>numOfCard>>sum;   
  10.     cout<<"请输入这sum张卡片的数字"<<endl;  
  11.     for(int i=0; i<numOfCard; i++) {  
  12. cin>>k[i];  
  13.     }  
  14.     int cdNum = numOfCard*(numOfCard+1)/2;  
  15.     int cdSum[cdNum];  
  16.     int i = 0;  
  17.     for(int a=0; a<numOfCard; a++) {  
  18.         for(int b=i; b<numOfCard; b++) {  
  19.         cdSum[i ++] = k[a] + k[b];  
  20.         }  
  21.     }  
  22.     //对数组进行排序   
  23.     sort(cdSum, cdSum + cdNum);  
  24. int index = -1;  
  25.     bool isBreakLoop = true;  
  26.     for(int a = 0; a < numOfCard && isBreakLoop; a ++) {  
  27.         for(int b = 0; b < numOfCard && isBreakLoop; b ++) {  
  28.             for(int c = 0; c < numOfCard && isBreakLoop; c++) {  
  29.             index = binarySearch2(cdSum, cdNum, sum - (k[a] + k[b]));  
  30.                 if(index >= 0) {  
  31. isBreakLoop = false;  
  32.                 }  
  33.             }  
  34.         }  
  35. }  
  36.    if(index >= 0){  
  37.     cout<<"Yes"<<endl;  
  38.    } else  
  39.     cout<<"No"<<endl;  
  40. }  

进一步思考

上面枚举K+ kb 时其实是有重复的,因为k[i] + k[j] == k[j] + k[i],去除重复值之后,K+ kb 值的个数是n(n+1)/2。至于n(n+1)/2怎么来,可以简单推导如下:

N     M

1     1

2      2+1

3     3+2+1

4     4+ 3+2+1

......

实现如下:

[cpp] view plain copy
  1. /** 
  2. 抽签问题  
  3. 解决方案,复杂度n^2 * log(n) 
  4. */   
  5. void drawLots3_1() {  
  6. int numOfCard, sum;  
  7. int k[MAX_N];  
  8. cout<<"输入numOfCard和sum"<<endl;  
  9.     cin>>numOfCard>>sum;   
  10.     cout<<"请输入这sum张卡片的数字"<<endl;  
  11.     for(int i=0; i<numOfCard; i++) {  
  12. cin>>k[i];  
  13.     }  
  14.     int cdNum = numOfCard*numOfCard;  
  15.     int cdSum[cdNum];  
  16.     for(int a=0; a<numOfCard; a++) {  
  17.         for(int b=0; b<numOfCard; b++) {  
  18.         cdSum[a*numOfCard + b] = k[a] + k[b];  
  19.         }  
  20.     }  
  21.     //对数组进行排序   
  22.     sort(cdSum, cdSum + cdNum);  
  23. int index = -1;  
  24.     bool isBreakLoop = true;  
  25.     for(int a = 0; a < numOfCard && isBreakLoop; a ++) {  
  26.         for(int b = 0; b < numOfCard && isBreakLoop; b ++) {  
  27.             for(int c = 0; c < numOfCard && isBreakLoop; c++) {  
  28.             index = binarySearch2(cdSum, cdNum, sum - (k[a] + k[b]));  
  29.                 if(index >= 0) {  
  30. isBreakLoop = false;  
  31.                 }  
  32.             }  
  33.         }  
  34. }  
  35.    if(index >= 0){  
  36.     cout<<"Yes"<<endl;  
  37.    } else  
  38.     cout<<"No"<<endl;  
  39. }  
原创粉丝点击