JavaScript 数据结构和算法——队列
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 2 天,点击查看活动详情。
介绍
队列和栈非常的类似,队列是一种先进先出的线性结构,生活当中类似的场景比如排队打饭,排队买票等。最先的那个人先得到服务,也称为先来先服务。在业务场景当中,我们经常会有发多个网络请求的情况,其中某个请求必须等前一个请求的数据,这时候我们可以实现一个队列来实现业务需求,当第一个请求请求回调会来,我们再调用下一个请求,依次类推,调用完所有的请求。
思路
队列的实现思路其实和栈很类似,只不过增加元素和删除元素的逻辑有改变。我们在对象当中依次添加属性,然后通过两个变量top、bottom来记录当前的队列首元素和队列尾元素。通过top记录当前首元素的key值,后续我们在获取首元素的时候,只需要通过top来获取就好了,而bottom则记录尾元素的后一位元素,当我们需要获取尾元素的时候,我们直接通过bottom - 1就可以获取当前队列的尾元素了。如果需要添加元素,则直接添加到bottom的位置上就可以了,然后bottom++。
代码实现
//创建Queue
class Queue {
constructor() {
//指向队列的首元素
this.top = 0;
//指向队列的尾元素
this.bottom = 0;
//创建队列对象
this.items = {};
}
//push增减队列元素
push(ele) {
this.items[this.bottom] = ele;
//尾部加一
this.bottom++;
}
//删除首元素
shift() {
if (this.isEmpty()) {
return;
}
let result = this.items[this.top];
delete this.items[this.top];
//首元素下标位移,总长度减少
this.top++;
return result;
}
//判断队列是否为空
isEmpty() {
return this.bottom - this.top === 0;
}
//返回队列长度
size() {
return this.bottom - this.top;
}
//返回队列首元素
peek() {
return this.items[this.top];
}
//清空队列
clear() {
//初始化队列
this.items = {};
this.count = 0;
this.top = 0;
this.bottom = 0;
}
// 转成字符串
toString() {
let result = ``;
for (let i = this.top; i <= this.bottom - 1; i++) {
result += this.items[i] + ",";
}
return result;
}
}
const queue = new Queue();
queue.push("jack");
queue.push("mark");
queue.push("lazy");
console.log(queue.size()); // 3
console.log(queue.shift()); // jack
console.log(queue.size()); //2
console.log(queue.toString()); // jmark,lazy,
双端队列介绍
双端队列顾名思义就是两端都可以增加删除元素。有点像栈和队列的集合。双端队列在现实生活当中的例子有电影院、餐厅中排队,你买完了票离开了队首,但是你还有一些东西需要咨询一下,于是你直接走到的队伍的首位,这时候就在队列的首元素前增加的一个元素。或者在队伍尾部的人如果不想买票了,那么他也可以离开队列。双端队列比较灵活。在日常的计算机科学当中,双端队列的一个常见的应用技术存储一些列的撤销操作,当你想撤销你的撤销操作的时候,就可以把撤销操作从撤销队列当中取出。
双端队列思路
按照这个思路,我们可以结合栈的方法来实现双端队列。首先定义一个{}来保存双端队列当中的元素。然后定义两个变量top、bottom来记录队列的首元素和尾元素。定义push、pop、shift、unshift方法来实现队列尾增减元素、队列首增减元素。push方法通过获取bottom的指向,向{}当中添加值,同时bottom++。pop方法通过bottom - 1来获取当前队列的最后一位,通过delete删除这一属性。shift方法通过top来获取首位元素,然后delete这一属性。unshift方法通过将队列当中的元素往后移一位,将前面的首位空出来,然后将新增加的元素添加到首位上。
双端队列实现
//创建双端队列
class PrivateAttr<T> {
public items: {
[propName: number]: T;
};
public top: number;
public bottom: number;
}
interface DeQueTypes<T> extends PrivateAttr<T> {
unshift(ele: T);
push(ele: T);
pop(): T;
shift(): T;
isEmpty(): boolean;
size(): number;
toString(): string;
peekTop(): number;
peekBottom(): number;
}
class DeQue extends PrivateAttr<number> implements DeQueTypes<number> {
constructor() {
super();
this.items = {};
this.top = 0;
this.bottom = 0;
}
//移除首部元素
shift(): number {
let result = this.items[this.top];
delete this.items[this.top];
this.top++;
return result;
}
//在首部添加元素
unshift(ele: number) {
for (let i = this.bottom; i >= this.top; i--) {
this.items[i] = this.items[i - 1];
}
this.items[this.top] = ele;
}
//在尾部添加元素
push(ele: number) {
this.items[this.bottom] = ele;
this.bottom++;
}
//删除尾部元素
pop(): number {
let result = this.items[this.bottom];
delete this.items[this.bottom];
this.bottom--;
return result;
}
//返回队列是否为空
isEmpty(): boolean {
return this.bottom - this.top === 0;
}
// 返回队列长度
size(): number {
return this.bottom - this.top;
}
//队列字符串
toString(): string {
let result = ``;
for (let i = this.top; i < this.bottom - 1; i++) {
result += this.items[i] + ",";
}
return result;
}
//返回首端元素
peekTop(): number {
console.log(this.items);
return this.items[this.top];
}
// 返回尾端元素
peekBottom(): number {
return this.items[this.bottom - 1];
}
}
const deQue = new DeQue();