数据结构与算法:栈和队列的学习

news2025/1/20 14:53:09

1.栈

1.栈的定义

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

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

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

栈遵循的规则先进后出的原则。

在这里插入图片描述

在这里插入图片描述

2.栈的使用

在这里插入图片描述

import java.util.Stack;

public class Test {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(1);//压栈
        stack.push(2);
        stack.push(3);
        Integer a = stack.pop();//出栈
        System.out.println(a);
        Integer b = stack.peek();//查看栈顶的元素
        System.out.println(b);
        Integer b2 = stack.peek();
        System.out.println(b2);
        System.out.println(stack.size());//获取栈中有效的元素个数
        System.out.println(stack.isEmpty());//判断栈是否为空
    }
}

在这里插入图片描述

3.栈的模拟实现

Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表。

package Stack;
import java.util.Arrays;


public class EmptyException extends RuntimeException {

    public EmptyException() {

    }

    public EmptyException(String message) {
        super(message);
    }
}

public class MyStack {
    public int[] elem;
    public int usedSize;

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

    //压栈
    public void push(int val) {
        if(isFull()) {
            //扩容
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize] = val;
        usedSize++;
        //elem[usedSize++] = val;
    }

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

    //出栈
    public int pop() {
        if (isEmpty()) {
            throw new EmptyException("此时栈为空!");
        }
        int val = elem[usedSize-1];
        usedSize--;
        return val;
        //return elem[--useSize];
    }

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

    public int peek() {
        if (isEmpty()) {
            throw new EmptyException("此时栈为空!");
        }
        return elem[usedSize - 1];
    }
}

4.栈、虚拟机栈、栈帧的区别

虚拟机栈:具有特殊作用的一块内存空间。

              jvm为了对数据进行更好的管理,将内存按照不同的需求划分出来的一种结构

              栈区:线程私有的,栈区中存放的是函数调用相关的一些信息,主要是栈帧

                         当栈区中内存空间不足时,会抛出StackOverflowException

                         当中的元素(栈帧)是按照数据结构中的栈的特性来实现的

栈帧:一种结构,这种结构与函数调用相关的,内部:局部变量表、操作数栈…

       每个方法在运行时,jvm都会创建一个栈帧,然后将栈帧压入到虚拟机栈中

       当方法调用结束时,该方法对应的栈帧会从虚拟机栈中出栈

2.队列

1.队列的概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。

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

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

队列遵循的规则:先进先出

在这里插入图片描述

2.队列的使用

在这里插入图片描述
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

class 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());
        }
    }
}

3.队列的模拟实现

package Stack;

import java.util.Arrays;

public class MyStack {
    public int[] elem;
    public int usedSize;

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

    //压栈
    public void push(int val) {
        if(isFull()) {
            //扩容
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize] = val;
        usedSize++;
        //elem[usedSize++] = val;
    }

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

    //出栈
    public int pop() {
        if (isEmpty()) {
            throw new EmptyException("此时栈为空!");
        }
        int val = elem[usedSize-1];
        usedSize--;
        return val;
        //return elem[--useSize];
    }

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

    public int peek() {
        if (isEmpty()) {
            throw new EmptyException("此时栈为空!");
        }
        return elem[usedSize - 1];
    }
}

3.循环队列

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
在这里插入图片描述
如何让数组下标循环呢?
在这里插入图片描述

在这里插入图片描述
如何区分数组满没满??
在这里插入图片描述

  • 1.设置一个标记位
    设置初始标记: boolean flag=false
    当入队列时,让rear往后移动,让flag=true
    当出队列时,让front往后移动,让flag=false
    当队列为空时: rear == front && flag==false
    当队列为满时: rear == front && flag == true

    1. 保留一个位置
      在这里插入图片描述
  • 3.计数count——队列中有效元素个数
    队列为空时,count == 0
    当有元素入队时,count++,当count和队列的maxsize相等时,代表队列已满

代码实现:

class MyCircularQueue {

    private int[] elem;
    private int front;//表示队列的头
    private int rear;//表示队列的尾


    public MyCircularQueue(int k) {
        this.elem = new int[k+1];
    }

