</> MAANG.io
coding interview · 201

Intermediate

Advanced coding interview preparation covering complex algorithms, system design, and optimization techniques for senior-level positions.

0/170 solved 0% complete

K-Sum

What is this?

You are given a stack of price tags and asked whether any three of them total exactly £100. Checking every combination is hopeless by hand. But sort them into a line, put a finger at each end, and the problem becomes tractable: if the pair in front of you is too cheap, the only way to improve is to slide the left finger up; too expensive, slide the right finger down. You never backtrack, and you never consider a combination twice.

The k-sum family is that trick with one or two prices held fixed while your fingers work on the rest.

flowchart TD A["sort the array"] --> B["fix k-2 indices with nested loops"] B --> C["converge left/right over the remainder"] C --> D{"compare against target"} D -->|"too small"| E["left++"] D -->|"too large"| F["right--"] D -->|"hit"| G["record, then move both
past their duplicates"] E --> D F --> D G --> D

💡 Fun fact: the general k-sum problem is 3SUM-hard — a whole complexity class is named after it. For decades it was conjectured that 3Sum could not be solved faster than O(n²), and although slightly sub-quadratic algorithms now exist, they are theoretical curiosities. The O(n²) you are asked to produce in an interview is, for practical purposes, the best known answer.

🔓 The 4 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: every problem here is sort → fix k − 2 values → converge two pointers. Only the objective changes: an exact hit records a result, a near miss updates a best-so-far, and a bound counts a whole block at once.


1. The template

nums.sort()
for i in range(len(nums) - 2):
    if i > 0 and nums[i] == nums[i - 1]:
        continue                      # skip duplicate anchors
    left, right = i + 1, len(nums) - 1
    while left < right:
        total = nums[i] + nums[left] + nums[right]
        if total < target:
            left += 1
        elif total > target:
            right -= 1
        else:
            record(i, left, right)
            left += 1
            right -= 1
            while left < right and nums[left] == nums[left - 1]:
                left += 1             # skip duplicate lefts
            while left < right and nums[right] == nums[right + 1]:
                right -= 1            # skip duplicate rights

There are three duplicate skips, and all three are required. The anchor skip stops the same triple being found from a repeated first element; the left and right skips stop it being found again within the same sweep. Most buggy submissions have one or two of the three.

The objective is the only thing that varies:

Problem Inside the loop
exact sum record the triple, step both, skip duplicates
closest to target track min(abs(total − target)); still move on the sign of the difference
count below target when total < target, add right − left and left++
four numbers wrap the whole thing in a second anchor loop

2. Why right − left counts a whole block

For 3Sum Smaller, once nums[i] + nums[left] + nums[right] < target, every element strictly between left and right is at most nums[right] — so pairing any of them with left also stays under the target. That is right − left valid triples in one step.

sorted: [-2, 0, 1, 3],  target = 2,  anchor i = 0 (value -2)

left=1 (0), right=3 (3):  -2 + 0 + 3 = 1 < 2
                          → every j in (1, 3] works with left=1
                          → add right - left = 2   [(-2,0,1), (-2,0,3)]
                          → left = 2
left=2 (1), right=3 (3):  -2 + 1 + 3 = 2, not < 2  → right = 2, loop ends
                                                     count = 2

Incrementing one at a time would still be correct, just quadratically slower inside an already quadratic loop.


3. Where you'll actually meet this

  • Financial reconciliation. "Which combination of these line items accounts for the discrepancy?" is subset-sum in general, but the fixed-size version is exactly this.
  • Bundling and pricing. Finding a set of k items closest to a budget — the 3Sum Closest objective — is how "build a bundle near £50" features are implemented.
  • Chemistry and bioinformatics. Mass-spectrometry peak matching looks for combinations of fragment masses summing to an observed total, within a tolerance.
  • Resource packing. Choosing k machines whose combined capacity most nearly meets a request, without going under.
  • Deduplication in analytics. The duplicate-skipping discipline here is the same care required whenever you report distinct combinations from a multiset.

4. Problems in this chapter

▶ 3Sum

Find all unique triples summing to zero. The archetype — and the duplicate handling, not the pointer walk, is what separates a passing answer from a correct one.
Pattern: sort + anchor + converge. Target: O(n²) time, O(1) extra space.

▶ 3Sum Closest

Return the triple sum nearest the target. Same sweep, different bookkeeping: track the best absolute difference, and keep moving on the sign of the current difference.
Pattern: converge, track best-so-far. Target: O(n²) time, O(1) space.

▶ 3Sum Smaller

Count triples summing to strictly less than the target. The batching insight — add right − left in one step — is the entire problem.
Pattern: converge, count in blocks. Target: O(n²) time, O(1) space.

▶ 4Sum

All unique quadruples summing to the target. Two nested anchors around the same converging pair, with duplicate skips at both anchor levels, plus overflow care in fixed-width languages.
Pattern: two anchors + converge. Target: O(n³) time, O(1) space.


5. Common pitfalls 🚫

  • Skipping duplicates in only one place. All three skips are needed: the anchor, the left after a hit, and the right after a hit.
  • Skipping the anchor with i instead of i > 0 and …. Comparing nums[i] == nums[i+1] and skipping forward drops legitimate triples where the anchor value genuinely repeats.
  • Deduping with a set of tuples. It works and it is what interviewers expect you to outgrow — say why the skip-based version is better (no hashing of results, no extra space).
  • Stepping one at a time in the counting variant instead of adding right − left.
  • Moving only one pointer after an exact hit. The other end must move too, or the loop finds the same pair forever.
  • Integer overflow in 4Sum. Four values near 10⁹ exceed a 32-bit int; use 64-bit outside Python.
  • Forgetting that sorting destroys original indices. If the problem wants indices rather than values, this whole approach is wrong — that is a hash-map problem instead.

6. Key takeaways

  1. Sort first — that is the algorithm, not setup. Order is what makes each pointer move provably safe.
  2. One template, four objectives. Fix k − 2, converge over the rest, and change only what happens at the comparison.
  3. Three duplicate skips, every time, or you will emit repeats.
  4. Count in blocks, not steps when the objective is "how many below the bound".
  5. k-sum costs O(n^(k−1)) — 3Sum is quadratic, 4Sum cubic — because each extra anchor adds a loop.
  6. If the answer needs original indices, abandon this pattern. Sorting has thrown that information away; use a hash map.

Order: 3Sum → 3Sum Closest → 3Sum Smaller → 4Sum.

3Sum

Core idea: Sort the array once, then fix one number and let two pointers — one from each end of the remaining slice — converge toward each other. Sorting turns a three-way search into a one-dimensional sweep, and it also lines duplicates up next to each other so you can skip them in a single comparison.


Problem, rephrased

You run a ledger-reconciliation job. Every night you get a list of signed adjustments — credits are positive, debits are negative — and your compliance rule says: flag every distinct group of three adjustments that perfectly cancel out (their amounts sum to exactly 0). Two flagged groups are "the same" if they contain the same three amounts, regardless of which specific entries they came from, so you must report each combination once.

Formally (LeetCode 15): given an integer array nums, return all unique triplets [a, b, c] such that:

  • the three values come from three different indices, and
  • a + b + c == 0.

The output is a list of triplets. Order of the triplets doesn't matter, and order within a triplet doesn't matter — but no triplet of values may appear twice.

![3Sum overview: nums = [-1, 0, 1, 2, -1, -4] sorted to [-4, -1, -1, 0, 1, 2]; triplets -1+0+1=0 and -1+-1+2=0 give answer [[-1, -1, 2], [-1, 0, 1]] with [-1, 0, 1] listed only once](https://maangioassets.blob.core.windows.net/diagrams/coding-interview/201/chapters/two-pointers/k-sum/3sum-diagram-1.svg)

Inputs / outputs

nums unique triplets summing to 0 notes
[-1, 0, 1, 2, -1, -4] [[-1, -1, 2], [-1, 0, 1]] the canonical case; one dup -1
[0, 1, 1] [] no three sum to 0
[0, 0, 0] [[0, 0, 0]] exactly one triplet
[0, 0, 0, 0] [[0, 0, 0]] the extra 0 must not create a dup
[-2, 0, 0, 2, 2] [[-2, 0, 2]] two 2s and two 0s, still one triplet

You return the set of distinct value-triplets — never the same combination twice.


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.