[路飞]_leetcode1670.设计前中后队列

203 阅读2分钟

题目简述

leetcode1670.设计前中后队列

请你设计一个队列,支持在前,中,后三个位置的 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] 。

示例

输入:
["FrontMiddleBackQueue", "pushFront", "pushBack", "pushMiddle", "pushMiddle", "popFront", "popMiddle", "popMiddle", "popBack", "popFront"]
[[], [1], [2], [3], [4], [], [], [], [], []]
输出:
[null, null, null, null, null, 1, 3, 4, 2, -1]

解释:
FrontMiddleBackQueue q = new FrontMiddleBackQueue();
q.pushFront(1);   // [1]
q.pushBack(2);    // [1, 2]
q.pushMiddle(3);  // [1, 3, 2]
q.pushMiddle(4);  // [1, 4, 3, 2]
q.popFront();     // 返回 1 -> [4, 3, 2]
q.popMiddle();    // 返回 3 -> [4, 2]
q.popMiddle();    // 返回 4 -> [2]
q.popBack();      // 返回 2 -> []
q.popFront();     // 返回 -1 -> [] (队列为空)

提示:

  • 1 <= val <= 109
  • 最多调用 1000 次 pushFront, pushMiddle, pushBack, popFront, popMiddle 和 popBack 。

javascript实现

// 用两个双端队列维护一个前中后队列
// 把一个队列分成两个双端队列,实现在中间位置插入元素
// 通过链表思想实现一个双端队列
// 实现双端队列实现需要虚拟头和虚拟尾

// 实现链表节点的结构,双向链表
function Node(val=0,next=null,pre=null) {
    this.val = val;
    this.next = next;
    this.pre = pre
    this.insert_pre = function(p){ // 实现在当前节点的前面插入一个新节点
        p.pre = this.pre;
        p.next = this;
        if(this.pre) this.pre.next = p;
        this.pre = p;
    }
    this.insert_next = function(p) { // 实现在当前节点的后面插入一个新节点
        p.pre = this;
        p.next = this.next;
        if(this.next) this.next.pre = p;
        this.next = p;
    }
    this.delete_pre = function() { // 实现删除当前的前一个节点
        if(this.pre == null) return; //如果前面没节点了就直接返回
        let p = this.pre;
        this.pre = p.pre;
        if(p.pre) p.pre.next = this;
    }
    this.delete_front = function() { // 实现删除当前的后一个节点
        if(this.next == null) return; //如果后面没节点了就直接返回
        let p = this.next;
        this.next = p.next;
        if(p.next) p.next.pre = this
    }
}

// 通过链表节点实现一个双端队列
var Queue = function Queue() {// 双端队列构造函数
    // 构造虚拟头和虚拟尾
    this.head = new Node(0);
    this.tail = new Node(0);
    this.head.next = this.tail;
    // this.head.pre = null;
    // this.tail.next = null;
    this.tail.pre = this.head;
    this.count = 0
}

Queue.prototype.push_back = function(val) { // 从队尾添加
    // 在虚拟尾的前面插入
    this.tail.insert_pre(new Node(val))
    this.count += 1
}
Queue.prototype.push_front = function(val) { // 从队头添加
    // 在虚拟头的后面插入
    this.head.insert_next(new Node(val))
    this.count += 1
}
Queue.prototype.pop_back = function() { // 从队尾删除
    // 删除先判空
    if(this.isEmpty()) return -1;
    let delVal = this.tail.pre.val;
    this.tail.delete_pre()
    this.count -= 1
    return delVal
}
Queue.prototype.pop_front = function() { // 从队头删除
    // 删除先判空
    if(this.isEmpty()) return -1;
    let delVal = this.head.next.val;
    this.head.delete_front()
    this.count -= 1
    return delVal
}
Queue.prototype.get_front = function() { // 获取头部节点
    return this.head.next.val
}
Queue.prototype.get_back = function() { // 获取尾部元素
    return this.tail.pre.val
}
Queue.prototype.isEmpty = function() { // 判空
    // 如果head.next == tail时, 链表只剩虚头和虚尾
    return this.head.next == this.tail
}
Queue.prototype.size = function() { // 记录元素数量
    return this.count;
}
// Queue.prototype.isFull = function(val) { // 判满
//     // 用链表实现的话,本质是没有满的情况,因为链表是可以无线扩容的
// }




// 构造两个双端队列q1,q2
// q1 存前一半的元素,q2存后一半的元素
var FrontMiddleBackQueue = function() {
    this.q1 = new Queue();
    this.q2 = new Queue()
};

/** 
 * @param {number} val
 * @return {void}
 */
FrontMiddleBackQueue.prototype.pushFront = function(val) {// 从最前面插入。q1的头部
    this.q1.push_front(val)
    this.updata() //更新元素数量。如果有偶数个元素就平分给q1、q2,如果实际数个就让q1多一个
};

/** 
 * @param {number} val
 * @return {void}
 */
FrontMiddleBackQueue.prototype.pushMiddle = function(val) {
    // 正常q1等于q2,或比q2多一个,
    // 先判断q1是否大于q2,大于的话,就先把q1的尾元素添加到q2的头部
    // 然后q1的尾部插入
    if(this.q1.size() > this.q2.size()) {
        this.q2.push_front(this.q1.get_back());
        this.q1.pop_back()
    }
    this.q1.push_back(val)
};

/** 
 * @param {number} val
 * @return {void}
 */
FrontMiddleBackQueue.prototype.pushBack = function(val) {
    this.q2.push_back(val) 
    this.updata()
};

/**
 * @return {number}
 */
FrontMiddleBackQueue.prototype.popFront = function() {
    if(this.isEmpty()) return -1
    let delVal = this.q1.pop_front();
    this.updata()
    return delVal
};

/**
 * @return {number}
 */
FrontMiddleBackQueue.prototype.popMiddle = function() {
    if(this.isEmpty()) return -1
    // q1应该是大于等于q2的
    // 删除q1尾部
    let delVal = this.q1.pop_back()
    this.updata()
    return delVal
};

/**
 * @return {number}
 */
FrontMiddleBackQueue.prototype.popBack = function() {
    if(this.isEmpty()) return -1
    let delVal = -1
    //  如果q2为空
    if(this.q2.isEmpty()) {
        delVal = this.q1.pop_back()
    }else {
        delVal = this.q2.pop_back()
    }
    this.updata()
    return delVal
};
FrontMiddleBackQueue.prototype.isEmpty = function() {
    return this.q1.size() == 0; //q1的元素不少于q2
};
FrontMiddleBackQueue.prototype.updata = function() {
    
    if(this.q1.size() < this.q2.size()){ 
        this.q1.push_back(this.q2.get_front()); // 把q2的头部元素插入到q1的尾部
        this.q2.pop_front(); // 并把q2头部元素删除
    }
    if(this.q1.size() == this.q2.size() + 2) { // 奇数个元素,q1比q2多一个;如果发现多两个了
        this.q2.push_front(this.q1.get_back()); //把q1的尾元素添加到q2的头部 
        this.q1.pop_back()
    }
};

/**
 * 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()
 */