[LeetCode] 81. Search in Rotated Sorted Array II

今天面试遇到这道题了。当场很勉强做出来,但是事后想想好多细节还是没有想清楚。
希望本次记录彻底解决本题。

首先,这道题如果没有重复元素,且只是判断是否能找到 target,那么很简单:

while(start + 1 < end){
    mid = start + (end - start) / 2;
    if(nums[mid] == target)
        return true;
    if(nums[mid] > nums[start]){ // mid 处于前一线段
        if(nums[start] <= target && target < nums[mid])// 注意一定是 nums[start] <= target
            end = mid;
        else
            start = mid;
    }
    else{
        // nums[mid] < nums[end]
        if(nums[mid] < target && target <= nums[end])// 注意这一一定是 target <= nums[end]
            start = mid;
        else
            end = mid;
    }
}

唯一需要注意的就是 nums[start] 和 nums[end] 有可能直接等于 target,一定要考虑这种情况,否则会移错区间。比如当 nums[start] == target,但是代码为

if(nums[start] < target && target < nums[mid])
    end = mid;
else
    start = mid;

会错误地将执行 start = mid

现在处理出现重复元素的情况。最直观的想法是将本情况手动转换成之前不重复的情况。事实上这也是个人觉得最好的最简洁的做法。

如何转换呢,由于输入数组是从有序序列旋转得到的,那么旋转之后重复元素只可能出现在:

  1. 全部为重复元素
  2. 出现在头或者尾或者同时出现在尾和头。

那么说明我们只需要判断头尾即可,所以我们可以在循环前部处理,将前后重复的部分消除。

while(nums[start] == nums[start + 1] && start + 1 < end)
    start++;
while(nums[end] == nums[end - 1] && start + 1 < end)
    end--;

这样处理之后,唯一可能还剩下的重复情况就是 nums[start] == nums[end],数组中其他所有元素都是不相等的。这时这两个相等的元素其实对我们最后的判断已经没有影响了,因为在前面我们已经考虑过
nums[start] == target 以及 nums[end] == target 的情况了,而我们的目的只是判断 nums 中是否有 target,所以不论最后找到的位置是 start 还是 end 都可以。
完整代码:

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if(nums.size() <= 0)
            return false;
        
        int start = 0, end = (int)nums.size() - 1;
        while(start + 1 < end){
            // 处理头尾重复元素
            while(nums[start] == nums[start + 1] && start + 1 < end){
                start++;
            }
            while(nums[end] == nums[end - 1] && start + 1 < end){
                end--;
            }
            
            // 按照无重复元素处理
            int mid = start + (end - start) / 2;
            if(nums[mid] == target)
                return true;
            
            if(nums[mid] > nums[start]){
                if(nums[start] <= target && target < nums[mid]){
                    end = mid;
                }
                else{
                    start = mid;
                }
            }
            else if(nums[mid] < nums[end]){
                if(nums[end] >= target && target > nums[mid]){
                    start = mid;
                }
                else{
                    end = mid;
                }
            }
        }
        if(nums[start] == target || nums[end] == target)
                return true;
        return false;
    }       
};

如果我们不用这种方法,那么我们需要更加深入地去判断每种可能出现地情况。

当 nums[mid] > nums[start] 和 nums[mid] < nums[end] 都不满足时,则: numd[start] <= nums[mid] 并且 nums[mid] >= nums[end]

如果我们再仔细想,由于数组旋转之前整体有序,那么旋转之后得到的两条线段本身应该是有序的,而且,nums[start] 一定会大于等于 nums[end],那么

  1. 当 nums[start] < nums[mid] 时,nums[mid] 只可能等于 nums[end],而不可能大于 nums[end];
  2. 当 nums[mid] > nums[end] 时,nums[start] 只可能等于 nums[mid],而不可能小于nums[mid]

这样其实我们就很好做了:

    // nums[mid] <= nums[start] && nums[mid] >= nums[end]
    else if(nums[start] > nums[mid]){ // nums[mid] == nums[end]
        end--;
    } else if(nums[mid] > nums[end]) { // nums[start] == nums[mid]
        start++;
    } else{ // start = mid = end
        start++;
        end--;
    }         

完整代码如下:

class Solution {
 public:
  bool search(vector<int>& nums, int target) {
    if (nums.size() <= 0) return false;

    int start = 0, end = (int)nums.size() - 1;
    while (start + 1 < end) {
   
      int mid = start + (end - start) / 2;
      if (nums[mid] == target) return true;

      if (nums[mid] > nums[start]) {
        if (nums[start] <= target && target < nums[mid]) {
          end = mid;
        } else {
          start = mid;
        }
      } else if (nums[mid] < nums[end]) {
        if (nums[end] >= target && target > nums[mid]) {
          start = mid;
        } else {
          end = mid;
        }
      }
      // mid <= start, mid >= end
      else if(nums[start] > nums[mid]){
          // nums[mid] == nums[end]
          end--;
      } else if(nums[mid] > nums[end]) {
          // nums[start] == nums[mid]
          start++;
      } else{
          // start = mid = end
          start++;
          end--;
      }
    }
    if (nums[start] == target || nums[end] == target) return true;
    return false;
  }
};
posted @ 2020-03-05 22:42  HZQTS  阅读(150)  评论(0编辑  收藏  举报