数据结构线性表-栈和队列的实现

1. 栈(Stack)

1.1 概念

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。

1.2 栈的使用

从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的Vector是线程安全;Vector类,是线程安全的动态数组,但是性能较差 , 现在已经不是很常用了 , 可以说已经过时了。

常用方法

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

public static void main(String[] args) {
Stack<Integer> s = new Stack();
s.push(1);
s.push(2);
s.push(3);
s.push(4);
System.out.println(s.size()); // 获取栈中有效元素个数---> 4
System.out.println(s.peek()); // 获取栈顶元素---> 4
s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
if(s.empty()){
System.out.println("栈空");
}else{
System.out.println(s.size());
 }
}

1.3 栈的模拟实现

从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安 全的。

代码实现

1. 构造方法

class MyStack{

    private int[] arr;

    // size 记录栈中元素个数
    private int size;

    public MyStack(){
        // 调用无参构造方法 默认最大容量12
        this(12);
    }

    public MyStack(int MaxSize){
        this.arr = new int[MaxSize];
    }
}

2. 入栈(push)

// 入栈
    public int push(int value){
        if(this.size == arr.length){
            // 栈满 ,需要扩容

            int[] copyArr;
            // 复制arr 数组并扩容一倍
            copyArr = Arrays.copyOf(arr,2 * arr.length);
            arr = copyArr;

        }
        //将元素添加到size位置
        this.arr[size] = value;
        // 元素个数加一
        this.size++;
        // 返回添加元素
        return value;
    }

3. 出栈(pop)

// 出栈
    public int pop(){
        if(this.size == 0){
            //没有元素
            //抛出运行时异常,此处也可以自定义异常
            throw new RuntimeException("栈中没有元素,不能出栈....");
        }
        // 获得栈顶元素
        int value = this.arr[size - 1];
        // size - 1 之后, 下一次插入时会覆盖原数据,利用覆盖替代删除
        this.size--;
        return value;
    }

4.获取栈顶元素(peek)

// 获取栈顶元素
    public int peek(){
        if(this.size == 0){
            //没有元素
            //抛出运行时异常,此处也可以自定义异常
            throw new RuntimeException("栈中没有元素,不能出栈....");
        }
        return this.arr[this.size - 1];
    }

5.获取元素个数(getSize)

//获取元素个数
    public int getSize(){
        return this.size;
    }

6.判断栈是否为空(isEmpty)

//判断元素是否为空
    public boolean isEmpty(){
        return this.size == 0;
    }

完整代码

import java.util.Arrays;

public class MyStack{

    private int[] arr;

    // size 记录栈中元素个数
    private int size;

    public MyStack(){
        // 调用无参构造方法 默认最大容量12
        this(12);
    }

    public MyStack(int MaxSize){
        this.arr = new int[MaxSize];
    }

    // 入栈
    public int push(int value){
        if(this.size == arr.length){
            // 栈满 ,需要扩容

            int[] copyArr;
            // 复制arr 数组并扩容一倍
            copyArr = Arrays.copyOf(arr,2 * arr.length);
            arr = copyArr;

        }
        //将元素添加到size位置
        this.arr[size] = value;
        // 元素个数加一
        this.size++;
        // 返回添加元素
        return value;
    }

    // 出栈
    public int pop(){
        if(isEmpty()){
            //没有元素
            //抛出运行时异常,此处也可以自定义异常
            throw new RuntimeException("栈中没有元素,不能出栈....");
        }
        // 获得栈顶元素
        int value = this.arr[size - 1];
        // size - 1 之后, 下一次插入时会覆盖原数据,利用覆盖替代删除
        this.size--;
        return value;
    }

    // 获取栈顶元素
    public int peek(){
        if(isEmpty()){
            //没有元素
            //抛出运行时异常,此处也可以自定义异常
            throw new RuntimeException("栈中没有元素,不能出栈....");
        }
        return this.arr[this.size - 1];
    }

    //获取元素个数
    public int getSize(){
        return this.size;
    }

    //判断元素是否为空
    public boolean isEmpty(){
        return this.size == 0;
    }
}

