2 POINTERS › TWO POINTER ON ARRAYS
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 first part of the problem is a left-to-right simulation. At index i, you must compare the current value with the value immediately to its right. If they match, the left value changes and the right value becomes zero. That new zero is part of the array state seen by the next operation, so the scan must use the modified array rather than the original values.
After every adjacent operation has happened, no more merging is allowed. The remaining task is stable compaction: read the array from left to right, copy each non-zero value into the next available position, and leave all positions after that filled with zero. Because values are copied in their original order, the non-zero order is preserved.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The merge pass performs one comparison per adjacent pair, and the compaction pass reads each element once. The passes are separate but each touches at most n positions, so their total work is still linear. |
| Space | O(1) extra | The result array uses O(n) space, but it is the required returned output and is excluded. Apart from that output, the algorithm stores only n, i, idx, and x; the bound does not worsen for any input shape. |
#include <vector>
using namespace std;
class Solution {
public:
vector<int> applyOperations(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n - 1; ++i) {
if (nums[i] == nums[i + 1]) {
nums[i] *= 2;
nums[i + 1] = 0;
}
}
vector<int> result(n, 0);
int idx = 0;
for (int x : nums) {
if (x != 0) {
result[idx++] = x;
}
}
return result;
}
};The boundary n - 1 is essential because nums[i + 1] must exist. The second pass does not try to move each zero individually; it only records where the next useful value belongs. Since result starts entirely at zero, the part after idx needs no separate shifting loop.
You can avoid the separate result array by compacting the modified nums itself. This is a genuine space optimisation: the required output remains the same, but the algorithm uses no output-sized auxiliary vector. It also makes the input array hold the final answer, so use this version only when mutating nums is acceptable.
#include <vector>
using namespace std;
class Solution {
public:
vector<int> applyOperations(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n - 1; ++i) {
if (nums[i] == nums[i + 1]) {
nums[i] *= 2;
nums[i + 1] = 0;
}
}
int write = 0;
for (int read = 0; read < n; ++read) {
if (nums[read] != 0) {
nums[write++] = nums[read];
}
}
while (write < n) {
nums[write++] = 0;
}
return nums;
}
};The read pointer always moves forward, while write never moves ahead of read because each copied value occupies the earliest unused position. That makes the compaction stable. The final loop restores zeros in every remaining slot; without it, old non-zero values could remain at the end after being overwritten earlier.