题目介绍
请你设计一个队列,支持在前,中,后三个位置的 push 和 pop 操作。
请你完成 FrontMiddleBack 类:
FrontMiddleBack() 初始化队列。 void pushFront(int val) 将 val 添加到队列的 最前面 。 void pushMiddle(int val) 将 val 添加到队列的 正中间 。 void pushBack(int val) 将 val 添加到队里的 最后面 。 int popFront() 将 最前面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。 int popMiddle() 将 正中间 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。 int popBack() 将 最后面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。 请注意当有 两个 中间位置的时候,选择靠前面的位置进行操作。比方说:
将 6 添加到 [1, 2, 3, 4, 5] 的中间位置,结果数组为 [1, 2, 6, 3, 4, 5] 。 从 [1, 2, 3, 4, 5, 6] 的中间位置弹出元素,返回 3 ,数组变为 [1, 2, 4, 5, 6] 。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/de… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
- 用两个循环双端队列实现前中后队列,循环双端队列用链表实现
- 先实现循环双端队列,
- 要有两个虚拟节点,分别是head和tail,head.next => tail tail.pre => head,判空条件为head.next === tial.pre
- 节点类 需要实现插入前一个简单,插入后一个节点,删除上一个节点,删除下一个节点的方法
- 双端队列类需要实现,头部插入,尾部插入,元素大小,判空,获取队首元素,队尾元素,队首元素,队尾元素删除方法
- 前中后队列实现
- 用两个循环双端队列去模拟,左边的元素要比右边的元素多一个或者相等,如果不在这两种情况中的话,就是通过update方法去调整两个队列,左边比右边小的话,就取右边的首元素放到左边的尾部,左边比右边大于1的话,就是左边尾部元素删除,右边头部插入
- 中间插入的方法,判断左边是否比右边打,如果是就把左边的尾部插入到右边的头部
- 头部插入就是左边队列头部插入,然后调用update方法。尾部插入也是同理,右边尾部插入,然后调用update方法
- 其他的方法就不说了,比较容易理解了
代码
// Node类
var Node = function(val, pre = null, next = null) {
this.val = val
this.pre = pre
this.next = next
this.insertPre = function(val) {
const pre = this.pre
this.pre = val
val.next = this
if (pre) pre.next = val
// pre.next = pre
val.pre = pre
}
this.inserNext = function(val) {
const next = this.next
this.next = val
val.pre = this
if (next) next.pre = val
val.next = next
}
this.delPre = function() {
// 删除当前节点的前一个节点
if (!this.pre) return -1
// p是当前要删除的节点
const p = this.pre
this.pre = p.pre
// 有可能是空所以需要判断一下
if (this.pre) this.pre.next = this
return p
}
this.delNext = function() {
if (!this.next) return -1
const p = this.next
this.next = p.next
if (this.next) this.next.pre = this
return p
}
}
// 循环双端队列类
var MyCircularDeque = function() {
this.head = new Node('head')
this.tail = new Node('tail')
this.head.next = this.tail
this.tail.pre = this.head
this.cnt = 0
};
/**
* @param {number} value
* @return {boolean}
*/
MyCircularDeque.prototype.insertFront = function(value) {
this.head.inserNext(new Node(value))
this.cnt++
return true
};
/**
* @param {number} value
* @return {boolean}
*/
MyCircularDeque.prototype.insertLast = function(value) {
this.tail.insertPre(new Node(value))
this.cnt++
return true
};
/**
* @return {boolean}
*/
MyCircularDeque.prototype.deleteFront = function() {
if (this.isEmpty()) return false
let val = this.getFront()
this.head.delNext()
this.cnt--
return val
};
/**
* @return {boolean}
*/
MyCircularDeque.prototype.deleteLast = function() {
if (this.isEmpty()) return false
let val = this.getRear()
this.tail.delPre()
this.cnt--
return val
};
/**
* @return {number}
*/
MyCircularDeque.prototype.getFront = function() {
if (this.isEmpty()) return -1
return this.head.next.val
};
/**
* @return {number}
*/
MyCircularDeque.prototype.getRear = function() {
if (this.isEmpty()) return -1
return this.tail.pre.val
};
/**
* @return {boolean}
*/
MyCircularDeque.prototype.isEmpty = function() {
return this.head.next === this.tail
};
MyCircularDeque.prototype.size = function() {
return this.cnt
}
// 前中后队列
class FrontMiddleBackQueue {
constructor() {
this.q = new MyCircularDeque()
this.q2 = new MyCircularDeque()
}
pushFront(value) {
this.q.insertFront(value)
this.update()
}
pushMiddle(value) {
// 因为中间始终在队列1中,所以先判断q的size是否比q2大如果是,就先把q的尾部元素放到q2头部去
if (this.q.size() > this.q2.size()) {
this.q2.insertFront(this.q.deleteLast())
}
this.q.insertLast(value)
// this.q2.insertLast(value)
// this.pushBack(value)
// this.pushFront(value)
}
pushBack(value) {
this.q2.insertLast(value)
this.update()
}
popFront() {
if (this.isEmpty()) return -1
let res = this.q.deleteFront()
this.update()
return res
}
popMiddle() {
if (this.isEmpty()) return -1
let res = this.q.deleteLast()
this.update()
return res
}
popBack() {
if (this.isEmpty()) return -1
let res = this.q2.isEmpty() ? this.q.deleteLast() : this.q2.deleteLast()
this.update()
return res
}
update() {
if (this.q.size() < this.q2.size()) {
this.q.insertLast(this.q2.deleteFront())
}
if (this.q.size() === (this.q2.size() + 2)) {
let val = this.q.deleteLast()
this.q2.insertFront(val)
}
}
isEmpty() {
return this.q.size() === 0
}
}
解题收获
这个解法应该是比较麻烦的了,如果用数组去实现的话,应该就不需要实现,node类和循环双队列类。循环双队列也是用链表去实现的。昨天的循环双队列是用数组实现,感觉对链表和队列的这种数据结构有了更深一点的认识,一个算法可以用不同的思想去实现。同时也感觉对自己的编码能力也是一种考验。