Leetcode 81.搜索选择排序数组Ⅱ

已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。

给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 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

提示:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
  • -104 <= target <= 104

进阶:

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

通过次数

208.8K

提交次数

509.1K

通过率

41.0%

我的答案:

一、信息

题目描述了一个整数数组 nums,它首先按非降序排列,然后在未知下标 k 处进行了旋转。数组中的元素可以重复。要求编写一个函数,判断给定的目标值 target 是否存在于数组中。

二、分析

  • 数组旋转:数组在某个下标 k 处旋转意味着数组被分为两部分,并且每部分内部仍然保持原有的排序。
  • 存在重复元素:与普通的二分查找不同,重复元素的存在使得我们无法直接判断哪部分是有序的。
  • 二分查找的应用:虽然数组被旋转,我们仍然可以尝试使用二分查找,但需要添加额外的条件来处理重复元素。

三、算法设计

  1. 初始化左右指针:设定左右边界 leftright
  2. 二分查找
    • 循环直到 left 大于 right
    • 计算中间位置 mid
    • 如果 nums[mid] 等于 target,返回 true
    • 如果 nums[left]nums[mid]nums[right] 相等,不能判断哪部分有序,此时将 leftright 分别向内移动。
    • 如果左侧有序(nums[left] <= nums[mid]):
      • 判断 target 是否在左侧,调整 right
      • 否则,调整 left
    • 如果右侧有序:
      • 判断 target 是否在右侧,调整 left
      • 否则,调整 right
  3. 找不到目标值时返回 false

四、代码实现(C++)

#include <vector>
using namespace std;

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

            if (nums[left] == nums[mid] && nums[mid] == nums[right]) {
                ++left; --right;
            } else if (nums[left] <= nums[mid]) {
                if (nums[left] <= target && target < nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        return false;
    }
};

 

五、实现代码过程中可能遇到的问题

  1. 边界处理:在处理边界条件时需要非常小心,尤其是在调整 leftright 时,以免错过目标值。
  2. 重复元素的处理:当遇到 nums[left]nums[mid]nums[right] 相等的情况,需要特别处理,此时简单地移动边界可能会影响查找效率。
  3. 时间复杂度:由于重复元素的存在,最坏情况下的时间复杂度可能退化到 O(n)。

Leetcode官方题解:

class Solution {
public:
    bool search(vector<int> &nums, int target) {
        int n = nums.size();
        if (n == 0) {
            return false;
        }
        if (n == 1) {
            return nums[0] == target;
        }
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[l] == nums[mid] && nums[mid] == nums[r]) {
                ++l;
                --r;
            } else if (nums[l] <= nums[mid]) {
                if (nums[l] <= target && target < nums[mid]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
        }
        return false;
    }
};

 学到了什么?

从题目「搜索旋转排序数组 II」中,我们可以学到一些重要的思想、思维方法和二分查找的技巧:

1. 灵活应用二分查找

  • 二分查找的适应性:通常,二分查找适用于严格有序的数组。然而,这个题目教会我们即使在部分有序的情况下,二分查找也可以被灵活应用。关键在于能够准确地识别出数组中的有序部分。

2. 处理复杂场景的思维训练

  • 应对重复元素:在数组中存在重复元素时,传统的二分查找可能无法直接应用。这要求我们对原有算法进行修改和调整,以适应更复杂的场景。
  • 增强判断逻辑:在无法直接判断哪部分是有序时,我们需要采取额外的步骤(如逐步缩小搜索范围),这增强了我们的逻辑判断能力。

3. 细节处理的重要性

  • 边界条件的关注:正确处理边界条件(如数组为空或仅有一个元素的情况)对于实现一个健壮的算法至关重要。
  • 安全的中点计算:学习到了如何安全地计算中点,避免 (left + right) / 2 可能导致的整数溢出问题。

4. 复杂度分析的意识

  • 最坏情况的分析:虽然二分查找通常具有 O(log n) 的时间复杂度,但在某些情况下,它可能会退化。了解算法在不同情况下的性能变化是重要的。

5. 灵活应对算法的限制

  • 算法退化情况的应对:了解到即使在算法可能退化的情况下,如何通过适当的策略来尽可能保持算法的有效性。

6. 质疑和验证的重要性

  • 不断验证假设:在实施算法的每一步,都需要验证当前的假设是否正确,特别是在有重复元素影响判断时。

通过这个题目,我们不仅学习了二分查找的技巧,还提高了我们处理复杂问题、细节关注和逻辑判断的能力。这些技巧和思维方法在解决其他算法问题时也非常有用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值