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

【数据结构】 栈和队列

【数据结构】 栈和队列

  • 一、栈
    • 1.1 栈的概念
    • 1.2 栈的使用
    • 1.3 栈的模拟实现
      • 1.3.1 数组 模拟栈的实现(顺序栈)
      • 1.3.2 双向链表 模拟栈的实现(链式栈)
    • 1.4 栈相关的 OJ题
      • 1.4.1 逆波兰表达式求值 / 后缀表达式求值
      • 1.4.2 括号匹配
      • 1.4.3 出栈入栈次序匹配
      • 1.4.4 最小栈
  • 二、队列
    • 2.1 队列的概念
    • 2.2 队列的使用
    • 2.3 队列的模拟实现
      • 2.3.1 双向链表 模拟实现 队列(链式队列)
      • 2.3.2 数组 模拟实现 队列(循环队列)
  • 三、双端队列
    • 3.1 双端队列的使用
    • 3.2 双端队列 相关OJ题
      • 3.2.1 用队列实现栈
      • 3.3.2 用栈实现队列

一、栈

之前学习的 局部变量存储在栈中 ,这个 指的是 内存。
而现在学习的,指的是一种 数据结构特点为:先进后出

1.1 栈的概念

在这里插入图片描述

1.2 栈的使用

在这里插入图片描述

