JAVA-队列

一、队列的概念

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

其实就是对一种结构里面的数据进行,头删和尾插的操作

二、队列的使用

2.1 Queue

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

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

队列的方法,出了offer和栈中的push方法名有所不同,其他方法名都是一样的,意思也大概一样。只是插入删除位置不同。

大概给大家介绍一下,Queue基本使用方法:

java 复制代码
public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        //删除队头元素
        System.out.println(queue.poll());//1
        System.out.println(queue.poll());//2
        //查看对他元素
        System.out.println(queue.peek());//3
        System.out.println(queue.peek());//3
        //队列中元素个数
        System.out.println(queue.size());//3
        //队列是否为空
        System.out.println(queue.isEmpty());//false
}

2.2 双向链表实现Queue

实现之前我们需要考虑一下,用哪一种数据结构好?

可惜的是入队操作需要时间复杂度需要O(n),我们尽量用一种插入删除都是O(1)的结构。

通过思考发现,双向链表可以很好的解决这个问题:

那我们就可以来实现

2.2.1 实现成员

java 复制代码
public class MyQueue<E> {
    
    static class ListNode {
        public Object val;//存放数据
        public ListNode prev;//记录前驱结点
        public ListNode next;//记录下一个结点

        public ListNode(Object val) {
            this.val = val;
            //引用类型不需要初始化,默认为null
        }
    }

    public ListNode head;//记录头结点
    public ListNode last;//记录尾结点
}

2.2.2 实现offer

java 复制代码
//相当于尾插
    public void offer(E val) {
        ListNode node = new ListNode(val);
        //队列是空的情况,头尾都是一个节点
        if(head == null) {
            head = last = node;
        }else {
        //结点指向前后的引用连接起来,last指向新的尾结点
            last.next = node;
            node.prev = last;
            last = last.next;
        }
    }

2.2.3 实现isEmpty

java 复制代码
public boolean isEmpty() {
//head等于空返回true,不为空返回false
    return head == null;
}

2.2.4 实现poll

java 复制代码
//相当于头删
    public E pop() {
        //判断链表是否为空,是最好用一个异常
        try {
            if (isEmpty()) {
                throw new MyQueueNullException("堆栈为空pop");
            }
        }catch (MyQueueNullException e) {
            e.printStackTrace();
        }
        
        因为类型是Object,在获取元素的时候一定要强转换!!!
        E ret = (E)head.val;
        if(head.next == null) {
        //只有一个结点的情况
            head = last = null;
        }else {
        //将head前移,并把现在结点的前驱结点置为null
            head = head.next;
            head.prev = null;
        }
        return ret;
    }

2.2.5 实现peek

只要poll实现了,peek就很简单了

java 复制代码
public E peek() {
     try {
           if (isEmpty()) {
               throw new MyQueueNullException("堆栈为空peek");
           }
     }catch (MyQueueNullException e) {
           e.printStackTrace();
     }
     //因为类型是Object,在获取元素的时候一定要强转换
     return (E)head.val;
}

2.2.6 实现size

java 复制代码
public int size() {
     int count = 0;
     ListNode cur = head;
     while (cur != null) {
         count++;
         cur = cur.next;
     }
     return count;
}

这个是用双链表实现的,但是如果我们用数组可以做到吗?

可以但是比链表麻烦

2.3 顺序表实现Queue

那肯定我们最好就是要往0位置放,不然数值一多会浪费很多空间

不过还是有问题,我们应该怎么分辨出元素满,和元素为空的情况:

既然last 是否等于 first无法判断,那么就需要借助其他东西来判断:

  1. 用usedSize记录元素个数,空usedSize == 0, 满 usedSize == length
  2. 用boolean flg = false;以标记的方法记录满和空
  3. 浪费末尾一个空间,last == first就是空,last下一个是first就是满

前面两个比较简单可以自己完成,我们做第3种相对于来说比较简单:

还有个非常核心的问题,我们如何让这个数组变成这种圆可以循环呢?