    /**
     * 入队列
     * @param value
     * @return
     */
    public boolean enQueue(int value) {
        //1.检查队列是否是满的
        if (isFull()) {
            return false;
        }
        //2.
        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 front == rear;
    }

    /**
     * 队列是否为满
     * @return
     */
    public boolean isFull() {
        /*if ((rear + 1) % elem.length == front) {
            return true;
        }
        return false;*/
        return (rear + 1) % elem.length == front;
    }
}

4.双端队列

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。

那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

//循环双端队列类
class DQueue{
	
	public int maxSize;	//双端队列的最大容量
	public int queue[];  	   	//数据域
	public int head = 0;		//队头指针,指向第一个元素
	public int tail = 0;		//队尾指针,指向最后一个元素的后一个位置
	
	public DQueue(int n) {
		//队尾指针需要指向最后一个元素的后一个位置,所以需要空出一个位置,即数组的实际大小+1
		this.maxSize = n + 1;
		queue = new int[this.maxSize];
	}
	
	
	//判断队列是否已满
	public boolean isFull() {
		return (tail + 1) % maxSize == head;
	}
	
	
	//判断队列是否为空
	public boolean isEmpty() {
		return tail == head;
	}
	
	
	//从头部入队
	public void headInsert(int data) {
		//先判断队列是否已满
		if(isFull())
			return ;
		//头插,head自减1找到待插入位置,使用%防止越界
		head = (head - 1 + maxSize) % maxSize;
		queue[head] = data;
	}
	
	
	//从尾部入队
	public void tailInsert(int data) {
		//先判断队列是否已满
		if(isFull())
			return ;
		queue[tail] = data;
		//尾插,tail自增1找到待插入的位置,使用%防止越界
		tail = (tail + 1) % maxSize;
	}
	
	
	//从头部出队
	public int headPop() {
		//先判断队列是否为空
		if(isEmpty())
			return -1;
		int value = queue[head];
		//出队后向后移动head指针
		head = (head + 1) % maxSize;
		return value;
	}
	
	
	//从尾部出队
	public int tailPop() {
		//先判断队列是否为空
		if(isEmpty())
			return -1;
		//先向前移动tail指针指向待出队数据位置
		tail = (tail - 1 + maxSize) % maxSize;
		int value = queue[tail];
		return value;
	}
	
	
	//获取队头元素
	public int getHead() {
		return queue[head];
	}
	
	
	//获取队尾元素
	public int getTail() {
		return queue[(tail - 1 + maxSize) % maxSize];
	}
	
	
	//遍历队列
	public void display() {
		if(isEmpty()) {
			System.out.println("队列空");
			return ;
		}
		while(head != tail) {
			System.out.println(queue[head]);
			//head向后移动
			head = (head + 1) % maxSize;
		}
	}
}

5.用队列实现栈

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 size = qu1.size();
            for (int i = 0; i < size - 1; i++) {
                int val = qu1.poll();
                qu2.offer(val);
            }
            return qu1.poll();
        } else {
            if (!qu2.isEmpty()) {
                int size = qu2.size();
                for (int i = 0; i < size - 1; i++) {
                    int val = qu2.poll();
                    qu1.offer(val);
                }
            }
            return qu2.poll();
        }
    }

    public int top() {
        if (empty()) {
            return -1;//两个队列都为空
        }
        if (!qu1.isEmpty()) {
            int size = qu1.size();
            int val = -1;
            for (int i = 0; i < size; i++) {
                val = qu1.poll();
                qu2.offer(val);
            }
            return val;
        } else {
            int size = qu2.size();
            int val = -1;
            for (int i = 0; i < size; i++) {
                val = qu2.poll();
                qu1.offer(val);
            }
            return val;
        }
    }





    public boolean empty() {
        return qu1.isEmpty() && qu2.isEmpty();
    }
}

6.用栈实现队列

class MyQueue {

    private Stack<Integer> stack1;
    private Stack<Integer> stack2;

    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

    public void push(int x) {
        stack1.push(x);
    }

