数据结构初阶(栈和队列)

news2025/1/20 20:02:00

文章目录

  • 一、栈
    • 1.1 什么是栈
    • 1.2 栈的使用
      • (1)底层代码
      • (2)方法
      • (3)栈的应用
  • 二、队列
    • 2.1 什么是队列
    • 2.2 队列的使用
      • (1)底层代码的实现
      • (2)队列的使用
    • 2.3 双端队列
    • 2.4 练习

一、栈

1.1 什么是栈

  • 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
    • 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则
  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
  • 出栈:栈的删除操作叫做出栈。出数据在栈顶。
  • JVM虚拟机栈 VS 栈
    • JVM虚拟机栈:系统的一块内存
    • 栈:数据结构

在这里插入图片描述

1.2 栈的使用

(1)底层代码

import java.util.Arrays;

public class MyStack {
    private int[] elem;     //stack的底层是数组
    private int usedSize;

    public MyStack() {
        this.elem = new int[5];
    }

    //压栈
    public void push(int val) {
        if(isFull()) {      //判断里面的空间是否为满的情况
           elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize] = val;
        usedSize++;
    }

    public boolean isFull() {
        return usedSize == elem.length;
    }


    //出栈
    public int pop() {
        //1、判断栈不为空
        if(empty()) {
            //抛出异常!!
            throw new StackEmptyException("栈为空!");
        }
        //2、开始删除
        return elem[--usedSize];     //elem[useSize]的数字没有被删掉,后续如果有push的话,会把值该赋给掉
    }

    //获取栈顶元素
    public int peek() {
        //1、判断栈不为空
        if(empty()) {
            //抛出异常!!
            throw new StackEmptyException("栈为空!");
        }
        //2、开始删除
        return elem[usedSize-1];
    }

    public boolean empty() {
        return usedSize == 0;
    }
}

(2)方法

方法功能
Stack()构造一个空的栈
E push(E e)将e入栈,并返回e
E pop()将栈顶元素出栈并返回
E peek()获取栈顶元素
int size()获取栈中有效元素个数
boolean empty()检测栈是否为空

(3)栈的应用

  • 改变元素的序列
    • 进栈过程中可以出栈 和 依次入栈,然后再依次出栈 对元素出栈的顺序的改变
  • 将递归转化为循环
//递归方式
public void show(ListNode head) {
    if(head == null) {
        return;
    }
    if(head.next == null) {
        System.out.println(head.val);
        return;
    }
    show3(head.next);
    System.out.println(head.val);
}

//循环方式
public void show2() {
    Stack<ListNode> stack = new Stack<>();
    ListNode cur = head;
    while (cur != null) {
        stack.push(cur);
        cur = cur.next;
    }
    //依次出栈
    while (!stack.empty()) {
        ListNode tmp = stack.pop();
        System.out.println(tmp.val);
    }
}
  • 逆波兰表达式求值
class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList<Integer>();
        int n = tokens.length;
        for (int i = 0; i < n; i++) {
            String token = tokens[i];
            if (isNumber(token)) {
                stack.push(Integer.parseInt(token));
            } else {
                int num2 = stack.pop();
                int num1 = stack.pop();
                switch (token) {
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);
                        break;
                    case "*":
                        stack.push(num1 * num2);
                        break;
                    case "/":
                        stack.push(num1 / num2);
                        break;
                    default:
                }
            }
        }
        return stack.pop();
    }

public boolean isNumber(String token) {
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }
}
  • 有效的括号
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 {
                if (stack.empty()){
                    return false;
                }else {
                    char tmp = stack.peek();
                    if (ch == ')' && tmp == '(' || ch == '}' && tmp == '{' 
                        || ch == ']' && tmp == '[') {
                            stack.pop();
                        }else {
                            return false;
                        }
                }
            }
        }

        if (!stack.empty()) {
            return false;
        }

        return true;
    }
}
  • 栈的压入、弹出序列
