4.2 单调栈

This commit is contained in:
zhangsan 2025-04-02 18:28:11 +08:00
parent 67048088ff
commit 03761c6e10
11 changed files with 486 additions and 1 deletions

View File

@ -0,0 +1,50 @@
package heap;
import java.util.Comparator;
import java.util.PriorityQueue;
/**
* 题目 215. 数组中的第K个最大元素 (floodFill)
* 描述给定整数数组 nums 和整数 k请返回数组中第 k 个最大的元素
* 请注意你需要找的是数组排序后的第 k 个最大的元素而不是第 k 个不同的元素
* 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题
* 示例 1
输入: [3,2,1,5,6,4], k = 2
输出: 5
* 链接https://leetcode.cn/problems/kth-largest-element-in-an-array/
*/
public class FindKthLargest {
//完全填满堆再删除元素 最大堆
public int findKthLargest1(int[] nums, int k) {
PriorityQueue<Integer>maxheap=new PriorityQueue<>(Comparator.reverseOrder());
for (int num : nums) {
maxheap.add(num);
}
for (int i = 0; i < k-1; i++) {
maxheap.poll();
}
return maxheap.peek();
}
//使用最小堆来存储前 k 个最大的元素堆顶元素是 k 个最大元素中的最小值这样堆的大小始终保持为 k当堆的大小大于 k 弹出堆顶元素最终堆顶元素就是第 k 大的元素不需要完全填满堆再删除元素
public int findKthLargest(int[] nums, int k) {
// 使用最小堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
// 遍历数组
for (int num : nums) {
// 如果堆的大小小于 k直接加入元素
if (minHeap.size() < k) {
minHeap.add(num);
} else if (num > minHeap.peek()) {
// 如果堆的大小已达到 k且当前元素大于堆顶元素则替换堆顶
minHeap.poll();
minHeap.add(num);
}
}
// 返回堆顶元素即为第 k 大的元素
return minHeap.peek();
}
}

View File

@ -65,7 +65,7 @@ public class Calculate2 {
Calculate2 calculator = new Calculate2(); Calculate2 calculator = new Calculate2();
// 测试用例 1 // 测试用例 1
String expression1 = "3+5/2"; String expression1 = "3/2";
System.out.println("输入: " + expression1); System.out.println("输入: " + expression1);
System.out.println("输出: " + calculator.calculate(expression1)); // 期望输出 5 System.out.println("输出: " + calculator.calculate(expression1)); // 期望输出 5

View File

@ -0,0 +1,58 @@
package stack;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 题目 150. 逆波兰表达式求值 (evalRPN)
* 描述给你一个字符串数组 tokens 表示一个根据 逆波兰表示法 表示的算术表达式
* 请你计算该表达式返回一个表示表达式值的整数
* 注意
* 有效的算符为 '+''-''*' '/'
* 每个操作数运算对象都可以是一个整数或者另一个表达式
* 两个整数之间的除法总是 向零截断
* 表达式中不含除零运算
* 输入是一个根据逆波兰表示法表示的算术表达式
*
* 示例 1
输入tokens = ["2","1","+","3","*"]
输出9
解释该算式转化为常见的中缀算术表达式为((2 + 1) * 3) = 9
* 链接https://leetcode.cn/problems/evaluate-reverse-polish-notation/
*/
public class EvalRPN {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new ArrayDeque<>();
for (String token : tokens) {
if (token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/")) {
// 如果是运算符出栈两个操作数进行运算
int b = stack.pop();
int a = stack.pop();
int result = 0;
switch (token) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
result = a / b; // 除法自动向零取整
break;
}
stack.push(result);
} else {
// 否则是数字直接入栈
stack.push(Integer.parseInt(token));
}
}
// 最后栈中只剩下一个数字就是计算结果
return stack.pop();
}
}

View File

