java中的队列

java中的队列

队列的概念

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

队列的模拟实现

队列中既然可以存放元素,底层要有能够保存元素的空间,线性表常见的空间类型有两种:顺序结构和链式结构,那么实现使用链式顺序结构好还是顺序结构好呢?

而双向链表也可以实现,并且尾部和头部都可以用来出队和入队的,因为前一个结点和后一个结点都可以快速找到,时间复杂度都是O(1)

我们通过双向链表来模拟实现队列,并且使用尾插来入队,头删来出队

java 复制代码
public class MyQueue {
    static class ListNode{
        public int val;
        public ListNode prev;
        public ListNode next;

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

    public ListNode first;
    public ListNode last;

    public int usedSize = 0;

    public void offer(int val){
        ListNode newNode = new ListNode(val);
        if(first == null){
            first = last = newNode;
        }else{
            last.next = newNode;
            newNode.prev = last;
            last = newNode;
        }
        usedSize++;
    }

    public int poll() {
        if(isEmpty()){
            return -1;
        }
        int val = first.val;
        first = first.next;
        if(first != null) {
            first.prev = null;
        }
        usedSize--;
        return val;
    }

    public int peek() {
        if(isEmpty()){
            return -1;
        }
        return first.val;
    }

    public boolean isEmpty() {
        return usedSize == 0;
        //return first == null;

    }
}
java 复制代码
public class Test {
    public static void main(String[] args) {
        MyQueue queue = new MyQueue();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        System.out.println(queue.poll());
        System.out.println(queue.peek());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        //结果为:
        //1
        //2
        //2
        //3
        //4
        //5
        //-1
    }
}

队列的使用

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

方法 功能
boolean offer(E e) 入队列
E poll() 出队列
E peek() 获取队头元素
int size() 获取队列中有效元素的个数
boolean isEmpty() 检测队列是否为空

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

java 复制代码
public class Test {
    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.size());
        System.out.println(queue.poll());
        System.out.println(queue.peek());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.isEmpty());
        System.out.println(queue.poll());
        System.out.println(queue.isEmpty());
        System.out.println(queue.poll());
        System.out.println(queue.isEmpty());
        //结果为:
        //5
        //1
        //2
        //2
        //3
        //4
        //false
        //5
        //true
        //null
        //true
    }
}

还有一个问题:底层可以通过采用数组来实现队列吗

答案是不行的,如果采用数组来实现会浪费大量空间的,数组一直往后走,前面的空间被浪费掉了

如果我们想重复利用数组空间,这样实现形成的是循环队列。

循环队列

循环队列也是常用的一种队列,如操作系统学习中生产消费者模型时就可以采用循环队列,环形队列通常采用数组实现。

我们如果通过front == rear 来判断队列为空,我们会发现循环队列为满时,rear == front,那么我们需要通过什么来判断队列空和满呢?

空:只要front和rear相遇就是空的

满:

  1. 定义usedSize,通过usedSize来判断满不满
  2. 添加标记,定义boolean类型元素
  3. 浪费一个空间(判断rear的下一个是不是 front)

我们下面将通过浪费一个空间来模拟实现

最后还有一个问题,我们要怎么让坐标从7走到0呢

我们可以通过

rear = (rear+1) % len

front = (front+1) % len

来实现
设计循环队列

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

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

你的实现应该支持如下操作:

MyCircularQueue(k): 构造器,设置队列长度为 k 。

Front: 从队首获取元素。如果队列为空,返回 -1 。

Rear: 获取队尾元素。如果队列为空,返回 -1 。

enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。

deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。

isEmpty(): 检查循环队列是否为空。

isFull(): 检查循环队列是否已满。

java 复制代码
class MyCircularQueue {

    public int[] elem;
    public int front = 0;
    public int rear = 0;

    public MyCircularQueue(int k) {
        elem = new int[k + 1];
    }
    
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }else{
            elem[rear] = value;
        }
        rear = (rear + 1) % elem.length;
        return true;
    }
    
    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }else{
            int val = elem[front];
            front = (front + 1) % elem.length;
        }
        return true;
    }
    
    public int Front() {
        if(isEmpty()){
            return -1;
        }
        int val = elem[front];
        return val;
    }
    
    public int Rear() {
        if(isEmpty()){
            return -1;
        }
        int index = (rear == 0) ? elem.length - 1 : rear - 1;  
        int val = elem[index];
        return val;   
    }
    
    public boolean isEmpty() {
        return rear == front;
    }
    
    public boolean isFull() {
        return (rear + 1) % elem.length == front;
    }
}

双端队列(Deque)

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

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

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

java 复制代码
Deque<Integer> stack = new ArrayDeque<>();  //双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();  //双端队列的链式实现

IO题

  1. 用队列实现栈

    请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

    实现 MyStack 类:

    void push(int x) 将元素 x 压入栈顶。

    int pop() 移除并返回栈顶元素。

    int top() 返回栈顶元素。

    boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

  2. 用栈实现队列

    请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

    实现 MyQueue 类:

    void push(int x) 将元素 x 推到队列的末尾

    int pop() 从队列的开头移除并返回元素

    int peek() 返回队列开头的元素

    boolean empty() 如果队列为空,返回 true ;否则,返回 false

    说明:

    你只能使用标准的栈操作 ------ 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。

    你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

java 复制代码
class MyQueue {

    Deque<Integer> s1;
    Deque<Integer> s2;

    public MyQueue() {
        s1 = new ArrayDeque<>();
        s2 = new ArrayDeque<>();
    }
    
    public void push(int x) {
        s1.push(x);
    }
    
    public int pop() {
        if(empty()){
            return -1;
        }
        if(s2.isEmpty()){
            while(!s1.isEmpty()){
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
    
    public int peek() {
        if(empty()){
            return -1;
        }
        if(s2.isEmpty()){
            while(!s1.isEmpty()){
                s2.push(s1.pop());
            }
        }
        return s2.peek();
    }
    
    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty();
    }
}

对于队列的学习我们先了解到这,希望这篇文章对您有帮助!!!

相关推荐
色空大师10 分钟前
23种设计模式
java·开发语言·设计模式
闲人一枚(学习中)10 分钟前
设计模式-创建型-建造者模式
java·设计模式·建造者模式
Bruce小鬼22 分钟前
QT文件基本操作
开发语言·qt
2202_7544215428 分钟前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
蓝染-惣右介30 分钟前
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
java·数据库·tomcat·mybatis
小林想被监督学习31 分钟前
idea怎么打开两个窗口,运行两个项目
java·ide·intellij-idea
HoneyMoose33 分钟前
IDEA 2024.3 版本更新主要功能介绍
java·ide·intellij-idea
我只会发热35 分钟前
Java SE 与 Java EE:基础与进阶的探索之旅
java·开发语言·java-ee
是老余36 分钟前
本地可运行,jar包运行错误【解决实例】:通过IDEA的maven package打包多模块项目
java·maven·intellij-idea·jar
crazy_wsp36 分钟前
IDEA怎么定位java类所用maven依赖版本及引用位置
java·maven·intellij-idea