队列初相识

什么是队列

在这里插入图片描述

栈是先进者后出,后进者先出,而队列是先进者先出,对于在内存中如何存储并没有要求,如果通过数组来实现叫做顺序队列,如果通过链表实现,则叫做链式队列,可以看出上图就是顺序队列,就好比地铁过安检时,每个人排队,依次进入检查

顺序队列

在这里插入图片描述

队列的特点是先进者先出,所以需要两个指针,一个指针指向第一条数据,另一个指向最后一条数据

在这里插入图片描述

因为数组创建时便已经指定大小,所以顺序队列的存储空间有限,为了提高空间的利用率,每当有出对操作时,我们可以进行数据搬移

在这里插入图片描述

但是如果频繁的进行数据搬移,会使得性能降低,所以当没有空闲空间时,我们只需要在入队时,集中触发一次数据的搬移操作即可.

public class ArrayQueue<E> {
    // 数组:items,数组大小:n
    private E[] items;
    private int n = 0;
    // head表示队头下标,tail表示队尾下标
    private int head = 0;
    private int tail = 0;

    // 申请一个大小为capacity的数组
    public ArrayQueue(int capacity) {
        items = (E[])new Object[capacity];
        n = capacity;
    }

    // 入队操作,将item放入队尾
    public boolean enqueue(E item) {
        // tail == n表示队列末尾没有空间了
        if (tail == n) {
            // tail ==n && head==0,表示整个队列都占满了
            if (head == 0) return false;
            // 数据搬移
            for (int i = head; i < tail; ++i) {
                items[i-head] = items[i];
            }
            // 搬移完之后重新更新head和tail
            tail -= head;
            head = 0;
        }

        items[tail] = item;
        ++tail;
        return true;
    }

    // 出队
    public E dequeue() {
        // 如果head == tail 表示队列为空
        if (head == tail) return null;
        E ret = items[head];
        ++head;
        return ret;
    }
}

循环队列

上面使用数组实现了队列的基本操作,但是总会涉及到搬移数据,这样入队操作的性能就会受到影响,那有没有办法避免数据搬移数据呢?

在这里插入图片描述

从上图我们可以看出这是一个大小为9,head=1,tail=5的队列,再有数据入队时,将会放到下标为5的位置,tail+1,但是当tail=8时,如何处理呢?

在这里插入图片描述

从图中我们可以很明显的看出当有数据入队时,数据放到下标为8的位置,而tail则要更新为0,转化为代码时,我们要如何判断呢?

使用数组实现队列时,个人觉得:最关键的是,确定队空和队满的判定条件.

在用数组实现的非循环队列中,队满的判断条件是 tail == n,队空的判断条件是 head == tail。那针对循环队列,如何判断队空和队满呢?

队列为空的判断条件仍然是 head == tail。但队列满的判断条件是什么呢?

在这里插入图片描述

在一般情况下,队满的判断条件为 tial + 1 = head

但是有个特殊情况, head = 0, tail = n-1, 如下图所示

在这里插入图片描述

综上所述:

当head !=0,
	tail+1=head
当head =0,
	tail+1=n
因为tail+1的最大值为n
所以
(tail+1)%n=head

代码实现如下

public class CircularQueue<E> {
  // 数组:items,数组大小:n
  private E[] items;
  private int n = 0;
  // head表示队头下标,tail表示队尾下标
  private int head = 0;
  private int tail = 0;

  // 申请一个大小为capacity的数组
  public CircularQueue(int capacity) {
    items = (E[])new Object[capacity];
    n = capacity;
  }

  // 入队
  public boolean enqueue(E item) {
    // 队列满了
    if ((tail + 1) % n == head) return false;
    items[tail] = item;
    tail = (tail + 1) % n;
    return true;
  }

  // 出队
  public E dequeue() {
    // 如果head == tail 表示队列为空
    if (head == tail) return null;
    E ret = items[head];
    head = (head + 1) % n;
    return ret;
  }
}

链表队列

public class QueueByList<E> {

  // 队列的队首和队尾
  private Node head = null;
  private Node tail = null;

  // 入队
  public void enqueue(E value) {
    if (tail == null) {
      Node newNode = new Node(value, null);
      head = newNode;
      tail = newNode;
    } else {
      tail.next = new Node(value, null);
      tail = tail.next;
    }
  }

  // 出队
  public E dequeue() {
    if (head == null) return null;

    E value = (E) head.data;
    head = head.next;
    if (head == null) {
      tail = null;
    }
    return value;
  }

  //结点
  private static class Node<E> {
    private E data;
    private Node next;

    public Node(E data, Node next) {
      this.data = data;
      this.next = next;
    }

    public E getData() {
      return data;
    }
  }
}

阻塞队列

阻塞队列是一个支持两个附加操作的队列。

  1. 当队列满时,队列会阻塞插入元素的线程,直到队列不满。
  2. 当队列为空时,获取元素的线程会等待队列变为非空

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

JDK7提供了7个阻塞队列。分别是

  • ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
  • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值