「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
前言
前端算法系列是我对算法学习的一个记录, 主要从常见算法、数据结构、算法思维、常用技巧几个方面剖析学习算法知识, 通过LeetCode平台实现刻意练习, 通过掘金和B站的输出来实践费曼学习法, 我会在后续不断更新优质内容并同步更新到掘金、B站和Github, 以记录学习算法的完整过程, 欢迎大家多多交流、点赞、收藏, 让我们共同进步, daydayup👊
目录地址:目录篇
相关代码地址: Github
相关视频地址: 哔哩哔哩-百日算法系列
一、循环队列
LeetCode: 622. 设计循环队列
循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
/**
* @param {number} k
*/
var MyCircularQueue = function(k) {
this.k = k
this.cnt = 0
this.head = 0
this.tail = 0
this._v = new Array(k)
};
/**
* @param {number} value
* @return {boolean}
*/
MyCircularQueue.prototype.enQueue = function(value) {
// 入队
if (this.isFull()) return false
this._v[this.tail] = value
this.tail = (this.tail + 1) % this.k
this.cnt++
return true
};
/**
* @return {boolean}
*/
MyCircularQueue.prototype.deQueue = function() {
// 出队
if (this.isEmpty()) return false
this.head = (this.head + 1) % this.k
this.cnt--
return true
};
/**
* @return {number}
*/
MyCircularQueue.prototype.Front = function() {
// 队首
if (this.isEmpty()) return -1
return this._v[this.head]
};
/**
* @return {number}
*/
MyCircularQueue.prototype.Rear = function() {
// 队尾
if (this.isEmpty()) return -1
return this._v[(this.tail - 1 + this.k) % this.k]
};
/**
* @return {boolean}
*/
MyCircularQueue.prototype.isEmpty = function() {
// 判空
return !this.cnt
};
/**
* @return {boolean}
*/
MyCircularQueue.prototype.isFull = function() {
// 判满
return this.cnt === this.k
};
/**
* Your MyCircularQueue object will be instantiated and called as such:
* var obj = new MyCircularQueue(k)
* var param_1 = obj.enQueue(value)
* var param_2 = obj.deQueue()
* var param_3 = obj.Front()
* var param_4 = obj.Rear()
* var param_5 = obj.isEmpty()
* var param_6 = obj.isFull()
*/
二、循环双端队列
LeetCode: 641. 设计循环双端队列
双端队列指即可以从头部出队、也可以从头部入队, 即可以 尾部出队, 也可以 尾部入队
/**
* @param {number} k
*/
var MyCircularDeque = function(k) {
this.k = k
this.cnt = 0
this.head = 0
this.tail = 0
this._v = new Array(k)
};
/**
* @param {number} value
* @return {boolean}
*/
MyCircularDeque.prototype.insertFront = function(value) {
// 队首入队
if (this.isFull()) return false
this.head = (this.head - 1 + this.k) % this.k
this._v[this.head] = value
this.cnt++
return true
};
/**
* @param {number} value
* @return {boolean}
*/
MyCircularDeque.prototype.insertLast = function(value) {
// 队尾入队
if (this.isFull()) return false
this._v[this.tail] = value
this.tail = (this.tail + 1) % this.k
this.cnt++
return true
};
/**
* @return {boolean}
*/
MyCircularDeque.prototype.deleteFront = function() {
// 队首出队
if (this.isEmpty()) return false
this.head = (this.head + 1) % this.k
this.cnt--
return true
};
/**
* @return {boolean}
*/
MyCircularDeque.prototype.deleteLast = function() {
// 队尾出队
if (this.isEmpty()) return false
this.tail = (this.tail - 1 + this.k) % this.k
this.cnt--
return true
};
/**
* @return {number}
*/
MyCircularDeque.prototype.getFront = function() {
// 队首
if (this.isEmpty()) return -1
return this._v[this.head]
};
/**
* @return {number}
*/
MyCircularDeque.prototype.getRear = function() {
// 队尾
if (this.isEmpty()) return -1
return this._v[(this.tail - 1 + this.k) % this.k]
};
/**
* @return {boolean}
*/
MyCircularDeque.prototype.isEmpty = function() {
// 判空
return !this.cnt
};
/**
* @return {boolean}
*/
MyCircularDeque.prototype.isFull = function() {
// 判满
return this.cnt === this.k
};
/**
* Your MyCircularDeque object will be instantiated and called as such:
* var obj = new MyCircularDeque(k)
* var param_1 = obj.insertFront(value)
* var param_2 = obj.insertLast(value)
* var param_3 = obj.deleteFront()
* var param_4 = obj.deleteLast()
* var param_5 = obj.getFront()
* var param_6 = obj.getRear()
* var param_7 = obj.isEmpty()
* var param_8 = obj.isFull()
*/
三、前中后队列
LeetCode: 1670. 设计前中后队列
// 一、使用链表构造一个双向队列
// 二、维护一前一后两个队列、当插入中间时插入到前面队列的尾端
// 三、如果两个队列的差值过大、进行修复
// 链表
var Node = function(val, next, pre) {
this.val = val || 0
this.next = next || null
this.pre = pre || null
}
Node.prototype.insertPre = function(val) {
let pre = this.pre
pre.next = new Node(val, this, pre)
this.pre = pre.next
return true
}
Node.prototype.insertNext = function(val) {
let next = this.next
this.next = new Node(val, next, this)
next.pre = this.next
return true
}
Node.prototype.delPre = function() {
if (!this.pre) return
let pre = this.pre
let lastPre = pre.pre
if (lastPre) {
lastPre.next = this
this.pre = lastPre
} else {
this.pre = null
}
return true
}
Node.prototype.delNext = function() {
if (!this.next) return
let next = this.next
let lastNext = next.next
if (lastNext) {
this.next = lastNext
lastNext.pre = this
} else {
this.next = null
}
return true
}
// 双向队列
var Queue = function() {
this.cnt = 0
this.head = new Node(0)
this.tail = new Node(0)
this.head.next = this.tail
this.tail.pre = this.head
}
Queue.prototype.pushBack = function(val) {
this.tail.insertPre(val)
this.cnt++
}
Queue.prototype.pushFront = function(val) {
this.head.insertNext(val)
this.cnt++
}
Queue.prototype.popBack = function() {
if (this.isEmpty()) return
let val = this.tail.pre.val
this.tail.delPre()
this.cnt--
return val
}
Queue.prototype.popFront = function() {
if (this.isEmpty()) return
let val = this.head.next.val
this.head.delNext()
this.cnt--
return val
}
Queue.prototype.back = function() {
return this.tail.pre.val
}
Queue.prototype.front = function() {
return this.head.next.val
}
Queue.prototype.isEmpty = function() {
return !this.cnt
}
Queue.prototype.size = function() {
return this.cnt
}
// 前中后队列
var FrontMiddleBackQueue = function() {
this.last = new Queue()
this.next = new Queue()
};
/**
* @desc 平衡前后两个队列的数量
* @return {void}
*/
FrontMiddleBackQueue.prototype.optimization = function() {
if(this.last.size() < this.next.size()) {
this.last.pushBack(this.next.front())
this.next.popFront()
} else if(this.last.size() === this.next.size() + 2) {
this.next.pushFront(this.last.back())
this.last.popBack()
}
};
/**
* @param {number} val
* @return {void}
*/
FrontMiddleBackQueue.prototype.pushFront = function(val) {
this.last.pushFront(val)
this.optimization()
};
/**
* @param {number} val
* @return {void}
*/
FrontMiddleBackQueue.prototype.pushMiddle = function(val) {
if (this.last.size() > this.next.size()) {
this.next.pushFront(this.last.back())
this.last.popBack()
}
this.last.pushBack(val)
this.optimization()
};
/**
* @param {number} val
* @return {void}
*/
FrontMiddleBackQueue.prototype.pushBack = function(val) {
this.next.pushBack(val)
this.optimization()
};
/**
* @return {number}
*/
FrontMiddleBackQueue.prototype.popFront = function() {
if (!this.last.size()) return -1
let val = this.last.popFront()
this.optimization()
return val
};
/**
* @return {number}
*/
FrontMiddleBackQueue.prototype.popMiddle = function() {
if (!this.last.size()) return -1
let val = this.last.popBack()
this.optimization()
return val
};
/**
* @return {number}
*/
FrontMiddleBackQueue.prototype.popBack = function() {
if (this.next.size()) {
let val = this.next.popBack()
this.optimization()
return val
} else if (this.last.size()) {
let val = this.last.popBack()
this.optimization()
return val
} else {
return -1
}
};
/**
* Your FrontMiddleBackQueue object will be instantiated and called as such:
* var obj = new FrontMiddleBackQueue()
* obj.pushFront(val)
* obj.pushMiddle(val)
* obj.pushBack(val)
* var param_4 = obj.popFront()
* var param_5 = obj.popMiddle()
* var param_6 = obj.popBack()
*/