    public int pop() {
        if (empty()) {
            return -1;
        }
        if (stack2.isEmpty()) {
            while(! stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

    public int peek() {
        if (empty()) {
            return -1;
        }
        if (stack2.isEmpty()) {
            while(! stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }

    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }
}

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

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

相关文章

AirServer2023免费无线Mac和PC电脑屏幕镜像投屏工具

AirServer2023是适用于 Mac 和 PC 的先进的屏幕镜像接收器。 它允许您接收 AirPlay 和 Google Cast 流&#xff0c;类似于 Apple TV 或 Chromecast 设备。AirServer 可以将一个简单的大屏幕或投影仪变成一个通用的屏幕镜像接收器 &#xff0c;是一款十分强大的投屏软件。AirSer…

Ansys Zemax | 眼科镜片设计

本文介绍了眼科镜片的设计原理&#xff0c;并讨论了镜片、眼睛和视觉环境中对镜片设计十分关键的参数&#xff0c;其中包括了常见镜片材料&#xff08;涵盖了玻璃和聚合物&#xff09;的玻璃目录。本文不包括渐进式镜片设计&#xff0c;尽管渐进式镜片时常根据一般的镜片曲率原…

【实际开发04】- XxxMapper.xml/java - 批量处理

目录 1. Model : XxxMapper.xml 1. IotTypeMapper.xml 基础 3 tips 2. Model : XxxMapper.java 1. IotTypeMapper.java 基础 3 tips 3. Others info 1. 模糊查询 2. 模糊查询 name 导致的异常 --> name 3. 连接查询 Where 限制主表 , 谨慎 : 使用副表限制 - ★ 4…

Java程序员如何使用代码来计算最大公约数和最小公倍数?

沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 一、前言 嘿&#xff0c;怎么突然讲到最大公约数了&#xff1f; 因为RSA算法&#xff0c;对于与欧拉结果计算的互为质数的公钥e&#xff0c;其实就需要使用到辗转相除法来计算出最大公约数。…

Java文件IO操作

目录 一、了解什么是文件 狭义的文件&#xff1a; 广义的文件&#xff1a; 二、文件的路径 ①文件的绝对路径 ②文件的相对路径 三、Java对于文件的操作 File类的构造方法 File类的普通方法 四、对于文件的内容操作 ①FileInputStream&#xff08;文件输入流&#xf…

ES索引备份还原

ES索引备份还原一、规划二、备份方案一&#xff1a;备份到集群共享目录方案二&#xff1a;备份到HDFSES还原一、规划 es数据出于线上数据安全考虑&#xff0c;对于es已有的索引数据可以进行安全备份&#xff0c;通常可以将es备份到共享文件目录或者一些其它的数据存储的文件系…

Splashtop Personal 安装教程

splashtop Personal 安装教程1. Splashtop Personal 概述2. splashtop Personal 安装步骤2.1 主控端&#xff08;Splashtop Business app&#xff09;2.2 被控端&#xff08;Splashtop Streamer&#xff09;2.3 打开主控端结束语1. Splashtop Personal 概述 Splashtop Persona…

java跳出循环的几种方式

在java中可以使用break、continue、return语句跳出for循环。break用于完全结束一个循环&#xff0c;跳出循环体&#xff1b;continue只是中止本次循环&#xff0c;接着开始下一次循环&#xff1b;return的功能是结束一个方法。 break语句 break用于完全结束一个循环&#xff0…

4.5 集成运放的种类及选择

一、集成运放的发展概述 集成运放自 20 世纪 60 年代问世以来&#xff0c;飞速发展&#xff0c;目前已经历了四代产品。 第一代产品基本沿用了分立元件放大电路的设计思想&#xff0c;采用了集成数字电路的制造工艺&#xff0c;利用了少量横向 PNP 管&#xff0c;构成以电流源…

Axure 原型设计的三步进阶法

平时跟很多同学朋友的交流过程中&#xff0c;对于axure的需要做到怎样&#xff0c;众说纷纭。总结了一下大家的意见&#xff0c;分别有以下几种&#xff1a; 1、掌握基本的搭建方法即可&#xff0c;不需要做交互&#xff1b; 2、既然做就要做到尽善尽美&#xff0c;页面和交互…

【Python百日进阶-数据分析】Day221 - plotly使用日期类型轴的时间序列 2

文章目录九、具有自定义日期范围的时间序列图9.1 使用plotly.express9.2 使用graph_objects9.3 手动设置日期范围十、带范围滑块的时间序列十一、带范围选择器按钮的时间序列十二、按缩放级别自定义刻度标签格式十三、隐藏周末和假期13.1 隐藏正常周末13.2 隐藏周末和指定日期1…

Windows 11关闭系统更新的方法有哪些?

之前问的最多的就是Win10关闭更新的方法&#xff0c;现在轮到了Windows11&#xff0c;下面我们就具体来看看如何关闭Windows11的系统更新。方案一&#xff1a;使用注册表编辑器关闭Win11更新Windows注册表实质上是一个庞大的数据库&#xff0c;存储着各种各样的计算机数据与配置…

Windows系统下Python安装教程

Python安装环境为Windows10系统&#xff08;64&#xff09; 1.Python下载 选择Python官网进行下载&#xff08;Welcome to Python.org&#xff09;&#xff0c;进入网站&#xff0c;点击Downloads&#xff0c;进入下载模块&#xff0c;鼠标指针放到Download&#xff0c;选择Wi…

聚焦行业,2022巨杉客户案例及产品荣获多项殊荣

2022年&#xff0c;巨杉客户实践案例及产品 广受肯定&#xff0c;荣获多项殊荣 荣誉不仅属于巨杉 它源自于每一位客户的信任和支持 感谢每一位客户对巨杉产品的选择和认可 客户创新实践案例 巨杉联合江阴农商行获评爱分析银行数字化创新实践案例 9月&#xff0c;在“2022…

InstallShield 制作INF驱动安装程序

题外话&#xff1a; 使用INF文件制作经过签名的CAT文件可以参考如下博文&#xff1a; https://blog.csdn.net/qq_29729577/article/details/113537243 回归正题&#xff1a; 本例使用InstallShield 2020版本 驱动文件准备 将INF、CAT、SYS等驱动相关文件放至同一目录下&…

【动态内存管理】C语言

前言&#xff1a; 为什么会存在动态内存分配 我们以前学过的开辟空间的方式有两个特点&#xff1a; 1 空间开辟大小是固定的&#xff1b; 2.数组在申明的时候&#xff0c;必须指定数组的长度&#xff0c;它所需要的内存在编译时分配&#xff1b; 但是对于空间的需求&#xff0c…

Qt基于CTK Plugin Framework搭建插件框架--插件通信【注册接口调用】

文章目录一、前言二、插件完善2.1、添加接口文件2.2、添加接口实现类2.3、服务注册&#xff08;Activator注册服务&#xff09;三、接口调用四、接口 - 插件 - 服务的关系4.1、1对14.2、多对14.3、1对多一、前言 通过Qt基于CTK Plugin Framework搭建插件框架–创建插件一文&am…

解决方案|Keithley吉时利源表测试软件的典型应用及案例介绍

数字源表又称源测量单元(SMU)&#xff0c;是数字万用表(DMM)、电压源、实际电流源、电子负载和脉冲发生器的有用功能集成在仪器中&#xff0c;相当于电压源、电流源、电压表、电流表和电阻表的综合体可以作为四象限电压源或电流源提供精确的电压或电流&#xff0c;同时测量电流…

聚类模型(K-means聚类,系统聚类,DBSCAN算法)

所谓的聚类&#xff0c;就是将样本划分为由类似的对象组成的多个类的过程。聚类后&#xff0c;我们可以更加准确的在每个类中单独使用统计模型进行估计、分析或预测&#xff1b;也可以探究不同类之间的相关性和主要差异。聚类和分类的区别&#xff1a;分类是已知类别的&#xf…

Kafka 生产者

Kafka 生产者 生产者就是负责向 Kafka 发送消息的。 生产者业务逻辑 &#xff08;生产者业务逻辑流程&#xff09; 生产者开发示例 一个正常的生产逻辑流程如下&#xff1a; 配置生产者客户端参数及创建相应的生产者实例 构建待发送的消息 发送消息 关闭生产者实例 生…