leetcode
leetcode 1501 ~ 1550
设计前中后队列

设计前中后队列

难度:

标签:

题目描述

代码结果

运行时间: 76 ms, 内存: 15.6 MB


/*
  题目思路:
  1. 使用双向链表Deque来存储队列元素,利用Java Streams进行操作。
  2. pushFront:使用Deque的addFirst方法将元素添加到头部。
  3. pushMiddle:通过Streams计算中间位置并插入元素。
  4. pushBack:使用Deque的addLast方法将元素添加到尾部。
  5. popFront:移除头部元素并返回值,使用Deque的pollFirst方法。
  6. popMiddle:通过Streams计算中间位置并移除元素。
  7. popBack:移除尾部元素并返回值,使用Deque的pollLast方法。
*/

import java.util.Deque;
import java.util.LinkedList;
import java.util.stream.Collectors;

class FrontMiddleBackQueue {
    private Deque<Integer> deque;

    public FrontMiddleBackQueue() {
        deque = new LinkedList<>();
    }

    public void pushFront(int val) {
        deque.addFirst(val);
    }

    public void pushMiddle(int val) {
        int midIndex = (deque.size() + 1) / 2;
        LinkedList<Integer> tempList = deque.stream().collect(Collectors.toCollection(LinkedList::new));
        tempList.add(midIndex, val);
        deque = tempList;
    }

    public void pushBack(int val) {
        deque.addLast(val);
    }

    public int popFront() {
        return deque.isEmpty() ? -1 : deque.pollFirst();
    }

    public int popMiddle() {
        if (deque.isEmpty()) return -1;
        int midIndex = (deque.size() - 1) / 2;
        LinkedList<Integer> tempList = deque.stream().collect(Collectors.toCollection(LinkedList::new));
        int value = tempList.remove(midIndex);
        deque = tempList;
        return value;
    }

    public int popBack() {
        return deque.isEmpty() ? -1 : deque.pollLast();
    }
}

解释

方法:

此题解采用了两个双端队列(deque)来高效地支持队列的前中后操作。两个队列分别称为 front 和 back,其中 front 队列处理前半部分的元素,而 back 队列处理后半部分的元素。对于中间的操作,当 front 队列的长度比 back 队列长时,将会调整元素以保持平衡。此方法通过两个队列来协调元素,使得添加和删除操作都可以尽可能接近 O(1) 时间复杂度。

时间复杂度:

O(1)

空间复杂度:

O(n)

代码细节讲解

🦆
在使用两个双端队列的设计中,为何选择在两个队列长度不平衡时从一个队列的末尾移动元素到另一个队列的开头,而不是从开头到末尾?
这种设计选择主要是为了保持队列的中间元素位置正确并简化操作。当元素从一个队列的末尾移动到另一个队列的开头时,我们能更容易地维持中间元素的位置,特别是在执行中间添加或删除操作时。这样的移动方式可以确保在任何操作后,两个队列都能迅速平衡,而不需要进行额外的元素重排。
🦆
如何处理在 pushMiddle 和 popMiddle 操作中,当队列具有偶数个元素时,中间位置的选择和元素的移动?
在设计中,当队列元素总数为偶数时,中间位置的元素被视为靠近前半部分队列的最后一个元素。因此,pushMiddle 操作会将新元素添加到 front 队列的末尾。如果添加后 front 队列比 back 队列长,会将 front 的最后一个元素移动到 back 的开头。对于 popMiddle,操作总是从 front 队列的末尾移除元素,这样可以保持操作简单并确保两个队列的平衡。
🦆
在 pushBack 操作中,当 back 队列长度超过 front 队列时,你是如何决定何时以及如何从 back 移动元素到 front 来保持平衡?
在 pushBack 操作中,每当一个元素被添加到 back 队列后,算法会检查两个队列的长度。如果 back 队列的长度超过 front 队列,此时会从 back 队列的开头移除一个元素,并将其添加到 front 队列的末尾。这种平衡策略确保了两个队列长度的平衡,同时也维持了队列中元素的顺序。
🦆
在 popFront 操作中,如果 front 队列为空而 back 队列不为空,为什么直接将 back 队列的元素移至 front,而不考虑 back 队列中元素的具体位置?
当 front 队列为空而需要执行 popFront 操作时,最简单和直接的方法是直接从 back 队列中转移元素到 front 队列。这种做法不仅简化了操作过程,而且由于 back 队列的元素本来就是队列后半部分的元素,直接转移不会影响队列元素的整体顺序。在 popFront 需要执行时,通常意味着队列正在从一个非常不平衡的状态恢复平衡,因此,这种简单的转移操作是恢复平衡的最快方式。

相关问题