1.4 栈的应用场景

1. 改变元素的序列

1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是( )

     A: 1,4,3,2                      B: 2,3,4,1                 C: 3,1,4,2                            D: 3,4,2,1

根据栈先进后出的性质,结合题目中进栈的过程中也可以出栈,如A选项:1进1出,2进3进4进,4出3出2出即符合题意,同理C选项,1进2进3进3出之后不可能直接出1,故C选项不可能实现。

2.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺 序是( )。

A: 12345ABCDE          B: EDCBA54321         C: ABCDE12345           D: 54321EDCBA

先进后出,依次入栈,依次出栈,故B选项合理

2. 将递归转化为循环

递归实现逆序打印

 public void display(ListNode head){
        if(head == null){
            return;
        }
     //直到链表末尾,再归回去
        if(head.next == null){
            System.out.println(head.val+" ");
            return;
        }
        display(head.next);
        System.out.println(head.val+" ");
}

使用栈实现逆序打印

public void display(ListNode head){
        if(head == null){
            return;
         }
        Stack<ListNode> stack  = new Stack<>();
        ListNode cur = head;
         while(cur!= null){
              stack.push(cur);
              cur = cur.next;
             }
        while(!stack.empty()){
            ListNode ret =   stack.pop();
            System.out.println(ret.val+" ");
        }
    }

2. 队列(Queue)

2.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)

入队列:进行插入操作的一端称为队尾(Tail/Rear)

出队列:进行删除操作的一端称为队头 (Head/Front)

2.2 队列的使用

在Java中,Queue是个接口,底层是通过链表实现的。


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

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

public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5); // 从队尾入队列
System.out.println(q.size());
System.out.println(q.peek()); // 获取队头元素
q.poll();
System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
if(q.isEmpty()){
System.out.println("队列空");
}else{
System.out.println(q.size());
}
}

2.3 队列模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构和链式结构 。

 
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 null;
        }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 null;
        }
        return first.value;
    }
    public int size() {
        return size;
    }
    public boolean isEmpty(){
        return first == null;
    }
}

2.4 循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列通常使用数组实现。

数组下标循环的小技巧 

1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length

2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset)%array.length

如何区分空与满

1. 通过添加 size 属性记录
2. 保留一个位置
3. 使用标记

public class CircularQueue {
    private int front;
    private int rear;
    private int[] circle;

    public CircularQueue(int k) {
        //浪费掉一个存储空间
        circle = new int[k+1];
    }

	//入队列
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        circle[rear] = value;
        //因为是循环队列,不能写++,要以取模的方式
        rear = (rear + 1) % circle.length;
        return true;
    }
	
	//出队列
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }
        front = (front + 1) % circle.length;
        return true;
    }
	
	//返回队头元素
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return circle[front];
    }

	//返回队尾元素
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        return circle[(rear - 1 + circle.length) % circle.length];
    }

    public boolean isEmpty() {
        return rear == front;
    }

    public boolean isFull() {
        return ((rear + 1) % circle.length) == front;
    }

    
}

3.  双端队列 (Deque)

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

Deque是一个接口,使用时必须创建LinkedList的对象。

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

4. 栈和队列的互相实现

用栈实现队列:

class MyQueue {
    public Stack<Integer> stack1;
    public Stack<Integer> stack2;
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
 
    public void push(int x) {
        stack1.push(x);
    }
 
    public int pop() {
        if (stack2.isEmpty()) {
in2out();
        }
        return stack2.pop();
    }
 
    public int peek() {
        if (stack2.isEmpty()){
in2out();
        }
        return stack2.peek();
    }
 
    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }
    private void in2out() {
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
    }
}

用队列实现栈:

class MyStack {
    Queue<Integer> queue1;
    Queue<Integer> queue2;
 
    public MyStack() {
        queue1 = new LinkedList<Integer>();
        queue2 = new LinkedList<Integer>();
    }
    
    public void push(int x) {
        queue2.offer(x);
        while (!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }
        Queue<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
    }
    
