9

先赞再看, 养成习惯 :P

俗话说得好, "时刻准备着"。

算法和数据结构, 对工程师来说是十分重要的。

而这一部分, 靠短期的冲刺学习是很难掌握的。只有靠刻意的学习和不断练习才能掌握。

今天我们就来复习下队列。

了解队列的使用姿势

队列是非常常见的数据结构, 面试中也经常出现。

今天我们就说一下这种数据结构, 然后看两道题。

文中可能涉及到链表,链表我在之前的文章中写过,参考链接:

[第28期] 回顾一下常见的链表操作
[第29期] 熟悉链表的几个经典题目

队列

队列是一种线性的数据结构, 表现上是先进先出, FIFO.

生活中典型的场景就是排队了,从后插入新元素, 移除最旧的元素:

下面我们用两种姿势来实现一个队列:

  1. 链表

leetcode #232 用栈实现队列

使用栈实现队列的下列操作:

  1. push(x) -- 将一个元素放入队列的尾部。
  2. pop() -- 从队列首部移除元素。
  3. peek() -- 返回队列首部的元素。
  4. empty() -- 返回队列是否为空。

示例:

MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek();  // 返回 1
queue.pop();   // 返回 1
queue.empty(); // 返回 false

1.用栈实现队列

栈是一种先进后出的数据结构, 队列是一种先进先出的数据结构,那怎么用栈来模拟队列呢?

很简单, 倒两次就行了。

用两个栈, 一个用于入队, 一个用于出队:

图解:

代码实现:

/**
 * Initialize your data structure here.
 */
var MyQueue = function() {
  this.input = [];
  this.output = [];
};

/**
 * Push element x to the back of queue.
 * @param {number} x
 * @return {void}
 */
MyQueue.prototype.push = function(x) {
  this.input.push(x);
};

/**
 * Removes the element from in front of queue and returns that element.
 * @return {number} 从队列首部移除元素
 */
MyQueue.prototype.pop = function() {
  while (this.input.length) {
    this.output.push(this.input.pop());
  }
  var first = this.output.pop();

  while (this.output.length) {
    this.input.push(this.output.pop());
  }
  return first;
};

/**
 * Get the front element. 返回队列首部的元素。
 * @return {number}
 */
MyQueue.prototype.peek = function() {
  return this.input[0];
};

/**
 * Returns whether the queue is empty.
 * @return {boolean} 返回队列是否为空
 */
MyQueue.prototype.empty = function() {
  return !this.input.length && !this.output.length;
};

var obj = new MyQueue();
obj.push(1);
obj.push(2);
obj.push(3);

var param_2 = obj.pop();
console.log(param_2); // 队首出来, 1

var param_3 = obj.peek();
console.log(param_3); // 返回队列首部的元素: 2

var param_4 = obj.empty();
console.log('isEmpty: ', param_4); // false

2. 用链表实现队列

链表我们都很熟悉了, 之前两期都是关于链表的:xxx, xxx

先看一下基础结构, 包含我们要实现哪些功能:

// Queue using linkedlist
function QueueUsingLinkList() {
  let Node = function(elm) {
    this.element = elm;
    this.next = null;
  };

  // To keep track of the size
  let length = 0;

  //To keep track of the list
  let head = null;

  // Enqueue data in the queue
  this.enqueue = function(elm) {};

  // Remove the item from the queue
  this.dequeue = function() {};

  // Return the first element in the queue
  this.peek = function() {};

  //Return the last element in the queue
  this.rear = function() {};

  //Check if queue is empty
  this.isEmpty = function() {};

  //Return the size of the queue
  this.size = function() {};

  //Clear the queue
  this.clear = function() {};
}

enqueue, 往队列中添加元素

首先要检查队列是不是空的, 如果是空的, 就把新元素当成头结点,否则, 就把新元素加到末尾。

代码实现:

//Enqueue data in the queue
this.enqueue = function (elm) {
    let node = new Node(elm),
        current;

    //If head is empty then
    //Add the node at the beginning
    if (head === null) {
        head = node;
    } else {
        //Else add the node as the
        //Next element of the existing list
        current = head;
        while (current.next) {
            current = current.next;
        }

        current.next = node;
    }

    //Increase the length
    length++;
};

dequeue, 从队列中删除元素