import java.util.Stack;

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
      Stack<Integer> stack = new Stack<>();
        int j = 0;
        for (int i = 0; i < pushA.length; i++) {
            stack.push(pushA[i]);
            while (!stack.empty() && j < popA.length
                    && stack.peek() == popA[j]) {
                stack.pop();
                j++;
            }
        }
        return stack.empty();
    }
}
  • 最小栈
class MinStack {
private Stack<Integer> stack ;
    private 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 {
            if(val <= minStack.peek()) {
                minStack.push(val);
            }
        }
    }
    
    public void pop() {
        //栈为空 则不能进行弹出元素
        if(stack.empty()) {
            return;
        }
        int val = stack.pop();
        if(val == minStack.peek()) {
            minStack.pop();
        }
    }
    //获取栈顶元素 和 最小栈没有关系
    public int top() {
        if(stack.empty()) {
            return -1;
        }
        return stack.peek();
    }

    //获取元素 不是删除元素
    public int getMin() {
        return minStack.peek();
    }

}

栈、虚拟机栈、栈帧有什么区别呢?

栈是数据结构
虚拟机栈是内存
栈帧是调用方法是,在虚拟机栈开辟的一个内存

二、队列

2.1 什么是队列

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
抽象场景:食堂排队买饭

在这里插入图片描述

2.2 队列的使用

(1)底层代码的实现

在Java中,Queue是个接口,底层是通过链表实现的
队列可以通过数组和链表实现

  • 如果是双向链表,那么入队和出队,均可以达到O(1),不管从哪边进
  • 如果是单链表,并且记录了最后一个节点的位置的情况下,我们可以采用从尾入队,从头出队的方式,都是O(1)。如果是从头进,从尾出,那么出队的复杂度为O(n)

(2)队列的使用

❤️创建
Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

Queue<Integer> q = new LinkedList<>();

❤️方法

方法功能
boolean offer(E e)入队列
E poll()出队列
peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空

❤️队列的模拟实现

  • 顺序结构
    双向链表
public class Queue {
    // 双向链表节点
    public static class ListNode{
        ListNode next;
        ListNode prev;
        int value;
        ListNode(int value){
            this.value = value;
        }
    }
    ListNode first; // 队头
    ListNode last; // 队尾
    int size = 0;
    // 入队列---向双向链表位置插入新节点
    public void offer(int e){
        ListNode newNode = new ListNode(e);
        if(first == null){
            first = newNode;
            // last = newNode;
        }else{
            last.next = newNode;
            newNode.prev = last;
            // last = newNode;
        }
        last = newNode;
        size++;
    }
    // 出队列---将双向链表第一个节点删除掉
    public int poll(){
        // 1. 队列为空
        // 2. 队列中只有一个元素----链表中只有一个节点---直接删除
        // 3. 队列中有多个元素---链表中有多个节点----将第一个节点删除
        int value = 0;
        if(first == null){
            return -1;
        }else if(first == last){
            last = null;
            first = null;
        }else{
            value = first.value;
            first = first.next;
            first.prev.next = null;
            first.prev = null;
        }
        --size;
        return value;
    }
    // 获取队头元素---获取链表中第一个节点的值域
    public int peek(){
        if(first == null){
            return -1;
        }
        return first.value;
    }
    public int size() {
        return size;
    }
    public boolean isEmpty(){
        return first == null;
    }
}

单向链表

public class MyQueue {
    static class ListNode {
        public int val;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }
    public ListNode head;
    public ListNode last;

    private int usedSize;

    public void offer(int val) {
        ListNode node = new ListNode(val);
        if(head == null) {
            head = node;
            last = node;
        }else {
            last.next = node;
            last = last.next;
        }
        usedSize++;
    }

    public int getUsedSize() {
        return usedSize;
    }

    public int poll() {
        if(head == null) {
            return -1;
        }
        int val = -1;
        if(head.next == null) {
            val = head.val;
            head = null;
            last = null;
            return val;
        }
        val = head.val;
        head = head.next;
        usedSize--;
        return val;
    }

    public int peek() {
        if(head == null) {
            return -1;
        }
        return head.val;
    }

}
  • 循环结构

环形队列通常使用数组实现

class MyCircularQueue {
    private int[] elem;
    private int front;//队头下标
    private int rear;//队尾下标

    public MyCircularQueue(int k) {
        this.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;
        }
        int index = (rear == 0) ? elem.length-1 : rear-1;
        return elem[index];
    }
    
    public boolean isEmpty() {
        return rear == front;
    }
    
    public boolean isFull() {
        return (rear+1) % elem.length == front;
    }
}

解析

  • 数组下标循环
    下标最后再往后:(rear + 1) % elem.length;
    下标最前再往前:(front + 1) % elem.length;
  • 如何区分空和满
    满: 通过添加 useSize 属性记录,当useSIze == len 的时候为满
    保留一个位置,用这个位置来表示满
    使用标记
    空: front == rear 时为空

2.3 双端队列

  • 双端队列(deque)是指允许两端都可以进行入队和出队操作的队列
  • deque 是 “double ended queue” 的简称。
  • 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队

在这里插入图片描述

  • Deque是一个接口,使用时必须创建相关的对象。
Deque<Integer> stack = new ArrayDeque<>();  双端队列的线性实现,底层是数组
Deque<Integer> queue = new LinkedList<>();  双端队列的链式实现,底层是链表

2.4 练习

一、用队列实现栈

import java.util.LinkedList;
import java.util.Queue;

class MyStack {

    private Queue<Integer> qu1;
    private Queue<Integer> qu2;

    public MyStack() {
        qu1 = new LinkedList<>();
        qu2 = new LinkedList<>();
    }
    
    public void push(int x) {
        //放到不为空的队列
        if(!qu1.isEmpty()) {
            qu1.offer(x);
        }else if(!qu2.isEmpty()) {
            qu2.offer(x);
        }else {
            //如果都是空的 放到第一个
            qu1.offer(x);
        }
    }
    
    public int pop() {
        //两个队列都是空的: 栈为空
        if(empty()) {
            return -1;
        }
        if(!qu1.isEmpty()) {
            int currentSize = qu1.size();
            for (int i = 0; i < currentSize-1; i++) {
                int x = qu1.poll();
                qu2.offer(x);
            }
            return qu1.poll();//最后一个数据返回
        }
        if(!qu2.isEmpty()) {
            int currentSize = qu2.size();
            for (int i = 0; i < currentSize-1; i++) {
                int x = qu2.poll();
                qu1.offer(x);
            }
            return qu2.poll();//最后一个数据返回
        }
        return -1;
    }
    //peek方法
    public int top() {
        if(empty()) {
            return -1;
        }
        if(!qu1.isEmpty()) {
            int currentSize = qu1.size();
            int x = -1;
            for (int i = 0; i < currentSize; i++) {
                x = qu1.poll();
                qu2.offer(x);
            }
            return x;//最后一个数据返回
        }
        if(!qu2.isEmpty()) {
            int currentSize = qu2.size();
            int x = -1;
            for (int i = 0; i < currentSize; i++) {
                x = qu2.poll();
                qu1.offer(x);
            }
            return x;//最后一个数据返回
        }
        return -1;
    }
    
    public boolean empty() {
        return qu1.isEmpty() && qu2.isEmpty();
    }
}

二、用栈实现队列

