算法思想之单调栈以及示例(转盘寿司、找朋友、火星文计算)

单调栈

  • 单调栈是一种特殊的栈数据结构,通常用于解决一些需要维护单调性的问题。单调栈的特点是栈内元素保持单调递增或单调递减的顺序。
  • 适用题型包括但不限于:
    • 下一个更大元素:给定一个数组,找到每个元素右边第一个比它大的元素。
    • 下一个更小元素:给定一个数组,找到每个元素右边第一个比它小的元素。
    • 柱状图中最大矩形:给定一个柱状图,找到最大的矩形面积。
    • 接雨水:给定一个数组表示高度,计算能接到的雨水量。
    • 单调栈应用:在求解递增子序列、求解最大子数组和等问题时,也可以使用单调栈来简化问题。
  • 单调栈的应用可以通过维护单调性来减少问题的复杂度,通常能够提供较高的效率。在解决某些需要维护单调性的问题时,可以考虑使用单调栈来优化算法。

转盘寿司 - 单调栈 - 较高

  • 题目描述

    寿司店周年庆,正在举办优惠活动回馈新老客户。

    寿司转盘上总共有 n 盘寿司,prices[i] 是第 i 盘寿司的价格,

    如果客户选择了第 i 盘寿司,寿司店免费赠送客户距离第 i 盘寿司最近的下一盘寿司 j,前提是 prices[j] < prices[i],如果没有满足条件的 j,则不赠送寿司。

    每个价格的寿司都可无限供应。

    输入要求

    输入的每一个数字代表每盘寿司的价格,每盘寿司的价格之间使用空格分隔,例如:

    3 15 6 14
    

    表示:

    • 第 0 盘寿司价格 prices[0] 为 3
    • 第 1 盘寿司价格 prices[1] 为 15
    • 第 2 盘寿司价格 prices[2] 为 6
    • 第 3 盘寿司价格 prices[3] 为 14

    寿司的盘数 n 范围为:1 ≤ n ≤ 500

    每盘寿司的价格 price 范围为:1 ≤ price ≤ 1000

    输出要求

    输出享受优惠后的一组数据,每个值表示客户选择第 i 盘寿司时实际得到的寿司的总价格。使用空格进行分隔,例如:

    3 21 9 17
    
  • 用例1

    输入:
    3 15 6 14
    输出:
    3 21 9 17
    

    用例2

    输入:
    5 12 7 13
    输出:
    5 19 12 18
    
  • 题解

    import java.util.*;
    public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            while (sc.hasNext()) {
                int[] arr = Arrays.stream(sc.nextLine().split(" "))
                    .mapToInt(Integer::parseInt).toArray();
                int[] ret = Arrays.copyOf(arr, arr.length);
                Stack<Integer> stack = new Stack<>();
                for (int i = 0; i < 2 * arr.length; i++) {
                    int idx = i % arr.length;
                    while (!stack.isEmpty() && arr[stack.peek()] > arr[idx]) {
                        Integer pop = stack.pop();
                        ret[pop] = arr[pop] + arr[idx];
                    }
                    stack.push(idx);
                }
                for (int i = 0; i < ret.length; i++) {
                    if (i == ret.length -1) System.out.println(ret[i]);
                    else System.out.print(ret[i] + " ");
                }
            }
        }
    }
    

