当前位置: 首页 > news >正文

牛客经典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
http://www.jsqmd.com/news/707655/

相关文章:

  • GAN训练算法与损失函数实战解析
  • Git Archaeologist:AI驱动的代码历史分析与决策追溯工具
  • 终极NCM文件解密指南:3步解锁网易云音乐加密格式
  • Arm Lumex平台性能分析工具链与SPE技术详解
  • AI代码审查助手altimate-code:架构解析与实战部署指南
  • ARM NEON与VFP向量指令集优化指南
  • 人形机器人行业日报:39自由度仿真机器人又来了,海外开始卷“像人感”服务前台
  • GHelper风扇曲线自定义:为华硕笔记本打造个性化的智能散热方案
  • 北京甲状腺专家怎么选?这些医生调理效果比错不错
  • DownKyi:三步掌握B站视频下载与管理的专业方案
  • Redis AOF 重写机制与性能优化
  • 手把手教你用CubeMX配置STM32F407的PWM驱动50Hz舵机,搭配OpenMV做视觉反馈
  • Chromatic:3个创新方案解决Chromium/V8注入难题的实战指南
  • SwiftUI图像填充与按钮布局
  • 2026年4月北京核磁医院评测:五家口碑服务推荐评价领先深度健检报告解读需求 - 品牌推荐
  • Iwara下载工具:解锁视频下载的智能解决方案
  • Qwen3.5-9B-GGUF基础教程:app.py源码结构解析与Gradio组件扩展方法
  • SDMatte多模态扩展探索:结合文本描述进行语义感知的抠图
  • 机器学习必备:线性代数核心知识与工程实践
  • FakeLocation终极指南:重新掌控你的Android位置隐私
  • OpenCV视频处理核心技术及工程实践指南
  • 数组和切片实战
  • DTVM框架解析:基于Vue ue.js 3与TypeScript的电视应用开发实践
  • 哪家北京核磁医院专业?2026年4月推荐评测口碑对比五家服务领先骨关节运动损伤影像评估 - 品牌推荐
  • DistilBart模型解析与文本摘要实战指南
  • 快速上手像素剧本圣殿:三步完成你的第一个剧本创作
  • 巴拿马电源在数据中心的应用
  • 像素剧本圣殿惊艳效果:Qwen2.5-14B-Instruct生成的8-Bit风格剧本PDF导出样例
  • Phi-3 Forest Laboratory 低成本运行方案:在消费级GPU上的部署与优化
  • dockerfile系列(六) 进阶技巧与调试-Dockerfile的黑魔法