cconnected.
Understand the pattern. Find your own way.Find your starting point
Your learning feed/Idea 91 of 218
Ideas this builds on
Backtracking91 / 218

Choose, explore, restore

Picture

Backtracking builds a candidate one decision at a time. It explores what follows from a choice, then restores the working state so the next choice starts from the same place. It is useful when you need constructions or must search choices whose validity depends on a partial solution.

Predict

You are listing subsets using one mutable path list. At a completed subset, should you save path or path.copy()?

Reveal the reasoning

Save a copy. Otherwise many answers refer to the same list, which later choices and undo operations will change. Restoring a working object is different from preserving a completed result.

Go deeper: the invariant is restoration

Assume the input items are distinct.

def subsets(values):
    answers, path = [], []
    def visit(i):
        if i == len(values):
            answers.append(path.copy())
            return
        visit(i + 1)               # Exclude this item.
        path.append(values[i])     # Include this item.
        visit(i + 1)
        path.pop()                 # Restore the caller's path.
    visit(0)
    return answers

assert subsets(["A", "B"]) == [[], ["B"], ["A"], ["A", "B"]]
assert subsets([]) == [[]]

Each call returns with path exactly as it found it. That local promise prevents a sibling branch from inheriting another branch’s choice. Recursion itself does not automatically undo mutations to shared objects.

There are 2**n outputs, with Θ(n·2**n) total copying time and output storage. Working path and recursion depth use O(n) extra space excluding output. A generator can reduce retained output memory, but still needs to generate every requested result.

Recognition test: are you required to list configurations, or do constraints depend on the partial construction? A choice search is a useful baseline. For existence, stop at the first success. For a count or optimum, ask next whether repeated sufficient states can share an answer.

Analogy limit: backtracking does not always require an explicit pop. Passing a new immutable tuple to each child achieves branch separation through new values, at a copying cost.

Change one rule

The input is ["A", "A"] and identical-looking subsets should appear once. Does this implementation satisfy that output contract?

Check the variation

No. It treats positions as distinct choices and emits ["A"] twice. You need a duplicate policy derived from whether identity belongs to positions or values.

Connect

Pruning avoids branches with a justification. Trie search rejects impossible prefixes. DP can share suffix answers without erasing required distinct outputs.