1574. 删除最短的子数组使剩余数组有序
1574. 删除最短的子数组使剩余数组有序
🟠 🔖 栈 数组 双指针 二分查找 单调栈 🔗 力扣 LeetCode
题目
Given an integer array arr, remove a subarray (can be empty) from arr such that the remaining elements in arr are non-decreasing.
Return the length of the shortest subarray to remove.
A subarray is a contiguous subsequence of the array.
Example 1:
Input: arr = [1,2,3,10,4,2,3,5]
Output: 3
Explanation: The shortest subarray we can remove is [10,4,2] of length 3. The remaining elements after that will be [1,2,3,3,5] which are sorted.
Another correct solution is to remove the subarray [3,10,4].
Example 2:
Input: arr = [5,4,3,2,1]
Output: 4
Explanation: Since the array is strictly decreasing, we can only keep a single element. Therefore we need to remove a subarray of length 4, either [5,4,3,2] or [4,3,2,1].
Example 3:
Input: arr = [1,2,3]
Output: 0
Explanation: The array is already non-decreasing. We do not need to remove any elements.
Constraints:
1 <= arr.length <= 10^50 <= arr[i] <= 10^9
题目大意
给你一个整数数组 arr ,请你删除一个子数组(可以为空),使得 arr 中剩下的元素是 非递减 的。
一个子数组指的是原数组中连续的一个子序列。
请你返回满足题目要求的最短子数组的长度。
示例 1:
输入: arr = [1,2,3,10,4,2,3,5]
输出: 3
解释: 我们需要删除的最短子数组是 [10,4,2] ,长度为 3 。剩余元素形成非递减数组 [1,2,3,3,5] 。
另一个正确的解为删除子数组 [3,10,4] 。
示例 2:
输入: arr = [5,4,3,2,1]
输出: 4
解释: 由于数组是严格递减的,我们只能保留一个元素。所以我们需要删除长度为 4 的子数组,要么删除 [5,4,3,2],要么删除 [4,3,2,1]。
示例 3:
输入: arr = [1,2,3]
输出: 0
解释: 数组已经是非递减的了,我们不需要删除任何元素。
示例 4:
输入: arr = [1]
输出: 0
提示:
1 <= arr.length <= 10^50 <= arr[i] <= 10^9
解题思路
- 寻找左右有序区间
首先确定从两端扩展的两个有序区间:
左侧有序区间: 从
arr[0]开始向右找出最长的非递减子数组,标记为[0, left]。右侧有序区间: 从
arr[n-1]开始向左找出最长的非递减子数组,标记为[right, n-1]。如果整个数组已经有序 (
left == n - 1),直接返回 0。
- 初步计算结果
在只保留左侧或右侧区间的情况下,分别删除的子数组长度为:
- 删除
[left+1, n-1],即n - left - 1。 - 删除
[0, right-1],即right。
初始结果取二者的较小值。
- 合并左右区间
尝试合并左侧和右侧的有序区间。用两个指针 i 和 j:
i从左侧有序区间[0, left]开始遍历。j从右侧有序区间[right, n-1]开始遍历。
对于每对 arr[i] 和 arr[j]:
- 如果
arr[i] <= arr[j],说明arr[i]和arr[j]可以连通,将删除区间长度更新为j - i - 1,并向右移动i。 - 如果
arr[i] > arr[j],说明当前的j无法与i连通,需要向右移动j。
- 返回结果
最终返回最小的删除区间长度 result。
复杂度分析
- 时间复杂度:
O(n),找到左右有序区间的复杂度为O(n),双指针合并左右区间的复杂度为O(n),总时间复杂度为O(n)。 - 空间复杂度:
O(1),只使用了常数级别的额外空间。
代码
/**
* @param {number[]} arr
* @return {number}
*/
var findLengthOfShortestSubarray = function (arr) {
const n = arr.length;
// 寻找左右有序区间
let left = 0;
while (left + 1 < n && arr[left] <= arr[left + 1]) {
left++;
}
if (left == n - 1) return 0;
let right = n - 1;
while (right > 0 && arr[right] >= arr[right - 1]) {
right--;
}
// 初步计算结果
let result = Math.min(n - left - 1, right);
// 合并左右区间
let i = 0,
j = right;
while (i <= left && j < n) {
if (arr[i] <= arr[j]) {
result = Math.min(result, j - i - 1);
i++;
} else {
j++;
}
}
return result;
};
相关题目
| 题号 | 标题 | 题解 | 标签 | 难度 | 力扣 |
|---|---|---|---|---|---|
| 2970 | 统计移除递增子数组的数目 I | 数组 双指针 二分查找 1+ | 🟢 | 🀄️ 🔗 | |
| 2972 | 统计移除递增子数组的数目 II | 数组 双指针 二分查找 | 🔴 | 🀄️ 🔗 |
