HASHING › IMPLEMENTARY 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 value v can appear only when 1 <= v <= n, so it naturally identifies one position: index v - 1. That makes the array a built-in address table. Instead of allocating a separate record for every value, you use the cell at index v - 1 to remember whether v has already appeared.
The sign of that cell supplies one extra bit of information without changing its magnitude. When you process a number v, inspect nums[v - 1]. A positive cell means v has not been marked yet, so negate it. A negative cell means an earlier occurrence already marked v, so v is exactly a duplicate. Taking the absolute value first keeps the current number readable even after earlier marks have changed signs.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The loop performs one constant-time address calculation, sign check, and possible negation for each of the n input positions. No value causes a nested scan, so the total number of operations grows directly with the number of elements. |
| Space | O(1) extra | Apart from the returned output vector, the solution stores only a few scalar variables. It reuses nums for all visited marks, so the working-memory bound stays constant even in the worst case where every value appears twice and the output has size n / 2. |
#include <cstdlib>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
vector<int> res;
for (int i = 0; i < static_cast<int>(nums.size()); ++i) {
int value = abs(nums[i]);
int idx = value - 1;
if (nums[idx] < 0) {
res.push_back(value);
} else {
nums[idx] = -nums[idx];
}
}
return res;
}
};The two assignments that carry the whole idea are value = abs(nums[i]) and nums[idx] = -nums[idx]. The first prevents a previous mark from corrupting the address; the second makes the next occurrence observable. The duplicate branch does not negate the cell again, because it only needs to report the value and leaving the existing negative mark unchanged keeps the state consistent.