单调栈
- 单调栈是一种特殊的栈数据结构,通常用于解决一些需要维护单调性的问题。单调栈的特点是栈内元素保持单调递增或单调递减的顺序。
- 适用题型包括但不限于:
- 下一个更大元素:给定一个数组,找到每个元素右边第一个比它大的元素。
- 下一个更小元素:给定一个数组,找到每个元素右边第一个比它小的元素。
- 柱状图中最大矩形:给定一个柱状图,找到最大的矩形面积。
- 接雨水:给定一个数组表示高度,计算能接到的雨水量。
- 单调栈应用:在求解递增子序列、求解最大子数组和等问题时,也可以使用单调栈来简化问题。
- 单调栈的应用可以通过维护单调性来减少问题的复杂度,通常能够提供较高的效率。在解决某些需要维护单调性的问题时,可以考虑使用单调栈来优化算法。
转盘寿司 - 单调栈 - 较高
-
题目描述
寿司店周年庆,正在举办优惠活动回馈新老客户。
寿司转盘上总共有 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:单调栈
使用一个单调递减栈来解决该问题,栈中存储的是小朋友的索引。
- 从左往右遍历小朋友的身高列表,对于每一个小朋友,如果栈为空,则将当前小朋友的索引入栈。
- 如果栈不为空,比较当前小朋友的身高和栈顶小朋友的身高,如果当前小朋友的身高大于栈顶小朋友的身高,则栈顶小朋友的好朋友就是当前小朋友,将栈顶小朋友出栈,更新结果列表对应位置的值为当前小朋友的索引。
- 遍历完所有小朋友后,栈中剩余的小朋友没有好朋友,将栈中剩余的小朋友索引对应的位置更新为0。
- 返回结果列表即为每个小朋友的好朋友位置。
这道题的解法利用了单调栈的特性,通过维护一个递减的栈,可以快速找到每个小朋友的好朋友位置。
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
其中
x
、y
是无符号整数地球人公式按照 C 语言规则进行计算
火星人公式中
#
符优先级高于$
相同的运算符按从左到右的顺序运算
输入描述
火星人字符串表达式,结尾不带回车换行
输入的字符串说明:
字符串为仅有无符号整数和操作符组成的计算表达式
- 用例保证字符串中操作数与操作符之间没有任何分隔符
- 用例保证操作数取值范围为
32
位无符号整数 - 保证输入以及计算结果不会出现整型溢出
- 保证输入的字符串为合法的求值报文 例如:
123#45#7678
- 保证不会出现非法的求值报文
例如:
#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); } }