    public int pop() {
        return queue1.poll();
    }
    
    public int top() {
        return queue1.peek();
    }
    
    public boolean empty() {
        return queue1.isEmpty();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年最新prometheus + grafana搭建和使用

一、安装prometheus 1.1 安装 prometheus官网下载地址 sudo -i mkdir -p /opt/prometheus #移动解压后的文件名到/opt/,并改名prometheus mv prometheus-2.45 /opt/prometheus/ #创建一个专门的prometheus用户&#xff1a; -M 不创建家目录&#xff0c; -s 不让登录 useradd…

2.6 A 的 LU 分解

一、A LU 线性代数很多关键的概念实际上就是矩阵的分解&#xff08;factorization&#xff09;。原始矩阵 A A A 变成两个或三个特殊矩阵的乘积。第一个分解&#xff0c;实际上也是最重要的分解&#xff0c;来自消元法。因子 L L L 和 U U U 都是三角形矩阵&#xff0c;分…

用 @icestack/ui 构建适配微信小程序的 daisyui

用 icestack/ui 构建适配微信小程序的 daisyui 用 icestack/ui 构建适配微信小程序的 daisyui 前言思考与实践如何使用? 安装初始化配置构建样式 作为 tailwindcss plugin 来使用 安装配置智能提示 在微信小程序里使用 安装注册构建 演示小程序收到启发的项目参考地址 前言…

在pom.xml中添加maven依赖,但是类里面import导入的时候报错

问题&#xff1a; Error:(27, 8) java: 类TestKuDo是公共的, 应在名为 TestKuDo.java 的文件中声明 Error:(7, 23) java: 程序包org.apache.kudu不存在 Error:(8, 23) java: 程序包org.apache.kudu不存在 Error:(9, 23) java: 程序包org.apache.kudu不存在 Error:(10, 30) jav…

网工内推 | 外企、合资公司急招网工,国内外旅游,健身年卡

01 深圳市耐施菲信息科技有限公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责项目的计划、实施、过程管控、项目验收等工作&#xff1b; 2、负责大型项目设备实施、安装调试等售后维护工作&#xff1b; 3、分析、设计网络拓扑结构、配置H3C、华为等交换机…

Unity3D中实现箭头指向目标点的效果(shader)

系列文章目录 Unity工具 文章目录 系列文章目录前言一、效果如下二、制作步骤2-1、制作shader2-2、shader代码2-3、制作材质球2-4、新建Quad2-5、制作预制体2-6 、实现代码2-7、设置Quad到脚本2-8、路径设置如下 三、说明四、运行程序总结 前言 大家好&#xff0c;我是心疼你…

将 ONLYOFFICE 协作空间的公共房间嵌入到网页

在 ONLYOFFICE 协作空间2.0版本中&#xff0c;我们新增了公共房间&#xff0c;可与外部用户共享文件。公共房间可以集成到您的网站或单页应用程序 (SPA) 中&#xff0c;访问者无需下载或注册自己的协作空间帐户即可查看文档。我们在本文中介绍了分步指南。 什么是公共房间&…

【vtkWidgetRepresentation】第六期 vtkFinitePlaneRepresentation

很高兴在雪易的CSDN遇见你 &#xff0c;给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文分享VTK中的平面Plane表示方法&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会继续努力分享&#xff0c;一起进步&#xff01; …

为什么数据科学应用要使用Python作为实现工具

1.3 为什么要使用Python作为实现工具 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解1.3节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。内容涵盖数据科学应用的全流程&#xff0…

【夯实技术基本功】「底层技术原理体系」全方位带你认识和透彻领悟正则表达式(Regular Expression)的开发手册(正则符号深入解析 )

[TOC](【夯实技术基本功】「底层技术原理体系」全方位带你认识和透彻领悟正则表达式(Regular Expression)的开发手册&#xff08;正则符号深入解析 &#xff09;) 借鉴官网的速查表 基础匹配符号 反向匹配表 各种操作符的运算优先级 承接上文&#xff0c;在正则表达式中&…

K8s 入门指南(一):单节点集群环境搭建

前言 官方文档&#xff1a;Kubernetes 文档 | Kubernetes 系统配置 CentOS 7.9&#xff08;2 核 2 G&#xff09; 本文为 k8s 入门指南专栏&#xff0c;将会使用 kubeadm 搭建单节点 k8s 集群&#xff0c;详细讲解环境搭建部署的细节&#xff0c;专栏后面章节会以实战代码介绍…

leetcode 面试题 02.02. 返回倒数第k个节点

提建议就是&#xff0c;有些题还是有联系的&#xff0c;建议就收看完 876.链表的中间节点&#xff08;http://t.csdnimg.cn/7axLa&#xff09;&#xff0c;再将这一题联系起来 面试题 02.02. 返回倒数第k个节点 题目&#xff1a; 实现一种算法&#xff0c;找出单向链表中倒数第…

geolife笔记:整理处理单条轨迹

以 数据集笔记 geolife &#xff08;操作篇&#xff09;_geolife数据集-CSDN博客 轨迹为例 1 读取数据 import pandas as pd data pd.read_csv(Geolife Trajectories 1.3/Data//000/Trajectory/20081023025304.plt,headerNone, skiprows6,names[Latitude, Longitude, Not_Im…

Volumetric Lights 2 HDRP

高清晰度渲染管道,包括先进的新功能,如半透明阴影图和直接灯光投射加上许多改进。 插件是一个快速,灵活和伟大的前瞻性光散射解决方案的高清晰度渲染管道。只需点击几下,即可改善场景中的照明视觉效果。 兼容: 点光源 聚光灯 碟形灯 矩形灯 通过覆盖摄像机周围大面积区域的…

oracle 下载java之前版本

登录oracle官网&#xff1a;Oracle | Cloud Applications and Cloud Platform 点击resource 进入该页面 点击这个 出现之前版本

学习pytorch19 pytorch使用GPU训练

pytorch使用GPU进行训练 1. 数据 模型 损失函数调用cuda()2. 使用谷歌免费GPU gogle colab 需要创建谷歌账号登录使用, 网络能访问谷歌3. 执行4. 代码 B站土堆学习视频&#xff1a; https://www.bilibili.com/video/BV1hE411t7RN/?p30&spm_id_frompageDriver&vd_sourc…

机器学习算法(7)-朴素贝叶斯算法和K最近邻算法

一、说明 在在这篇文章中&#xff0c;我将解释两种机器学习算法&#xff0c;称为贝叶斯定理和 K 最近邻算法。贝叶斯定理以 18 世纪英国数学家托马斯贝叶斯的名字命名&#xff0c;是确定条件概率的数学公式。k 最近邻算法&#xff0c;也称为 KNN 或 k-NN&#xff0c;是一种非参…

【Pyqt】QObject::connect: Cannot queue arguments of type ‘QTextCursor‘

问题说明 文本框接收到新的数据 不会自动滚动&#xff0c;并提示警告 QObject::connect: Cannot queue arguments of type ‘QTextCursor’ (Make sure ‘QTextCursor’ is registered using qRegisterMetaType().) 原因 线程回来的槽函数里面 调用了ui的代码 我们不能通过线程…

测试文档---智力冲刺

文章目录 项目背景测试计划UI测试接口测试手工测试 测试总结 项目背景 项目描述&#xff1a;“智力冲刺”是一款网页小游戏&#xff0c;就像我们平时看到的网页游戏一样&#xff0c;前端页面负责展示游戏效果&#xff0c;后端服务器来实现游戏的逻辑。在我们的“智力冲刺”游戏…

【从零认识ECS云服务器 | 快速上线个人网站】三、对外发布网站

3.1 配置域名 用户是如何访问网站的呢&#xff1f; 用户在浏览器(IE、Chrome、FireFox等)上输入域名&#xff0c;如&#xff1a;http://www.aliyun.com &#xff1b; 浏览器自动调用DNS&#xff08;域名服务&#xff09;将域名解析为IP地址&#xff0c;如&#xff1a;123.123…