补充:大家可能对计算机中用 ++较小数取余较大数不太熟悉 :++

java 复制代码
public static void main(String[] args) {
        System.out.println(0%3);//0
        System.out.println(1%3);//1
        System.out.println(2%3);//2
        System.out.println(3%3);//0
        System.out.println(4%3);//1
}

根据观察发现,较小数 % 较大数 = 较小数

622. 设计循环队列 - 力扣(LeetCode) 这里刚好有个题就是做循环队列的,那我们就来做这个题

要求:

实现,在代码中我写了非常详细的注释:

java 复制代码
class MyCircularQueue {
    public int[] elem;

    public int first;
    public int last;

    //构造器,设置队列长度为 k
    public MyCircularQueue(int k) {
        this.elem = new int[k];
    }

    //向循环队列插入一个元素。如果成功插入则返回真
    public boolean enQueue(int value) {
        if(isFull()) {
            //队列满,没叫我们扩容就不用扩容
            return false;
        }
        elem[last] = value;
        last = (last + 1) % elem.length;
        return true;
    }

    //从循环队列中删除一个元素。如果成功删除则返回真
    public boolean deQueue() {
        if(isEmpty()){
            //队列为空
            return false;
        }
        //first前移一位,就删除了元素,后面要用会自己覆盖掉
        first = (first + 1) % elem.length;
        return true;
    }


    //队首获取元素。如果队列为空,返回 -1
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return elem[first];
    }

    // 获取队尾元素。如果队列为空,返回 -1
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        //这样写有个问题,如果last=0的情况,
        // 就不能用这个
//        return elem[last - 1];
        int index = (last == 0) ? elem.length - 1 : last - 1;
        return elem[index];
    }

    //检查循环队列是否为空。
    public boolean isEmpty() {
        return first == last;
    }

    //检查循环队列是否已满。
    public boolean isFull() {
        //      假设为满   %   空间       == first
        return (last + 1) % elem.length == first;
    }
}

唯一注意的是,获取尾部元素的这种情况:

2.4 如何在LeetCode上看报错

但是因为我们方法的原因,有一个错误,也借助这个错误。我记录也教一下大家如何在LeetCode上看报错

java 复制代码
public MyCircularQueue(int k) {
        this.elem = new int[k + 1];
}

三、双端队列

3.1 Deque

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

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

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

(在命名的时候区分一下)

java 复制代码
Deque<Integer> queue1 = new LinkedList<>();//底层是双向链表

Deque<Integer> queue2 = new ArrayDeque<>();//底层是动态顺序表

使用:

java 复制代码
public static void main(String[] args) {
        Deque<Integer> queue1 = new LinkedList<>();
        queue1.offer(1);//1
        queue1.offer(2);//1 2
        queue1.offerFirst(3);//3 1 2
        queue1.offerLast(4);//3 1 2 4

        System.out.println(queue1.poll());//3
        System.out.println(queue1.pollFirst());//1
        System.out.println(queue1.pollLast());//4

        Deque<Integer> queue2 = new ArrayDeque<>();
        queue2.offer(1);//1
        queue2.offer(2);//2
        queue2.offerFirst(3);//3 1 2
        queue2.offerLast(4);//3 1 2 4

        System.out.println(queue2.poll());//3
        System.out.println(queue2.pollFirst());//1
        System.out.println(queue2.pollLast());//4
}
相关推荐
2401_8576100312 分钟前
Spring Boot框架:电商系统的技术优势
java·spring boot·后端
希忘auto28 分钟前
详解MySQL安装
java·mysql
娅娅梨31 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
汤米粥36 分钟前
小皮PHP连接数据库提示could not find driver
开发语言·php
冰淇淋烤布蕾39 分钟前
EasyExcel使用
java·开发语言·excel
拾荒的小海螺1 小时前
JAVA:探索 EasyExcel 的技术指南
java·开发语言
Jakarta EE1 小时前
正确使用primefaces的process和update
java·primefaces·jakarta ee
马剑威(威哥爱编程)1 小时前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法