队列的相关算法"/>
堆/栈/队列的相关算法
前言: \textcolor{Green}{前言:} 前言:
💞本专栏用于本人刷算法的过程。主要包含刷题中的感受以及知识点缺陷。对于学习者来说可以作为参考。
目前更新的算法内容会比较多,很多都是通过刷题来进行知识点的总结,其中部分来源于网络总结,如有侵权请联系。💞
文章存在时候,会随着博主的学习过程进行不间断更新,只增不减,请放心使用
堆/栈/队列的相关算法
- 可以解决的经典问题
- 栈
- 队列
- 算法实例
- 1. 用两个栈实现队列
- 2. 包含min函数的栈
- 3. 有效括号序列
- 4. 最小的K个数
- 5. 寻找第K大
- 6. 数据流中的中位数
- 总结归纳
- 堆
- 队列
可以解决的经典问题
栈
- 括号匹配是使用栈解决的经典问题。
- 字符串去重问题
- 逆波兰表达式问题
队列
- 滑动窗口最大值
算法实例
1. 用两个栈实现队列
题目来源:牛客网
等级: E a s y \textcolor{OrangeRed}{等级:Easy} 等级:Easy
👉题目描述
用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
数据范围: n ≤ 1000 n ≤ 1000 数据范围: n\le1000n≤1000 数据范围:n≤1000n≤1000
要求:存储n个元素的空间复杂度为 O(n) ,插入与删除的时间复杂度都是 O(1)
👉代码编写
👉👉方法1
import java.util.Stack;public class Solution {Stack<Integer> stack1 = new Stack<Integer>();Stack<Integer> stack2 = new Stack<Integer>();public void push(int node) {stack1.push(node);}public int pop() {if (stack1.isEmpty() && stack2.isEmpty()) {return -1;}if (!stack2.isEmpty()) {return stack2.pop();}while (!stack1.isEmpty()) {stack2.push(stack1.pop());}return stack2.pop();}
}
2. 包含min函数的栈
题目来源:牛客网
等级: E a s y \textcolor{OrangeRed}{等级:Easy} 等级:Easy
👉题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。
此栈包含的方法有:
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素
数据范围:操作数量满足 0 ≤ n ≤ 300 0≤n≤300 0≤n≤300 ,输入的元素满足 ∣ v a l ∣ ≤ 10000 ∣val∣≤10000 ∣val∣≤10000
进阶:栈的各个操作的时间复杂度是 O(1) ,空间复杂度是 O(n)
示例:
输入: ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
输出: -1,2,1,-1
解析:
"PSH-1"表示将-1压入栈中,栈中元素为-1
"PSH2"表示将2压入栈中,栈中元素为2,-1
“MIN”表示获取此时栈中最小元素==>返回-1
"TOP"表示获取栈顶元素==>返回2
"POP"表示弹出栈顶元素,弹出2,栈中元素为-1
"PSH1"表示将1压入栈中,栈中元素为1,-1
"TOP"表示获取栈顶元素==>返回1
“MIN”表示获取此时栈中最小元素==>返回-1
示例1
输入:["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
复制
返回值:
-1,2,1,-1
👉代码编写
👉👉方法1
import java.util.Stack;public class Solution {private Stack<Integer> stack = new Stack<>();private Stack<Integer> minStack = new Stack<>();public void push(int node) {stack.push(node);if (minStack.isEmpty()){minStack.push(node);} else {int min = minStack.peek();if (min > node) {minStack.push(node);} else {minStack.push(min);}} }public void pop() {stack.pop();minStack.pop();}public int top() {if (stack.isEmpty()) {return -1;}return stack.peek();}public int min() {return minStack.peek();}
}
👉 可以提炼的知识
-
堆栈类peek()方法peek()方法在java.util包中使用。
-
peek()方法用于从此Stack中返回顶部元素,并且它不删除
检索元素
。 -
peek()方法是一种非静态方法,只能通过类对象访问,如果尝试使用类名访问该方法,则会收到错误消息。
-
peek()方法在返回top元素时不会引发异常。
3. 有效括号序列
题目来源:牛客网
等级:简单 \textcolor{OrangeRed}{等级:简单} 等级:简单
👉题目描述
给出一个仅包含字符’(‘,’)‘,’{‘,’}‘,’[‘和’]',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()“和”()[]{}“都是合法的括号序列,但”(]“和”([)]"不合法。
数据范围:字符串长度 0 ≤ n ≤ 100000 ≤ n ≤ 10000 0\le n \le 100000≤n≤10000 0≤n≤100000≤n≤10000
要求:空间复杂度 O(n),时间复杂度 O(n)
示例1
输入:"["
返回值:false
示例2
输入:"[]"
返回值:true
👉代码编写
👉👉方法1
import java.util.*;public class Solution {/*** * @param s string字符串 * @return bool布尔型*/public boolean isValid (String s) {// write code hereint len = s.length();if (len % 2 == 1) {return false;}Stack<Character> stack = new Stack<>();for (int i = 0; i < len; ++i) {char c = s.charAt(i);if (c == '(') {stack.push(')');} else if (c == '{') {stack.push('}');} else if (c == '[') {stack.push(']');} else if (stack.isEmpty() || stack.peek() != c) {return false;} else {stack.pop();}}if (stack.isEmpty()) {return true;}return false;}
}
4. 最小的K个数
题目来源:牛客网
等级: M e d i u m \textcolor{OrangeRed}{等级:Medium} 等级:Medium
使用方法:大根堆
👉题目描述
给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。
数据范围: ≤ k , n ≤ 100000 ≤ k , n ≤ 10000 ,数组中每个数的大小 0 ≤ v a l ≤ 10000 ≤ v a l ≤ 1000 \le k,n \le 100000≤k,n≤10000,数组中每个数的大小0 \le val \le 10000≤val≤1000 ≤k,n≤100000≤k,n≤10000,数组中每个数的大小0≤val≤10000≤val≤1000
要求:空间复杂度 O(n) ,时间复杂度 O(nlogk)
示例1
输入:
[4,5,1,6,2,7,3,8],4
返回值:[1,2,3,4]
说明:
返回最小的4个数即可,返回[1,3,2,4]也可以
示例2
输入:[1],0
返回值:[]
示例3
输入:[0,1,2,1,2],3
返回值:[0,1,1]
👉代码编写
👉👉方法1
import java.util.*;public class Solution {public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {ArrayList<Integer> res = new ArrayList<>();if (k == 0 || input.length == 0) {return res;}// large root heapQueue<Integer> maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1);// Build large root heapfor (int i = 0; i < k; ++i) {maxQueue.offer(input[i]);}for (int i = k; i < input.length; ++i) {// Small elements are piledif (maxQueue.peek() > input[i]) {maxQueue.poll();maxQueue.offer(input[i]);}}// Extract elements from the heapfor (int i = 0; i < k; ++i) {res.add(maxQueue.poll());}return res;}
}
👉 注意点
- 为什么是大根堆?因为大根堆弹出来的元素是最大值可以与接下来的值进行比较,如果比最大值要小,那么就将该值弹出,并将比较的值offer进去,反之不动。
- 大根堆的构建(注意要重写)
Queue<Integer> maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1);
👉 可以提炼的知识
- 如何用 PriorityQueue 实现大根堆 maxQueue?
- PriorityQueue 默认是小根堆,大根堆需要重写比较器。
- 可以在 new PriorityQueue<>() 中的参数部分加入比较器。
- 具体写法是:
(v1, v2) -> v2 - v1
。 - Queue 类中加入值是
offer()
方法,弹出是poll()
方法。Queue<Integer> maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1); maxQueue.offer(1); maxQueue.poll(); maxQueue.size();
5. 寻找第K大
题目来源:牛客网
等级: M e d i u m \textcolor{OrangeRed}{等级:Medium} 等级:Medium
使用方法:小根堆
👉题目描述
有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数。
给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在。
要求:时间复杂度 O(nlogn)O(nlogn),空间复杂度 O(1)O(1)
数据范围: 0 ≤ n ≤ 10000 ≤ n ≤ 10001 ≤ K ≤ n 1 ≤ K ≤ n ,数组中每个元素满足 0 ≤ v a l ≤ 100000000 ≤ v a l ≤ 10000000 0\le n \le 10000≤n≤10001 \le K \le n1≤K≤n,数组中每个元素满足 0 \le val \le 100000000≤val≤10000000 0≤n≤10000≤n≤10001≤K≤n1≤K≤n,数组中每个元素满足0≤val≤100000000≤val≤10000000
示例1
输入:[1,3,5,2,2],5,3
返回值:2
示例2
输入:
[10,10,9,9,8,7,5,6,4,3,4,2],12,3
返回值:9
说明:
去重后的第3大是8,但本题要求包含重复的元素,不用去重,所以输出9 \
👉代码编写
👉👉方法1
import java.util.*;public class Solution {public int findKth(int[] a, int n, int K) {// write code hereif (K == 0 || n == 0) {return -1;}// Build small root heap with a sizeof KPriorityQueue<Integer> smallQueue = new PriorityQueue<>(K);for (int i = 0; i < K; ++i) {if (i > n) {break;}smallQueue.offer(a[i]);}for (int i = K; i < n; ++i) {if (smallQueue.peek() < a[i]) {smallQueue.poll();smallQueue.offer(a[i]);}}return smallQueue.peek();}
}
👉👉方法2
👉 注意点
- 小根堆不需要重写比较器。创建多大的小根堆就直接填入值就行
// Build small root heap with a sizeof K PriorityQueue<Integer> smallQueue = new PriorityQueue<>(K);
👉 可以提炼的知识
6. 数据流中的中位数
题目来源:牛客网
等级: M e d i u m \textcolor{OrangeRed}{等级:Medium} 等级:Medium
使用方法:小根堆+大根堆
👉题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
数据范围:$数据流中数个数满足 1 \le n \le 1000 \1≤n≤1000 ,大小满足 1 \le val \le 1000 \1≤val≤1000 $
进阶: 空间复杂度 O(n) , 时间复杂度 O(nlogn)
示例1
输入:[5,2,3,4,1,6,7,0,8]
返回值:"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明:
数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...
示例2
输入:[1,1,1]
返回值:"1.00 1.00 1.00 "
👉代码编写
👉👉方法1
参考大佬写法
[最大堆左边最大 leftMax] | [右边最小rightMin 最小堆] |
---|
直接插入到对面的堆中,然后再转移到自己的堆中但是它的 时间复杂度提高, 每次都插入时间复杂度O(log n)
- 数据是偶数的时候 insert的数据进入 [右边,最小堆]中
先把cur插入[最大堆|左边最大leftMax]中,
然后把leftMax放入[右边最小rightMin|最小堆]中
就可以保证: 左边 < 右边 - 数据是奇数的时候 insert的数据进入 [左边,最大堆]中
先把cur插入[右边最小rightMin|最小堆]中,
然后把rightMin放入[最大堆|左边最大leftMax]中
就可以保证: 左边 < 右边 - 最后:
如果是偶数:中位数mid= (leftMax+right)/2
如果是奇数:中位数mid= leftMax
import java.util.*;public class Solution {// Build large root heap, private PriorityQueue<Integer> leftHeap = new PriorityQueue<>((v1, v2) -> v2 - v1);// Build small root heapprivate PriorityQueue<Integer> rightHeap = new PriorityQueue<>();// Judge whether it is oddprivate boolean isOdd = true;public void Insert(Integer num) {if (isOdd) {rightHeap.offer(num);leftHeap.offer(rightHeap.poll());} else {leftHeap.offer(num);rightHeap.offer(leftHeap.poll());}isOdd = !isOdd;}public Double GetMedian() {if (leftHeap.isEmpty()) {return 0.0;}if (!isOdd) {return leftHeap.peek() / 1.0;} else {return (leftHeap.peek() + rightHeap.peek()) / 2.0;}}
}
👉👉方法2
👉 注意点
一点要保证两个堆中的数据数目差不能超过1,这样可以使中位数只会出现在两个堆的交接处;
保证大根堆所有数据都小于小根堆
👉 可以提炼的知识
总结归纳
堆
对于海量数据和流的数据,用最大堆和最小堆来管理
堆通常是一个可以被看做一棵树的数组对象。堆是非线性数据结构,相当于一维数组,有两个直接后继。堆总是满足下列性质:
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树;
- 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
求第k大,就要用小根堆,每一次更新后,顶点时刻都是整个树中数值最小的节点,它的孩子都比他大,而我们求第k大,就构建一个只有 k 个节点的堆,最后返回堆顶。
PriorityQueue<Integer> smallQueue = new PriorityQueue<>(K);
求第K小,就要用大根堆,每一次更新后,顶点都是整棵树中数值最大的节点,它的孩子都比他小,那么就第K小,就构建一个只有k个节点的堆,最后返回堆顶。
Queue<Integer> maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1);
加入:offer()
弹出:poll()
大小:size()
队列
peek() :返回栈顶元素,不在堆栈中删除它。
pop() :返回栈顶元素,并在进程中删除它。
push():在栈顶增加元素
更多推荐
堆/栈/队列的相关算法
发布评论