【数据结构】栈和队列【详解篇3】

本文介绍了Java中栈(Stack)和队列(Queue)的概念,包括它们的操作原理(后进先出LIFO和先进先出FIFO),并详细讲解了利用顺序表和链表实现的栈和队列,以及循环队列的实现方法。讨论了为何这些数据结构的限制有助于编程和Java框架的运用,以及它们在内存管理和编程实践中的作用。
摘要由CSDN通过智能技术生成

栈(Stack)

栈和队列也是基于List来实现的,但是它的限制比List更严格,换句话说它提供的操作更少或者List比栈和队列要更灵活。

  • 对于栈(Stack)来说,它只支持3个核心操作:入栈、出栈、取栈顶元素,因为它具有的这些操作,所以他有一个特点叫做后进先出
  • 对于队列(Queue)来说,它也支持3个核心操作:入队列、出队列、取队首元素,因为它具有的这些操作,所以他有一个特点叫做先进先出(类似于食堂排队,谁先来谁先打饭谁就先离开)。

为什么他们的限制比List更严格呢?

  • 在写程序的时候,限制不见得是坏事,灵活往往也不见得是好事,越灵活就越容易出错。
  • 正因为有限制,未来在java学习的后面,会学习Java中的一些框架。
  • 所谓框架的本质,就是限制你写代码的灵活性。有了框架的限制,就可以让你的代码写的不会很差。
  • JVM的内存区域划分(也称为JVM的内存模型),其中有一个JVM虚拟机栈,此处的JVM栈特指JVM中的那个内存区域。而现在所说的栈是数据结构中的通用概念。
  • 数据结构和编程语言是无关的,各种编程语言都有数据结构的概念。

概念

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈

顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

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

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

栈图:Stack的Push和Pop遵循先进后出(Last In First Out)规则

image-20211010192730618

栈的实现

  • 利用顺序表实现,即使用尾插+尾删的方式实现。
  • 利用链表实现,则头尾皆可。

相对来说,顺序表的实现上要更为简单一些,所以优先用顺序表实现栈。

public class MyStack {
    //管理一些int元素即可,也不考虑扩容问题
    private int[] array=new int[100];//创建一个数组,初始化成100个元素
    private int size=0;//表示栈中存在多少个有效元素
    //《要实现的三个方法如下》
    //入栈:插入元素
    public  void  push(int x){
     array[size]=x;
     size++;
    }
    //取栈顶(最后进来的那个元素)
    public int peek(){
        return array[size-1];
    }
    //出栈
    public int pop(){
        int ret=array[size-1];
        size--;
        return ret;
    }
//从以上代码来看,实现一个栈还是很容易的,只要在顺序表的基础上稍加限制就可以了
// 一个栈只要能支持这三种操作就好了,栈的实现还是很容易的,主要是我们能够结合栈这样的思想来帮助我们解决一些实际问题
}

队列(Queue)

概念

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

In First Out)的规则。

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

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

队列图:先进先出FIFO(First In First Out) 。

image-20211010193617529

队列的实现

队列也可以使用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

package java2021_1003;

/**
 * Description:通过链表实现队列
 */
public class MyQueueByLinkedList {
    //定义一个链表节点
    //static的效果就是:常见Node的实例不依赖MyQueueByLinkedList 这个类的实例
    static class Node{//Node 这个类叫做“内部类”,内部类就是定义在某个类或者方法中的类
       public int val;
       Node next=null;//初始化为空
        //alt+insert,找一个构造方法
        public Node(int val) {
            this.val = val;
        }
    }
    //创建一个链表,需要有头结点
    //基于链表来实现队列,可以入队列从尾部插入,出队列从头部删除;也可以入队列从头部插入,出队列从尾部删除
    //但是不管是哪种实现方式,最好把头和尾都记录下来
    //此时这个头head结点不是傀儡节点
    private Node head=null;//头结点初始为null
    private Node tail=null;

    //《要实现的三个方法如下》
    //此处按照尾部入队列,头部出队列的方式实现
    //入队列(标准库中的队列,入队列操作就叫offer(提供))
    public void offer(int val){
        Node newNode=new Node(val);
        if(head==null){//如果是空队列,直接让head和tail都指向新结点
            head=newNode;
            tail=newNode;
            return;
        }
        //当前如果不是空链表,就可以把这个结点插到末尾
        tail.next=newNode;
        tail=tail.next;
    }
    //出队列(从头部删除)
    public int  poll(){//把删除的元素返回来
        //如果当前队列就是空队列,再去poll显然不科学
        if(head==null){
            //如果出队列失败,就返回一个错误值
            return -1;//我们不知道当前得到的-1,是因为出队列失败得到的-1,还是说本来这里面存的就是-1,所以说-1在这里不能作为非法值
            //为了能够更好地进行返回值操作,可把int改为Integer,出错的时候直接返回一个null,null肯定是一个非法值
            //Integer叫做包装类,包装类也是一个引用类型的对象,引用类型就可以是一个空引用,空引用就可以表示她是非法情况
            //如果是其他值,那它就是非空引用,在里面包含一个具体的值就ok了。
        }
        int ret=head.val;
        head=head.next;
        if(head==null){//删除当前元素之后,队列变成了空的队列
            tail=null;
        }
        return ret;
    }
    //取队首元素
    public Integer peek(){
        if(head==null){
            return null;
        }
        return head.val;
    }
}

循环队列的实现

image-20211010195056729

使用数组实现队列的代码示例:

package java2021_1003;

/**
 * Description:通过数组实现队列
 */
public class MyQueueArray {
    //如果使用数组实现队列它的出队列效率太低,不太适合,所以可以使用循环队列的方式实现出队列操作
    private int[] array=new int[100];
    //有效元素的范围是[head,tail),注意,tail可能在head之前的情况
    private int head=0;
    private int tail=0;
    private int size=0;//size表示元素个数
    //写里面的操作
    //进队列操作(插入元素)
    public void offer(int val){
        //先判断队列是否为满
        if(size==array.length){
            //此时表明队列满了,无法继续插入,直接返回
            return;
        }
        //保证这个操作下标不能越界
        array[tail]=val;//如果没满的话就把tail位置的元素赋成val
        tail++;
        if(tail>=array.length) {//如果tail++之后,超出了数组的有效范围,就从头开始
            tail=0;
        }
        size++;//同时增加元素
    }
    //出队列操作
    public Integer poll(){
        //先判断size是否为0,如果等于0,就直接返回null队列
        if(size==0){
            return null;
        }
        //如果不是空队列
        Integer ret=array[head];
        head++;
        if(head>=array.length){
            head=0;
        }
        size--;//同时删除元素
        return ret;
    }
    //取队首元素
    public Integer peek(){
        if(size==0){//判断队列是否为空
            return null;
        }
        return array[head];//如果队首为null,直接返回head
    }
}

双端队列

概念

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

那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。因为,栈(Stack),它是一个类,所以我们在使用栈的时候,就可以直接去创建Stack这样的一个实例,而Queue是一个接口,所以在使用它的时候只能基于其他的类来创建实例。

Queue

错误处理抛出异常返回特殊值
入队列add(e)offer(e)
出队列remove()poll()
队首元素element()peek()

如果队列满了或者队列为空,失败就会抛出异常;

返回特殊值是指如offer会返回true或false这样的结果特殊值;

poll 、peek会返回一个null表示当前是一个特殊情况.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值