堆/栈/队列的相关算法

编程入门 行业动态 更新时间:2024-10-09 02:25:53

堆/栈/<a href=https://www.elefans.com/category/jswz/34/1771257.html style=队列的相关算法"/>

堆/栈/队列的相关算法

前言: \textcolor{Green}{前言:} 前言:

💞本专栏用于本人刷算法的过程。主要包含刷题中的感受以及知识点缺陷。对于学习者来说可以作为参考。
目前更新的算法内容会比较多,很多都是通过刷题来进行知识点的总结,其中部分来源于网络总结,如有侵权请联系。💞

文章存在时候,会随着博主的学习过程进行不间断更新,只增不减,请放心使用

堆/栈/队列的相关算法

  • 可以解决的经典问题
    • 队列
  • 算法实例
    • 1. 用两个栈实现队列
    • 2. 包含min函数的栈
    • 3. 有效括号序列
    • 4. 最小的K个数
    • 5. 寻找第K大
    • 6. 数据流中的中位数
  • 总结归纳
    • 队列

可以解决的经典问题

  1. 括号匹配是使用栈解决的经典问题。
  2. 字符串去重问题
  3. 逆波兰表达式问题

队列

  1. 滑动窗口最大值

算法实例

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);

👉 可以提炼的知识

  1. 如何用 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)

  1. 数据是偶数的时候 insert的数据进入 [右边,最小堆]中
    先把cur插入[最大堆|左边最大leftMax]中,
    然后把leftMax放入[右边最小rightMin|最小堆]中
    就可以保证: 左边 < 右边
  2. 数据是奇数的时候 insert的数据进入 [左边,最大堆]中
    先把cur插入[右边最小rightMin|最小堆]中,
    然后把rightMin放入[最大堆|左边最大leftMax]中
    就可以保证: 左边 < 右边
  3. 最后:
    如果是偶数:中位数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():在栈顶增加元素

更多推荐

堆/栈/队列的相关算法

本文发布于:2024-02-28 06:59:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1768410.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:队列   算法

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!