队列

93 阅读2分钟

队列基础知识

概念

先入先出 FIFO
一片连续的存储区域,可以存储任意类型的数据
队列会有两个指针:
- 一个头指针
- 一个尾指针:指向最后一个元素的下一位,一般描述区间都是左闭又开的方式,尾指针上不放元素,这样【tail - head】正好是队列当前存放数据的数量
队列就像是去排队买火车票的场景

支持两种基本操作:
- 入队:尾指针向后移动一位,将数据放进去
- 出队:对于普通队列来说只支持从队首出队,就是头指针向后移动一位。

队列溢出:队列满了。不能再放入数据。

队列假溢出:队列的尾指针走到了队列的最后一位,普通队列会判断队列已经满了,但是实际上队列前方还有可以存放数据的空间。

循环队列:为了解决队列的假溢出。尾指针在向后移动到最后一位时,发现指向了一个不存在的地址,此时会移动到队列的队首。循环队列不算是特殊的队列,只是为了有效利用空间,为了解决假溢出的一种处理方式

几种经典队列实现方式

普通队列

// 结构定义
// 标准实现方式,head和tail一开始都指向下标为0的位置
// 入队时讲数据放入tail指向的位置,并且将tail向后移动一位
class Queue {
  constructor(n = 10) {
    this.head = 0;
    this.tail = 0;
    this.arr = [];
    this.length = n;
  }
  // 入队
  push (data) {
    // 判断队列是否已经满了
    if (this.full()) {
      console.error('队列满了');
      return;
    }
    this.arr[this.tail] = data;
    this.tail += 1;
    return;
  }
  // 出队-头指针向后移动一位即完成了出队
  pop () {
    if (this.empty()) {
      console.error('队列已经没有数据了');
      return;
    }
    this.head += 1;
  }
  // 判断队列是否是空的
  empty () {
    return this.head === this.tail;
  }
  // 普通队列判断队列是否是满的-尾指针指向最后一个元素的下一位
  full () {
    return this.tail && (this.tail === this.length)
   }
  // 查看队首元素
  front () {
    return this.arr[this.head];
  }
  // 查看队列中元素的数量
  size () {
    return this.tail - this.head;
  }
  // 打印队列中所有元素
  output () {
    for (let i = this.head; i < this.arr.length; i++){
      console.log(this.arr[i]);
    }
  }
}
const q = new Queue(5);
q.push(1);
q.push(2);
q.push(3);
q.output(); // 1 2 3
console.log(q.front()); // 1
console.log(q.size()); // 3
q.pop();
q.output(); // 2 3
console.log(q.size()); // 2
 

简单粗暴的循环队列

class CircularQueue {
  constructor(n) {
    this.length = n;
    this.head = 0;
    this.tail = 0;
    this.arr = [];
    // 当前队列中元素数量
    this.cnt = 0;
  }
  // 入队
  push (data) {
    if (this.full()) {
      console.error('队列已经满了');
      return
    }
    this.arr[this.tail] = data;
    this.tail += 1;
    this.cnt += 1;
    if (this.tail === this.length) {
      this.tail = 0;
    }
  }
  // 出队
  pop () {
    if (this.empty()) {
      console.error('队列中已经空了');
      return
    }
    this.cnt -= 1;
    this.head += 1;
    if (this.head === this.arr.length) {
      this.head = 0
    }
   }
  // 判断队列是否已经空了
  empty () {
    this.cnt === 0;
  }
  // 判断队列是否已经满了
  full () {
    return this.cnt === this.length;
  }
  // 返回队首元素
  front () {
    return this.arr[this.head];
  }
  // 当前队列元素数量
  size () {
    return this.cnt;
  }
  // 打印当前所有元素
  output () {
    let result = '';
    for (let i = 0, j = this.head; i < this.cnt; i++){
      result += this.arr[j] + ' ';
      j += 1;
      if (j === this.length) {
        j = 0;
      }
    }
    console.log('output', result);
  }
}
console.log('----------------------------------------')
console.log('循环队列');
const cq = new CircularQueue(5);
cq.push(1);
cq.push(2);
cq.push(3);
cq.output(); // output 1 2 3 
cq.pop();
cq.output(); // output 2 3 
cq.push(4);
cq.push(5);
cq.output(); // output 2 3 4 5 
cq.push(6);
cq.output(); // output 2 3 4 5 6 
console.log('----------------------------------------')