队列是先进先出的数据结构, 所以出队的时候, 要移除并返回头部的元素,长度减一,并把指针往后移步一位。

代码实现:

  //Remove the item from the queue
  this.dequeue = function () {
      let current = head;

      //If there is item then remove it 
      //and make the next element as the first
      if (current) {
          let elm = current.element;
          current = current.next;
          head = current;
          length--;
          return elm;
      }

      return null;
  }

其他辅助方法

核心的入队, 出队,我们已经实现了, 为了方便调试, 我们顺便实现几个辅助函数:

  • peek 查看头部元素
  • rear 查看尾部元素
  • to array 转换成数组
  • size 查看大小
  • isEmpty 查看是否为空
  • 清空队列

isEmpty

//Check if queue is empty
this.isEmpty = function() {
    return length === 0;
}

size

//Return the size of the queue
this.size = function() {
    return length;
}

以数组的形势查看:

//Convert the queue to an array
this.toArray = function () {
    let arr = [];
    let current = head;
    while (current) {
        arr.push(current.element);
        current = current.next;
    }

    return arr;
}

rear 查看尾部元素

//Return the last element in the queue
this.rear = function () {
    let current = head;

    //If head is empty
    //Return null
    if (current === null) {
        return null;
    }

    //Return the last elememnt
    while (current.next) {
        current = current.next;
    }

    return current.element;
}

peek

//Return the first element in the queue
this.peek = function () {
    if (head) {
        return head.element;
    }

    return null;
}

clear

//Clear the queue
this.clear = function() {
  head = null;
  length = 0;
}

测试


let queue = new QueueUsingLinkList();
console.log(queue.isEmpty()); // true

queue.enqueue('pranav');
queue.enqueue('sachin');
queue.enqueue('yogesh');
console.log(queue.toArray()); // ["pranav", "sachin", "yogesh"]

queue.dequeue();
queue.dequeue();
console.log(queue.toArray()); // ["yogesh"]

queue.enqueue('prashant');
queue.enqueue('yadav');
queue.dequeue();
console.log(queue.toArray()); // ["prashant", "yadav"]
console.log(queue.size()); // 2
console.log(queue.peek()); // "prashant"
console.log(queue.rear()); // "yadav"

完整代码

//Queue using linkedlist
function QueueUsingLinkList() {
    //Node 
    let Node = function (elm) {
        this.element = elm;
        this.next = null;
    }

    //To keep track of the size  
    let length = 0;

    //To keep track of the list
    let head = null;

    //Enqueue data in the queue
    this.enqueue = function (elm) {
        let node = new Node(elm),
            current;

        //If head is empty then 
        //Add the node at the beginning
        if (head === null) {
            head = node;
        } else {
            //Else add the node as the
            //Next element of the existing list
            current = head;
            while (current.next) {
                current = current.next;
            }

            current.next = node;
        }

        //Increase the length
        length++;
    }

    //Remove the item from the queue
    this.dequeue = function () {
        let current = head;

        //If there is item then remove it 
        //and make the next element as the first
        if (current) {
            let elm = current.element;
            current = current.next;
            head = current;
            length--;
            return elm;
        }

        return null;
    }

    //Return the first element in the queue
    this.peek = function () {
        if (head) {
            return head.element;
        }

        return null;
    }

    //Return the last element in the queue
    this.rear = function () {
        let current = head;

        //If head is empty
        //Return null
        if (current === null) {
            return null;
        }

        //Return the last elememnt
        while (current.next) {
            current = current.next;
        }

        return current.element;
    }

    //Convert the queue to an array
    this.toArray = function () {
        let arr = [];
        let current = head;
        while (current) {
            arr.push(current.element);
            current = current.next;
        }

        return arr;
    }

    //Check if queue is empty
    this.isEmpty = function () {
        return length === 0;
    }

    //Return the size of the queue
    this.size = function () {
        return length;
    }

    //Clear the queue
    this.clear = function () {
        head = null;
        length = 0;
    }

}

总结

掌握这些常见的数据结的基础操作,对我们的日常工作也有好处。

要学好数据结构和算法, 需要 三分理论, 七分实践。

希望今天的内容对你有所启发,谢谢 :)

最后

如果你觉得内容有帮助, 可以关注下我的公众号 「 前端e进阶 」,一起学习, :)
clipboard.png


皮小蛋
8k 声望12.8k 粉丝

积跬步,至千里。