• 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不能换位置。尚不知为何。

  • 相关阅读:
    一行代码搞定Dubbo接口调用
    测试周期内测试进度报告规范
    jq 一个强悍的json格式化查看工具
    浅析Docker容器的应用场景
    HDU 4432 Sum of divisors (水题,进制转换)
    HDU 4431 Mahjong (DFS,暴力枚举,剪枝)
    CodeForces 589B Layer Cake (暴力)
    CodeForces 589J Cleaner Robot (DFS,或BFS)
    CodeForces 589I Lottery (暴力,水题)
    CodeForces 589D Boulevard (数学,相遇)
  • 原文地址:https://www.cnblogs.com/forcheryl/p/4106547.html
Copyright © 2020-2023  润新知