很多人喜欢用数组的shift与push来实现出队与入队,但数组的unshift时间复杂度是O(n)的,我们可以用链表来实现一个入队时间复杂对为O(1)的简单队列类,实现代码如下:
enum ADDOREINSERT {
ADD = "ADD",
INSERT = "INSERT"
}
class NodePoint {
val: any;
next: any;
constructor(val:any, next?:NodePoint) {
this.val = val || undefined;
this.next = next || null;
}
}
// 通用队列
export const Queue = class {
head: NodePoint | null;
tail: NodePoint | null;
size: number;
constructor(val:any) {
// 初始化队列
if(val) {
this.head = this.tail = new NodePoint(val);
this.size = 1;
}else {
this.head = this.tail = null;
this.size = 0;
}
}
// 向队列里添加一个值,可以添加到头部,也可以添加到尾部,看具体调用的是pushQueue还是insertQueue
addVal(val:any, AddOrInsert:ADDOREINSERT) {
const node = new NodePoint(val);
if(this.size === 0) {
this.head = this.tail = node;
}else if(AddOrInsert === ADDOREINSERT.ADD) {
(this.tail as NodePoint).next = node;
this.tail = (this.tail as NodePoint).next;
}else if(AddOrInsert === ADDOREINSERT.INSERT) {
node.next = this.head;
this.head = node;
}
this.size++;
}
// 入队
pushQueue(...elements: any[]) {
// 遍历传入的值,向队列尾部添加值
for(let val of elements) {
this.addVal(val, ADDOREINSERT.ADD);
}
}
// 向队头插队
insertQueue(...elements: any[]) {
// 遍历传入的值,向队列头部添加值
/* 按照一般思维,如果要将[1,2,3] 插入 [4,5,6]头部,应该是[1,2,3,4,5,6],但若正序遍历1,2,3,插入到4,5,6
最终的结果会是[3,2,1,4,5,6],所以此处我们需要倒序插入到队列头
*/
const len = elements.length;
for(let i=len-1; i>=0; i--) {
this.addVal(elements[i], ADDOREINSERT.INSERT);
}
}
// 出队,移除队头元素,返回队头元素的值
popQueue() {
const ret = this.head?.val || null;
if(this.size === 0) {
return null;
}
if(this.size === 1) {
this.head = this.tail = null;
}else {
this.head = (this.head as NodePoint).next;
}
this.size--;
return ret;
}
// 移除指定元素,返回false移除失败,返回true移除成功
// 根据值进行匹配,对引用类型的值总是能正确执行,对基本数据类型就只移除第一个匹配到的元素
removeVal(val: any) {
// 队列里没有值
if(this.size === 0) {
return false;
}
// 若只有一个值,则头节点与尾节点指向同一个值,将头尾节点都置为null即可
if(this.size === 1) {
this.head = this.tail = null;
}else { // 否则找到对应的元素,将该元素指针置为null,并指向下一个元素
let cur = this.head;
// 声明一个慢指针pre,记录找到的节点的前一个节点
let pre = null;
// 遍历队列,找到与val相等的节点
while(cur && cur.val !== val) {
pre = cur;
cur = cur.next;
}
// 如果找到的节点是null,那么返回false
if(!cur) {
return false;
}
// 如果找到的节点是首节点,将首节点指向下一个节点就好
if(cur === this.head && cur.val === val) {
this.head = this.head.next;
}else if(cur === this.tail && cur.val === val) { // 如果找到的是尾节点,则将尾节点指向前一个节点,再将next指向null
this.tail = pre;
(this.tail as NodePoint).next = null;
}else if(cur.val === val) { // 找到的是中间的节点,则将上一个节点的next指向此节点的next即可
(pre as NodePoint).next = cur.next;
}
this.size--;
return true;
}
}
}