您好,登录后才能下订单哦!
单调栈(Monotonic Stack)是一种特殊的栈结构,它在处理一些特定问题时非常高效。单调栈的核心思想是维护一个栈,使得栈中的元素始终保持单调递增或单调递减的顺序。这种数据结构在解决一些与区间、边界、极值相关的问题时非常有用,比如寻找下一个更大元素、计算最大矩形面积等。
本文将详细介绍单调栈的概念、实现方式以及在Java中的具体应用,并通过几个经典问题来展示单调栈的强大之处。
单调栈是一种栈结构,栈中的元素始终保持单调递增或单调递减的顺序。根据栈中元素的单调性,单调栈可以分为两种类型:
单调栈的核心思想是通过维护栈的单调性,快速找到某个元素的前驱或后继关系。例如,在寻找数组中每个元素的下一个更大元素时,单调栈可以高效地完成这一任务。
在Java中,单调栈的实现通常基于Stack
类或Deque
接口。以下是单调栈的基本实现框架:
import java.util.Stack;
public class MonotonicStack {
public static void main(String[] args) {
int[] nums = {2, 1, 2, 4, 3};
int[] result = nextGreaterElement(nums);
for (int num : result) {
System.out.print(num + " ");
}
}
// 单调栈示例:寻找下一个更大元素
public static int[] nextGreaterElement(int[] nums) {
int n = nums.length;
int[] result = new int[n];
Stack<Integer> stack = new Stack<>();
// 从后往前遍历数组
for (int i = n - 1; i >= 0; i--) {
// 维护单调递减栈
while (!stack.isEmpty() && stack.peek() <= nums[i]) {
stack.pop();
}
// 如果栈不为空,当前元素的下一个更大元素是栈顶元素
result[i] = stack.isEmpty() ? -1 : stack.peek();
// 将当前元素压入栈中
stack.push(nums[i]);
}
return result;
}
}
在上面的代码中,我们使用单调递减栈来寻找数组中每个元素的下一个更大元素。具体步骤如下:
单调栈在解决一些与区间、边界、极值相关的问题时非常高效。以下是几个经典的应用场景:
问题描述:给定一个数组,找到每个元素的下一个更大元素。如果不存在,则输出-1。
示例:
输入: [2, 1, 2, 4, 3]
输出: [4, 2, 4, -1, -1]
Java实现:
public int[] nextGreaterElement(int[] nums) {
int n = nums.length;
int[] result = new int[n];
Stack<Integer> stack = new Stack<>();
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && stack.peek() <= nums[i]) {
stack.pop();
}
result[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i]);
}
return result;
}
问题描述:给定一个由非负整数组成的柱状图,计算其中可以勾勒出的最大矩形的面积。
示例:
输入: [2, 1, 5, 6, 2, 3]
输出: 10
Java实现:
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n]; // 记录每个柱子左边第一个小于它的柱子的下标
int[] right = new int[n]; // 记录每个柱子右边第一个小于它的柱子的下标
Stack<Integer> stack = new Stack<>();
// 计算每个柱子左边第一个小于它的柱子的下标
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
stack.pop();
}
left[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
// 计算每个柱子右边第一个小于它的柱子的下标
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
stack.pop();
}
right[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}
// 计算最大矩形面积
int maxArea = 0;
for (int i = 0; i < n; i++) {
maxArea = Math.max(maxArea, heights[i] * (right[i] - left[i] - 1));
}
return maxArea;
}
问题描述:给定一个温度数组,表示每天的温度,返回一个数组,表示需要等待多少天才能遇到更高的温度。如果不存在,则输出0。
示例:
输入: [73, 74, 75, 71, 69, 72, 76, 73]
输出: [1, 1, 4, 2, 1, 1, 0, 0]
Java实现:
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] result = new int[n];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) {
int prevIndex = stack.pop();
result[prevIndex] = i - prevIndex;
}
stack.push(i);
}
return result;
}
单调栈的时间复杂度通常是O(n),其中n是数组的长度。这是因为每个元素最多只会被压入和弹出栈一次。
空间复杂度为O(n),因为需要使用一个栈来存储元素。
单调栈是一种非常高效的数据结构,特别适合处理与区间、边界、极值相关的问题。通过维护栈的单调性,单调栈可以快速找到某个元素的前驱或后继关系,从而解决一些复杂的问题。
在Java中,单调栈的实现通常基于Stack
类或Deque
接口。通过几个经典问题的示例,我们可以看到单调栈在解决实际问题时的强大之处。掌握单调栈的使用方法,可以大大提高我们解决算法问题的效率。
希望本文对你理解单调栈的概念和应用有所帮助!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。