数据结构与算法之队列

队列

  • 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
  • 进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
  • 队列的数据元素又称为队列元素。
  • 在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表,LIFO

特点

  1. 线性表:链表或者数组
  2. FIFO

队列分类

  • 顺序队列(Queue) 只能在一端插入数据,另一端删除数据

在这里插入图片描述

  • 循环(双向)队列(Deque):每一端都可以进行插入数据和删除数据操作
    在这里插入图片描述

队列基本操作者

我们知道,栈只支持两个基本操作:入栈push()和出栈pop()。
队列跟栈非常相似,支持的操作也很有限,最基本的操作也是两个:

  • 入队enqueue(),放一个数据到队列尾部;
  • 出队dequeue(),从队列头部取一个元素。

所以,队列跟栈一样,也是一种操作受限的线性表数据结构。作为一种非常基础的数据结构,队列的应用也非常广泛,例如AQS框架里维护了一个CLH队列、条件等待队列,线程池里的等待队列,RabbitMQ和其他消息中间件等。

队列实现方式

基于数组实现的顺序队列

package com.DataConstruct.queue;

public class MyArrayQueue implements MyQueue{
    private Object[] table;
    private int top=0;
    private int defaultCapacity=10;
    private int head=0;
    public MyArrayQueue() {
        table=new Object[defaultCapacity];
    }

    @Override
    public void push(Object val) {
        if(val==null) return;
        if (top>table.length)
            throw new RuntimeException("队列满了");
        table[top++]=val;
    }

    @Override
    public Object pop() {
        if(head==top)
            throw new RuntimeException("队列为空");
        return table[head++];
    }
    public Object peek() {
        if(head==top)
            throw new RuntimeException("队列为空");
        return table[head++];
    }
    @Override
    public int size() {
        return top ;
    }
    public void print(){
        for (int i=head;i<top;i++){
            System.out.println(table[i].toString());
        }
    }

    public static void main(String[] args) {
        MyArrayQueue queue=new MyArrayQueue();
        queue.push("dd");
        queue.push("CC");
        queue.push("AA");
        queue.push("WW");
        queue.print();
        System.out.println("-----------------");
        System.out.println(queue.pop());
        System.out.println(queue.pop());
        System.out.println("--------------");
        queue.print();
    }
}

基于链表的队列实现

package com.DataConstruct.queue;

import com.DataConstruct.stack.MyLinkedStack;

public class MyLinkedQueue implements MyQueue {
    private Node head;
    private Node tail;
    private int top=0;

    public MyLinkedQueue() {
    }

    @Override
    public void push(Object val) {
        Node newNode=new Node(val);
        if(head==null){
            head=newNode;
            tail=newNode;
        }else {
            tail.next=newNode;
            tail=newNode;
        }
        top++;
    }

    @Override
    public Object pop() {
        if(isEmpty())return -1;
        Object result=head.data;
        head=head.next;
        top--;
        return result;
    }

    @Override
    public int size() {
        return top;
    }

    @Override
    public Object peek() {
        if(isEmpty())return -1;
        return head.data;
    }
    public boolean isEmpty(){
        if(top==0){
            return true;
        }else {
            return false;
        }
    }
    public void print(){
        Node temp=head;
        while (temp!=null){
            System.out.println(temp.data);
            temp=temp.next;
        }
    }
    public static void main(String[] args) {
        MyLinkedQueue stack=new MyLinkedQueue();
        stack.push("dd");
        stack.push("ad");
        stack.push("cd");
        stack.push("ed");
        stack.print();

        System.out.println("------");
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        System.out.println("------");
        stack.print();
        System.out.println("------");
        System.out.println(stack.peek());
    }
}
class Node {
    Object data;
    Node next;
    Node pre;
    public Node(Object data) {
        this.data = data;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }
}

就是链表的特殊操作而已。

两种队列的实现都有各自的问题:

  1. 数组队列元素不能复用;
  2. 链表队列无限容量;

针对数组队列出队后,数组元素就不能再使用了,而我们的数组内存是一次性申请好的,申请完了后,不再使用但是依旧占用着内存,存在空间浪费;
解决方法:

  1. 在数组队列里,当push数据时,队列满时,就去移动数组元素的位置。
  2. 使用循环队列

循环队列

怎么判断循环队列满了?

  1. 添加一个变量统计队列里存在元素的个数,当和容量相等时,则满了
  2. (tail+1)%n==head

实现:

package com.DataConstruct.queue;

public class CircleQueue implements MyQueue {
    private Object[] data;
    private int head=0;
    private int tail=0;
    private int capacity;
    private final static  int defaultCapacity=10;
    public CircleQueue() {
        this(defaultCapacity);
    }

    public CircleQueue(int capacity) {
        data=new Object[capacity];
        this.capacity=capacity;
    }


    @Override
    public void push(Object val) {
        if (isFull()){
            return;
        }else {
            data[tail]=val;
            tail=(tail+1)%capacity;
        }
    }

    @Override
    public Object pop() {
        System.out.println("head==>"+head + "  "+" tail==>"+tail);
        if (isEmpty()){
            return -1;
        }
        Object result=data[head];
        head=(head+1)%capacity;
        return result;
    }

    @Override
    public int size() {
        return (tail-head+capacity)%capacity ;
    }

    @Override
    public Object peek() {
        if (isEmpty()){
            return -1;
        }
        return data[head];
    }

    public boolean isFull(){
        if((tail+1)% capacity== head){
            return true;
        }else {
            return false;
        }
    }
    public boolean isEmpty(){
        if(tail==head){
            return true;
        }else {
            return false;
        }
    }
    public void print(){
        int temp=head;
         for (int i=temp;temp!=tail%capacity;temp=(temp+1)%capacity){
             System.out.println(data[temp]);
         }
    }

    public static void main(String[] args) {
        CircleQueue circleQueue=new CircleQueue();
        circleQueue.push("11");
        circleQueue.push("22");
        circleQueue.push("33");
        circleQueue.push("44");
        circleQueue.push("55");
        circleQueue.push("66");
        circleQueue.push("77");
        circleQueue.push("88");
        circleQueue.push("dd");
        circleQueue.print();

        System.out.println("==============");


        System.out.println("========pop start=======");
        System.out.println(circleQueue.pop());
        System.out.println(circleQueue.pop());
        System.out.println(circleQueue.pop());
        System.out.println("===pop end============");
        circleQueue.print();

    }
}

队列应用场景

  1. AQS用于存储获取锁的线程的队列;
  2. 线程池的阻塞队列
  3. 消息中间件
  4. LRU算法

一般都是异步队列,存储一些消息或者任务,然后异步的运行,降低耦合度,提高响应速度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值