import java.util.Stack;public class Test {public static void main(String[] args) {Stack<Integer> stack = new Stack<>();stack.push(12);stack.push(23);stack.push(34);stack.push(45);int val = stack.pop();//删除System.out.println(val);//45val = stack.pop();System.out.println(val);//34int val2 = stack.peek();//只获取,不删除System.out.println(val2);//23int val3 = stack.peek();//只获取,不删除System.out.println(val3);//23boolean empty =  stack.empty();//判空System.out.println(empty);//false}
}

1.3 栈的模拟实现

1.3.1 数组 模拟栈的实现(顺序栈)

import java.util.Arrays;
import java.util.Stack;public class MyStack {//数组模拟栈 的实现//push():存储数据private int[] elem;private int usedSize;//usedSize:当前数组有多少个元素, 默认为0private static final int DEAULT_SIZE = 10;public MyStack(){elem = new int[DEAULT_SIZE];}public void push(int val){if(isFull()){grow(); //扩容}elem[usedSize] = val;usedSize++;}private void grow(){elem = Arrays.copyOf(elem,elem.length);}public boolean isFull(){return usedSize == elem.length;}//pop():删除数据public int pop(){if(isEmpty()){//return -1;throw new StackEmptyException("栈为空异常!");}usedSize--;return elem[usedSize];}public boolean isEmpty(){return usedSize == 0;}//peek():获取栈顶元素,但是不删除当前元素public int peek(){if(isEmpty()){//return -1;throw new StackEmptyException("栈为空异常!");}//usedSize--;return elem[usedSize - 1];}//size()public int size(){return usedSize;}
}

测试代码:

import java.util.Stack;public class Test {public static void main(String[] args) {MyStack stack = new MyStack();stack.push(12);stack.push(23);stack.push(34);stack.push(45);int val = stack.pop();//删除System.out.println(val);//45val = stack.pop();System.out.println(val);//34int val2 = stack.peek();//只获取,不删除System.out.println(val2);//23int val3 = stack.peek();//只获取,不删除System.out.println(val3);//23boolean empty =  stack.isEmpty();//判空System.out.println(empty);//false}

输出结果:

在这里插入图片描述
数组实现栈:入栈O(1);出栈O(1)。

1.3.2 双向链表 模拟栈的实现(链式栈)

(1)单向链表 模拟栈(从头入栈,从头出栈

  • 入栈从头入,出栈从头出;可以保证入栈和出栈的时间复杂度为O(1)。
  • 从链表的尾巴 入栈和出栈 不行,因为入栈可以做到O(1),但是出栈还是O(n)。

(2)双向链表 模拟栈
在这里插入图片描述

  • 无论从从头还是从尾,时间复杂度 都为O(1)。
    -== 双向链表(LinkedList)可以被当做 栈来使用 ==!

1.4 栈相关的 OJ题

1.4.1 逆波兰表达式求值 / 后缀表达式求值

(1)LC150:逆波兰表达式求值

在这里插入图片描述
在这里插入图片描述

(2)图解

  • 中缀表达式 转换 后缀表达式推导:

在这里插入图片描述

  • 利用栈进行转换:

在这里插入图片描述
在这里插入图片描述

(3)Java 代码实现

import java.util.Stack;class Solution {public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack<>();  for(int i = 0; i < tokens.length; i++){String s = tokens[i];if(!isOperation(s)){ Integer ret = Integer.valueOf(s);stack.push(ret);} else {  // 是运算符Integer val2 = stack.pop();Integer val1 = stack.pop();switch(s){case "+":stack.push(val1 + val2);break;case "-":stack.push(val1 - val2);break;case "*":stack.push(val1 * val2);break;case "/":stack.push(val1 / val2);break;}}}return stack.pop();   }// 判断当前字符串是否为+ - * /private boolean isOperation(String val){ if(val.equals("+") || val.equals("-") || val.equals("*") || val.equals("/")){return true;}return false;}
}

1.4.2 括号匹配

(1)LC20.有效的括号

在这里插入图片描述
在这里插入图片描述
(2)图解
在这里插入图片描述

在这里插入图片描述

(3)Java 代码实现

class Solution {public boolean isValid(String s) {Stack<Character> stack = new Stack<>();for(int i = 0;i < s.length();i++){char ch = s.charAt(i);if(ch == '(' || ch == '{' || ch == '['){stack.push(ch);//左括号 入栈}else{//ch = 右括号if(stack.isEmpty()){//4.字符串没有遍历完成,但是栈为空return false;}//拿到栈顶元素char peekCh = stack.peek();if( peekCh == '(' && ch == ')'|| peekCh == '{' && ch == '}'|| peekCh == '[' && ch == ']'){stack.pop();}else{return false;//2.左右括号不匹配}}}if(!stack.isEmpty()){//3.字符串遍历完成,但是栈当中不为空return false;}return true;}
}

1.4.3 出栈入栈次序匹配

(1)牛客链接:JZ31 栈的压入、弹出序列
在这里插入图片描述
(2)图解

在这里插入图片描述

在这里插入图片描述
(3)Java 代码实现

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param pushV int整型一维数组 * @param popV int整型一维数组 * @return bool布尔型*/public boolean IsPopOrder (int[] pushV, int[] popV) {// write code hereStack<Integer> stack = new Stack<>();int j = 0;for(int i = 0;i < pushV.length;i++){stack.push(pushV[i]);while(!stack.isEmpty() && j < popV.length && stack.peek() == popV[j]){//栈不为空,j位置合法,取栈顶元素比较stack.pop();j++;}}return stack.isEmpty();}
}

1.4.4 最小栈

(1)LC155.最小栈
在这里插入图片描述

(2)图解
在这里插入图片描述

在这里插入图片描述
(3)Java 代码实现

class MinStack {Stack<Integer> stack;Stack<Integer> minStack;public MinStack() {stack =  new Stack<>();minStack =  new Stack<>();}public void push(int val) {stack.push(val);if(minStack.empty()){minStack.push(val);}else{int peekMinVal = minStack.peek();if(val <= peekMinVal){minStack.push(val);}}}public void pop() {int val = stack.pop();if(val == minStack.peek()){minStack.pop();}}//peek()public int top() {return stack.peek();}//O(1)public int getMin() {return minStack.peek();}
}

二、队列

2.1 队列的概念

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2 队列的使用

在这里插入图片描述

注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

package queuedemo;import java.util.LinkedList;
import java.util.Queue;public class Test {public static void main(String[] args) {Queue<Integer> queue = new LinkedList<>();queue.offer(12);//offer 添加元素12queue.offer(23);queue.offer(34);queue.offer(45);System.out.println(queue.poll());//12:poll 删除元素System.out.println(queue.peek());//23:peek 获取元素 不删除System.out.println(queue.isEmpty());//false}
}

2.3 队列的模拟实现

2.3.1 双向链表 模拟实现 队列(链式队列)

(1) 图解

  • offer()

在这里插入图片描述

  • poll()

在这里插入图片描述
(2)Java 代码实现

package queuedemo;public class MyQueue {static class Node {public int val;public Node prev;public Node next;public Node(int val) {this.val = val;}}public Node front;//队头:链表的头public Node rear;//队尾: 链表的尾巴public void offer(int val) { // offer(): 相当于尾插法Node node = new Node(val);if (front == null) {front = node;rear = node;} else {rear.next = node;node.prev = rear;rear = node;}}public int poll(){//删除元素if(front == null){return -1;}int val = front.val;front = front.next;//防止 只有一个节点if(front != null){front.prev = null;}else{rear = null;}return val;}public int size(){Node cur = front;int count = 0;while(cur != null){count++;cur = cur.next;}return count;}public int peek(){if(front == null){return -1;}return front.val;}
}

测试代码:

package queuedemo;import java.util.LinkedList;
import java.util.Queue;public class Test {public static void main(String[] args) {MyQueue queue = new MyQueue();queue.offer(12);queue.offer(23);queue.offer(34);queue.offer(45);System.out.println(queue.poll());//12System.out.println(queue.peek());//23}

2.3.2 数组 模拟实现 队列(循环队列)

(1)LC622.设计循环队列

在这里插入图片描述

(2)图解

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(3)Java 代码实现

  • 方法2:牺牲一个空间 实现循环队列
package queuedemo;// 方法2:牺牲一个空间 实现循环队列
public class MyCircularQueue {public int[] elem;public int front;public int rear;public MyCircularQueue(int k) {elem = new int[k+1];}public boolean enQueue(int value) {//入队列 操作if(isFull()){return false;}elem[rear] = value;rear = (rear + 1) % elem.length;return true;}public boolean deQueue() {//出队列if(isEmpty()){return false;}front = (front + 1) % elem.length;return true;}public int Front() {//获取队首元素if(isEmpty()){return -1;}return elem[front];}public int Rear() {//获取队尾元素if(isEmpty()){return -1;}//处理0下标位置int index = rear == 0 ? elem.length - 1: rear - 1;return elem[index];}public boolean isEmpty() {return front == rear;}public boolean isFull() {if((rear + 1) % elem.length == front){return true;}return false;}
}
  • 方法3:使用标记 实现循环队列
package queuedemo;//方法3:使用标记 实现循环队列
public class MyCircularQueue2 {public int[] elem;public int front;public int rear;public boolean isFull;//标记public MyCircularQueue2(int k) {elem = new int[k];isFull = false;}public boolean enQueue(int value) {//入队列 操作if(isFull()){return false;}elem[rear] = value;rear = (rear + 1) % elem.length;if(rear == front){isFull = true;}return true;}public boolean deQueue() {//出队列if(isEmpty()){return false;}front = (front + 1) % elem.length;isFull = false;return true;}public int Front() {//获取队首元素if(isEmpty()){return -1;}return elem[front];}public int Rear() {//获取队尾元素if(isEmpty()){return -1;}//处理0下标位置int index = (rear - 1 + elem.length) % elem.length;return elem[index];}public boolean isEmpty() {return !isFull && front == rear;}public boolean isFull() {return isFull;}
}

三、双端队列

3.1 双端队列的使用

  • 双端队列(deque)是指允许两端都可以进行入队和出队操作的队列。
  • 元素可以从队头出队和入队,也可以从队尾出队和入队。
  • Deque是⼀个接口,使用时必须创建LinkedList的对象。

在这里插入图片描述

package queuedemo;import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;public class Test {public static void main(String[] args) {Deque<Integer> dequeue = new LinkedList<>();dequeue.offerFirst(12);dequeue.pollLast();Deque<Integer> stack = new LinkedList<>();stack.push(10);//数组实现的双端队列Deque<Integer> stack2 = new ArrayDeque<>();//双端队列的线性实现Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现}

3.2 双端队列 相关OJ题

3.2.1 用队列实现栈

(1) LC225.用队列实现栈

在这里插入图片描述

(2)图解

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(3)Java 代码实现

package queuedemo_queue_eg;
//LC225.用队列实现栈
import java.util.LinkedList;
import java.util.Queue;public class MyStack {//首先 要有2个普通队列private Queue<Integer> qu1;private Queue<Integer> qu2;public MyStack() {qu1 = new LinkedList<>();qu2 = new LinkedList<>();}//模拟入栈public void push(int x) {if(empty()){//如果2个队列都为空,放到qu1qu1.offer(x);return;}//有1个队列不为空if(!qu1.isEmpty()){//如果qu1不为空,放到qu1qu1.offer(x);}else{qu2.offer(x);}}//模拟出栈public int pop() {if(empty()){//栈本身是空的return -1;}if(!qu1.isEmpty()){//如果qu1不为空,放到qu1int size = qu1.size();while(size -1 != 0){qu2.offer(qu1.poll());size--;}return qu1.poll();}else{int size = qu2.size();while(size -1 != 0){qu1.offer(qu2.poll());size--;}return qu2.poll();}}public int top() {if(empty()){//栈本身是空的return -1;}if(!qu1.isEmpty()){int size = qu1.size();int tmp = -1;while(size != 0){tmp = qu1.poll();qu2.offer(tmp);size--;}return tmp;}else{int size = qu2.size();int tmp = -1;while(size != 0){tmp = qu2.poll();qu1.offer(tmp);size--;}return tmp;}}//2个队列都是空 说明模拟的栈是空的public boolean empty() {return qu1.isEmpty() && qu2.isEmpty();}
}

3.3.2 用栈实现队列

(1)LC232.用栈实现队列

在这里插入图片描述

(2)图解
在这里插入图片描述
在这里插入图片描述

(3)Java 代码实现

package queuedemo_queue_eg;
//LC232.用栈实现队列
import java.util.Stack;public class MyQueue {//至少需要2个栈private Stack<Integer> s1;private Stack<Integer> s2;public MyQueue() {s1 = new Stack<>();s2 = new Stack<>();}public void push(int x) {s1.push(x);}public int pop() {if(empty()) {//模拟的队列是空的return -1;}if(s2.isEmpty()) {//把s1这个栈 当中的所有数据 倒过来while(!s1.isEmpty()) {s2.push(s1.pop());}}return s2.pop();}public int peek(){if (empty()) {return -1;}if (s2.isEmpty()) {while (!s1.isEmpty()) {s2.push(s1.pop());}}return s2.peek();}public boolean empty() {return s1.isEmpty() && s2.isEmpty();}
}
http://www.xdnf.cn/news/8241.html

相关文章:

  • 微软全新开源的Agentic Web网络项目:NLWeb,到底是什么 ?
  • 鸿蒙App开发学习路径
  • JAVA|后端编码规范
  • 仿腾讯会议——视频发送接收
  • 计算机发展史
  • 从零基础到最佳实践:Vue.js 系列(7/10):《常用内置 API 与插件》
  • scratch课后一练--事件模块
  • Linux系统编程 | IPC对象---消息队列
  • DeepSeek:开启IT领域人效管理新时代
  • Java-根据路径获取JSON字符串的value值
  • zabbix 常见问题
  • 深入解析JVM垃圾回收器:原理、实践与调优指南
  • 实用重复文件批量处理工具
  • 关于SQL SERVER中round函数的用法和示例
  • 一台机器怎么部署k8s集群
  • React-fiber架构
  • Python可视化设计原则
  • 【424. 替换后的最长重复字符】
  • docker-compose常用命令介绍
  • 已经 上线 Vue 项目 国际化 i18n 中译英
  • OpenCV 图像对象的创建与赋值
  • Apollo10.0学习——planning模块(9)之参数详解一
  • Vscode +Keil Assistant编译报错处理
  • C++ -- vector
  • 系统性能分析基本概念(5) : 何时开始性能分析
  • 【语法】C++的map/set
  • 平安健康2025年一季度深耕医养,科技赋能见成效
  • Android Service与BroadcastReceiver深度解析:从零到一的实现与优化
  • python实现web请求
  • 力扣小题, 力扣113.路径总和II力扣.111二叉树的最小深度 力扣.221最大正方形力扣5.最长回文子串更加优秀的算法:中心扩展算法