【DS】Java实现队列及集合Queue,Deque的使用

一.队列(Queue)的概念

  1. 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)。入队列:进行插入操作的一端称为队尾(Tail/Rear)。出队列:进行删除操作的一端称为队头(Head/Front)

  1. 循环队列(OJ:设计循环队列

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

我们知道顺序表是具有随机访问的特点的 , 将若干个元素入队后 , 每次出队操作后 , 该元素原来所在的空间就无法再使用了 , 这就使得顺序表得空间利用不充分 ;

而如果采用循环队列就可以解决这个问题 , 如果数组最后一个空间已经有了元素 , 但前面由于出队有了空缺 , 此时再有元素入队就能重新从数组的尾部跳到数组的头部 , 对已经出队的空间进行重新利用 , 这样就避免了空间的浪费 。

数组下标循环的小技巧

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

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

如何区分空与满

1. 通过添加 size 属性记录

2. 保留一个位置

3. 使用标记

  1. 双端队列

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

双端队列同时遵守了先进先出和后进先出的原则,所以可以说它是一种把队列和栈相结合的一种数据结构 ;所以双端队列既能够当队列使用,也能当栈使用,Java底层是使用双链表(LinkedList)来实现双端队列(Deque)和队列(Queue)的 ;

限制一端进行出队或入队的双端队列称为受限的双端队列。

二.队列的使用

  1. 集合-Queue,Deque类的介绍

在Java中,Queue和Deque是两个接口,底层是通过双链表实现的 , 使用时必须创建LinkedList的对象 。

这里观察Queue,Deque接口与LinkedList类关系;LinkedList类实现了Queue,Deque接口,Deque接口扩展了Queue接口,三者都扩展或继承了Collection和Iterable接口。

  1. 常用方法

由于在实际开发中Deque使用的并不是非常多 , 所以这里只列出Queue接口中常用的方法 :

1. add系列方法与offer系列方法的区别:
两者都是在队列队头或队尾插入元素,前者(add)插入元素失败会引发异常,后者(offer)插入元素失败不会引发异常,只会以返回false的形式表示插入元素失败,如果是有容量限制的队列,使用offer系列方法更加合适。
2. remove系列方法与poll系列方法的区别:
两者都是在队列队头或队尾删除并返回元素,前者(remove)删除元素失败会引发异常,后者(poll)删除元素失败不会引发异常,只会以返回null的形式表示删除元素失败,如果是有容量限制的队列,使用poll系列方法更加合适。
3. get系列方法与peek系列方法的区别:
两者都是在队列队头或队尾获取并返回元素,前者(get)获取元素失败会引发异常,后者(peek)获取元素失败不会引发异常,只会以返回null的形式表示获取元素失败,如果是有容量限制的队列,使用peek系列方法更加合适。
  1. 代码实现

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

三.队列的模拟实现

  1. 思考

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 链式结构。那我们思考下:队列的实现使用顺序结构还是链式结构好?

在Java集合框架中的队列就是用双链表来实现的 , 在这里采用单链表来实现队列 .
我们这里设置单链表中的两个引用head和tail用来指向头节点和尾节点 , 需要注意的是我们应该让单链表的头做队头 , 单链表的尾做队尾 , 也就是从单链表的尾入队 , 头出队 , 此时入队和出队操作的时间时间复杂度都为O(1) ; 而如果反过来入队使用头插 , 入队的时间复杂度为O(1) , 此时出队要删除单链表最后一个节点 , 需要先找到其前一个节点 , 出队的时间复杂度就为O(N)了;
如果使用双链表来实现栈的话那就简单了 , 由于是是双向的 , 不管哪一端来做队头/队尾 , 时间复杂度都为O(1) ; 会了单链表的 , 双链表的不在话下了 .
  1. 代码实现

package demo;

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User:YY
 * Date:2023-02-16
 * Time:15:46
 */
public class MyQueue {
    static class ListNode {
        private int val;
        private ListNode next;

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

    public ListNode head;
    public ListNode tail;
    public int usedSize;

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

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

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

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

    public int getUsedSize(){
        return this.usedSize;
    }


    public static void main(String[] args) {
        MyQueue queue = new MyQueue();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        System.out.println(queue.poll());
        System.out.println(queue.peek());
        System.out.println(queue.getUsedSize());
        System.out.println(queue.empty());

        System.out.println(queue.getUsedSize());
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏黎世卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值