Leetcode 39 Combination Sum

Published: (December 17, 2025 at 11:40 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Problem Statement

Given an array of distinct integers candidates and a target integer target, return all unique combinations of candidates where the chosen numbers sum to target.
You may use each candidate an unlimited number of times. Two combinations are considered different if the frequency of at least one number differs.

Example: [2,3,3], [3,2,3], and [3,3,2] represent the same unique combination.

Example

Input

candidates = [2,3,5]
target = 8

Output

[[2,2,2,2], [2,3,3], [3,5]]

Mathematical Formulation

The problem can be expressed as finding non‑negative integers x, y, z such that

2·x + 3·y + 5·z = 8

Solution

class Solution:
    def combinationSum(self, candidates, target):
        results = []

        def backtrack(remain, comb, start):
            if remain == 0:
                # found a valid combination
                results.append(list(comb))   # make a shallow copy
                return
            if remain < 0:
                # exceeded the sum, stop exploring this path
                return

            for i in range(start, len(candidates)):
                # choose the candidate
                comb.append(candidates[i])
                # explore further with the same start index (allow reuse)
                backtrack(remain - candidates[i], comb, i)
                # backtrack
                comb.pop()

        backtrack(target, [], 0)
        return results

Common Pitfalls

PitfallWhy it HappensFix
results.append(comb)Appends a reference to the mutable list comb; later modifications corrupt stored results.Use results.append(list(comb)) to store a shallow copy.
Looping over all candidates each recursion: for i in range(len(candidates))Allows permutations of the same combination (e.g., [2,3] and [3,2]).Loop from the current start index: for i in range(start, len(candidates)).
Not handling remain < 0Leads to unnecessary recursion after the sum is exceeded.Return immediately when remain < 0.

Depth‑First Search Walkthrough

The recursion tree for the example (candidates = [2,3,5], target = 8) is illustrated below (each node shows i – index, v – value, remain, and start).

root (target: 8)
└── [i:0, v:2] remain: 6, start: 0
    ├── [i:0, v:2] remain: 4, start: 0
    │   ├── [i:0, v:2] remain: 2, start: 0
    │   │   ├── [i:0, v:2] remain: 0, start: 0 ── Success → [2,2,2,2]
    │   │   ├── [i:1, v:3] remain: -1 ── Fail
    │   │   └── [i:2, v:5] remain: -3 ── Fail
    │   ├── [i:1, v:3] remain: 1, start: 1
    │   │   ├── [i:1, v:3] remain: -2 ── Fail
    │   │   └── [i:2, v:5] remain: -4 ── Fail
    │   └── [i:2, v:5] remain: -1 ── Fail
    ├── [i:1, v:3] remain: 3, start: 1
    │   ├── [i:1, v:3] remain: 0 ── Success → [2,3,3]
    │   └── [i:2, v:5] remain: -2 ── Fail
    └── [i:2, v:5] remain: 1, start: 2
        └── [i:2, v:5] remain: -4 ── Fail
└── [i:1, v:3] remain: 5, start: 1
    ├── [i:1, v:3] remain: 2, start: 1
    │   ├── [i:1, v:3] remain: -1 ── Fail
    │   └── [i:2, v:5] remain: -3 ── Fail
    └── [i:2, v:5] remain: 0 ── Success → [3,5]
└── [i:2, v:5] remain: 3, start: 2
    └── [i:2, v:5] remain: -2 ── Fail

Reference Table

Current PathRemainStarti (Index)Candidates[i]Result
[2]6002Continue
[2,2]4002Continue
[2,2,2]2002Continue
[2,2,2,2]0002Success (8)
[2,2,2,3]-1013Fail
[2,2,2,5]-3025Fail
[2,2,3]1013Continue
[2,2,3,3]-2113Fail
[2,2,3,5]-4125Fail
[2,2,5]-1025Fail
[2,3]3013Continue
[2,3,3]0113Success (8)
[2,3,5]-2125Fail
[2,5]1025Continue
[2,5,5]-4225Fail
[3]5113Continue
[3,3]2113Continue
[3,3,3]-1113Fail
[3,3,5]-3125Fail
[3,5]0125Success (8)
[5]3225Continue
[5,5]-2225Fail
Back to Blog

Related posts

Read more »

Flatten a Nested List

Hey everyone! 👋 I know I've been a bit quiet lately. I actually came down with a pretty bad flu last week, which completely knocked me out. 🤒 That's why I mis...