每天一道LeetCode-----有序数组右移n位后查找某个元素

Search in Rotated Sorted Array

原题链接Search in Rotated Sorted Array
每天一道LeetCode-----有序数组右移n位后查找某个元素_第1张图片
一个无重复项的递增序列右移了一定距离,在右移后的序列中查找是否有某个元素
比如[0,1,2,4,5,6,7]右移4位变为[4,5,6,7,0,1,2],在[4,5,6,7,0,1,2]中查找某个元素,事先不知道移动了多少位


对于有序序列,首先可以想到的是利用二分法查找,但是序列别移动后的起点不再是最左边的位置。比如说上面的例子,起点0在下标为4的位置,所以不一定满足nums[0] < nums[n - 1],不能直接使用二分法。
但是因为是整体移动,局部仍然是有序的,所以如果确定了某个区间是有序的,那么还是可以使用二分法的

下面的*代表起点

o   *   o   o   o   o   o
大          小          中
left       middle      right
如果起点在middle的左侧,那么nums[left],nums[middle],nums[right]的大小关系如上
可通过nums[middle] < nums[right]判断起点在middle左边

o   o   o   o   o   *   o
中          大          小
left       middle      right
如果起点在middle的右侧,那么nums[left],nums[middle],nums[right]的大小关系如上
可通过nums[middle] > nums[right]判断起点在middle右边

以第一种情况为例(起点在middle左侧)即nums[middle] < nums[right]
如果目标元素在[middle, right]区间,那么一定有nums[target] >= nums[middle] && nums[target] <= nums[right]
否则,目标元素在[left, middle]区间内
因为[middle, right]一定是递增的,所以可以判断目标元素是否在这个区间内,而[left, middle]区间不是递增的,故不能判断。

第二种情况同理
代码如下

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while(left < right)
        {
            int middle = (left + right) / 2;
            if(nums[middle] == target)
                return middle;

            /* 第一种情况,起点在middle左边 */
            if(nums[middle] < nums[right])
            {
                /* 目标在[middle, right]区间,因为nums[middle]已经比较过了,所以middle不需要等号 */
                if(target > nums[middle] && target <= nums[right])
                    left = middle + 1;
                else
                    right = middle - 1;
            }
            /* 第二种情况,起点在middle右边 */
            else
            {
                /* 目标在[left, middle]区间,middle不需要等号,原因同上 */
                if(target < nums[middle] && target >= nums[left])
                    right = middle - 1;
                else
                    left = middle + 1;
            }
        }

        return left < nums.size() && nums[left] == target ? left : -1;
    }

};

扩展
原题链接Search in Rotated Sorted Array II
每天一道LeetCode-----有序数组右移n位后查找某个元素_第2张图片

要求和上面的一样,只是所给序列可能带有重复项。
此时情况就不仅仅是上面两种,比如

o   o   o   o   o   *   o
小          小  大       小
left       middle      right
如果起点在middle的右侧,那么有可能nums[middle] == nums[right]

[3,1,1]中找3为例
此时nums[middle] == nums[right],那么通过上面的方法判断得知起点在右边(进入else),但是此时[left, middle]不是递增的。而上面的方法必须保证

  • 如果起点在右边,那么[left, middle]是递增的
  • 如果起点在左边,那么[middle, right]是递增的

解决办法是通过比较nuns[middle] < nums[right]判断起点在左边,比较nums[middle] > nums[right]判断起点在右边,否则有重复项,将right左移,重新判断。

为什么左移right
因为nums[middle] == nums[right],可知存在重复项,那么无法保证[left, middle]是递增的,所以缩小范围,将区间变为[left, right-1],尝试去掉重复项。

代码如下

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while(left < right)
        {
            int middle = (left + right) / 2;
            if(nums[middle] == target)
                return true;

            /* 起点在左边 */
            if(nums[middle] < nums[right])
            {
                if(target > nums[middle] && target <= nums[right])
                    left = middle + 1;
                else
                    right = middle - 1;
            }
            /* 起点在右边 */
            else if(nums[middle] > nums[right])
            {
                if(target < nums[middle] && target >= nums[left])
                    right = middle - 1;
                else
                    left = middle + 1;
            }
            /* 有重复项 */
            else
            {
                --right;
            }
        }
        return left < nums.size() && nums[left] == target ? true : false;
    }
};

你可能感兴趣的:(LeetCode,leetcode)