图解LeetCode——768. 最多能完成排序的块 II(难度:困难)

一、题目

这个问题和“最多能完成排序的块”相似,但给定数组中的元素可以重复,输入数组最大长度为2000,其中的元素最大为10**8

arr是一个可能包含重复元素的整数数组,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。

我们最多能将数组分成多少块?

二、示例

2.1> 示例 1:

【输入】 arr = [5,4,3,2,1]
【输出】 1
【解释】
将数组分成2块或者更多块,都无法得到所需的结果。
例如,分成 [5, 4], [3, 2, 1] 的结果是 [4, 5, 1, 2, 3],这不是有序的数组。

2.2> 示例 2:

【输入】 arr = [2,1,3,4,4]
【输出】 4
【解释】
我们可以把它分成两块,例如 [2, 1], [3, 4, 4]。
然而,分成 [2, 1], [3], [4], [4] 可以得到最多的块数。

注意:

  • arr的长度在[1, 2000]之间。
  • arr[i]的大小在[0, 10**8]之间。

三、解题思路

3.1> 堆栈 + top指针

根据题意,我们要计算出最多的分组块数。那么约束条件就是,无论分成多少组,只要我们满足,在每个子组内对元素进行升序排序之后,组成的总的数组与将整体数组按照升序排列的结果是一样的就可以了。题目比较绕嘴,我们可以举个例子,比如:原有的数组为:arr = [2,1,4,3,7,8],那么如果我们按照升序排列的话,最终结果就是:arr=[1,2,3,4,7,8]。那么,如果我们先执行分组,再执行每个分组内部元素的排序,最终结果也要保证是arr=[1,2,3,4,7,8]。比如,我们将原数组arr = [2,1,4,3,7,8]分成两部分,即:[2,1,4,3][7,8],那么分别对这两个数组内部的元素进行升序排序,结果为:[1,2,3,4][7,8],那么将这两个数组拼装在一起,就是[1,2,3,4,7,8],那么这种分组方式,就满足了题目中的条件了。具体操作,如下图所示:

不过,需要注意的是,题目中要求获得的是最多的分组块数,所以,我们最终的分组情况应该是[2,1][4,3][7][8]这四组,才满足最多分组块数并且最终排序结果为[1,2,3,4,7,8]。具体操作,如下图所示:

我们现在了解了题目的含义。那么,如何去计算分组呢?其实在上面的两个分组的例子中,我们也能找到一些规律。比如,以上面的例子为例,分为了四组,分别为[2,1][4,3][7][8]这四组。那么我们可以发现,当某一个组内最大的那个值,它大于组内的任意一个元素同时它小于任意一个组外的元素,那么,就可以满足分组排序后结果依然是整体升序了。按照题目中升序的条件,我们可以采用堆栈的方式进行数据的存储,但是,我们没有必要存储所有的元素,因为只要知道最多分多少组就可以了,而并不需要知道每个分组的详情。所以,我们将满足条件的组内最大值存入到堆栈中即可。

这里面具体操作有如下几个步骤:

  • 首先:如果堆栈为空,或者遍历到当前数组元素大于等于栈顶元素(top)的话,就将该元素(arr[index])执行入栈操作。
  • 其次:如果arr[index]小于栈顶元素,则去对比除栈顶元素之外的元素(可以先pop()掉栈顶元素进行缓存,然后最后再push到堆栈中),如果对比堆栈中的元素大于arr[index],则将堆栈中的该元素执行出栈操作(执行pop()操作);否则,结束对比。
  • 最后:将堆栈中存在元素进行总和统计,返回的数量就是可以拆分最大分组数量。

了解到了具体的操作步骤之后,我们再通过一个例子,来看一下具体的操作过程是怎样的。 我们以arr = [4,2,6,1,8,7,9,10]为例。从头开始遍历数组中的每个元素。首先,因为堆栈中是空的,所以,我们将index[0]=4这个元素插入到堆栈中;遍历第二个元素index[1]=2,由于栈顶top只有一个元素,并且它小于top指向的元素,所以,没有元素出栈和入栈;遍历第三个元素index[2]=6,由于它大于top元素,所以,将6指向入栈操作,此时堆栈中存在两个元素,分别为:4和6。具体操作,如下图所示:

遍历第四个元素index[3]=1,由于它小于栈顶top元素,所以继续对比非top的元素,因为index[3] < 4,所以4被踢出堆栈,执行pop()方法。由于此时4这个元素已经是在栈底了,没有可以对比的其他元素了,所以结束对比操作;继续遍历第五个元素index[4]=8,由于大于top栈顶元素,所以执行入栈操作,此时堆栈中保存的元素为:6和8;继续遍历第六个元素index[5]=7,由于它小于栈顶元素top,所以对比非top的元素,由于index[5] > 6,所以元素6不用出栈,依然保存在堆栈中,并结束对比操作。具体操作,如下图所示:

遍历第七个元素index[6]=9,由于它大于top栈顶元素8,所以执行入栈操作;遍历第八个元素index[7]=10,由于它大于top栈顶元素9,所以执行入栈操作;遍历所有数组结束之后,堆栈中保存的元素为:6、8、9、10,总数是4,所以最终可以分组的最大数量为4。

以上就是解题思路和具体例子的分步骤操作演示。代码实现,请参照: 4.1> 堆栈 + top指针

四、代码实现

4.1> 堆栈 + top指针

class Solution {
    public int maxChunksToSorted(int[] arr) {
        Deque<Integer> stack = new ArrayDeque();
        stack.push(arr[0]);
        for (int i = 1; i < arr.length; i++) {
            int top = stack.peek();
            if (top <= arr[i]) {
                stack.push(arr[i]);
            } else {
                while(!stack.isEmpty()) {
                    int stackElements = stack.peek();
                    if (stackElements > arr[i]) {
                        stack.pop();
                    } else {
                        break;
                   }
                }
                stack.push(top);
            }
        }
        return stack.size();
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值