上一篇:算法随笔_37: 交替合并字符串-CSDN博客
=====
题目描述如下:
给定一个长度为 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] 可以得到最多的块数。 对每个块单独排序后,结果为 [0, 1], [2], [3], [4]
=====
算法思路:
让我们从右往左来分析这道题。
1. 倒数第一个数arr[-1],它自己为一个区间。我们暂时把它放入一个res的数组中。如果题目就一个元素,那最后答案肯定就为1。此时res=[arr[-1]]
2. 倒数第二个数arr[-2],如果它小于res[-1](即arr[-1]),最多的区间数就是2。并存入res。此时res=[arr[-1], arr[-2]]。依次类推,如果原数组中从后往前的序列是递减的趋势,那么区间数就是元素数,每个元素各自占一个区间。
3. 我们继续来看,当递减趋势的第一次升高时,比如在arr[-6]处,它大于arr[-5],即res[-1]。那它必然要和arr[-5]处在同一个区间,才能符合题目要求。
而且我们还注意到,如果arr[-6]也大于arr[-4],那arr[-6],arr[-5],arr[-4]这三个数都要处在同一个区间。换句话说,如果前一个区间最大的数,大于后一个区间最小的数,这两个区间必须合并为同一个区间。
因此,对于这种情况,我们需要做如下的处理:
a. arr[-6]需要从后往前与res里的元素逐个比较,对于小于它的数,从res里删除。直到碰到比它大的数为止。
b. 对于已经删除的数中最小的那个值,需要保留。比如arr[-6]大于arr[-5],arr[-4],arr[-3],但是小于arr[-2]。需要保留arr[-5]。
c. arr[-6]也无需存入res。
因此最终的res=[arr[-1], arr[-2], arr[-5]]。arr[-5]就是它所在区间最小的那个数。
到目前为止的分析,我们能从中发现一点规律了。我们发现res是一个递减序列。它的每个元素就是它所在区间的最小值。由于我们是从右往左遍历原数组,所以res数组总体是符合题目要求的,即,把res颠倒过来,再对每个区间排序,就可得到题目要求的样子。因此最终答案的区间总数就是res的长度。
当我们继续枚举原数组时,如果当前元素大于res[-1],我们按照步骤3做处理。如果小于res[-1],我们按照步骤2做处理。
下面是代码实现:
class Solution(object):
def maxChunksToSorted(self, arr):
"""
:type arr: List[int]
:rtype: int
"""
arr_len=len(arr)
res=[arr[-1]]
for i in range(arr_len-2,-1,-1):
num_min=res[-1]
while arr[i] > res[-1]:
if len(res)>1 and arr[i]>res[-2]:
res.pop()
else:
res[-1]=num_min
break
if arr[i] < res[-1]:
res.append(arr[i])
return len(res)