5分钟图解数据结构之队列!

179 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

1 什么是队列?

  • 队列是一种先进先出(First In First Out,FIFO)的数据结构。其中是一组有序的数据项,遵循头部删除数据,尾部添加数据的原则。生活中的队列有哪些?排队买电影票、飞机票等这种都是先到先服务,当天买完了就没有了。队列也是一样先来先入队出队。如一组数据从左到右按顺序进行入队:1,3,1,4,5,2,0

    image.png

  • 队列可以做什么?

    • push:在队列尾部添加元素。
    • pop:在队列头部删除元素。
    • peek:获取队列的第一个元素,也就是队头,不改变队列原有元素。
    • isEmpty:判断队列是否为空。
  • 队列的Javascript实现?

  • 以两个栈为例:定义两个数组stackIn和stackOut作为存储栈,stackIn栈临时存储push进来的值,stackOut存储真正队列的数据序列。每次存到stackIn中的值都会被添加到stackOut数组开头(也就是队尾),获取队头也就是获取stackOut栈顶的数据,这样就遵循了队列先进先出的原则了。

     var MyQueue = function() {
        this.stackIn = [];// 临时存储数据元素队列
        this.stackOut = [];// 真正存储数据元素队列
     };
    

    image.png

  • 队列操作主要方法:对临时存储数组进行一一出栈添加到另一数组的开头,stackOut最后一个元素就是队头,数组的第一个元素就是队尾,符合队列的先进后出的原则。

    MyQueue.prototype.fillStack = function(){
        if(this.stackIn.length===0) return;
        while(this.stackIn.length){// 判断存储数据的栈
            this.stackOut.unshift(this.stackIn.pop());// 在数组开头添加元素
        }
    }
    
  • 每次添加元素都会对stackOut进行操作:

    MyQueue.prototype.push = function(x) {
       this.stackIn.push(x);
       this.fillStack();
    };
    
  • 读取队头(改变原来数组):this.stackOut.pop();

  • 读取队头(不改变原来数组):this.stackOut.slice(-1);;

2 什么是双端队列?

  • 双端队列:类似生活中的双火车头,火车前后都可以添加火车头,也可以去掉车厢节。同样的道理队列的前后都可以添加或删除队列元素。
var MyCircularDeque = function(k) {
   this.size=k;
   this.circularDeque = [];
};

image.png

  • 双端队列的实现:

  • insertFront:在队列头部添加元素。

    MyCircularDeque.prototype.insertFront = function(value) {
      if(this.isFull()){
          return false;
      }else{
          this.circularDeque.unshift(value);
      }
      return true;
    };
    
  • insertLast:在队列尾部添加元素。

    MyCircularDeque.prototype.insertLast = function(value) {
      if(this.isFull()){
          return false;
      }else{
          this.circularDeque.push(value);
      }
      return true;
    };
    
  • deleteFront:在队列头部删除元素。 this.circularDeque.shift()

  • deleteLast:在队列尾部删除元素。 this.circularDeque.pop()

  • getFront:获取队列头部元素。this.circularDeque[0]

  • getRear:获取队列尾部元素。this.circularDeque[this.circularDeque.length-1]

  • isEmpty:判断队列是否为空。

  • isFull:判断队列元素是否已经达到队列最大限制长度了。

3 什么是循环队列?

  • 循环队列:为了合理使用队列空间,通过head和tail指针控制队列添加或删除数据,初始的时候,head和tail都是指向队列索引0位置,当队列空间已经满了,head和tail又重新指回同一位置,怎么实现队列循环的呢?当tail指针已经指向了一圈了,此时队列还有空间,可以通过tail%size(队列长度)充分利用剩余空间。

  • 当队列添加元素时,tail指针后移,head指针保持原有位置不变。 image.png

  • 当队列删除元素时,head指针后移,tail指针保持原有位置不变。 image.png

  • 当元素已经到达了队列满空间位置了,head指针与tail指针又重新组合了,此时tail = tail%size。

    image.png

  • 循环队列的实现

  • enQueue:添加元素,tail指针后移。

    MyCircularQueue.prototype.enQueue = function (value) {
        if (this.isFull()) {
            return false;
        } else {
            this.circularQue[this.tail % this.size] = value;
            this.tail++;
            return true;
        }
    };
    
  • deQueue:删除元素,head指针后移。

    MyCircularQueue.prototype.deQueue = function () {
        if (this.isEmpty()) {
            return false;
        } else {
            this.head++;
        }
        return true;
    };
    
  • Front:获取对头元素。

    MyCircularQueue.prototype.Front = function () {
        if (this.isEmpty()) {
            return -1;
        } else {
            return this.circularQue[this.head% this.size];
        }
    };
    
  • Rear:获取队尾元素。

    MyCircularQueue.prototype.Rear = function () {
        if (this.isEmpty()) {
            return -1;
        } else {
            return this.circularQue[(this.tail-1)%this.size];
        }
    };
    
  • isEmpty:判断当前队列是否为空。

    MyCircularQueue.prototype.isEmpty = function () {
        return this.head === this.tail;
    };
    
  • isFull:判断当前队列空间是否已满。

    MyCircularQueue.prototype.isFull = function () {
        if (this.tail - this.head === this.size) {
            return true;
        }
        return false;
    };
    

4 leetcode实操: