二分搜索-旋转数组

1. 给你一个旋转数组,和一个目标数,问你在这个数组中是否存在这个目标数?(数组无重复元素/有重复元素)

2. 给你一个旋转数组,要求旋转数组的最小值

 

解析:

1.

1)如果数组不存在重复元素

在二分搜索过程中,判断A[l]与A[mid]的关系

a. A[l] < A[mid] ,可能是下图两种情况:

 

 或者

第一种情况是数组本身单调递增,第二种情况是左半段单调递增,因此,只需要判断target是否落在这个区间以内

b. A[l] == A[mid]

由于在二分搜索过程中,会先判断target是否等于A[mid],因此在该中情况下,target != A[l],因此l = mid + 1

c. A[l] > A[mid],只可能是一下一种情况:

即右半段是单调递增的,因此只需要判断target是否落在右边段区间内

具体代码如下:

class Solution
{
public:
    int search(vector<int> &nums, int target)
    {
        int n = nums.size();
        int ans = -1;
        if(n <= 0)
        {
            return ans;
        }
        int low = 0;
        int high = n - 1;
        while(low <= high)
        {
            int mid = (low + high) >> 1;
            if(target == nums[mid])
            {
                ans = mid;
                break;
            }

            if(nums[low] < nums[mid])
            {
                if(target >= nums[low] && target < nums[mid])
                {
                    high = mid - 1;
                }
                else
                {
                    low = mid + 1;
                }
            }
            else if(nums[low] == nums[mid])
            {
                low = mid + 1;
            }
            else
            {
                if(target > nums[mid] && target <= nums[high])
                {
                    low = mid + 1;
                }
                else
                {
                    high = mid - 1;
                }
            }
        }
        return ans;
    }
};

 

2)如果数组存在重复元素

 上述a和c的情况基本是一直的,但是对于b情况,当A[l]==A[mid]时,无法判断是左半段单调递增还是右边半段单调递增,因为存在以下两种可能的情况:

i. 1 0 1 1 1 和1 1 1 0 1,唯一可以判断的是target != A[l],因此,此时只需要++l即可,具体代码如下:

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

        int l = 0;
        int r = n - 1;
        while(l <= r)
        {
            int m = (l + r) >> 1;
            if(nums[m] == target)
            {
                found = true;
                break;
            }

            if(nums[l] < nums[m])
            {
                if(target >= nums[l] && target < nums[m])
                {
                    r = m - 1;
                }
                else
                {
                    l =  m + 1;
                }
            }
            else if(nums[l] > nums[m])
            {
                if(target > nums[m] && target <= nums[r])
                {
                    l = m + 1;
                }
                else
                {
                    r = m - 1;
                }
            }
            else
            {
                ++l;
            }
        }
        return found;
    }
};

2. 和1是一样的题目,只不过要求数组的最小值

1)如果数组不存在重复元素

一共有四种情况:

a. A[l] < A[r]

说明整个数组是单调递增的,因此最小值为A[l],直接返回即可

b. A[l] < A[mid]

说明左半段单调递增,如下图情况:

说明最小值一定在mid的右边,因此更新l = mid + 1

c. A[l] == A[mid]

因为没有重复的元素,如果整个数组只有一个元素,l = mid + 1 之后(l <=r 循环判断会失败)则返回A[r];否则有多个元素,在右半段一定可以找到一个<= A[l]的,因此也是l = mid + 1

d. A[l] > A[mid] 

说明右半段单调递增,如下图情况:

因此最小值在mid的左边或者在mid上,因此r = mid

全部代码如下(注意最后返回A[r],这个可以用具体的例子试以下)

class Solution
{
public:
    int findMin(vector<int>& nums)
    {
            int ans = -1;
            int n = nums.size();
            if(n <= 0)
            {
                return ans;
            }

            int l = 0;
            int r = n - 1;
            while(l <= r)
            {
                if(nums[l] < nums[r])
                {
                    ans = nums[l];
                    return ans;
                }

                int m = l + ((r - l) >> 1);
                if(nums[l] <= nums[m])
                {
                    l = m + 1;
                }
                else
                {
                    r = m;
                }
            }

            return nums[r];
    }
};

2)如果数组存在重复元素

如果存在重复的元素,对于上述等于的情况,实际上是无法判断最小值在左边区间,还是在右边区间,但无论在左半段或者右半段,最小值一定在l的右边或等于l,因此++l总是可以找到最小值,具体代码如下:

class Solution
{
public:
    int findMin(vector<int>& nums)
    {
            int ans = -1;
            int n = nums.size();
            if(n <= 0)
            {
                return ans;
            }
            int l = 0;
            int r = n - 1;
            while(l <= r)
            {
                if(nums[l] < nums[r])
                {
                    ans = nums[l];
                    return ans;
                }

                int m = (l + r) >> 1;
                if(nums[l] < nums[m])
                {
                    l = m + 1;
                }
                else if(nums[l] > nums[m])
                {
                    r = m;
                }
                else
                {
                    ++l;
                }
            }
            return nums[r];
    }
};

 

posted @ 2016-02-17 22:56  Shirley_ICT  阅读(170)  评论(0)    收藏  举报