目录
[9. 用两个栈实现队列](#9. 用两个栈实现队列)
[30. 包含 min 函数的栈](#30. 包含 min 函数的栈)
[31. 栈的压入、弹出序列](#31. 栈的压入、弹出序列)
[40. 最小的 K 个数](#40. 最小的 K 个数)
[41.1 数据流中的中位数](#41.1 数据流中的中位数)
[41.2 字符流中第一个不重复的字符](#41.2 字符流中第一个不重复的字符)
[59. 滑动窗口的最大值](#59. 滑动窗口的最大值)
9. 用两个栈实现队列
java
package stack_queue_heap;
import java.util.Stack;
// ====================== 核心思路 ======================
// 题目:用两个栈实现队列
// 队列:先进先出 FIFO
// 栈:先进后出 FILO
// 解法:
// 1. stack1 只负责入队(push)
// 2. stack2 只负责出队(pop)
// 3. 当 stack2 为空时,一次性把 stack1 所有元素倒入 stack2
// 这样 stack2 弹出顺序就变成了队列顺序
// 时间复杂度:均摊 O(1)
// 空间复杂度:O(n)
// ======================================================
public class QueueByTwoStack {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
Integer node = stack1.pop();
stack2.push(node);
}
}
return stack2.pop();
}
public static void main(String[] args) {
QueueByTwoStack test = new QueueByTwoStack();
test.push(2);
System.out.println(test.pop());
test.push(1);
System.out.println(test.pop());
}
}
30. 包含 min 函数的栈
java
package stack_queue_heap;
import java.util.Stack;
// ====================== 核心思路 ======================
// 题目:实现包含 min 函数的栈,要求 push、pop、top、min 都是 O(1)
// 解法:用两个栈
// 1. stack:正常存储所有元素
// 2. smallStack:同步存储【当前栈的最小值】
// 每压入一个数,最小栈就压入当前最小;弹出时两个栈一起弹
// 时间复杂度:O(1),所有操作都是常数时间
// 空间复杂度:O(n),需要一个辅助栈
// ======================================================
public class MinStack {
Stack<Integer> stack = new Stack<>();
Stack<Integer> smallStack = new Stack<>();
public void push(int node) {
stack.push(node);
if (smallStack.isEmpty()) {
smallStack.push(node);
} else {
Integer peek = smallStack.peek();
if (peek < node) {
smallStack.push(peek);
} else {
smallStack.push(node);
}
}
}
public void pop() {
stack.pop();
smallStack.pop();
}
public int top() {
return stack.peek();
}
public int min() {
return smallStack.peek();
}
public static void main(String[] args) {
MinStack test = new MinStack();
test.push(-1);
test.push(2);
System.out.println(test.min());
System.out.println(test.top());
test.pop();
test.push(1);
System.out.println(test.top());
System.out.println(test.min());
}
}
31. 栈的压入、弹出序列
java
package stack_queue_heap;
import java.util.Stack;
// ====================== 核心思路 ======================
// 题目:判断第二个序列是否是第一个序列的合法弹出顺序
// 解法:用一个栈模拟压入和弹出操作
// 1. 按入栈顺序,逐个将元素压入辅助栈
// 2. 每压入一个元素,就检查栈顶是否等于当前待弹出的元素
// 3. 如果相等,就弹出栈顶,并将待弹出指针后移一位,继续检查
// 4. 当所有元素都压入后,如果辅助栈为空,说明是合法弹出序列
// 时间复杂度:O(n),每个元素入栈、出栈各一次
// 空间复杂度:O(n),需要一个辅助栈存储元素
// ======================================================
public class IsPopOrder {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param pushV int整型一维数组
* @param popV int整型一维数组
* @return bool布尔型
*/
public boolean IsPopOrder(int[] pushV, int[] popV) {
// write code here
Stack<Integer> stack = new Stack<>();
int length = pushV.length;
for (int pushIndex = 0, popIndex = 0; pushIndex < length; pushIndex++) {
stack.push(pushV[pushIndex]);
while (!stack.isEmpty() && stack.peek() == popV[popIndex]) {
stack.pop();
popIndex++;
}
}
return stack.isEmpty();
}
public static void main(String[] args) {
IsPopOrder test = new IsPopOrder();
int[] pushV = new int[]{1, 2, 3, 4, 5};
int[] popV = new int[]{4, 3, 5, 2, 1};
System.out.println(test.IsPopOrder(pushV, popV));
}
}
40. 最小的 K 个数
java
package stack_queue_heap;
import java.util.ArrayList;
import java.util.PriorityQueue;
// ====================== 核心思路 ======================
// 题目要求:找出数组中不去重的最小 k 个数
// 解法:使用【最大堆】维护当前最小的 k 个元素
// 1. 维护一个大小为 k 的最大堆,堆顶始终是堆内最大值
// 2. 遍历数组,每个元素都加入堆
// 3. 当堆大小超过 k 时,弹出堆顶(淘汰最大的,保留小的)
// 4. 遍历结束后,堆中剩下的就是最小 k 个数
// 时间复杂度:O(n log k),每个元素入堆出堆耗时 log k
// 空间复杂度:O(k),堆只保存 k 个元素
// ======================================================
public class KSmallestWithDuplicates {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param input int整型一维数组
* @param k int整型
* @return int整型ArrayList
*/
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
// write code here
if (input == null || input.length == 0 || input.length < k) {
return new ArrayList<>();
}
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);//默认小顶堆
for (int i : input) {
maxHeap.add(i);
if (maxHeap.size() == k + 1) {
maxHeap.poll();
}
}
return new ArrayList<>(maxHeap);
}
public static void main(String[] args) {
int[] input = new int[]{4, 5, 1, 6, 2, 7, 3, 8};
int k = 4;
KSmallestWithDuplicates test = new KSmallestWithDuplicates();
System.out.println(test.GetLeastNumbers_Solution(input, k));
}
}
41.1 数据流中的中位数
java
package stack_queue_heap;
import java.util.PriorityQueue;
// ====================== 核心思路 ======================
// 题目:不停插入数据流,随时获取中位数
// 中位数:奇数个取中间,偶数个取中间两数平均值
// 解法:用两个堆维护数据流
// 1. leftMaxHeap:大顶堆,存放较小的一半数,堆顶 = 左边最大
// 2. rightMinHeap:小顶堆,存放较大的一半数,堆顶 = 右边最小
// 3. 保证:左边数量 = 右边数量 或 左边 = 右边 + 1
// 4. 奇数个:中位数 = 左边堆顶
// 偶数个:中位数 = (左堆顶 + 右堆顶) / 2
// 时间复杂度:Insert O(log n),GetMedian O(1)
// 空间复杂度:O(n)
// ======================================================
public class FindMedianFromDataFlow {
private PriorityQueue<Integer> leftMaxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
private PriorityQueue<Integer> rightMinHeap = new PriorityQueue<>();
private int N = 0;
public void Insert(Integer num) {
N++;
if (N % 2 == 0) {
leftMaxHeap.add(num);
rightMinHeap.add(leftMaxHeap.poll());
} else {
rightMinHeap.add(num);
leftMaxHeap.add(rightMinHeap.poll());
}
}
public Double GetMedian() {
if (N % 2 == 0) {
return (leftMaxHeap.peek() + rightMinHeap.peek()) / 2.0;
} else {
return (double) leftMaxHeap.peek();
}
}
public static void main(String[] args) {
FindMedianFromDataFlow test = new FindMedianFromDataFlow();
test.Insert(5);
System.out.println(test.GetMedian());
test.Insert(2);
System.out.println(test.GetMedian());
test.Insert(3);
System.out.println(test.GetMedian());
test.Insert(4);
System.out.println(test.GetMedian());
test.Insert(1);
System.out.println(test.GetMedian());
test.Insert(6);
System.out.println(test.GetMedian());
test.Insert(7);
System.out.println(test.GetMedian());
test.Insert(0);
System.out.println(test.GetMedian());
test.Insert(8);
System.out.println(test.GetMedian());
}
}
41.2 字符流中第一个不重复的字符
java
package stack_queue_heap;
import java.util.LinkedList;
import java.util.Queue;
// ====================== 核心思路 ======================
// 题目:字符流中第一个不重复的字符
// 要求:Insert插入字符,FirstAppearingOnce返回第一个只出现一次的字符
// 解法:计数数组 + 队列
// 1. times[128]:记录每个ASCII字符出现的次数
// 2. 队列:按插入顺序保存字符,保证队首始终是【第一个不重复的字符】
// 3. 每次插入后清理队首:重复出现的字符直接弹出,直到队首只出现一次
// 4. 无满足条件字符时返回 '#'
// 时间复杂度:O(1) Insert,O(1) 获取结果
// 空间复杂度:O(n)
// ======================================================
public class FirstUniqueCharInStream {
private int[] times = new int[128];
private Queue<Character> queue = new LinkedList<>();
//Insert one char from stringstream
public void Insert(char ch) {
times[ch]++;
queue.add(ch);
while (!queue.isEmpty() && times[queue.peek()] > 1) {
queue.poll();
}
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce() {
return queue.isEmpty() ? '#' : queue.peek();
}
public static void main(String[] args) {
FirstUniqueCharInStream test = new FirstUniqueCharInStream();
test.Insert('g');
System.out.println(test.FirstAppearingOnce());
test.Insert('o');
System.out.println(test.FirstAppearingOnce());
test.Insert('o');
System.out.println(test.FirstAppearingOnce());
test.Insert('g');
System.out.println(test.FirstAppearingOnce());
test.Insert('l');
System.out.println(test.FirstAppearingOnce());
test.Insert('e');
System.out.println(test.FirstAppearingOnce());
}
}
59. 滑动窗口的最大值
java
package stack_queue_heap;
import java.util.ArrayList;
import java.util.PriorityQueue;
// ====================== 核心思路 ======================
// 题目:求滑动窗口中的最大值
// 解法:利用最大堆(大顶堆)动态维护窗口内的最大值
// 1. 先将第一个窗口的元素加入最大堆,堆顶即为当前最大值
// 2. 窗口向右滑动,移除左端离开窗口的元素,加入右端新进入的元素
// 3. 每次取堆顶作为当前窗口最大值,加入结果集
// 时间复杂度:O(n*k) 堆移除元素需要遍历查找,效率较低
// 空间复杂度:O(k) 堆最多存储 k 个元素
// ======================================================
public class MaxSlidingWindow {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param num int整型一维数组
* @param size int整型
* @return int整型ArrayList
*/
public ArrayList<Integer> maxInWindows(int[] num, int size) {
// write code here
if (num.length == 0 || size <= 0 || size > num.length) {
return new ArrayList<>();
}
int n = num.length;
ArrayList<Integer> res = new ArrayList<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(((o1, o2) -> o2 - o1));
for (int i = 0; i < size; i++) {
maxHeap.add(num[i]);
}
res.add(maxHeap.peek());
for (int i = 0, j = i + size; j < n; i++, j++) {
maxHeap.remove(num[i]);
maxHeap.add(num[j]);
res.add(maxHeap.peek());
}
return res;
}
public static void main(String[] args) {
int[] num = new int[]{2, 3, 4, 2, 6, 2, 5, 1};
int size = 3;
MaxSlidingWindow test = new MaxSlidingWindow();
System.out.println(test.maxInWindows(num, size));
}
}