(数组)581. 最短无序连续子数组

题目

给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

请你找出符合题意的 最短 子数组,并输出它的长度。

示例 1:

复制代码
输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。

示例 2:

复制代码
输入:nums = [1,2,3,4]
输出:0

示例 3:

复制代码
输入:nums = [1]
输出:0

提示:

  • 1 <= nums.length <= 10<sup>4</sup>
  • -10<sup>5</sup> <= nums[i] <= 10<sup>5</sup>

进阶: 你可以设计一个时间复杂度为 O(n) 的解决方案吗?

算法

将给定的数组 nums 表示为三段子数组拼接的形式,分别记作 numsA(左侧有序段),numsB(中间乱序段),numsC(右侧有序段)。当我们对整个序列进行排序,numsA 和 numsC 都不会改变。

本题要求我们找到最短的 numsB,即找到最大的 numsA 和 numsC 的长度之和。因此我们将原数组 nums 排序与原数组进行比较,只需要找到乱序段numsB的最左端left和最右端right,长度就是right - left + 1。

假设 nums B 在 nums 中对应区间为 **left** ,**right**。

  • 正扫找右边界 right :从左向右 遍历,维护 遍历过的前缀最大值 maxn :
    • 正常递增数组中,当前元素一定 ≥ 左边所有元素的最大值;
    • 如果当前元素 < maxn,说明这个元素位置错位,属于乱序段;
    • 持续更新,最后一个满足「元素 < 前缀最大值」的位置 ,就是乱序段的最右端。
  • 反扫找左边界 left :从右向左 遍历,维护 遍历过的后缀最小值 minn :
    • 正常递增数组中,当前元素一定 ≤ 右边所有元素的最小值;
    • 如果当前元素 > minn,说明这个元素位置错位,属于乱序段;
    • 持续更新,最靠前的满足「元素 > 后缀最小值」的位置 ,就是乱序段的最左端

同一轮循环做到两个方向遍历:正扫下标为i,反扫对称下标为n - i - 1

两个方向的遍历进度完全同步,因此可以放在同一个 for 循环里同时计算,不需要写两轮独立遍历。

代码

Java 复制代码
class Solution {
    public int findUnsortedSubarray(int[] nums) {
        int n = nums.length;

        // 正扫变量:maxn维护前缀最大值,right记录乱序段最右端下标
        int maxn = Integer.MIN_VALUE, right = -1;
        // 反扫变量:minn维护后缀最小值,left记录乱序段最左端下标
        int minn = Integer.MAX_VALUE, left = -1;

        // 一趟循环同时做从左到右、从右到左的对称扫描
        for (int i = 0; i < n; i++) {
            // ===== 正扫:从左到右定位右边界 =====
            // 当前元素比前缀最大值小 → 位置错位,属于乱序段,更新右边界
            if (maxn > nums[i]) {
                right = i;
            } else {
                // 正常递增,更新前缀最大值
                maxn = nums[i];
            }

            // ===== 反扫:从右到左定位左边界 =====
            // 对称下标:n-i-1,从末尾往头遍历
            int reverseIdx = n - i - 1;
            // 当前元素比后缀最小值大 → 位置错位,属于乱序段,更新左边界
            if (minn < nums[reverseIdx]) {
                left = reverseIdx;
            } else {
                // 正常递增,更新后缀最小值
                minn = nums[reverseIdx];
            }
        }

        // right=-1说明全程没有错位,数组本身有序,返回0;否则返回乱序段长度
        return right == -1 ? 0 : right - left + 1;
    }
}
相关推荐
I_belong_to_jesus5 小时前
C++关于分支预测的优化1
开发语言·c++·算法
Code_Solitude12 小时前
C语言动态内存 + 柔性数组 + 数组名退化
c语言·算法·柔性数组
上海潭龙东海科技有限公司13 小时前
客运站管控之动态孪生技术方案(新增一屏管控模块
数据仓库·人工智能·算法·重构·动态规划
文丑颜不良啊14 小时前
LeetCode2821~2840题目解析
java·leetcode
YLCHUP15 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
思麟呀16 小时前
背包DP(01背包+完全背包)
c++·算法
笑容掩饰我的失落17 小时前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
Pniubi17 小时前
力扣24. 两两交换链表中的节点(递归)
算法·leetcode·链表
nice_lcj52018 小时前
LRU 缓存入门:它是什么、怎么用、面试怎么手写
java·数据结构·缓存·面试·lru