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 →The queue starts empty and promises first-in, first-out behavior: new values join at the back, while removal happens only at the front. That means each type 1 command changes the state by appending, and each type 2 command changes it by removing the oldest value when one exists. The container itself should preserve exactly this order for you.
Types 3 through 6 do not change the queue. They observe its front, back, emptiness, or size, and each observation contributes exactly one string to the answer. Process commands from left to right so every observation sees the state produced by all earlier commands. Empty front and back queries need explicit fallback values, while removing from an empty queue is simply skipped.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The loop examines each query once, and every queue operation, emptiness check, front or back lookup, size lookup, and integer-to-string conversion handles one command's data without scanning the queue. No query is processed twice. |
| Space | O(n) extra | The working queue can contain up to n values when many type 1 commands occur, and the result can also contain up to n strings, but the result is output and is excluded here. Thus the extra queue storage is O(n), which is reached by an input whose updates all enqueue without removals. |
#include <deque>
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
vector<string> processQueries(vector<vector<int>> queries) {
deque<int> q;
vector<string> answer;
for (const vector<int>& query : queries) {
int type = query[0];
if (type == 1) {
q.push_back(query[1]);
} else if (type == 2) {
if (!q.empty()) {
q.pop_front();
}
} else if (type == 3) {
answer.push_back(q.empty() ? "-1" : to_string(q.front()));
} else if (type == 4) {
answer.push_back(q.empty() ? "-1" : to_string(q.back()));
} else if (type == 5) {
answer.push_back(q.empty() ? "true" : "false");
} else if (type == 6) {
answer.push_back(to_string(static_cast<int>(q.size())));
}
}
return answer;
}
};The empty checks belong exactly where an operation needs them. Type 2 checks before pop_front because removal from an empty queue has no effect. Types 3 and 4 use conditional expressions because they must record a fallback instead of attempting an invalid access. Type 5 reverses the usual interpretation: an empty queue records true, while a non-empty queue records false.
std::queue provides every operation this problem needs: push at the back, pop from the front, front, back, empty, and size. Replacing deque with queue is not an asymptotic optimisation; both versions use O(1) time per command and O(n) extra queue space. The narrower type is a reasonable choice when you want the code to advertise that only FIFO operations are intended.
#include <queue>
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
vector<string> processQueries(vector<vector<int>> queries) {
queue<int> q;
vector<string> answer;
for (const vector<int>& query : queries) {
int type = query[0];
if (type == 1) {
q.push(query[1]);
} else if (type == 2) {
if (!q.empty()) {
q.pop();
}
} else if (type == 3) {
answer.push_back(q.empty() ? "-1" : to_string(q.front()));
} else if (type == 4) {
answer.push_back(q.empty() ? "-1" : to_string(q.back()));
} else if (type == 5) {
answer.push_back(q.empty() ? "true" : "false");
} else if (type == 6) {
answer.push_back(to_string(static_cast<int>(q.size())));
}
}
return answer;
}
};