@ -0,0 +1,109 @@
package stack;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 题目 84. 柱状图中最大的矩形
* 描述给定 n 个非负整数用来表示柱状图中各个柱子的高度每个柱子彼此相邻且宽度为 1
* 求在该柱状图中能够勾勒出来的矩形的最大面积
* 示例 1
输入heights = [2,1,5,6,2,3]
输出10
解释最大的矩形为图中红色区域面积为 10
*
* 链接https://leetcode.cn/problems/largest-rectangle-in-histogram/
*/
//初见不会
public class LargestRectangleArea {
//暴力法 依次遍历柱形的高度对于每一个高度分别向两边扩散求出以当前高度为矩形的最大宽度多少
public int largestRectangleArea1(int[] heights) {
int len = heights.length;
// 特判
if (len == 0) {
return 0;
}
int res = 0;
for (int i = 0; i < len; i++) {
// 找左边最后 1 个大于等于 heights[i] 的下标
int left = i;
int curHeight = heights[i];
while (left > 0 && heights[left - 1] >= curHeight) {
left--;
}
// 找右边最后 1 个大于等于 heights[i] 的索引
int right = i;
while (right < len - 1 && heights[right + 1] >= curHeight) {
right++;
}
int width = right - left + 1;
res = Math.max(res, width * curHeight);
}
return res;
}
//动态规划 暴力解优化
public int largestRectangleArea2(int[] heights) {
int size = heights.length;
int[] minLeftIndex = new int[size];
int[] minRightIndex = new int[size];
// 记录每个柱子左边第一个小于该柱子的下标
minLeftIndex[0] = -1; // 初始化防止死循环
for (int i = 1; i < size; i++) {
int t = i - 1;
// 不断向左寻找直到找到第一个小于当前柱子的柱子
while (t >= 0 && heights[t] >= heights[i]) {
t = minLeftIndex[t]; // 更新为左边第一个小于当前柱子的下标
}
minLeftIndex[i] = t;
}
// 记录每个柱子右边第一个小于该柱子的下标
minRightIndex[size - 1] = size; // 初始化防止死循环
for (int i = size - 2; i >= 0; i--) {
int t = i + 1;
// 不断向右寻找直到找到第一个小于当前柱子的柱子
while (t < size && heights[t] >= heights[i]) {
t = minRightIndex[t]; // 更新为右边第一个小于当前柱子的下标
}
minRightIndex[i] = t;
}
// 计算最大矩形面积
int result = 0;
for (int i = 0; i < size; i++) {
int width = minRightIndex[i] - minLeftIndex[i] - 1;
int area = heights[i] * width;
result = Math.max(result, area);
}
return result;
}
//单调栈
public int largestRectangleArea(int[] heights) {
Deque<Integer>stack=new ArrayDeque<>();
int max=Integer.MIN_VALUE;
stack.push(-1);
for (int i = 0; i < heights.length; i++) {
while(stack.size()>=2 && heights[i]<heights[stack.peek()]){
int cur=stack.pop();
int width=i-stack.peek()-1;
if(max<width*heights[cur])
max=width*heights[cur];
}
stack.push(i);
}
while(stack.size()>=2){
int cur=stack.pop();
int width=heights.length-stack.peek()-1;
if(max<width*heights[cur])
max=width*heights[cur];
}
return max;
}
}

View File

@ -0,0 +1,50 @@
package stack;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
/**
* 题目 496. 下一个更大元素 I (nextGreaterElement)
* 描述nums1 中数字 x 下一个更大元素 是指 x nums2 中对应位置 右侧 第一个 x 大的元素
*
* 给你两个 没有重复元素 的数组 nums1 nums2 下标从 0 开始计数其中nums1 nums2 的子集
*
* 对于每个 0 <= i < nums1.length 找出满足 nums1[i] == nums2[j] 的下标 j 并且在 nums2 确定 nums2[j] 下一个更大元素 如果不存在下一个更大元素那么本次查询的答案是 -1
*
* 返回一个长度为 nums1.length 的数组 ans 作为答案满足 ans[i] 是如上所述的 下一个更大元素
*
* 示例 1
输入nums1 = [4,1,2], nums2 = [1,3,4,2].
输出[-1,3,-1]
解释nums1 中每个值的下一个更大元素如下所述
- 4 用加粗斜体标识nums2 = [1,3,4,2]不存在下一个更大元素所以答案是 -1
- 1 用加粗斜体标识nums2 = [1,3,4,2]下一个更大元素是 3
- 2 用加粗斜体标识nums2 = [1,3,4,2]不存在下一个更大元素所以答案是 -1
* 链接https://leetcode.cn/problems/next-greater-element-i/
*/
public class NextGreaterElement {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Deque<Integer>stack=new ArrayDeque<>();
HashMap<Integer,Integer>map=new HashMap<>();
int[] res=new int[nums1.length];
for (int cur : nums2) {
while (!stack.isEmpty()) {
int curtop = stack.peek();
if (cur > curtop) {
stack.pop();
map.put(curtop, cur);
}else break;
}
stack.push(cur);
}
while (!stack.isEmpty()) {
map.put(stack.pop(),-1);
}
for (int i = 0; i < nums1.length; i++) {
res[i]=map.get(nums1[i]);
}
return res;
}
}

View File

@ -0,0 +1,48 @@
package stack;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 题目 503. 下一个更大元素 II (nextGreaterElements)
* 描述给定一个循环数组 nums nums[nums.length - 1] 的下一个元素是 nums[0] 返回 nums 中每个元素的 下一个更大元素
* 数字 x 下一个更大的元素 是按数组遍历顺序这个数字之后的第一个比它更大的数这意味着你应该循环地搜索它的下一个更大的数如果不存在则输出 -1
* <p>
* 示例 1
* 输入: nums = [1,2,1]
* 输出: [2,-1,2]
* 解释: 第一个 1 的下一个更大的数是 2
* 数字 2 找不到下一个更大的数
* 第二个 1 的下一个最大的数需要循环搜索结果也是 2
* <p>
* 链接https://leetcode.cn/problems/next-greater-element-ii/description/
*/
//不会学习
public class NextGreaterElements {
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;
int[] result = new int[n];
Deque<Integer> stack = new ArrayDeque<>();
// 初始化结果数组为 -1表示如果没有更大元素则为 -1
for (int i = 0; i < n; i++) {
result[i] = -1;
}
// 遍历数组两倍的长度以模拟循环
for (int i = 0; i < 2 * n; i++) {
int currentIndex = i % n; // 当前元素的下标模拟循环
// 当栈不为空且当前元素大于栈顶对应的元素时更新结果
while (!stack.isEmpty() && nums[currentIndex] > nums[stack.peek()]) {
int prevIndex = stack.pop();
result[prevIndex] = nums[currentIndex];
}
// 只在前 n 个元素时将索引加入栈中
if (i < n) {
stack.push(currentIndex);
}
}
return result;
}
}

