33. 搜索旋转排序数组

这题本质上还是 二分查找,只是数组被"旋转"了。

正常二分里,数组整体有序。

但这里:

复制代码
[4,5,6,7,0,1,2]

整体不是有序的。

不过有个非常关键的性质:

每次二分后,左右两边一定有一边是有序的。

这就是突破口。


一、核心思路

每次取中点:

复制代码
mid = (left + right) / 2

然后判断:

复制代码
nums[left] <= nums[mid]

如果成立:

说明 左半边有序

否则:

说明 右半边有序

然后看 target 是否落在有序区间内。


二、举例彻底理解

数组:

复制代码
[4,5,6,7,0,1,2]

target = 0


第一次

复制代码
left = 0 -> 4
right = 6 -> 2

mid = 3 -> 7

现在:

复制代码
4 5 6 7 | 0 1 2

看:

复制代码
nums[left] <= nums[mid]
4 <= 7

成立。

说明:

复制代码
左边 [4,5,6,7] 有序

然后判断:

target=0 是否在:

复制代码
[4,7]

之间。

显然不在。

所以:

复制代码
去右边找

即:

复制代码
left = mid + 1

第二次

现在:

复制代码
0 1 2

即:

复制代码
left = 4
right = 6
mid = 5
nums[mid] = 1

看:

复制代码
nums[left] <= nums[mid]
0 <= 1

成立。

说明:

复制代码
左边 [0,1] 有序

target=0 是否在:

复制代码
[0,1]

之间?

在。

所以:

复制代码
去左边继续二分

即:

复制代码
right = mid - 1

第三次

复制代码
left = 4
right = 4
mid = 4

发现:

复制代码
nums[mid] == target

返回 4。


三、最重要的理解

旋转数组虽然整体无序:

复制代码
4 5 6 7 0 1 2

但它一定是:

复制代码
一段升序 + 一段升序

所以:

每次二分后,一定至少有一边是完全有序的。

我们就利用这一边判断 target 在不在里面。


四、代码(最经典写法)

java 复制代码
class Solution {
    public int longestValidParentheses(String s) {
        int max = 0;
        int[] dp = new int[s.length()];

        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == ')') {

                // 1. ()
                if (s.charAt(i - 1) == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                }

                // 2. ))
                else if (i - dp[i - 1] - 1 >= 0 
                        && s.charAt(i - dp[i - 1] - 1) == '(') {

                    dp[i] = dp[i - 1] + 2;

                    if (i - dp[i - 1] - 2 >= 0) {
                        dp[i] += dp[i - dp[i - 1] - 2];
                    }
                }

                max = Math.max(max, dp[i]);
            }
        }

        return max;
    }
}

五、你真正需要记住的只有两句话

1. 判断哪边有序

复制代码
nums[left] <= nums[mid]

成立:

复制代码
左边有序

否则:

复制代码
右边有序

2. 判断 target 在不在有序区间

左边有序:

复制代码
nums[left] <= target && target < nums[mid]

右边有序:

复制代码
nums[mid] < target && target <= nums[right]

六、为什么时间复杂度还是 O(log n)

因为每次都:

复制代码
砍掉一半区间

所以仍然是二分查找。


这题是二分查找里非常经典的一道"变形题"。

你如果已经会普通二分,这题真正难的点只有:

"每次一定有一半有序"

一旦理解这一点,后面就是普通二分逻辑了。

相关推荐
ZhangShao06074 分钟前
题解:CF2249B
c++·算法
小玮看世界4 分钟前
[Python]线段树与二分法
数据结构·算法
Tisfy3 小时前
LeetCode 3345.最小可整除数位乘积 I:暴力枚举(从n开始尝试)
数学·算法·leetcode·题解·枚举
DolphinDB智臾科技3 小时前
告别 TB 级数据搬运:DolphinDB 滤波算法库,重塑物联网数据价值链
物联网·算法
可编程芯片开发4 小时前
基于双PI控制器结构的六步逆变器供电无刷直流电机调速simulink仿真
算法
Sagittarius_A*4 小时前
后量子密码|通识认知 02|量子威胁核心原理:Shor 算法如何击穿传统公钥密码
算法·信息安全·密码学·量子计算
青山木4 小时前
Hot 100 --- 在排序数组中查找元素的第一个和最后一个位置
java·数据结构·算法·leetcode
其美杰布-富贵-李4 小时前
聚类算法详解:K-means、层次聚类与 DBSCAN
算法·机器学习·kmeans·聚类
碧海银沙音频科技研究院5 小时前
4 BES2710编译环境搭建方法
嵌入式硬件·算法
mifengxing5 小时前
LeetCode 238 除自身以外数组的乘积|无除法O(n)时间+O(1)空间双解法
java·算法·leetcode