二分查找及其扩展

之前所说的lower_bound和upper_bound其实就是二分查找的一种扩展形式,在SGI STL里面,二分查找算法实际调用的是lower_bound。所以lower_bound和upper_bound还是非常重要的。主要用到的是二分查找的思想。

一、二分查找:

输入:有序的一个数组,一个待查找的元素。
输出:待查找元素是否在这个数组里面。
每次都会利用到数组的有序性,丢弃数组中一半的数据。这点非常重要,在他 的扩展形式中,每次也是对于确定不需要查找的一半元素进行丢弃。下面详细说明。
首先给出二分查找的算法:

int binary_search_my(const int A[],int length,const int v){
    int left = 0,right = length - 1;
    while(left <= right){//= should be reached because length=1
//      int mid = (left + right)/2;
        int mid = left + (right - left)/2;//avoid over flow of+
        if(nums[mid] == v)
            return 1;
        else if(nums[mid] > v)//v cann't locate [mid,right]
            right = mid -1;
        else
            left = mid +1;//v cann't locate [left,mid]
    }
    return 0;
}

输入的时候: A[0,length1],A[0]A[1]A[length1]
循环: A[first]A[mid]A[right]firstright 当只有一个元素的时候查找也需要成立,所以 = 是能够取到的。因为right所指元素是可以访问。如果采用STL形式的写法 = 就取不到。

  • v==A[mid]的时候,说明找到了这个元素。

    • v<A[mid]A[mid+1]A[right] 的时候,说明这个元素不可能在 A[mid,right] 之间,所以 v 可能存在的区间为[first,mid1]
    • v>A[mid]A[mid1]A[first] 的时候,说明这个元素不可能在 A[first,mid] 之间,所以 v 可能存在的区间为[mid+1,right]
      结束:如果找到待查找元素,那么在循环 firstright 的时候就停止了,否则 first>right 时候结束。
      此外,给出 STL形式的 [first,last) 写法
    • int binary_search_my(const int A[],int length,int v){
          int first = 0,last = length;
          while(first != last){
              int mid = (first + last)/2;
              if(A[mid] == v)
                  return 1;
              else if(A[mid] > v)
                  last = mid ;
              else
                  first = mid +1;
          }
          return 0;
      }
      

      二、旋转数组的最小值(无重复)

      题目来源
      Suppose a sorted array is rotated at some pivot unknown to you beforehand.
      (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
      Find the minimum element.
      You may assume no duplicate exists in the array.
      对于一个排序好的数组,在某个位置进行了旋转,所以最小元素不一定在数组的开始了,而可能在数组的中间。

      • 输入: A[leftright],
      • A[left]<A[left+1]A[mid1]>A[min]|<A[min+1]<A[right]
      • min 表示最小元素所在的位置。
      • 输出:最小元素

      • 可以通过min这个位置把整个数组分为两个部分 A[first,min1],A[min,right] 可以看出来,这两个部分都是有序。 first 始终指向第一个数组的开始, last 始终指向第二个数组的结束。如果 first==right 的时候,可以认为结束查找,因为这个数肯定就是最小值。数组部分有序,可以考虑使用二分查找。

      • 所以对于中间元素 mid=(left+right)/2leftmid<right
        因为 midright ,所以不存在 A[mid]==A[right] ,但是可能存在 A[first]==A[mid]
        所以我们可以根据信息来丢弃一半最小元素肯定不在里面的那部分。
        如果 A[mid]>A[right] 那么说明最小元素不可能在 [first,mid] 当中,包括 A[mid] 不可能是最小元素,因为最小元素肯定是要小于 A[right] 的。所以这个时候查找的区间为 [mid+1,right]
        否则: A[mid]<A[right] 为什么是小于而不是小于等于呢?因为 midright 所以等号是取不到的。这个时候最小值,可能就是 A[mid] 所以下一步的查找区间变为 [first,mid] 这个时候需要包含 mid 因为, A[mid] 可能就是最小值。所以根据上面的分析写代码如下:
      int rotatemin(const int A[],int length){
          int left = 0,right = length-1;
          while(left < right){//when left == right,right is mininum
              int mid = (left + right )/2;//may overflow of int
              if(A[mid] > A[right])//[mid+1,right]
                  left = ++mid;
              else
                  right = mid;//[left,mid],mid may be mininum
          }
          return A[left];
      }

      我们来分析一下,如果是和前一部分的区间进行比较,该如何进行丢弃一半的区间

      • mid=(left+right)/2
        如果 A[mid]>A[first] 说明 A[mid] 在前部分的有序区间,最小元素在 [mid+1,right] ,但是如果 [first,right] 在运行过程当中, first 越过在前面的有序数组的最后位置,那么这个时候 [first,last] 就是有序的数组了,那么下面的分析,就没有意义了,为了防止这种情况的发生,只要 first 越过前面有序数组的位置,那么我们就可以判断这个时候 first 指向了后半部分有序数组的第一个位置,也就是最小值的位置,这个时候终止。
        如果 A[mid]==A[first] 由于数组当中没有重复元素,所以这个时候说明 mid==first 说明 first+1==right ,最小元素是 A[right] 前提是, first 指向前半部分的有序数组。
        如果 A[mid]<A[first] 最小元素不可能在 [mid+1,right] 所以最小元素在 [first,mid] 注意这个时候如果 A[mid] 刚好是最小元素时,所以这个位置还是不能被丢弃。
      • 从上面的分析可以看出来,如果是和 first 进行比较还是情况比较多的,也比较麻烦,所以选择和 right 进行比较还是简单的,可以十分确定丢弃最小值不可能存在的区间的一半元素,正好符合二分查找的思想。下面是上面分析的代码。
      int rotatemin_first(const int A[],const int length){
          int left =0,right = length-1;
          while(left != right){
              if(A[left] < A[right])//[first,right] is sorted..
                  return A[left];
              int mid = (left+right)/2;
              if(A[mid] > A[left])
                  left = mid +1;
              else if(A[mid] < A[left])
                  right = mid;
              else{
               return A[right];//mid==first so first +1 ==right
              }
          }
          return A[left];
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值