class MyQueue {

    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(!s2.empty()) {
            return s2.pop();
        }else {
            while(!s1.empty()) {
                int val = s1.pop();
                s2.push(val);
            }
            return s2.pop();
        }
    }
    
    public int peek() {
        if(!s2.empty()) {
            return s2.peek();
        }else {
            while(!s1.empty()) {
                int val = s1.pop();
                s2.push(val);
            }
            return s2.peek();
        }
    }
    
    public boolean empty() {
        return s1.empty() && s2.empty();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/469158.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

字节跳动发放年终奖,远超预期~

最近一段时间&#xff0c;国内互联网大厂接连公布年终奖情况&#xff0c;整个后厂村都洋溢在春节般的喜庆气氛里。 虽然由于各种各样的顾虑&#xff08;主要是人员流失问题&#xff09;&#xff0c;大部分公司都将年终奖发放时间调整到了年中&#xff0c;但好饭不怕晚&#xf…

06_Callable接口

Thread类、Runnable接口使得多线程编程简单直接。 但Thread类和Runnable接口都不允许声明检查型异常&#xff0c;也不能定义返回值。没有返回值这点稍微有点麻烦。不能声明抛出检查型异常则更麻烦一些。 public void run()方法规范意味着你必须捕获并处理检查型异常。即使你小…

磁盘被格式化了能找到资料吗?资料找到的具体方法

磁盘格式化了还能找到&#xff0c;用读卡器接到电脑&#xff0c;就可以作为可移动磁盘来找到资料。下面讲下磁盘被格式化了能找到资料吗&#xff1f;资料找到的具体方法 磁盘被格式化了能找到资料吗&#xff1f;资料找到的具体方法 工具/软件&#xff1a;sayRecy 步骤1&#xf…

博世中国创新软件开发中心 BCSC

Bosch China Innovation and Software Development Campus 博世中国创新软件开发中心 BCSC 擎软件&#xff01; 拓未来&#xff01;Bosch China Innovation and Software Development Campus——IntroductionBOSCH——Our AdvantagesBOSCH——Hotly recruited positions Welcom…

wifi芯片行业信息汇总

1、Wifi概述 Wi-Fi这个术语被人们普遍误以为是指无线保真&#xff08;Wireless Fidelity&#xff09;&#xff0c;并且即便是Wi-Fi联盟本身也经常在新闻稿和文件中使用“Wireless Fidelity”这个词。 主要版本&#xff1a; 随着最新的 802.11 ax 标准发布&#xff0c;新的 W…

『Linux』第九讲:Linux多线程详解(一)_ 线程概念 | 线程控制之线程创建 | 虚拟地址到物理地址的转换

「前言」文章是关于Linux多线程方面的知识&#xff0c;讲解会比较细&#xff0c;下面开始&#xff01; 「归属专栏」Linux系统编程 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」 「每篇一句」 我与春风皆过客&#xff0c; 你携秋水揽星河。 ——网络…

高精度延时

在使用STM32的时候可以使用SYSTICK来实现高精度延时。 I.MX6U没有SYSTICK定时器&#xff0c;但是有GPT定时器来实现高精度延时。 GPT&#xff08;General Purpose Timer&#xff09; GPT定时器是一个32位向上定时器&#xff08;也就是从0x00000000开始向上递增计数&#xff0…

C#【必备技能篇】制作NuGet程序包,并发布到NuGet官网

文章目录 一、准备工作&#xff1a;在NuGet上创建并获取API Keys1、首先需要登录&#xff0c;直接用微软账户登录即可2、点击右上角菜单API Keys&#xff0c;创建Key3、填写信息并创建4、复制API Key 二、制作一个简单的dll三、创建发布文件夹四、上传NuGet程序包并发布1、方法…

java 上传压缩包遍历内容

项目环境&#xff1a;Spring Boot 2.0.6.RELEASE <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.6.RELEASE</version><relativePath/> <!-- loo…

Linux安装部署 seata server 1.4.2

Linux安装部署 seata server 1.4.2 下载安装包上传至服务器 1.首先从GitHub拉取seata压缩包 https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip 下载到本地后上传至服务器 或使用命令拉取 wget https://github.com/seata/seata/release…

数据库系统-故障恢复

文章目录 一、数据库故障恢复思路1.1 故障类型 影响1.1.1 DMBS运行方式1.1.2 故障类型 1.2 故障恢复1.2.1 数据库故障恢复1.2.2 事务故障恢复1.2.3 系统故障恢复1.2.4 介质故障恢复 二、运行日志及其检查点2.1 DB Log2.1.1 事务的操作2.1.2 缓冲区处理策略 2.2 日志记录 三、三…

Binder与 四大组件工作原理 Service、BroadCastReceiver、ContentProvider

Service 工作原理 Service有两套流程&#xff0c;一套是启动流程&#xff0c;另一套是绑定流程。我们做App开发的同学都应该知道 1&#xff09;在新进程启动Service 我们先看Service启动过程&#xff0c;假设要启动的Service是在一个新的进程中&#xff0c;分为5个阶段&#…

农业信息化有哪些SCI期刊推荐? - 易智编译EaseEditing

以下是一些农业信息化领域的SCI期刊推荐&#xff1a; Computers and Electronics in Agriculture&#xff1a; 该期刊涵盖了计算机和电子技术在农业生产和食品生产中的应用&#xff0c;如精准农业、农业机械自动化、智能传感器、农业模型和模拟等方面的研究。 影响因子为4.5…

离散数学组合计数

基本的组合计数公式 主要内容 加法法则和乘法法则排列与组合二项式定理与组合恒等式多项式定理 加法法则和乘法法则 加法法则乘法法则分类处理与分步处理 问题1&#xff1a;某旅游团从南京到上海&#xff0c;可以乘骑车&#xff0c;也可以乘火车&#xff0c;假定骑车每日有…

PHP源码的加密方法分享

关于PHP PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的WEB站点。 PHP是能让你生成动态网页的工具之一。PHP网页文件被当作一般HTML网页文件来处理并且在编辑时你可以用编辑HTML的常规方法编写PHP。 有些时候,我们需要对P…

流程图拖拽视觉编程-流程编辑器

目录 一、简介 二、流程编辑器-视图实现 三、参考资料 一、简介 前期文章&#xff1a; 流程图拖拽视觉编程--概述_Jason~shen的博客-CSDN博客 本期内容&#xff1a; 本期将介绍流程编辑器模块的实现方法&#xff0c;效果图如下所示。该模块基于QT Graphics/View实现&…

AVL 树(自平衡二叉搜索树) 介绍

AVL 树&#xff08;自平衡二叉搜索树) 介绍 前言 在介绍二叉搜索树的章节中提到&#xff0c;二叉搜索树可能退化为线性链表&#xff0c;失去作为二叉树的各种优势。那么过程中需要维持二叉树的形式&#xff0c;同时左右子树的深度差异可控&#xff0c;如果能实现这两个条件&a…

要强的董经贵,与跳不出两轮电动车品质“陷阱”的雅迪

文|智能相对论 作者|陈明涛 “雅迪缺乏一个明确的战略定位&#xff0c;整个企业都没有方向&#xff0c;直接导致雅迪在前期竞争中失去先机。” “行业主要竞争对手更擅长销售和价格战&#xff0c;雅迪一直疲于应付、资源消耗大&#xff0c;结果也不理想。” “团队和经销商…

weblogic 反序列化 (CVE-2020-2551)漏洞复现(vulfocus)

漏洞简介 weblogic 反序列化 &#xff08;CVE-2020-2551&#xff09;漏洞是基于IIOP协议执行远程代码进行利用。 启动服务 http://192.168.5.128:10710/console/login/LoginForm.jsp 首先打开此连接&#xff0c;初始化weblogic服务。 准备工具 exp-自己写一个即可. javac…

企业编码生成系统--Python基础项目(4)

1. 成品展示&#x1f697;&#x1f680;&#x1f6eb; 运行&#x1f680;&#x1f680;&#x1f680;&#xff1a;1.在PyCharm中运行《企业编码生成系统》即可进入如图1所示的系统主界面。2.在该界面中可以选择要使用功能对应的菜单进行不同的操作。3.在选择功能菜单时&#x…