15. 3Sum
mediumAsked at Shopify3Sum is Shopify's classic two-pointer follow-up to Two Sum. The interviewer wants you to sort, fix one element, and two-pointer the remainder — and to handle duplicate-skipping cleanly.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Shopify loops.
- Glassdoor (2026-Q1)— Shopify Backend Developer + Senior Engineering onsites cite 3Sum as a frequent follow-up to Two Sum.
- Reddit r/cscareerquestions (2025-11)— Shopify new-grad reports include this as the medium-round after Two Sum.
Problem
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. Notice that the solution set must not contain duplicate triplets.
Constraints
3 <= nums.length <= 3000-10^5 <= nums[i] <= 10^5
Examples
Example 1
nums = [-1,0,1,2,-1,-4][[-1,-1,2],[-1,0,1]]Example 2
nums = [0,1,1][]Example 3
nums = [0,0,0][[0,0,0]]Approaches
1. Brute-force triple loop with Set dedup
Three nested loops; for each (i, j, k) summing to 0, add the sorted triplet to a Set.
- Time
- O(n^3)
- Space
- O(n^3) worst case for the Set
function threeSumBrute(nums) {
const set = new Set();
const out = [];
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
for (let k = j + 1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] === 0) {
const t = [nums[i], nums[j], nums[k]].sort((a, b) => a - b);
const key = t.join(',');
if (!set.has(key)) { set.add(key); out.push(t); }
}
}
}
}
return out;
}Tradeoff: Cubic; barely passes for n = 3000. Use only as the warmup explanation.
2. Sort + two-pointer (canonical optimal)
Sort the array. For each i, two-pointer (left, right) the suffix looking for nums[i] + nums[left] + nums[right] = 0. Skip duplicates at every level.
- Time
- O(n^2)
- Space
- O(1) excluding output
function threeSum(nums) {
nums.sort((a, b) => a - b);
const out = [];
for (let i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] === nums[i - 1]) continue;
if (nums[i] > 0) break;
let left = i + 1, right = nums.length - 1;
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum === 0) {
out.push([nums[i], nums[left], nums[right]]);
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return out;
}Tradeoff: O(n^2) which is what Shopify expects. The `if (nums[i] > 0) break` short-circuit cuts work when the array is mostly positives. The duplicate-skipping logic at all three levels is the hard part — practice it until it's automatic.
3. Hash set 'two-sum on the suffix' alternative
Sort first, then for each i, do a hash-set-based two-sum on nums[i+1..end] looking for target = -nums[i].
- Time
- O(n^2)
- Space
- O(n)
function threeSumHash(nums) {
nums.sort((a, b) => a - b);
const out = [];
for (let i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] === nums[i - 1]) continue;
const seen = new Set();
let j = i + 1;
while (j < nums.length) {
const need = -nums[i] - nums[j];
if (seen.has(need)) {
out.push([nums[i], need, nums[j]]);
while (j + 1 < nums.length && nums[j] === nums[j + 1]) j++;
}
seen.add(nums[j]);
j++;
}
}
return out;
}Tradeoff: Same O(n^2) but uses extra O(n) memory per i. Two-pointer is cleaner for the duplicate-skipping. Bring it up only if the interviewer prefers hash maps.
Shopify-specific tips
Shopify's grading bar: explicit handling of duplicates at all THREE pointer levels (i, left, right). Most candidates fail by skipping duplicates only at the outer level. The expected follow-up is 3Sum Closest (LeetCode 16) or 4Sum (LeetCode 18) — both extend the same sort + two-pointer template.
Common mistakes
- Forgetting `nums[i] > 0 -> break` — minor perf opt but interviewers notice.
- Skipping duplicates only at the i level; need to skip at left and right after finding a triplet too.
- Comparing `nums[left] === nums[left - 1]` (back-check) which fails when left points just inside the current window — use the forward-check pattern shown.
- Returning indices instead of values (the problem asks for values; common confusion with Two Sum).
Follow-up questions
An interviewer at Shopify may pivot to one of these next:
- 3Sum Closest (LeetCode 16) — find triplet with sum closest to target.
- 4Sum (LeetCode 18) — extend to four numbers.
- 3Sum Smaller (LeetCode 259) — count triplets with sum < target.
- kSum — generalize to k numbers (recursion + base case as Two Sum).
- What if duplicates are allowed in the output (a triplet can appear once per distinct ordering)?
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why does sort + two-pointer beat hash maps here?
Both are O(n^2). Two-pointer uses O(1) extra space (no inner hash); the duplicate-skipping is cleaner because sorting clusters duplicates together. Shopify expects two-pointer as the default.
Can you do better than O(n^2)?
Not in the comparison-based model — 3Sum has a known matching lower bound. For specialized inputs (integers in a small range) you can do O(n^2 / log n) via fancy tricks, but they're not interview-grade.
Free learning resources
Curated free links for this problem.
Practice these live with InterviewChamp.AI
Drill 3Sum and other Shopify interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →