• 二分查找小结


      在弄dp时感觉一道题需非要弄清二分查找不可。以前学二分一直就很迷惑,网上资料也各种各样。的确二分是个很容易写错的算法,今天只好不算太耐心的再看一遍二分。总感觉时间不够用。。

      二分查找有许多细节,这次先抓主要矛盾。关于什么(left+rigth)/2溢出的问题啊先不考虑了。对我来说二分迷惑的地方还是在1.while(left?right) ?处到底是<还是<= 2.判断后mid到底是加一还是减一还是不变? 3.返回left还是right?

      这次大概明白了一些,因为二分查找是看区间开闭的,对于左闭右开[l,r)一般是left<right,对于左闭又闭[l,r]一般是left<=right.这里区间开闭是针对数组下标而言,比如a={1,2,3,4,5},数组下标在[0,4](a[0]到a[4]),[0,4]就是左闭又闭区间,而[0,5)就是左闭右开区间。看似其实查询的都是同一个数组,前一种方式初始时left=0,right=4,后一种方式初始时left=0,right=5。这小小的差别就会造成很大的隐患bug。

    经典的查询key是否在数组中,是返回下标否则返回-1:

     1 int BinarySearch(int array[],int n,int key)
     2 {
     3     int left=0,right=n-1;
     4     while(left<=right)
     5     {
     6         int mid=(left+right)>>1;
     7         if(array[mid]>key) 
     8             right=mid-1;
     9         else if(array[mid]<key) 
    10             left=mid+1;
    11         else return mid;
    12     }
    13     return -1;
    14 }

    这里是左闭又闭区间查找,此时:

      1.一定要是 while(left<=right)。因为若是 while(left<right)可能找不到key。例如array={1,2,3,4,5},key=5当left==right时才找到key。

      2.一定要是 left=mid+1; 否则可能死循环。如上面例子,当left指向4时,right指向5,两个指针相邻mid永远等于left,发生死循环。产生死循环的根本原因在于left,因为left可能永远等于mid,而right不会因为等于mid死循环。所以这里我觉得right也一定要减一。其实这个代码看上去是很好理解的,就是大于双闭闭区间查找,大于key就在[left,mid-1]中找,小于key就在[mid+1,right]中找。

    当改成左闭右开区间时,需要修改:

      1.循环的条件是while(left<right)

      2.循环内当array[mid]>key 时,right=mid

    至于这些细节,以后有时间(不知道会不有>_<..)再细抠。

    下面记录一下经典变形(采用左闭右闭写法):

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

     1 int searchFirstEqual(int *arr, int n, int key)
     2 {
     3     int left = 0, right = n - 1;
     4     while (left <= right) {
     5         int mid = (left + right) >> 1;
     6         if (arr[mid] >= key) right = mid - 1;
     7         else if (arr[mid] < key) left = mid + 1;
     8     }
     9     if (left < n&&arr[left] == key) return left;
    10     //arr[right]<key<=arr[left]
    11     //right是最后一个小于key的
    12     //left是第一个大于等于key的
    13     return -1;
    14 }

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

     1 int searchLastEqual(int *arr, int n, int key)
     2 {
     3     int left = 0, right = n - 1;
     4     while (left <= right) {
     5         int mid = (left + right) >> 1;
     6         if (arr[mid] > key) right = mid - 1;
     7         else if (arr[mid] <= key) left = mid + 1;
     8     }
     9     //arr[right]<=key<arr[left]
    10     //right是最后一个小于等于可以的
    11     //left是第一个大于key的
    12     if (right >= 0 && arr[right] == key) return right;
    13     return -1;
    14 }

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

    例如 arr={1,2,2,2,4,8,10},查找2,返回第一个2的下标1;查找3,返回4的下标4,查找4,返回4的下标4.

    解释:条件为arr[mid]>=key,意思是key小于等于中间值,则左半区查找。如在arr中查找2.第一步,left=0,right=6,则mid=3,arr[mid]>=key,往左半部分{1,2,2}中继续查找。终止前一步为:left=right,则mid=left,若arr[mid]>=key,则right会变,而left指向当前元素,即满足要求的元素;若arr[mid]<key,则left会变,而left指向mid的下一个元素。

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

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

    例如:int[] a = {1,2,2,2,4,8,10},查找2,返回4的下标4;查找3,返回4的下标4;查找4,返回8的下标5。与上面的代码仅一个等于符号不同。

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

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

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

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

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

    完整测试程序:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 using namespace std;
      6 const int MAXN = 107;
      7 int dp[MAXN][MAXN];
      8 char s[MAXN];
      9 
     10 
     11 
     12 int search(int *arr, int n, int key)
     13 {
     14     int left = 0, right = n - 1;
     15     while (left <= right) {
     16         int mid = (left + right) >> 1;
     17         if (arr[mid] == key) return mid;
     18         else if (arr[mid] > key) right = mid - 1;
     19         else left = mid + 1;
     20     }
     21     return -1;
     22 }
     23 
     24 //1.找出第一个与key相等的元素
     25 int searchFirstEqual(int *arr, int n, int key)
     26 {
     27     int left = 0, right = n - 1;
     28     while (left <= right) {
     29         int mid = (left + right) >> 1;
     30         if (arr[mid] >= key) right = mid - 1;
     31         else if (arr[mid] < key) left = mid + 1;
     32     }
     33     if (left < n&&arr[left] == key) return left;
     34     //arr[right]<key<=arr[left]
     35     //right是最后一个小于key的
     36     //left是第一个大于等于key的
     37     return -1;
     38 }
     39 
     40 
     41 //2.找出最后一个与key相等的元素
     42 int searchLastEqual(int *arr, int n, int key)
     43 {
     44     int left = 0, right = n - 1;
     45     while (left <= right) {
     46         int mid = (left + right) >> 1;
     47         if (arr[mid] > key) right = mid - 1;
     48         else if (arr[mid] <= key) left = mid + 1;
     49     }
     50     //arr[right]<=key<arr[left]
     51     //right是最后一个小于等于可以的
     52     //left是第一个大于key的
     53     if (right >= 0 && arr[right] == key) return right;
     54     return -1;
     55 }
     56 
     57 //3.查找第一个等于或大于key的元素
     58 int searchFirstEqualOrLarger(int *arr, int n, int key)
     59 {
     60     int left = 0, right = n - 1;
     61     while (left <= right) {
     62         int mid = (left + right) >> 1;
     63         if (arr[mid] >= key) right = mid - 1;
     64         else if (arr[mid] < key) left = mid + 1;
     65     }
     66     return left;
     67 }
     68 
     69 //4.查找第一个大于key的元素
     70 int searchFirstLarger(int *arr, int n, int key)
     71 {
     72     int left = 0, right = n - 1;
     73     while (left <= right) {
     74         int mid = (left + right) >> 1;
     75         if (arr[mid] > key) right = mid - 1;
     76         else if (arr[mid] <= key) left = mid + 1;
     77     }
     78     return left;
     79 }
     80 
     81 //5.查找最后一个等于或者小于key的元素
     82 int searchLastEqualOrSmaller(int *arr, int n, int key)
     83 {
     84     int left = 0, right = n - 1;
     85     while (left <= right) {
     86         int mid = (left + right) >> 1;
     87         if (arr[mid] > key) right = mid - 1;
     88         else if (arr[mid] <= key) left = mid + 1;
     89     }
     90     return right;
     91 }
     92 
     93 //6.查找最后一个小于key的元素
     94 int searchLastSmaller(int *arr, int n, int key)
     95 {
     96     int left = 0, right = n - 1;
     97     while (left <= right) {
     98         int mid = (left + right) >> 1;
     99         if (arr[mid] >= key) right = mid - 1;
    100         else if (arr[mid] < key) left = mid + 1;
    101     }
    102     return right;
    103 }
    104 
    105 
    106 
    107 int main()
    108 {
    109     int arr[17] = { 1,2,2,5,5,5,5,5,5,5,5,5,5,6,6,7 };
    110     printf("First Equal           : %2d 
    ", searchFirstEqual(arr, 16, 5));
    111     printf("Last Equal            : %2d 
    ", searchLastEqual(arr, 16, 5));
    112     printf("First Equal or Larger : %2d 
    ", searchFirstEqualOrLarger(arr, 16, 5));
    113     printf("First Larger          : %2d 
    ", searchFirstLarger(arr, 16, 5));
    114     printf("Last Equal or Smaller : %2d 
    ", searchLastEqualOrSmaller(arr, 16, 5));
    115     printf("Last Smaller          : %2d 
    ", searchLastSmaller(arr, 16, 5));
    116     system("pause");
    117     return 0;
    118 }
    119 
    120 /*输出:
    121 First Equal           :  3
    122 Last Equal            : 12
    123 First Equal or Larger :  3
    124 First Larger          : 13
    125 Last Equal or Smaller : 12
    126 Last Smaller          :  2
    127 */

    参考博客(感谢~):

    【1】:http://blog.csdn.net/yefengzhichen/article/details/52372407

    【2】:https://61mon.com/index.php/archives/187/

    【3】:https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/04.01.md#分析与解法

    【4】:http://www.cnblogs.com/luoxn28/p/5767571.html

    【5】:http://www.cnblogs.com/bofengyu/p/6761389.html

    【6】:http://blog.chinaunix.net/uid-1844931-id-3337784.html

    【7】:https://www.zhihu.com/question/36132386

    【8】:http://www.ahathinking.com/archives/179.html

  • 相关阅读:
    事理学神器PDCA
    单元测试
    web框架学习路线
    编译器实现(三)
    markdown test
    设计模式
    编译器实现(二)
    编译器实现(一)
    操作系统结构
    emacs配置笔记(windows10)
  • 原文地址:https://www.cnblogs.com/zxhyxiao/p/7426878.html
Copyright © 2020-2023  润新知