news 2026/4/27 8:31:58

牛客经典101题题解集--堆/栈/队列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
牛客经典101题题解集--堆/栈/队列

目录

用两个栈实现队列

题目

分析

代码

java

python

包含min函数的栈

题目

分析

代码

java

python

有效括号序列

题目

分析

代码

java

python

表达式求值

题目

分析

代码

java

python

队列

滑动窗口的最大值

题目

分析

代码

java

python

数据流中的中位数

题目

分析

代码

java

python

最小的k个数

题目

分析

代码

java

python

寻找第k大

题目

分析

代码

java

python

用两个栈实现队列

题目

用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。

数据范围: 𝑛≤1000n≤1000

要求:存储n个元素的空间复杂度为 𝑂(𝑛)O(n) ,插入与删除的时间复杂度都是 𝑂(1)O(1)

示例1

输入:["PSH1","PSH2","POP","POP"]

返回值:1,2

说明:"PSH1":代表将1插入队列尾部 "PSH2":代表将2插入队列尾部 "POP“:代表删除一个元素,先进先出=>返回1 "POP“:代表删除一个元素,先进先出=>返回2

示例2

输入:["PSH2","POP","PSH1","POP"]

返回值:2,1

分析

栈是先进后出,队列是先进先出,所以我们这里想到用两个栈来模拟队列。

一个用来输入数据,一个用来输出数据。

代码

java
import java.util.*; 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(!stack2.isEmpty()){ return stack2.pop(); } while(!stack1.isEmpty()){ stack2.push(stack1.pop()); } return stack2.pop(); } }
python
# -*- coding:utf-8 -*- class Solution: def __init__(self): self.stack1 = [] self.stack2 = [] def push(self, node): # write code here self.stack1.append(node) def pop(self): # return xx if self.stack2: return self.stack2.pop() while self.stack1: self.stack2.append(self.stack1.pop()) return self.stack2.pop()

包含min函数的栈

题目

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。

此栈包含的方法有:

push(value):将value压入栈中

pop():弹出栈顶元素

top():获取栈顶元素

min():获取栈中最小元素

数据范围:操作数量满足 0≤𝑛≤300 0≤n≤300 ,输入的元素满足 ∣𝑣𝑎𝑙∣≤10000 ∣val∣≤10000
进阶:栈的各个操作的时间复杂度是 𝑂(1) 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

分析

这个难的地方在于找到最小值。那么可以使用两个栈。在放入第一个元素的时候都放进去,然后继续在第一个栈中放入元素,如果当前元素小于第二个的栈顶元素,那么就压入

进栈的时候必须是stack2.peek()>=node,如果没有等于的话,对于:2 2,第二个2不会再压入,而弹出最小值以后栈变成空了,实际上弹出一次以后他的最小值还是2

代码

java
import java.util.*; import java.util.Stack; public class Solution { Stack<Integer> stack1=new Stack<>(); Stack<Integer> stack2=new Stack<>(); public void push(int node) { stack1.push(node); if(stack2.isEmpty() ||stack2.peek()>=node){ stack2.push(node); } } public void pop() { if(stack1.isEmpty()){ return ; } int a=stack1.pop(); if(stack2.peek()==a){ stack2.pop(); } } public int top() { return stack1.peek(); } public int min() { return stack2.peek(); } }
python
# -*- coding:utf-8 -*- class Solution: stack1=[] stack2=[] def push(self, node): # write code here self.stack1.append(node) if not self.stack2 or self.stack2[-1]>=node: self.stack2.append(node) def pop(self): # write code here if not self.stack1: return a=self.stack1.pop() if self.stack2[-1]==a: self.stack2.pop() def top(self): # write code here return self.stack1[-1] return def min(self): # write code here return self.stack2[-1]

有效括号序列

题目

给出一个仅包含字符仅由括号字符 ‘[’‘[’、‘]’‘]’、‘(’‘(’、‘)’‘)’、‘{’‘{’、‘}’‘}’ 的括号序列字符串 𝑠s(0≦∣𝑠∣≦1040≦∣s∣≦104),你需要判断给出的括号序列字符串 𝑠s 是否是有效的括号序列。

有效括号序列的定义如下:
∙ ∙空序列是有效括号序列;
∙ ∙如果 𝐴A 是有效括号序列,则 (A)、[A] 和 A都是有效括号序列;
∙ ∙如果 𝐴A 和 𝐵B 都是有效括号序列,则它们的拼接 𝐴𝐵AB 也是有效括号序列。

如果括号序列字符串 𝑠 是有效的括号序列,返回一个布尔值 true;否则返回一个布尔值 false。

示例1

输入:"["

返回值:false

示例2

输入:"[]"

返回值:true

备注:要求:空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛)。

