队列

队列

队列是一种先进先出的数据结构,基本操作就是入队,放一个数据到队列尾部;出队,从队列头部取一个元素. 队列与栈一样,也是一种操作受限的线性表数据结构;
在这里插入图片描述栈与队列

顺序队列与链式队列

队列可以使用数组实现,也可以使用链表实现,用数组实现的队列叫顺序队列,用链表实现的队列叫链式队列

数组的实现方式:

// 用数组实现的队列
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;
  }

  // 入队
  public boolean enqueue(String item) {
    // 如果 tail == n 表示队列已经满了
    if (tail == n) return false;
    items[tail] = item;
    ++tail;
    return true;
  }

  // 出队
  public String dequeue() {
    // 如果 head == tail 表示队列为空
    if (head == tail) return null;
    // 为了让其他语言的同学看的更加明确,把 -- 操作放到单独一行来写了
    String ret = items[head];
    ++head;
    return ret;
  }
}

对于栈来说,只需一个栈顶指针即可,但队列需要两个指针,一个是head指针,指向队头;一个是tail指针,指向队尾;

如下图所示,当a,b,c,d依次入队之后,队列中head指针指向下标为0的位置,tail指针指向下标为4的位置.
在这里插入图片描述
当两次出队操作之后,队列中head指针指向下标为2的位置,tail指针仍然指向下标为4的位置
在这里插入图片描述
随着不停的进行入队,出队操作,head和tail都会持续后移动,当tail移动到最右边,即使数组中还有空闲空间,也无法继续往队列中添加数据了;如何解决?

用数据搬移解决,每次进行出队操作都相当于删除数组下标为0的数据,要搬移整个队列中数据,这样出队操作的时间复杂度就会从原来的O(1)变为O(n),能不能优化一下

实际上,出队时可以不用搬移数据,如果没有空闲时间,只需在入队时再集中触发一次数据的搬移操作改造一下equeue()的实现

 // 入队操作,将 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;![在这里插入图片描述](https://img-blog.csdnimg.cn/20190623223240743.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pfc196MjAxNg==,size_16,color_FFFFFF,t_70)
    ++tail;
    return true;
  }

当队列指针移动到数组的最右边后,如果有新的数据入队,就可以将head到tail之间的数据,整体搬移到数组0到tail-head的位置
在这里插入图片描述

基于链表的队列实现方法

需要两个指针: head指针和tail指针,分别指向链表的第一个节点和最后一个节点,入队时,tail->next=new_node,tail=tail->next;出队时,head=head->next
在这里插入图片描述

循环队列

在用数组实现队列时,在tail==n时,会有数据搬移操作,这样会影响入队操作性能;循环队列,长得像一个环,原本数组是有头有尾的,现在把收尾相连,扳成一个环;
在这里插入图片描述
如图所示,队列大小为8,当前head=4,tail=7,当有一个新元素a入队时,放入下标为7的位置,但此时不将tail更新为8,而是将其在环中后移一位,到下标为0的位置,当再有一个元素b入队时,然后tail加1更新为1;如图所示在这里插入图片描述
循环队列避免了数据的搬移操作,但是实现一个循环队列,关键是要确定好对列空和队满的判定条件

队列为空的判断条件仍然是head==tail;队满的判断条件要总结规律,队满时 (tail+1)%n=head
在这里插入图片描述

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;
  }
}

阻塞队列

就是在队列基础上增加了阻塞操作,就是在队列为空的时候,从队头取数据会被阻塞,因此此时还没有数据可取,直到队列中有数据才能返回,如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回

在这里插入图片描述
线程池没有空闲线程时,新的任务请求线程资源时,线程池该如何处理?各种处理策略如何实现?
一般两种处理策略,第一种是非阻塞的处理方式,直接拒绝任务请求;另一种是阻塞的处理方式,请请求排队,等到有空闲线程时,取出排队的请求继续处理; 如何存储排队的请求?

若希望公平的处理每个排队的请求,先进者先服务,所以队列这种数据结构很适合来存储排队请求;

基于链表的实现方式,可以实现一个支持无限排队的无界队列,但是可能导致过多的请求排队等待,请求处理的响应时间过长,故针对响应时间比较敏感的系统,基于链表实现无限排队的线程池是不合适的;

基于数组实现的有界队列,队列的大小有限,所以线程池中排队的请求超过队列大小时,接下来的请求就会被拒绝,这种方式对响应时间敏感的系统来说更加合理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值