使数组按非递减顺序排序[问题转换 + dp + 单调栈]

前言

递减、递增和单调栈紧密相关,单调栈是单调问题的基础。该题可练习预先的问题转换 + 单调栈的使用,除此之外,还可以感受动态规划的本质,记录信息并提供信息。

一、使数组按非递减顺序排序

在这里插入图片描述

二、单调栈+问题转换

1、思路版

/*
    单调栈解:保持nums剩余元素递增,维持一个栈从右到左添加nums元素,呈递减。
    问题转换:消除多少趟使得nums递增,消除的本质:元素x会被左边大于他的元素y消除,毕竟y的右边不允许比它小的。
    到底元素x会被y第几次消除?nums中还存在很多像x 与 y关系的元素对。所以问题就变成了这些元素对中谁的y消除x的时间最长,成为了总步骤。
    所以维持一个单调栈保持从右到左都是非递增的,如果不递减,就消除栈中所有x元素比当前y元素小的值,记录最大消除时间。
     */

    public int totalSteps(int[] nums) {
        // 用于存储从右到左的递减元素的下表,这个下标为了配合--消除时间数组,记录每个位置曾经消除了多少次。
        Stack<Integer> stack = new Stack<>();
        // ArrayDeque真的很好用。
        // ArrayDeque<Integer> deque = new ArrayDeque<>();
        int n = nums.length;
        // 消除时间的数组,记录每个位置曾经消除多少右边的小元素x。
        int[] dp = new int[n];
        // 记录最多消除右边小元素x的数量。
        int max = 0;
        for (int i = n - 1; i >= 0; i--) {
            // 左边的当前元素y需要消除栈中比它小的(右边的)元素x。
            while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
                // dp[i]表示它曾经消除了多少右边小元素x;+1表示要把当前栈顶这个比它小的消除掉;
                // max()表示nums[i]既然消除了nums[pop],那么nums[i]也能消除nums[pop]曾经消除的所有小元素。
                // 把两个消除区间合并一起,取消除最多元素的值。
                dp[i] = Math.max(dp[i] + 1, dp[stack.pop()]);
                // 记录当前为了递减而消除是否为最大消除数。
                if (max < dp[i]) max = dp[i];
            }
            // 递减,直接加入单调递减栈中。
            stack.push(i);
        }
        return max;
    }

2、简洁版

// 简化版。
    public int totalSteps2(int[] nums) {
        Stack<Integer> stack = new Stack<>();
        int dp[] = new int[nums.length], max = 0;
        for (int i = nums.length - 1; i >= 0; stack.push(i--)) {
            while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
                max = Math.max(max, dp[i] = Math.max(dp[i] + 1, dp[stack.pop()]));
            }
        }
        return max;
    }

总结

1)脑筋急转弯、找规律并问题转换、信息预处理等是很多算法的预处理步骤。
2)单调栈练习。
3)dp本质记录有用的信息。

参考文献

[1] LeetCode 使数组按非递减顺序排序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值