持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
题目链接:769. 最多能完成排序的块
题目描述
给定一个长度为 n 的整数数组 arr ,它表示在 [0, n - 1] 范围内的整数的排列。
我们将 arr 分割成若干 块 (即分区),并对每个块单独排序。将它们连接起来后,使得连接的结果和按升序排序后的原数组相同。
返回数组能分成的最多块数量。
提示:
n == arr.lengtharr中每个元素都 不同
示例 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] 可以得到最多的块数。
整理题意
题目要求首先对给定数组进行分块,块需要是连续的数组元素,然后对每一块进行排序,使得块排序后的数组成为升序数组(因为数组中每个元素不同),问最多能够分成多少块。
解题思路分析
注意题目的数据范围,数组中的元素范围在 且每个元素互不相同,所以排序后的数组下标对应数组元素。
思维 + 暴力
- 当遍历到第
i个位置时,如果可以切分为块,那前i个位置的最大值一定等于i;否则,一定有比i小的数划分到后面的块,那块排序后,一定不满足升序。 - 如果当前下标
i(包括i)之前的最大值为i,则说明可以切分为块;否则前半部分有大于i的元素,后半部分有小于i的元素,块排序后必不满足题意。
单调栈
该题还可以使用单调栈的解法,不过在空间复杂度上是不如 贪心思维 的,不过当数组中存在重复元素时是无法使用这种贪心思维的,会导致下标错位。
具体实现
思维 + 暴力
从头到尾遍历数组,记录前缀中的最大元素值,如果当前下标等于前缀中的最大值,那么就可以将此处切一刀进行分块,记录块的数量。然后依次直至遍历完数组,输出块的数量。
单调栈
单调栈的解法为:
- 始终保持栈栈底为较小元素,栈顶为较大元素;
- 从头到尾遍历数组,如果当前元素小于栈顶元素,那么说明当前元素是和栈顶(前一块)是同一块的;
- 记录栈顶最大的元素值,然后一直弹出栈顶元素,直至栈为空或者大于栈顶元素。
- 然后将刚刚记录的最大元素压入栈顶,用这个最大元素表示当前这个块,这个过程可以看作把当前元素融入前面的块,同时把前面的块融为一块。
- 重复这个过程,最后栈中的元素即为每个块中的最大元素,输出栈中的元素个数即为最大可拆分的块数量。
复杂度分析
- 时间复杂度:,其中
n为数组arr的长度。使用 思维 + 暴力 和 单调栈 的方法时间复杂度都为 - 空间复杂度:使用 思维 + 暴力 的方法时间复杂度为 ,不计返回值的空间;使用 单调栈 的方法由于使用到了栈,最坏的情况下需要 的栈空间。
代码实现
思维 + 暴力
class Solution {
public:
int maxChunksToSorted(vector<int>& arr) {
// n 为数组大小,m 为前缀中的最大值,ans 为分块数量
int n = arr.size(), m = 0, ans = 0;
for(int i = 0; i < n; i++){
// 维护最大值 m
m = max(m, arr[i]);
// 当最大值等于下标时可以分块
if(m == i) ans++;
}
return ans;
}
};
单调栈
class Solution {
public:
int maxChunksToSorted(vector<int>& arr) {
// 单调栈
stack<int> s; while(s.size()) s.pop();
for(int &num : arr){
// 因为 arr 中每个元素都不同,可以省略等号
// s.top() < num
if(s.empty() || s.top() <= num) s.push(num);
else{
// m 记录当前块最大值
int m = s.top();
while(!s.empty() && s.top() > num){
m = max(m, s.top());
s.pop();
}
s.push(m);
}
}
return s.size();
}
};
总结
- 该题的贪心思维是比较巧妙的,但是这是建立在题目的种种条件下的特殊情况,因为数组元素不重复且数组元素范围为
[0, n - 1];而单调栈的解法较为常规,并且可以处理数组中存在重复元素的情况。 - 测试结果:
结束语
成功往往在多次失败之后才姗姗来迟,每一次跌倒后再爬起来,都能使你本领更强大、信心更充足。永不放弃,持之以恒,将挫折变成你进步的阶梯,你就获得了成功的机会。新的一天,加油!