分析

用栈来实现,如果是左括号,那么压右括号入栈中,如果左括号不等于右括号,那么return fasle,如果循环结束栈中元素不为0,也return false

代码

java
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param s string字符串 * @return bool布尔型 */ public boolean isValid (String s) { // write code here Stack<Character> stack=new Stack<>(); for(int i=0;i<s.length();i++){ if(s.charAt(i)=='('){ stack.push(')'); }else if(s.charAt(i)=='['){ stack.push(']'); }else if(s.charAt(i)=='{'){ stack.push('}'); }else if(stack.isEmpty() || stack.peek()!=s.charAt(i)){ return false; }else{ stack.pop(); } } return stack.isEmpty(); } }
python
# # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param s string字符串 # @return bool布尔型 # class Solution: def isValid(self , s: str) -> bool: # write code here stack=[] for i in range(len(s)): if s[i]=='(': stack.append(')') elif s[i]=='[': stack.append(']') elif s[i]=='{': stack.append('}') elif not stack or stack[-1]!=s[i]: return False else: stack.pop() return False if stack else True

表达式求值

题目

请写一个整数计算器,支持加减乘三种运算和括号。

数据范围:0≤∣𝑠∣≤100,保证计算结果始终在整型范围内

要求:空间复杂度: 𝑂(𝑛),时间复杂度 𝑂(𝑛)

示例1

输入:"1+2"

返回值:3

示例2

输入:"(2*(3-4))*5"

返回值:-10

复制

示例3

输入:"3+2*3*4-1"

返回值:26

分析

定义两栈,一个存数字,一个存符号

代码

java
import java.util.*; public class Solution { public int solve (String s) { int n = s.length(); return dfs(s.toCharArray(), 0, n - 1); } public int dfs(char[] s, int L, int R) { Deque<Integer> nums = new ArrayDeque<>(); Deque<Integer> ops = new ArrayDeque<>(); int[] prior = new int[128]; prior['+'] = prior['-'] = 1; prior['*'] = prior['/'] = 2; nums.push(0); // 处理负数开头 for (int i = L; i <= R; i++) { int c = s[i]; if (c >= '0' && c <= '9') { // 数字 int x = c - '0'; while (i + 1 <= R && s[i+1] >= '0' && s[i+1] <= '9') { x = x * 10 + s[++i] - '0'; } nums.push(x); } else if (c == '+' || c == '-' || c == '*' || c == '/') { // 运算符 calc(nums, ops, prior, c); } else if (c == '(') { // 括号,递归 int j = i + 1, level = 1; while (level > 0) { if (s[j] == '(') level++; else if (s[j] == ')') level--; j++; } nums.push(dfs(s, i + 1, j - 2)); i = j - 1; } } calc(nums, ops, prior, '+'); // 把剩下的全部算完 return nums.pop(); } public void calc(Deque<Integer> nums, Deque<Integer> ops, int[] prior, int c) { while (!ops.isEmpty() && nums.size() > 1 && prior[ops.peek()] >= prior[c]) { int op = ops.pop(); int y = nums.pop(); int x = nums.pop(); if (op == '+') x += y; else if (op == '-') x -= y; else if (op == '*') x *= y; else if (op == '/') x /= y; nums.push(x); } ops.push(c); } }
python
class Solution: def solve(self , s: str) -> int: return self.dfs(s, 0, len(s) - 1) def dfs(self, s, L, R): nums = [] ops = [] prior = {'+':1, '-':1, '*':2, '/':2} nums.append(0) # 处理负数开头 i = L while i <= R: if s[i].isdigit(): x = 0 while i <= R and s[i].isdigit(): x = x * 10 + int(s[i]) i += 1 nums.append(x) continue c = s[i] if c in "+-*/": self.calc(nums, ops, prior, c) i += 1 elif c == '(': j = i + 1 level = 1 while level > 0: if s[j] == '(': level += 1 elif s[j] == ')': level -= 1 j += 1 val = self.dfs(s, i+1, j-2) nums.append(val) i = j - 1 else: i += 1 self.calc(nums, ops, prior, '+') return nums.pop() def calc(self, nums, ops, prior, c): while ops and prior[ops[-1]] >= prior[c]: op = ops.pop() y = nums.pop() x = nums.pop() if op == '+': x += y elif op == '-': x -= y elif op == '*': x *= y elif op == '/': x = int(x / y) nums.append(x) ops.append(c)

队列

滑动窗口的最大值

题目

给定一个长度为 n 的数组 num 和滑动窗口的大小 size ,找出所有滑动窗口里数值的最大值。

例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

窗口大于数组长度或窗口长度为0的时候,返回空。

数据范围: 1≤𝑛≤10000,0≤𝑠𝑖𝑧𝑒≤10000,数组中每个元素的值满足 ∣𝑣𝑎𝑙∣≤10000

要求:空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛)

示例1

输入:[2,3,4,2,6,2,5,1],3

返回值:[4,4,6,6,6,5]

示例2

输入:[9,10,9,-7,-3,8,2,-6],5

返回值:[10,10,9,8]

示例3

输入:[1,2,3,4],5

返回值:[]

分析

Deque是两端都能进和出的操作

peekFirst()看最左边第一个元素(队头)只拿出来看一眼,不删除

peekLast()看最右边最后一个元素(队尾)只看不删


2. 删除(看完就删掉) remove /poll 系列

removeFirst()删掉最左边第一个元素

removeLast()删掉最右边最后一个元素


3. add 系列

addLast(x)从队尾右边加元素

addFirst(x)从队头左边加元素

代码

java
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param num int整型一维数组 * @param size int整型 * @return int整型ArrayList */ public ArrayList<Integer> maxInWindows (int[] num, int size) { // write code here ArrayList<Integer> res=new ArrayList<>(); // write code here if(num.length==0 ||size==0){ return res; } Deque <Integer> que=new LinkedList<>(); for(int j=0,i=1-size;j<num.length;i++,j++){ if(i>0 && que.peekFirst()==num[i-1]){ que.removeFirst(); } while(!que.isEmpty() && que.peekLast()<=num[j]){ que.removeLast(); } que.addLast(num[j]); if(i>=0){ res.add(que.peekFirst()); } } return res; } }
python
# # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param num int整型一维数组 # @param size int整型 # @return int整型一维数组 # import collections class Solution: def maxInWindows(self , num: List[int], size: int) -> List[int]: result=[] if len(num)==0 or size==0: return result # write code here que=collections.deque() for j in range(len(num)): i=j-size+1 if i>0 and que[0]==num[i-1]: que.popleft() while que and que[-1]<=num[j]: que.pop() que.append(num[j]) if i>=0: result.append(que[0]) return result

数据流中的中位数

题目

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

数据范围:数据流中数个数满足 1≤𝑛≤1000 ,大小满足 1≤𝑣𝑎𝑙≤1000

进阶: 空间复杂度 𝑂(𝑛) , 时间复杂度 𝑂(𝑛𝑙𝑜𝑔𝑛)

示例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 "

分析

设置两个队列:

a是从小到大,b是从大到小,a是较大的后半部分,b是较小的前半部分

代码

java
import java.util.*; public class Solution { Queue<Integer> a=new PriorityQueue<>();//由小到大 Queue<Integer> b=new PriorityQueue<>((a,b) ->b-a);//由大到小 public void Insert(Integer num) { if(a.size()!=b.size()){ a.add(num); b.add(a.poll()); }else{ b.add(num); a.add(b.poll()); } } public Double GetMedian() { if(a.size()==b.size()){ return (a.peek()+b.peek())/2.0; }else{ return a.peek()*1.0; } } }
python
# -*- coding:utf-8 -*- from heapq import * class Solution: def __init__(self): self.A=[] self.B=[] def Insert(self, num): # write code here if len(self.A) != len(self.B): heappush(self.A, num) heappush(self.B, -heappop(self.A)) else: heappush(self.B, -num) heappush(self.A, -heappop(self.B)) def GetMedian(self): # write code here return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0]) / 2.0

python只有较小堆没有较大堆,所以取负号,然后在最后结果的时候再取回来

最小的k个数

题目

给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。

数据范围:0≤𝑘,𝑛≤10000,数组中每个数的大小0≤𝑣𝑎𝑙≤1000

要求:空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛𝑙𝑜𝑔𝑘)

示例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]

分析

利用小顶堆,最小的在尖尖上

小顶堆 :从小到大 (a, b) -> a - b 大顶堆: 从大到小 (a, b) -> b - a

代码

java
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param input int整型一维数组 * @param k int整型 * @return int整型ArrayList */ public ArrayList<Integer> GetLeastNumbers_Solution (int[] input, int k) { // write code here ArrayList<Integer> res=new ArrayList<>(); PriorityQueue<Integer> p=new PriorityQueue<>((a,b) ->a-b); for(int i:input){ p.add(i); } for(int i=0;i<k;i++){ res.add(p.poll()); } return res; } }
python
# # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param input int整型一维数组 # @param k int整型 # @return int整型一维数组 # import heapq class Solution: def GetLeastNumbers_Solution(self , input: List[int], k: int) -> List[int]: # write code here # 结果列表 res = [] # Python 小顶堆(默认就是小顶堆,和Java (a,b)->a-b 等价) heapq.heapify(input) # 直接把数组转成堆,效率更高 # 取出前k个最小的数 for _ in range(k): res.append(heapq.heappop(input)) return res

python默认的堆就是小顶堆

寻找第k大

题目

有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数。

给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在。

要求:时间复杂度 𝑂(𝑛𝑙𝑜𝑔𝑛),空间复杂度 𝑂(1)

数据范围:0≤𝑛≤1000, 1≤𝐾≤𝑛,数组中每个元素满足 0≤𝑣𝑎𝑙≤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

分析

首先要了解什么是快速排序:

快速排序在每一轮挑选一个基准函数,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成了两个部分。

代码

java
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param a int整型一维数组 * @param n int整型 * @param K int整型 * @return int整型 */ Random r=new Random(); public int findKth (int[] a, int n, int K) { // write code here int target = a.length - K;//找到第k大的下标 int start = 0, end = a.length - 1; int pivot = privot(a, start, end); while (pivot != target) { if (pivot > target) { end = pivot - 1; } else { start = pivot + 1; } pivot = privot(a, start, end); } return a[pivot]; } public int privot(int [] a,int start,int end){ int pri=r.nextInt(start-end+1)+start; swap(a,pri,end); int index=start-1; for(int i=start;i<=end;i++){ if(a[i]<a[end]){ swap(a,i,++index); } } swap(a,++index,end); return index; } public void swap(int[] a,int i,int j){ int temp=a[i]; a[i]=a[j]; a[j]=temp; } }
python
# # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param a int整型一维数组 # @param n int整型 # @param K int整型 # @return int整型 # import random class Solution: def findKth(self , a: List[int], n: int, K: int) -> int: # write code here target=len(a)-K start = 0 end = len(a) - 1 # 核心:循环找,直到下标 == target while True: p = self.privot(a, start, end) # 基准下标 if p == target: return a[p] elif p > target: end = p - 1 # 去左边找 else: start = p + 1 # 去右边找 def privot(self,a:List[int],start:int,end:int)-> int: r=random.randint(start,end) a[r],a[end]=a[end],a[r] index=start-1 for i in range(start,end+1): if a[i]<a[end]: index+=1 a[index],a[i]=a[i],a[index] index+=1 a[index],a[end]=a[end],a[index] return index
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 8:31:01

3步搞定B站视频下载:Downkyi无水印高清下载终极指南

3步搞定B站视频下载&#xff1a;Downkyi无水印高清下载终极指南 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xf…

作者头像 李华
网站建设 2026/4/27 8:30:09

Qwen3.5-2B部署教程:HTTPS反向代理配置(Nginx)+域名访问企业内网方案

Qwen3.5-2B部署教程&#xff1a;HTTPS反向代理配置&#xff08;Nginx&#xff09;域名访问企业内网方案 1. 项目概述 Qwen3.5-2B是一款20亿参数的轻量级多模态大语言模型&#xff0c;专为企业内网部署优化设计。该模型支持轻量对话、文案创作、多语言翻译、基础代码生成等功能…

作者头像 李华
网站建设 2026/4/27 8:28:05

Alas智能脚本技术架构深度解析:碧蓝航线自动化引擎的创新应用

Alas智能脚本技术架构深度解析&#xff1a;碧蓝航线自动化引擎的创新应用 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研&#xff0c;全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript Al…

作者头像 李华
网站建设 2026/4/27 8:18:33

GLM-4.1V-9B-Base入门必备:JDK1.8环境下Java客户端调用指南

GLM-4.1V-9B-Base入门必备&#xff1a;JDK1.8环境下Java客户端调用指南 1. 为什么需要这份指南 很多企业还在使用JDK1.8运行关键业务系统&#xff0c;而GLM-4.1V-9B-Base作为新一代大模型&#xff0c;其官方SDK往往要求更高版本的Java环境。这就产生了一个现实问题&#xff1…

作者头像 李华
网站建设 2026/4/27 8:13:15

靠谱的新疆生态修复排名情况

在生态修复领域&#xff0c;新疆因其独特的地理环境和生态需求&#xff0c;成为了众多企业一展身手的舞台。新疆绿景生态集团有限公司作为其中的佼佼者&#xff0c;凭借卓越的实力和丰富的经验&#xff0c;在新疆生态修复市场占据着重要地位。实力雄厚&#xff0c;资质荣誉加持…

作者头像 李华
网站建设 2026/4/27 8:09:44

全新二级域名分发系统网站源码_终极最强版

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示一、详细介绍 全新二级域名分发系统网站源码_终极最强版 附教程 亲测 一、系统核心优势 高性能架构&#xff1a;基于PHP8.1Swoole扩展开发&#xff0c;支持10万并发请求 智能分发引擎&#xff1a;实时动态解析二级域…

作者头像 李华