携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
栈的概念
队列是一种先入先出(First In First Out)的数据结构。
什么是先入先出?相信大家都有在外面吃饭时碰到排队等号的情况吧,假设大家食量一样,干饭速度相同的情况下,是不是我比你先吃,我就先抹抹嘴走人了?队列就是这样的数据结构,先排上号的先吃先走,后排上号的晚吃晚走,依次有序。
我们看个图加深一下理解:
可以发现,原本的进入顺序是 1->2->3,出去的顺序依然是 1->2->3,依次有序。
队列的几个名词
队头(front)与队尾(rear)
由于队列是 先入先出 的数据结构,所以队头就是最前面的元素,也就是第一个元素,队尾就是最新进入的元素,也就是最后一个元素。
入队(enQueue)与出队(deQueue)
我们都知道栈是 前入先出 的,入代表着进入,因此入队这个操作我们是向队尾添加元素,使之成为新的队尾元素,队尾元素是最后一个出去的。而出队代表着出,前入先出,所以出队这个操作是 将队头元素弹出。通过入队和出队这两个概念的理解,我们可以发现队列和栈的不同点在于,队列的头是第一个元素,出队出的是 队头 元素,也就是第一个元素,队列的尾是最后一个元素,入队是向 队尾 入的,也就是往最后面的位置添加,像 排队 一样。
需要注意的是,我们每执行一次 出队 操作,就意味着这个被出队的位置 再也不能 存储数据了。如上图,一个 capacity=5 的队列,当前元素为 queue=[1,2,3],执行一次出队操作后,队头元素 1 出队了,剩余元素 [,2,3],此时队头已经变成 2 所在的位置了,那么 1 原本的位置上已经不能存储数据了。
这也是队列的弊端之一,后面会讲解一个新的数据结构——循环队列,就是用来解决容量浪费的问题。
分析队列结构
通过以上的几点相信大家对队列有了一定的理解,那么我们都知道 Javascript 中原生是没有提供队列这个数据结构的,所以如果我们想要使用,则需要手动去实现它。那么基于前面理解的几个特性,我们很容易就联想到 使用数组来模拟队列结构。
手撕队列
class Queue {
constructor(capacity) {
this.queue = [];
this.start = 0;
this.end = 0;
this.capacity = capacity;
}
isEmpty() {
return this.start === this.end;
}
front() {
return this.queue[this.start];
}
rear() {
if (this.isEmpty()) return -1;
return this.queue[this.end - 1];
}
enQueue(v) {
if (this.end >= this.capacity) return false;
this.queue[this.end++] = v;
return true;
}
deQueue() {
if (this.isEmpty()) return -1;
return this.queue[this.start++];
}
}
const queue = new Queue(3);
console.log(queue.isEmpty()); // true
console.log(queue.deQueue()); // -1
console.log(queue.enQueue(1)); // true
console.log(queue.enQueue(2)); // true
console.log(queue.rear()); // 2
console.log(queue.enQueue(3)); // true
console.log(queue.rear()); // 3
console.log(queue.enQueue(4)); // false
console.log(queue.deQueue()); // 1
console.log(queue.enQueue(5)); // false
巩固练习题
大家有需要巩固练习的也可以看看几道 LeetCode 上的题目: