Java数据结构—队列

今天我们来学习队列🧐

 

队列是由双向链表LinkedList作为底层的一个接口,为数据结构的一种。下面让我们正式开始队队列的学习吧😎

🌱队列的特点

队列的特点是先进先出,就像排队一样,队尾进队头出


🌱队列

🌿Queue的方法

Queue的方法不多,只有以下六种👇:

方法名作用
add(E)在队尾添加元素
offer(E)在队尾添加元素
remove()弹出队头元素
poll()弹出队头元素
element()返回队头元素但不弹出
peek()返回队头元素但不弹出

细心的同学可能会发现,怎么Queue的这六个方法两个两个都是重复的🧐它们又有什么不同嘞?

✨add和offer方法的区别

其实在平常的使用中,我们常使用的方法有三个:offer(E)、poll()、peek()。那么它们的真正区别是什么呢?

要知道这些相同作用的方法有什么区别,还要从源头——源码查起

在IDEA上,我们通过使用ctrl+鼠标左键点击进入到add和offer的源码,在add和offer的上方我们分别可以看到其注释👇

将add和offer的注释分别翻译后比较👇:

 我们可以由上面的图得知,在容量满的时候,offer相较于add的直接抛异常,offer会好一些不会直接暂停程序。

当然,其实选择哪一个方法其实差别都不算太大,作者较喜欢使用的是offer、poll、peek这三个函数😎大家可以自己选择


🌿Queue创建与LinkedList创建的差别

我们来看下面两行代码👇:

Queue<Integer> qu1 = new LinkedList<>();
LinkedList<Integer> qu2 = new LinkedList<>();

这两行代码虽然后半段都是相同的,但由于创建变量的类型不同,导致qu1的方法会远远少于qu2,这是因为qu1的类型是接口,而Queue接口只有6个方法,要创建实体得利用LinkedList,但是只是相当于LinkedList将Queue中的方法给重写了,而Queue实际只有6个方法的这个事实无法改变。通过在IDEA中试一下就可知道👇:

 在解决完这个问题后,我们又想,为什么LinkedList会有这么多方法?🧐

✨LinkedList不仅继承了Queue接口,并且继承了Deque接口

Deque接口的底层是一个双端队列,也就是怎么进出都可。如:队头进,队头出;队头进,队尾出;队尾进,队头出;队尾进,队尾出;


🌿Queue的仿写

要正式了解一个数据结构,仿写是一个最好的了解方式,下面我们可以自己仿写一个Queue。

✨链表作为底层仿写

若在单向链表的情况下,我们需要考虑一个问题,入队的时候是使用头插还是尾插?

根据头插和尾插入队来分析:

  • 若使用头插,则入队的时间复杂度为O(1),但出队的时间复杂度要O(n)。
  • 若使用尾插,则入队的时间复杂度要O(n),但出队的时间复杂度为O(1)。

所以头插尾插入队都差不多,要是想要入队出队时间复杂度都为O(1)的话,我们需要设置多一个last节点,专门在最后一个节点的位置处,用于出队,这样就可以让入队出队的时间复杂度都为O(1)了。但我们要知道,真正的单向链表中,是没有last这个成员属性的。

下面开始真正的仿写😎

public class MyLinkedList {
    public static class Node{
        public int val;
        public Node next;

        public Node(){

        }

        public Node(int val){
            this.val = val;
        }
    }

    public Node head;
    public Node last;

    //从尾入,从头出
    //尾插法
    public void offer(int val){
        if(head==null){
            head = new Node(val);
            last = head;
        }
        Node cur = new Node(val);
        last.next = cur;
        last = cur;
    }

    public int poll(){
        if(head==null||last==null){
            throw new QueueEmptyException("队列为空");
        }
        int val = head.val;
        head = head.next;
        return val;
    }

    public int peek(){
        if(head==null||last==null){
            throw new QueueEmptyException("队列为空");
        }
        int val = head.val;
        return val;
    }

    public int size(){
        Node cur = head;
        int size = 0;
        while(cur!=null){
            cur = cur.next;
            size++;
        }
        return size;
    }

}

以上是用链表实现,再来看用数组要怎样实现仿写?


✨数组作为底层仿写

数组仿写相对来说较为麻烦,因为数组仿写会有空间浪费的情况出现,原因是当我们出队列的时候front(队头)下标会往后走,从而产生空间浪费。

 这是,要让我们的空间不会浪费,就需要用到循环数组👇:

 当我们入队列或者出队列的时候,front下标和rear下标都可以跟着,以达到不浪费空间的情况。

在利用循环数组创建列表前,要先理清两个问题:

  1. 如何判断数组什么时候空或是满
  2. 当rear或者front下标到达图中的7下标时,要怎么样到下标0

1️⃣首先解决第一个问题,要判断空或是满,有三种解决方法:①使用flag;②实际大小==数组大小;③将front位置的前一个位置置为空,不放入数据。在后面的代码里我们用第三种方法解决。

2️⃣第二个问题要解决,我们可以使用(rear+1)%数组大小   这条公式就可以解决。

利用循环数组来仿写队列在LeetCode中是有题目的,我们可以通过尝试写这道题目来理解😎下面分别是LeetCode题目链接和博主写的该题目的链接👇

LeetCode第622题.设计循环队列(原题目)LeetCode第622题—设计循环队列(博主讲解版本)

在模仿完了队列后,相信我们会对队列的了解更加深刻😎


🍃实际写题应用

在学习完队列后,我们可以开始写题了,下面是博主认为不错的题目,分享给大家,也有博主的题目分析😎

LeetCode原题博主分析

LeetCode  225、用队列实践栈

LeetCode第225题—用队列实践栈

LeetCode  232、用栈实现队列

LeetCode第232题—用栈实现队列

LeetCode  155、最小栈

LeetCode第155题—最小栈


以上!便是全部的啦😎

又是收获满满的一天~

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值