找朋友 - 数组、单调栈 - 较高

  • 题目描述

    在学校中,N 个小朋友站成一队, 第 i 个小朋友的身高为 height[i],第 i 个小朋友可以看到的右边的第一个比自己身高更高的小朋友 j,那么 j 是 i 的好朋友(j > i)。

    请重新生成一个列表,对应位置的输出是每个小朋友的好朋友位置,如果没有看到好朋友,请在该位置用 0 代替。小朋友人数范围是 [0, 40000]。

    输入

    第一行输入 N,表示有 N 个小朋友

    第二行输入 N 个小朋友的身高 height[i],都是整数

    输出

    输出 N 个小朋友的好朋友的位置

  • 样例

    输入:8
    	 123 124 125 121 119 122 126 123
    输出:1 2 6 5 5 6 0 0
    
  • 题解1:暴力破解

    import java.util.*;
    public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            while (sc.hasNext()) {
                int m = sc.nextInt();
                int[] arr = new int[m];
                for (int i = 0; i < m; i++) {
                    arr[i] = sc.nextInt();
                }
                int[] ret = new int[m];
                for (int i = 0; i < m; i++) {
                    ret[i] = 0;
                    for (int j = i + 1; j < m; j++) {
                        if (arr[j] > arr[i]) {
                            ret[i] = j;
                            break;
                        }
                    }
                }
                for (int i = 0; i < ret.length; i++) {
                    if (i == ret.length -1) System.out.println(ret[i]);
                    else System.out.print(ret[i] + " ");
                }
            }
        }
    }
    

    题解2:单调栈

    使用一个单调递减栈来解决该问题,栈中存储的是小朋友的索引。

    1. 从左往右遍历小朋友的身高列表,对于每一个小朋友,如果栈为空,则将当前小朋友的索引入栈。
    2. 如果栈不为空,比较当前小朋友的身高和栈顶小朋友的身高,如果当前小朋友的身高大于栈顶小朋友的身高,则栈顶小朋友的好朋友就是当前小朋友,将栈顶小朋友出栈,更新结果列表对应位置的值为当前小朋友的索引。
    3. 遍历完所有小朋友后,栈中剩余的小朋友没有好朋友,将栈中剩余的小朋友索引对应的位置更新为0。
    4. 返回结果列表即为每个小朋友的好朋友位置。

    这道题的解法利用了单调栈的特性,通过维护一个递减的栈,可以快速找到每个小朋友的好朋友位置。

    import java.util.*;
    public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            while (sc.hasNext()) {
                int m = sc.nextInt();
                int[] arr = new int[m];
                for (int i = 0; i < m; i++) {
                    arr[i] = sc.nextInt();
                }
                int[] ret = new int[m];	// 数组的元素默认为0
                Stack<Integer> stack = new Stack<>();
                for (int i = 0; i < m; i++) {
                    while (!stack.isEmpty() && arr[i] > arr[stack.peek()]) {
                        ret[stack.pop()] = i;	// 将找到好朋友的索引弹出并将其朋友更新为当前小朋友
                    }
                    stack.push(i);
                }
                for (int i = 0; i < ret.length; i++) {
                    if (i == ret.length -1) System.out.println(ret[i]);
                    else System.out.print(ret[i] + " ");
                }
            }
        }
    }
    

火星文计算 - 栈 - 一般

  • 题目描述

    已知火星人使用的运算符号为 #$

    他们与地球人的等价公式如下:

    • x#y = 4*x+3*y+2
    • x$y = 2*x+y+3

    其中 xy 是无符号整数

    地球人公式按照 C 语言规则进行计算

    火星人公式中 # 符优先级高于 $

    相同的运算符按从左到右的顺序运算

    输入描述

    火星人字符串表达式,结尾不带回车换行

    输入的字符串说明:

    字符串为仅有无符号整数和操作符组成的计算表达式

    1. 用例保证字符串中操作数与操作符之间没有任何分隔符
    2. 用例保证操作数取值范围为 32 位无符号整数
    3. 保证输入以及计算结果不会出现整型溢出
    4. 保证输入的字符串为合法的求值报文 例如: 123#45#7678
    5. 保证不会出现非法的求值报文

    例如:

    • #4$5 这种缺少操作数;
    • 4$5# 这种缺少操作数;
    • 4#$5 这种缺少操作数;
    • 4 $5 有空格;
    • 3+4-5*6/7 有其他操作符;
    • 12345678987654321$54321 32 位整数溢出

    输出描述

    根据火星人字符串输出计算结果,结尾不带回车换行

  • 用例1

    输入:7#6$5#12
    输出:157
    说明:7#6$5#12=(4*7+3*6+2)$5#12
            	=48$5#12
            	=48$(4*5+3*12+2)
            	=48$58
            	=2*48+58+3
            	=157
    
  • 题解
    先计算 #,再计算 $

    import java.util.*;
    public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            String s = sc.nextLine();
            char[] arr = (s + " ").toCharArray(); // 在字符串末尾加一个空格,用于最后一个数字
            int num = 0;
            Stack<Integer> stack = new Stack<>();
            boolean flag = false;
            for (char c : arr) {
                if (Character.isDigit(c))
                    num = num * 10 + (c - '0');
                else {
                    stack.push(num);
                    num = 0;
                    if (flag) {
                        Integer y = stack.pop();
                        stack.push(4 * stack.pop() + 3 * y + 2);
                    }
                    flag = c == '#';
                }
            }
            Integer ret = stack.get(0);
            for (int i = 1; i < stack.size(); i++) {
                ret = 2 * ret + stack.get(i) + 3;
            }
            System.out.println(ret);
        }
    }
    
评论 132
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨鸦_Cormorant

大家喜欢的话可以点个关注投币哟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值