View File

@ -0,0 +1,101 @@
package stack;
import java.util.Deque;
import java.util.LinkedList;
/**
* 题目 42. 接雨水 (trap)
* 描述给定 n 个非负整数表示每个宽度为 1 的柱子的高度图计算按此排列的柱子下雨之后能接多少雨水
* 示例 1
* 输入height = [0,1,0,2,1,0,1,3,2,1,2,1]
* 输出6
* 解释上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图在这种情况下可以接 6 个单位的雨水蓝色部分表示雨水
*
* 链接https://leetcode.cn/problems/trapping-rain-water/
*/
//困难题 不会
public class Trap {
//暴力解法
public int trap1(int[] height) {
int sum = 0;
for (int i = 0; i < height.length; i++) {
// 第一个柱子和最后一个柱子不接雨水
if (i == 0 || i == height.length - 1) {
continue;
}
int rHeight = height[i]; // 记录右边柱子的最高高度
int lHeight = height[i]; // 记录左边柱子的最高高度
for (int r = i + 1; r < height.length; r++) {
if (height[r] > rHeight) {
rHeight = height[r];
}
}
for (int l = i - 1; l >= 0; l--) {
if (height[l] > lHeight) {
lHeight = height[l];
}
}
int h = Math.min(lHeight, rHeight) - height[i];
if (h > 0) {
sum += h;
}
}
return sum;
}
//动态规划
public int trap2(int[] height) {
if (height.length <= 2) return 0;
int n = height.length;
int[] maxLeft = new int[n];
int[] maxRight = new int[n];
// 记录每个柱子左边柱子最大高度
maxLeft[0] = height[0];
for (int i = 1; i < n; i++) {
maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
}
// 记录每个柱子右边柱子最大高度
maxRight[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; i--) {
maxRight[i] = Math.max(height[i], maxRight[i + 1]);
}
// 求和
int sum = 0;
for (int i = 1; i < n-1; i++) {
int count = Math.min(maxLeft[i-1], maxRight[i+1]) - height[i];
if (count > 0) {
sum += count;
}
}
return sum;
}
//单调栈
public int trap(int[] height) {
int sum = 0;
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < height.length; i++) {
// 当栈不为空且当前柱子的高度大于栈顶柱子的高度时
while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
int top = stack.pop();
// 如果栈为空则没有左边的柱子来接雨水
if (stack.isEmpty()) break;
// 计算当前柱子与栈顶柱子之间的距离
int distance = i - stack.peek() - 1;
// 计算可以接的雨水高度两边较低的高度减去中间柱子的高度
int boundedHeight = Math.min(height[i], height[stack.peek()]) - height[top];
sum += distance * boundedHeight;
}
// 将当前索引入栈
stack.push(i);
}
return sum;
}
}

View File

@ -0,0 +1,16 @@
package stack;
import org.junit.Test;
import static org.junit.Assert.*;
public class EvalRPNTest {
@Test
public void evalRPN() {
String[] tokens = {"4","-2","/","2","-3","-","-"};
EvalRPN solution = new EvalRPN();
int res=solution.evalRPN(tokens);
System.out.println(res);
}
}

View File

@ -0,0 +1,16 @@
package stack;
import org.junit.Test;
import static org.junit.Assert.*;
public class LargestRectangleAreaTest {
@Test
public void largestRectangleArea() {
LargestRectangleArea solution = new LargestRectangleArea();
int[] heights = {2,1,5,6,2,3};
int res=solution.largestRectangleArea(heights);
System.out.println(res);
}
}

View File

@ -0,0 +1,19 @@
package stack;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class NextGreaterElementTest {
@Test
public void nextGreaterElement() {
NextGreaterElement solution = new NextGreaterElement();
int[] nums1 = {4,1,2};
int[] nums2 = {1,3,4,2};
int[] res=solution.nextGreaterElement(nums1,nums2);
System.out.println(Arrays.toString(res));
}
}

View File

@ -0,0 +1,18 @@
package stack;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class NextGreaterElementsTest {
@Test
public void nextGreaterElements() {
NextGreaterElements solution = new NextGreaterElements();
int[] nums = {1,2,3,2,1};
int []res=solution.nextGreaterElements(nums);
System.out.println(Arrays.toString(res));
}
}