769. 最多能完成排序的块(元素不可重复)
https://leetcode-cn.com/problems/max-chunks-to-make-sorted/
数组arr是[0, 1, ..., arr.length - 1]的一种排列,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。我们最多能将数组分成多少块?
arr
的长度在[1, 10]
之间。arr[i]
是[0, 1, ..., arr.length - 1]
的一种排列。
输入: arr = [4,3,2,1,0]
输出: 1
解释:
将数组分成2块或者更多块,都无法得到所需的结果。
例如,分成 [4, 3], [2, 1, 0] 的结果是 [3, 4, 0, 1, 2],这不是有序的数组。
输入: arr = [1,0,2,3,4]
输出: 4
解释:
我们可以把它分成两块,例如 [1, 0], [2, 3, 4]。
然而,分成 [1, 0], [2], [3], [4] 可以得到最多的块数。
规律,暴力,O(n)
class Solution(object):
def maxChunksToSorted(self, arr):
"""
:type arr: List[int]
:rtype: int
"""
# 当遍历到第i个位置时,如果可以切分为块,那前i个位置的最大值一定等于i。
# 否则,一定有比i小的数划分到后面的块,那块排序后,一定不满足升序。
res = 0
maxV = 0
n = len(arr)
for i in range(n):
maxV = max(maxV, arr[i]) # 统计前i个位置的最大元素
if maxV == i:
res += 1
return res
768. 最多能完成排序的块 II (元素可重复)
https://leetcode-cn.com/problems/max-chunks-to-make-sorted-ii/
这个问题和“最多能完成排序的块”相似,但给定数组中的元素可以重复。
arr
的长度在[1, 2000]
之间。arr[i]
的大小在[0, 10**8]
之间。
arr是一个可能包含重复元素的整数数组,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。我们最多能将数组分成多少块?
输入: arr = [5,4,3,2,1]
输出: 1
解释:
将数组分成2块或者更多块,都无法得到所需的结果。
例如,分成 [5, 4], [3, 2, 1] 的结果是 [4, 5, 1, 2, 3],这不是有序的数组。
输入: arr = [2,1,3,4,4]
输出: 4
解释:
我们可以把它分成两块,例如 [2, 1], [3, 4, 4]。
然而,分成 [2, 1], [3], [4], [4] 可以得到最多的块数。
单调栈
- 排序块 充分条件: 分成块的前提是,前一个块的最大值,小于后一个块的最小值
- 排序块 最短长度为 1,即单个元素可以独立看作一个排序块。
class Solution(object):
def maxChunksToSorted(self, arr):
# 核心:分成块的前提是,前一个块的最大值,小于后一个块的最小值
stack = []
for num in arr:
# 递增栈
# (1)遇到大于等于栈顶元素的数字就压入栈
# (2)遇到小于栈顶元素的数字后:首先取出栈顶元素,即当前最大值,维护单调递增栈
# 再进行循环,如果栈不为空,且新的栈顶元素大于当前数字,则移除栈顶元素
# 单调栈的元素个数实际上是遍历到当前数字之前可以拆分成的块儿的个数
# 遇到一个大于栈顶的元素,就将其压入栈,
# 但是一旦后面遇到小的数字了,就要反过来检查前面的数字,
# 有可能之前认为的可以拆分成块儿的地方,现在就不能拆,
# 因为这个更小的值,可能大于前一个块的最大值。
if not stack or stack[-1] <= num:
stack.append(num)
else:
cur_max = stack.pop()
while stack and stack[-1] > num:
stack.pop()
stack.append(cur_max) # 再把最大值压栈
return len(stack)
- 时间复杂度 O(N):遍历一遍 arrarr 为 O(N),修正排序块最多遍历一遍 arrarr 为 O(N)
- 空间复杂度 O(N) :极端情况下排序块数量等于数组长度,此时 stack 占用线性大小额外空间