队列的基础知识

1,139 阅读2分钟

什么是队列

通常情况下,有一片连续的存储区,并且里面存储着不同类型的元素,还有两个指针,一个头指针,一个尾指针。并且尾指针指向的位置是最后一个元素的下一位(通常情况下)。支持出队入队操作,这就是队列。

普通队列

普通队列只支持从队首出队,队尾入队。出队操作头指针往后移动一位,入队操作尾指针向后移动一位,把元素放进队列。

队列溢出

在普通队列中,插入的元素数量,大于队列长度时,此时叫做队列溢出。但是在队列中元素并没有满,而只是尾指针移动到了队列末尾,叫做队列假溢出。

循环队列

为了对应解决队列假溢出的问题,出现了循环队列,如果尾指针指向队列末尾,此时队首是空,那么就可以将尾指针移动到队首位置,如还有添加元素入队操作,那么依次判断并往后移动,这样就类似与链表有环,所以叫做循环队列。

队列的典型应用场景

  1. CPU的超线程技术,每个CPU都有N个核心如双核四核,每个核心对应一个指令队列,如果一个核心对应多个队列,就叫虚拟核心。比如双核对应4个任务队列就叫虚拟四核(标准实现一般都是一个核心对应两个队列,以达到最大利用率和性能)。
  2. 线程池的任务队列,(扩展:进程和线程,进程代表资源的总和,线程代表进程中的某个事物,一个进程可以包含多个线程)
  3. 总结:队列的使用场景是用做缓冲区。

使用JS创建一个普通队列

实现普通队列的基本属性和操作(不是标准答案,只是实现队列大概的结构)

class Queue {
  constructor(length){
    this.queue = new Array(length); // 初始化队列长度
    this.head = 0; // 初始化头指针
    this.tail = 0; // 初始化尾指针
  }
  push(val) { // 入队
    if (this.full()) {
      console.error("队列已满");
      return;
    }
    this.queue[this.tail] = val;
    this.tail += 1; // 尾指针向后移动一位
    this.getQueueElement();
  }
  pop() { // 出队
    if (this.empty()) return;
    this.head += 1; //头指针往后走一位
    this.getQueueElement();
  }
  empty() { // 判空
    return this.head === this.tail;
  }
  full() { // 判满
    return this.tail === this.queue.length;
  }
  front() { // 查看队首元素
    return this.queue[this.head];
  }
  getQueueElement() {
    let ret = ""
    for (let i = this.head; i < this.tail; i++){
      ret += ` ${this.queue[i]} `;
    }
    console.log(ret);
  }
}
const ordinaryQueue = new Queue(5);
ordinaryQueue.push(1);
ordinaryQueue.push(2);
ordinaryQueue.push(3);
ordinaryQueue.pop();
ordinaryQueue.pop();
ordinaryQueue.push(4);
ordinaryQueue.push(5);
ordinaryQueue.push(6); // 队列假溢出。
console.log(ordinaryQueue.front()) // 队首元素

使用JS创建一个循环队列

实现循环队列的基本属性和操作(不是标准答案,只是实现队列大概的结构)

class Queue {
  constructor(length){
    this.queue = new Array(length); // 初始化队列长度
    this.head = 0; // 初始化头指针
    this.tail = 0; // 初始化尾指针
    this.length = length; // 队列总大小
    this.cnt = 0; // 队列元素中的数量
  }
  push(val) { // 入队
    if (this.full()) {
      console.error("队列已满");
      return;
    }
    this.queue[this.tail] = val;
    this.tail += 1; // 尾指针向后移动一位
    this.cnt += 1;
    if (this.tail === this.length){ // 判断是否指向数组最后一位
      this.tail = 0; // 继续从队列头开始走,达到一圈一圈循环的效果
    }
    this.getQueueElement();
  }
  pop() { // 出队
    if (this.empty()) return;
    this.head += 1; //头指针往后走一位
    this.cnt -= 1;
    if (this.head === this.length){ // 判断是否指向数组最后一位
      this.head = 0; // 继续从队列头开始走,达到一圈一圈循环的效果
    }
    this.getQueueElement();
  }
  empty() { // 判空
    return this.cnt === 0;
  }
  full() { // 判满
    return this.cnt === this.length;
  }
  front() { // 查看队首元素
    return this.queue[this.head];
  }
  size() { // 返回队列中元素的数量
    return this.cnt;
  }
  getQueueElement() {
    let ret = ""
    for (let i = 0, j = this.head; i < this.cnt; i++){
      ret += ` ${this.queue[j]} `;
      j +=1;
      if(j === this.length) j = 0; // 因为head有可能已经走了n圈了。所以cnt个输出,需要重置j
    }
    console.log(ret);
  }
}
const loopQueue = new Queue(5);
loopQueue.push(1);
loopQueue.push(2);
loopQueue.push(3);
loopQueue.pop();
loopQueue.pop();
loopQueue.push(4);
loopQueue.push(5);
loopQueue.push(6); // 队列依然可插入。
loopQueue.push(7); 
loopQueue.push(8); // 队列真溢出。
console.log(loopQueue.front()) // 队首元素