81. Search in Rotated Sorted Array II(搜索旋转排序数组 II)两种解法(C++ & 注释)

1. 题目描述

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。

编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

示例 1:

输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true

示例 2:

输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false

进阶:

  • 这是 搜索旋转排序数组 的延伸题目,本题中的 nums 可能包含重复元素。
  • 这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?

题目链接:中文题目英文题目

2. 二分法 & 双指针(Binary Search & Two Pointers)

2.1 解题思路

本体是33. Search in Rotated Sorted Array(搜索旋转排序数组)一道相关联的题目,和上一题不同的是:这次数组会有重复数字。

所以这里笔者只总结和讲解重点的部分,一些细节的地方可以参照上一题:33. Search in Rotated Sorted Array(搜索旋转排序数组)两种解法(C++ & 注释)

首先我们来回顾一下上一题所涉及的典型例子:

  1. 旋转后形成峰顶和谷底:[0,1,2,4,5,6,7] -> [4,5,6,7,0,1,2];
  2. 旋转后形成降序:[1,3] -> [3,1];
  3. 旋转后升序不变:[1,3] -> [1,3];
  4. 空数组:[] -> [];

本题自然也包含上述这些情况,只是有一种非常特殊的情形:旋转后形成峰顶和谷底,也两边界会相等:[0,0,1,2,2,5,6] -> [2,5,6,0,0,1,2],这种情形使用上一题的思路还是OK的,应该mid取不到两边相等的数,所以木有问题,但是对于下面这种情况就GG了:

[4,4,4,4,4,7,4,4]

所以我们不能向上题那样,判断nums[mid]和nums[left]和nums[right]的大小关系来进行递归,因为峰顶可能存在于两边,所以将计就计,分别查找两边是否有峰顶,找得到则返回序号,找不到返回-1。找到峰顶序号以后的操作和上题一致,依次对三种(完全升序,峰顶左边,峰顶右边)情况进行分别查找即可。

嘛,最后发现本题简化后的代码完全适用上一题哒,也算是做了一下优化吧。到此也回答了follow up里面的问题:和上题相比,有重复数字会影响时间复杂度么?答案是不会哒,都是O(logn),而且代码和思路都是通用的,只是上一题是这题的一个特例。

最后说一下binary_search(first, last, target)函数的用法:这个函数是在[first, last)范围内二分查找target,找到了返回true,反之返回false;first 和 last为迭代器。

2.2 实例代码

class Solution {
    bool twoPointers(vector<int>& nums, int left, int right, int target) {
        while (left <= right) {
            if (nums[left] == target) return true;
            if (nums[right] == target) return true;
            left++;
            right--;
        }

        return false;
    }

    int findPeak(vector<int>& nums, int left, int right) {
        if (left > right) return -1;
        int mid = (right - left) / 2 + left;

        // 找到峰顶
        if (nums[mid - 1 >= 0 ? mid - 1 : 0] <= nums[mid] && nums[mid] > nums[mid + 1 >= nums.size() ? nums.size() - 1 : mid + 1]) return mid;

        // 没找到峰顶,两边继续寻找
        int peakLeft = findPeak(nums, left, mid - 1), peakRight = findPeak(nums, mid + 1, right);
        return peakLeft == -1 ? (peakRight == -1 ? -1 : peakRight) : peakLeft;
    }

public:
    // twoPointers和binary_search二选一即可
    bool search(vector<int>& nums, int target) {
        int len = nums.size(), peak = findPeak(nums, 0, len - 1);

        if (peak == -1) { // 完全升序
            // return twoPointers(nums, 0, len - 1, target);
            return binary_search(nums.begin(), nums.end(), target); 
        } 
        else if (nums[0] <= target && target <= nums[peak]) { // 峰顶左边
            // return twoPointers(nums, 0, peak, target);
            return binary_search(nums.begin(), nums.begin() + peak + 1, target);
        }
        else if (nums[peak + 1] <= target && target <= nums[len - 1]) { // 峰顶右边
            // return twoPointers(nums, peak + 1, len - 1, target);
            return binary_search(nums.begin() + peak + 1, nums.end(), target);
        }

        return false;
    }
};

3. 参考资料

  1. 二分法logn时间复杂度解释

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值