算法随笔_38: 最多能完成排序的块

上一篇:算法随笔_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)

你可能感兴趣的:(算法,python,数据结构)