• leetcode Ch1-search 2014


    1. Search Insert Position

     1 class Solution {
     2 public:
     3     int searchInsert(int A[], int n, int target) {
     4         int left=0,right=n-1;
     5         while(left<=right)
     6         {
     7             int mid=left+(right-left)/2;
     8             if(A[mid]==target) return mid;
     9             if(A[mid]<target)
    10                 left=mid+1;
    11             else right=mid-1;
    12         }
    13         return left;
    14     }
    15 };

    当循环结束时,如果没有找到目标元素,那么left一定停在恰好比目标大的元素的index上,right一定停在恰好比目标小的index上.

    特殊点的例子:当在{1,2,3,4,5}里执行二分查找找0时,此时的left=0, right=-1.(left和right也是挺拼的,为了比target大/小,不惜越界。)

    这个规律非常有用,合理利用left和right在未找到target的情况下退出while循环时的特性,能解决很多问题,尤其体现在实现upperBound和lowerBound函数时。如果要利用left或right,需要保证target不在当前区间内,这样才能让while循环以未找到target的状态退出。为了实现这一点,我们需要在即使发现target==A[mid]的情况下仍然假装没看见,继续缩小搜索范围。

    因此如果要在二分搜索的基础上计算bound的话,其实就是怎么处理那几个值为target的元素的问题。也就是是否把这部分值恰好为target的元素纳入下一轮搜索范围。(二分搜索本质上就是不断缩小搜索范围)。

    以upperBound为例。upperBound函数是找[left,right]内第一个大于target的元素下标------所以值为target的元素肯定要排除在搜索范围之外。这样当A[mid]==target时我们仍将其排除在搜索范围外,即让low=mid+1。一直到搜索范围里已经没有值为target的元素了,此时根据二分查找的性质,当区间里没有找到target时,while循环退出后low指向刚好大于target的位置,high指向刚好小于target的位置。所以此时我们返回low即为刚好大于target的位置,即——第一个大于target的位置。

    lowerBound同理。lowerBound是寻找[left,right]内第一个值不小于target的元素下标。“不小于”target的不好求,但刚好小于target的那个元素位置比较好求,因为这就是right的返回值。所以我们按照上述思路,在遇到A[mid]==target的情况下仍假装看不见,继续缩小搜索范围,直到当前范围里没有了target,最终循环退出时right就指向值刚好小于target的元素位置。而(right+1)即为不小于target的元素下标。

     lowerBound和upperBound的实现代码在下一题代码中。

    What if there are duplicates?

    2. Search for a Range

     1 class Solution {
     2 public:
     3     vector<int> searchRange(int A[], int n, int target) {
     4         int left=0,right=n-1;
     5         vector<int> res(2,-1);//[-1,-1]是默认值
     6         while(left<=right)
     7         {
     8             int mid=left+(right-left)/2;
     9             if(A[mid]<target) 
    10                 left=mid+1;
    11             else if(A[mid]>target)
    12                 right=mid-1;
    13             else
    14             {
    15                 res[0]=lowerBound(A,left,mid,target);
    16                 res[1]=upperBound(A,mid,right,target)-1;//别忘了减1                                
    17                 return res;
    18             }
    19         }
    20         return res;//这一句别忘了。当查找失败时返回[-1,-1].
    21     }
    22 private:
    23     int upperBound(int A[], int left, int right, int target)
    24     {
    25         int low = left, high = right;
    26         while (low <= high)
    27         {
    28             int mid = low + (high - low) / 2;
    29             if (A[mid] <= target)
    30                 low = mid + 1;
    31             else
    32                 high = mid - 1;
    33         }
    34         return low;
    35     }
    36     int lowerBound(int A[], int left, int right, int target)
    37     {
    38         int low = left, high = right;
    39         while (low <= high)
    40         {
    41             int mid = low + (high - low) / 2;
    42             if (A[mid] >= target)
    43                 high = mid - 1;
    44             else
    45                 low = mid + 1;
    46         }
    47         return high + 1;
    48     }
    49 };

    lowerBound和upperBound与binarySearch之间的关系上一题里已经分析过了。

    注意几个小细节,已在注释中标明。细节决定成败,bug-free需要谨小慎微。

    3. Search in Rotated Sorted Array

     1 class Solution {
     2 public:
     3     int search(int A[], int n, int target) {
     4         int left=0,right=n-1;
     5         while(left<=right)
     6         {
     7             int mid=left+(right-left)/2;
     8             if(A[mid]==target) return mid;
     9             if(A[mid]<A[right])//说明右半段是有序的
    10             {
    11                 if(target>A[mid]&&target<=A[right])
    12                     left=mid+1;
    13                 else
    14                     right=mid-1;                    
    15             }
    16             else
    17             {
    18                 if(target>=A[left]&&target<A[mid])
    19                     right=mid-1;
    20                 else
    21                     left=mid+1;
    22             }
    23         }
    24         return -1;
    25     }
    26 };

    最关键的把握这个规律:"总有一半是有序的,而且和另一半无区间重叠"。code ganker的总结很好。

    4. Search in Rotated Sorted Array II

    1 class Solution {
    2 public:
    3     bool search(int A[], int n, int target) {
    4         for(int i=0;i<n;i++)
    5             if(A[i]==target) return true;
    6         return false;
    7     }
    8 };

    由于允许有duplicates,会导致没有办法像I中那样根据A[mid]和A[left]、A[right]的比较来确定是哪一半有序,应该在哪一半查找。

    导致最坏时间复杂度变为O(n)。因此用最简单的遍历来实现就可以。

    5. Search a 2D Matrix

     1 class Solution {
     2 public:
     3     bool searchMatrix(vector<vector<int> > &matrix, int target) {
     4         if(matrix.size()==0||matrix[0].size()==0) return false;
     5         int m=matrix.size(); int n=matrix[0].size();
     6         int left=0,right=m*n-1;
     7         while(left<=right)
     8         {
     9             int mid=left+(right-left)/2;
    10             int midX=mid/n; int midY=mid%n;//注意:这里是n,不是m!
    11             if(matrix[midX][midY]==target) return true;
    12             if(matrix[midX][midY]<target)            
    13                 left=mid+1;
    14             else
    15                 right=mid-1;
    16         }
    17         return false;
    18     }
    19 };

    6. sqrt(x)

     1  class Solution {
     2 public:
     3     int sqrt(int x) {
     4         if(x<2) return x;
     5         int left=1,right=x/2+1;
     6         while(left<=right)
     7         {
     8             int mid=left+(right-left)/2;
     9             if(mid<=x/mid&&(mid+1)>x/(mid+1))//防止溢出
    10                 return mid;
    11             if(mid>x/(mid))
    12                 right=mid-1;
    13             else
    14                 left=mid+1;
    15         }
    16         return -1;
    17     }
    18 };

     比较蹊跷的是if(mid>x/mid)和下面的else不能换位置。尚不知为何。

  • 相关阅读:
    创建新用户
    发生tcp丢包(拥堵、超时)重传
    centos7装机和初步运维
    论上山和下山哪个费力
    一、Linux简介
    服务器设置FTP
    自定义部署资源服务器
    代码管理工具 Git
    远程连接工具rdcman
    dubbo学习(八)dubbo项目搭建--消费者(服务消费者)
  • 原文地址:https://www.cnblogs.com/forcheryl/p/4106547.html
Copyright © 2020-2023  润新知