7.7 栈 堆

This commit is contained in:
zhangsan 2025-07-13 22:02:52 +08:00
parent 730f26f950
commit 8c8f0d0b20
9 changed files with 220 additions and 98 deletions

View File

@ -17,6 +17,7 @@ package binary_search;
//二刷不会
public class FindMedianSortedArrays {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length1 = nums1.length, length2 = nums2.length;
int totalLength = length1 + length2;

View File

@ -15,8 +15,10 @@ import java.util.PriorityQueue;
* 链接https://leetcode.cn/problems/kth-largest-element-in-an-array/
*/
//二刷不会 背一下堆的创建 快速选择
public class FindKthLargest {
//完全填满堆再删除元素 最大堆
//完全填满堆再删除元素 最大堆 O(nlogn)
public int findKthLargest1(int[] nums, int k) {
PriorityQueue<Integer>maxheap=new PriorityQueue<>(Comparator.reverseOrder());
for (int num : nums) {
@ -28,6 +30,7 @@ public class FindKthLargest {
return maxheap.peek();
}
//使用最小堆来存储前 k 个最大的元素堆顶元素是 k 个最大元素中的最小值这样堆的大小始终保持为 k当堆的大小大于 k 弹出堆顶元素最终堆顶元素就是第 k 大的元素不需要完全填满堆再删除元素
// O(nlogk)
public int findKthLargest2(int[] nums, int k) {
// 使用最小堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
@ -47,7 +50,7 @@ public class FindKthLargest {
// 返回堆顶元素即为第 k 大的元素
return minHeap.peek();
}
//快速选择
//快速选择 O(n)
public int quickselect(int[] nums, int l, int r, int k) {
// 当区间内只有一个元素时直接返回该元素
if (l >= r) {

View File

@ -19,17 +19,16 @@ import java.util.PriorityQueue;
* 链接https://leetcode.cn/problems/find-k-pairs-with-smallest-sums/
*/
//有思路 但是超出时间限制需要优化
//二刷不会优化
public class KSmallestPairs {
/**
* 可以把所有可能的组合看作一个虚拟的二维矩阵
*
* 行代表 nums1[i]
* 列代表 nums2[j]
* 每个点 (i,j) 对应的值是 nums1[i] + nums2[j]
* 这个矩阵的特点是从左到右从上到下递增因为两个数组都升序
*
* 我们不需要全部生成矩阵而是只维护当前最小的候选对类似 Dijkstra 算法或 BFS
*
* 初始时把前 k 行的第一列 (nums1[i], nums2[0])放入堆中因为后面的列比前面的大我们先选列头
* 每次弹出堆顶最小的和把它加入结果列表
* 如果该对还有下一列 (i, j+1) 存在就把 (i, j+1) 加入堆作为下一个候选
@ -45,26 +44,25 @@ public class KSmallestPairs {
// 边界处理任一数组为空 k==0直接返回空结果
if (nums1.length == 0 || nums2.length == 0 || k == 0) return res;
// 小顶堆存储的是每对数的索引 [i, j]按对应的和升序排列
// 小顶堆存储的是每对数的索引 [i, j]按对应的和 升序排列
PriorityQueue<int[]> minHeap = new PriorityQueue<>(
(a, b) -> nums1[a[0]] + nums2[a[1]] - nums1[b[0]] - nums2[b[1]]
);
// 初始化 nums1 的前 k 个元素与 nums2[0] 组成的数对放入堆中
// 因为只推进列所以只需要每一行的第一个元素
// 初始把每一行的第 0 最多 k 加入堆
for (int i = 0; i < Math.min(nums1.length, k); i++) {
minHeap.offer(new int[]{i, 0}); // i 表示 nums1 的索引j=0 是固定的起点
}
// 出堆顶最小的数对直到取满 k 个或者堆空为止
// 最小的 k
while (k-- > 0 && !minHeap.isEmpty()) {
int[] pos = minHeap.poll(); // 当前最小的数对在 nums1[pos[0]], nums2[pos[1]]
int i = pos[0], j = pos[1];
int[] idx = minHeap.poll();
int i = idx[0], j = idx[1];
// 把数对值加入结果集
res.add(List.of(nums1[i], nums2[j]));
// 如果当前数对还有下一列就推进下一列仍然在第 i
// 推入同一行的下一列 若这一行所有可能的列都已被消费完了就不再往堆里推新的 (i, j+1)
if (j + 1 < nums2.length) {
minHeap.offer(new int[]{i, j + 1});
}

View File

@ -14,10 +14,46 @@ import java.util.PriorityQueue;
* 链接https://leetcode.cn/problems/top-k-frequent-elements/
*/
// 二刷不会
public class TopKFrequent {
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer,Integer> map=new HashMap<>();
int cnt=0;
PriorityQueue<int[]> minHeap=new PriorityQueue<>( //小根堆
(a,b) -> a[1]-b[1]
);
int[] temp;
int[] res=new int[k];
for (int i = 0; i < nums.length; i++) {
int cur=nums[i];
map.put(cur, map.getOrDefault(cur,0)+1);
}
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(cnt<k){
minHeap.add(new int[]{entry.getKey(),entry.getValue()});
cnt++;
}else {
temp=minHeap.peek();
if(entry.getValue()>temp[1]) {
minHeap.poll();
minHeap.add(new int[]{entry.getKey(),entry.getValue()});
}
}
}
cnt=0;
for (int[] ints : minHeap) {
res[cnt++]=ints[0];
}
return res;
}
public int[] topKFrequent2(int[] nums, int k) {
HashMap<Integer,Integer>map=new HashMap<>();
PriorityQueue<int[]> minHeap = new PriorityQueue<>(
PriorityQueue<int[]> maxHeap = new PriorityQueue<>( //大根堆
(a, b) -> b[1] - a[1]);
int[] res=new int[k];
int i=0;
@ -28,11 +64,11 @@ public class TopKFrequent {
map.put(num,map.get(num)+1);
}
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
minHeap.add(new int[]{entry.getKey(),entry.getValue()});
maxHeap.add(new int[]{entry.getKey(),entry.getValue()});
}
while (!minHeap.isEmpty()) {
while (!maxHeap.isEmpty()) {
if(i<k)
res[i++] = minHeap.poll()[0];
res[i++] = maxHeap.poll()[0];
else break;
}
return res;

View File

@ -14,8 +14,28 @@ import java.util.Deque;
* 链接https://leetcode.cn/problems/daily-temperatures/
*/
//第一次做没想到怎么利用栈
//二刷会做
public class DailyTemperatures {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] ans = new int[n]; // 默认全 0不用后面再填
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
// 只要栈不空且当前温度更高就把栈顶结算出来
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int prev = stack.pop();
ans[prev] = i - prev;
}
stack.push(i);
}
// 剩下的索引对应的 ans 已经是 0 Java 数组默认值
return ans;
}
public int[] dailyTemperatures1(int[] temperatures) {
// 结果数组初始化为全零
int[] result = new int[temperatures.length];

View File

@ -3,6 +3,7 @@ package stack;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;
/**
* 题目 394. 字符串解码 (decodeString)
@ -18,8 +19,10 @@ import java.util.LinkedList;
* 链接https://leetcode.cn/problems/search-in-rotated-sorted-array/
*/
//不会 有难度
//二刷不会
public class DecodeString {
//s = "3[a]2[bc]" "3[a2[c]]"
// s = "3[a]2[bc]" "3[a2[c]]"
public String decodeString(String s) {
// 定义两个栈
Deque<Integer> countStack = new LinkedList<>();

View File

@ -16,7 +16,10 @@ import java.util.HashMap;
*
* 链接https://leetcode.cn/problems/valid-parentheses/
*/
//二刷会做
public class IsValid {
public boolean isValid(String s) {
// 如果长度为奇数直接返回 false
int sz = s.length();

View File

@ -16,94 +16,151 @@ import java.util.Deque;
* 链接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++) {
// public int largestRectangleArea(int[] heights) {
// int len=heights.length;
// int[] leftIndex=new int[len],rightIndex=new int[len];
// leftIndex[0]=-1;
// rightIndex[len-1]=len;
// for (int i = 1; i < len; i++) {
// int cur=heights[i];
// int left=i-1;
// //向左找到第一个小于cur的index
// while (left>=0 && cur<=heights[left]){
// left=leftIndex[left];
// }
// leftIndex[i]=left;
// }
// for (int i = len-2; i >=0 ; i--) {
// int cur=heights[i];
// int right=i+1;
// while (right<len && cur<=heights[right])
// right=rightIndex[right];
// rightIndex[i]=right;
// }
// int max=Integer.MIN_VALUE;
// for (int i = 0; i < len; i++) {
// max=Math.max(max,(rightIndex[i]-leftIndex[i]-1)*heights[i]);
// }
// return max;
// }
// 找左边最后 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;
}
//单调栈
/**
* 暴力法
* 对于每一个柱子 i当做最低高度向左扫描找到第一个高度比它小的位置 left
* 向右扫描找到第一个比它小的位置 right那么以它为高的最大矩形宽度就是 rightleft1面积就是 heights[i] * (right - left - 1)
*/
// 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;
// }
//
//
// /**
// * 动态规划 暴力解优化
// * 预先为每根柱子 i 计算出
// * minLeftIndex[i]右边第一个小于它的柱子的下标
// * minRightIndex[i]左边第一个小于它的柱子的下标
// * 计算完后对于每个 i它能扩展的最大宽度就是 (minRightIndex[i] - minLeftIndex[i] - 1)面积同理用高度乘宽度即可
//
// */
// 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;
// }
//
/**
* 单调栈解法思路
* - 使用一个栈保存柱子的索引栈内对应的高度单调递增
* - 遍历每个柱子 i
* - 当当前柱子高度小于栈顶高度时说明以栈顶柱子为高的矩形在 i 处结束
* pop 出栈顶 prev计算宽度为 i - stack.peek() - 1面积 = heights[prev] * width
* - 重复上述步骤直到栈空或栈顶高度不超过当前高度
* - 将当前索引 i 压入栈中
* - 遍历结束后栈中仍有索引依次弹出并计算
* 宽度 = n - stack.peek() - 1
* - 时间复杂度 O(n)空间复杂度 O(n)
*/
public int largestRectangleArea(int[] heights) {
Deque<Integer>stack=new ArrayDeque<>();
int max=Integer.MIN_VALUE;
int n = heights.length;
Deque<Integer> stack = new ArrayDeque<>();
// 先放一个哨兵方便计算宽度
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];
int maxArea = 0;
for (int i = 0; i < n; i++) {
// 当前高度小于栈顶高度时栈顶矩形结束
while (stack.peek() != -1 && heights[i] < heights[stack.peek()]) {
int prev = stack.pop(); // 栈顶索引
int width = i - stack.peek() - 1; // 计算以 heights[prev] 为高的宽度
maxArea = Math.max(maxArea, heights[prev] * width);
}
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];
// 处理剩余索引
while (stack.peek() != -1) {
int prev = stack.pop();
int width = n - stack.peek() - 1;
maxArea = Math.max(maxArea, heights[prev] * width);
}
return max;
return maxArea;
}
}

View File

@ -1,4 +1,4 @@
package stack;
package 实现类功能;
import java.util.Stack;
@ -15,6 +15,7 @@ import java.util.Stack;
*
* 链接https://leetcode.cn/problems/min-stack/description/
*/
//二刷不会
public class MinStack {
private Stack<Integer> stack; // 主栈