06 数据结构-队列

优化队列实现:顺序、循环与并发,深度解析

队列概念可以理解为一堆人排队买票,先来的先买,后来的排在后面,不允许插队。也就是先进先出,这就是队列

上一篇栈中,我们学到,栈只有2个操作,入栈和出栈。队列也只有2个操作,入队和出队。

  • 入队:放一个数据到队列尾部
  • 出队:从队列头部拿出一个数据。

 一、顺序队列和链式队列

队列和栈一样,既可以用数组实现,也可以用链表来实现。用数组实现的队列叫顺序队列,用链表实现的队列叫链式队列

顺序队列的实现:

// 用数组实现的队列
public class ArrayQueue {
  // 数组:items,数组大小:n
  private String[] items;
  private int n = 0;
  // head表示队头下标,tail表示队尾下标
  private int head = 0;
  private int tail = 0;

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


   // 入队操作,将item放入队尾
  public boolean enqueue(String 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 String dequeue() {
    // 如果head == tail 表示队列为空
    if (head == tail) return null;
    // 为了让其他语言的同学看的更加明确,把--操作放到单独一行来写了
    String ret = items[head];
    ++head;
    return ret;
  }
}

这个实现中,一共有2个变量,用来标记数据的头和尾。当数组满后,无法入队。当数组后半部分满了之后,我们入队的时候,需要将数据移动到数组的左边,然后重新修改head和tail。使得数组可以继续入队。

 二、循环队列

上面的操作中,当tail==数组的大小n,但是head并不等于0 的时候,我们进行了数据的迁移,这样入队操作的性能肯定会有影响,我们可以用循环队列来解决这个问题。

 如图:原本数组是有头有尾的,如果我们能让tail到达7的时候,再有数据入队的时候,tail=0,那么就不需要数据的迁移,而能够继续插入数据了。

循环队列的实现难度在于如何判定队列空队列满

队列为空的条件仍然是tail == head。队列满的时候,有一个规律:(tail+1)%n = head。

按下图就是(3+1)%8 = 4

 为了判断队列满,我们需要浪费一个存储空间:


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

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

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

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

 三、阻塞队列和并发队列

阻塞队列实际就是在队列为空的时候,从队列取数据会被阻塞,程序一直在等待,直到队列中有数据,才会拿到数据返回。同理,插入数据如果队列满了也会被阻塞,直到有位置可以插入。

阻塞队列在多线程情况下会用到,比如生产者消费者模型,快的一方会被阻塞。

多线程如果有多个生产者,同时入队,会有线程安全问题,比如往同一个位置存储数据,会造成数据覆盖而丢失,这个时候,就需要并发队列--线程安全的队列。

四、总结

队列和栈很相似,也只有2个操作,入队和出队。属于先进先出。

循环队列是重点,想要代码实现,重点是判空和判满的条件。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

七号公园的忧伤

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

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

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

打赏作者

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

抵扣说明:

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

余额充值