牛客经典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
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
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
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
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
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
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
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
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() **看最右边最后一个元素(队尾)**只看不删


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

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

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


  1. add 系列

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

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

代码

java
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
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
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
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
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
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
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
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
相关推荐
csbysj20202 小时前
Memcached append 命令详解
开发语言
凯瑟琳.奥古斯特2 小时前
常见排序算法性能对比
数据结构·算法·排序算法
Hello!!!!!!2 小时前
C++基础(十二)——标准库算法
c++·算法
故事还在继续吗2 小时前
C++内存模型
开发语言·c++·内存
Tairitsu_H2 小时前
C++:构造函数与初始化列表详解
开发语言·c++·构造函数
琪露诺大湿2 小时前
VeloQueue-测试报告
java·开发语言·消息队列·单元测试·项目·测试报告
茅盾体2 小时前
Electron图标相关
java·前端·electron
minji...2 小时前
Linux 网络套接字编程(四)支持多客户端同时在线、消息能转发给所有人的 UDP 聊天室服务器
linux·运维·开发语言·网络·c++·算法·udp