Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://leetcode.com/problems/search-in-rotated-sorted-array-ii/

Follow up for "Search in Rotated Sorted Array":
What if duplicates are allowed?

Would this affect the run-time complexity? How and why?

Write a function to determine if a given target is in the array.

解题思路:

这题与上一题Search in Rotated Sorted Array略有不同,数组中允许重复元素。粗粗一想,不太容易判断重复元素会给我们带来什么样的影响,还有到底重复元素出现在什么地方,会给我们带来关键性的影响?这是这道题的两个关键问题。那么我们可以先把上一道题的代码塞进去跑看看。

结果出来有错误,

Input: [1,3,1,1,1], 3
Output: false
Expected: true

原来当A[mid] == A[start]的时候,原来可以放在第二种情况里,也就是2 4 5 6 7 0 1这种情况。但是现在由于有重复元素,我们不能判断到底属于哪种情况了。

其实呢?肉眼可以看出来,上面的例子里,A[mid] == A[start]的时候,如果mid往前有>A[mid]的时候,就可以认为是A[start] > a[mid]的情况,放在这里处理。所以下面的代码写了另外的一个方法,如果A[mid] == A[start],就从start到mid往后一个个看,出现>A[mid]的元素,就当作是A[start] > a[mid]的情况。

public class Solution {
    public boolean search(int[] A, int target) {
        int start = 0;
        int end = A.length - 1;
        
        while(start <= end){
            int mid = (start + end) / 2;
            if(target == A[mid]){
                return true;
            }
            if(A[start] > A[mid] || hasBigger(A, start, mid, A[mid])){
                //后半段一定比mid大,所以这时target一定在前面
                if(target < A[mid]){
                    end = mid - 1;
                }
                //有可能在后半段,也有可能在rotate到前面的那部分
                if(target > A[mid]){
                    if(target <= A[end]){
                        start = mid + 1;
                    }else{
                        end = mid - 1;
                    }
                }
            }else{
                //前半段一定比mid小,所以这时target一定在后面
                if(target > A[mid]){
                    start = mid + 1;
                }
                if(target < A[mid]){
                    if(target < A[start]){
                        start = mid + 1;
                    }else{
                        end = mid - 1;
                    }
                }
            }
        }
        return false;
    }
    
    public boolean hasBigger(int[] A, int start, int end, int target){
        for(int i = start; i < end; i++){
            if(A[i] > target){
                return true;
            }
        }
        return false;
    }
}

后来从网上看到一个更为简单的方法,不用额外写判断条件了。当A[mid] == A[start]的时候,start++再判断,重新定义二分搜索的范围,就可以了。整个代码要简洁很多。

public class Solution {
    public boolean search(int[] A, int target) {
        int start = 0;
        int end = A.length - 1;
        
        while(start <= end){
            int mid = (start + end) / 2;
            if(target == A[mid]){
                return true;
            }
            if(A[start] > A[mid]){
                //后半段一定比mid大,所以这时target一定在前面
                if(target < A[mid]){
                    end = mid - 1;
                }
                //有可能在后半段,也有可能在rotate到前面的那部分
                if(target > A[mid]){
                    if(target <= A[end]){
                        start = mid + 1;
                    }else{
                        end = mid - 1;
                    }
                }
            }else if(A[start] < A[mid]){
                //前半段一定比mid小,所以这时target一定在后面
                if(target > A[mid]){
                    start = mid + 1;
                }
                if(target < A[mid]){
                    if(target < A[start]){
                        start = mid + 1;
                    }else{
                        end = mid - 1;
                    }
                }
            }else if(A[start] == A[mid]){
                start++;
            }
        }
        return false;
    }
}

 上面两个方法,时间复杂度在最差的时候,都是O(n),比如在111111111151,搜索5。两种方法都要花大量时间去在前半段找大于1的数字。

另外一个问题,根据循环不变式loop invariant的原理,在二分搜索循环的内部如果更新了start和end,就必须立刻进入下一次循环,不能再对新的start或者end进行判断、返回了。否则会破坏循环不变的性质。

比如第二种方法start++后就不能在本次循环内再处理了,必须进入下次循环。妄想多次更新start,避免多次循环,会破坏循环的性质。

// 2018/09/16

class Solution {
    public boolean search(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;            
            if (nums[mid] == target) {
                return true;
            }
            if (nums[left] == nums[mid]) {
                left++;
                continue;
            }
            if (nums[left] < nums[mid]) {
                if (target >= nums[left] && target <= nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            
            if (nums[right] == nums[mid]) {
                right--;
                continue;
            }
            if (nums[mid] < nums[right]) {
                if (target >= nums[mid] && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        return false;
    }
}

 

posted on 2015-03-10 14:41  NickyYe  阅读(281)  评论(0编辑  收藏  举报