Medium
题目描述
给定一个长度为 n 的整数数组 arr,它表示在范围 [0, n-1] 内所有整数的一个排列。
我们将 arr 分割成若干个数据块(即分区),并对每个数据块单独排序。之后我们将所有排序的数据块连接起来,结果应该与排序后的数组相等。
返回我们能够分割的最大数据块数目。
示例 1:
输入: arr = [4,3,2,1,0]
输出: 1
解释:
将数组分割成2个或者更多块,都无法得到所需的结果。
例如,分割成 [4, 3], [2, 1, 0] 的结果是 [3, 4, 0, 1, 2],这不是有序的数组。
示例 2:
输入: arr = [1,0,2,3,4]
输出: 4
解释:
我们可以把它分成两块,例如 [1, 0], [2, 3, 4]。
然而,分成 [1, 0], [2], [3], [4] 可以得到最多的块数。
约束条件:
- n == arr.length
- 1 <= n <= 10
- 0 <= arr[i] < n
- arr 的所有元素都是唯一的。
解题思路
解题思路
这道题的核心思想是:当我们能够确定前 k 个位置的数字都能在前 k 个位置找到时,就可以形成一个独立的数据块。
由于数组是 [0, n-1] 的排列,排序后应该是 [0, 1, 2, …, n-1]。我们可以利用这个特性:
方法一:前缀最大值法(推荐)
- 遍历数组,维护当前位置的前缀最大值
- 如果在位置 i,前缀最大值等于 i,说明前 i+1 个元素恰好是 [0, 1, …, i] 的某个排列
- 此时可以在位置 i 处切分,形成一个独立的数据块
- 时间复杂度 O(n),空间复杂度 O(1)
方法二:前缀和法
- 计算前缀和,如果位置 i 的前缀和等于 0+1+…+i = i*(i+1)/2,说明可以切分
- 原理相同,但需要额外的数学计算
方法三:单调栈法
- 维护一个单调递增的栈,栈中存储每个数据块的最大值
- 适用于更一般的情况,但对这道题稍显复杂
例如 [1,0,2,3,4]:
- i=0: max=1, 1≠0,不能切分
- i=1: max=1, 1=1,可以切分(第1个块:[1,0])
- i=2: max=2, 2=2,可以切分(第2个块:[2])
- i=3: max=3, 3=3,可以切分(第3个块:[3])
- i=4: max=4, 4=4,可以切分(第4个块:[4])
总共4个数据块。
代码实现
class Solution {
public:
int maxChunksToSorted(vector<int>& arr) {
int chunks = 0;
int maxSoFar = 0;
for (int i = 0; i < arr.size(); i++) {
maxSoFar = max(maxSoFar, arr[i]);
if (maxSoFar == i) {
chunks++;
}
}
return chunks;
}
};
class Solution:
def maxChunksToSorted(self, arr: List[int]) -> int:
chunks = 0
max_so_far = 0
for i in range(len(arr)):
max_so_far = max(max_so_far, arr[i])
if max_so_far == i:
chunks += 1
return chunks
public class Solution {
public int MaxChunksToSorted(int[] arr) {
int chunks = 0;
int maxSoFar = 0;
for (int i = 0; i < arr.Length; i++) {
maxSoFar = Math.Max(maxSoFar, arr[i]);
if (maxSoFar == i) {
chunks++;
}
}
return chunks;
}
}
var maxChunksToSorted = function(arr) {
let chunks = 0;
let max = 0;
for (let i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
if (max === i) {
chunks++;
}
}
return chunks;
};
复杂度分析
| 复杂度类型 | 前缀最大值法 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |