Opening the reading…
Opening the reading…
QUEUE › IMPLEMENTATION PROBLEMS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A middle operation is awkward only when the queue is stored as one sequence: inserting or removing near the center shifts everything after it. Split the sequence into a left half and a right half instead. The boundary between the halves is the middle, so every required operation can touch an endpoint of one deque.
Keep the left deque with either the same number of elements as the right deque or exactly one more. The full queue is left followed by right. When the total size is odd, the extra element belongs at the back of left, so that element is the frontmost middle. When the total size is even, the two middle positions are left.back() and right.front(), and the frontmost one is left.back().
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) total, O(1) amortized per operation | Each operation performs a constant number of deque endpoint insertions, removals, and size checks. Rebalancing moves at most one boundary element because each update changes the size difference by at most one, so no element is repeatedly scanned or shifted. |
| Space | O(n) extra space | The two deques hold the live queue state, which can contain O(n) values in the worst case when pushes dominate. The returned vector of strings is required output and is excluded from the extra-space bound. |
#include <deque>
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
vector<string> execute(vector<string>& operations, vector<vector<int>>& args) {
deque<int> left;
deque<int> right;
auto rebalance = [&]() {
if (left.size() > right.size() + 1) {
right.push_front(left.back());
left.pop_back();
} else if (left.size() < right.size()) {
left.push_back(right.front());
right.pop_front();
}
};
vector<string> result;
for (int i = 0; i < static_cast<int>(operations.size()); ++i) {
const string& operation = operations[i];
if (operation == "FrontMiddleBackQueue") {
result.push_back("null");
} else if (operation == "pushFront") {
left.push_front(args[i][0]);
rebalance();
result.push_back("null");
} else if (operation == "pushMiddle") {
if (left.size() > right.size()) {
right.push_front(left.back());
left.pop_back();
}
left.push_back(args[i][0]);
rebalance();
result.push_back("null");
} else if (operation == "pushBack") {
right.push_back(args[i][0]);
rebalance();
result.push_back("null");
} else if (operation == "popFront") {
if (left.empty() && right.empty()) {
result.push_back("-1");
} else {
int value;
if (!left.empty()) {
value = left.front();
left.pop_front();
} else {
value = right.front();
right.pop_front();
}
rebalance();
result.push_back(to_string(value));
}
} else if (operation == "popMiddle") {
if (left.empty() && right.empty()) {
result.push_back("-1");
} else {
int value;
if (left.size() >= right.size()) {
value = left.back();
left.pop_back();
} else {
value = right.front();
right.pop_front();
}
rebalance();
result.push_back(to_string(value));
}
} else if (operation == "popBack") {
if (left.empty() && right.empty()) {
result.push_back("-1");
} else {
int value;
if (!right.empty()) {
value = right.back();
right.pop_back();
} else {
value = left.back();
left.pop_back();
}
rebalance();
result.push_back(to_string(value));
}
}
}
return result;
}
};The special handling in pushMiddle is the key placement detail. If left is already larger, its last element is the old frontmost middle; moving that element to the front of right creates equal halves, so appending the new value to left puts it before the old middle. If the halves are equal, appending directly to left makes the new value the sole middle or the frontmost of two.