Technology  /  Python

🐍 Python 78 guides · updated 2026

From first variable to OOP, generators, and real projects — the language that runs everything from data pipelines to AI agents, taught the practical way.

Find All Subsets of an Array in Python: Backtracking and Bit Manipulation

The power set of an array is the set of all its subsets, including the empty set and the array itself. For an array of length n, there are 2ⁿ subsets. Both main approaches — backtracking and bitmask enumeration — have the same time complexity for this reason, but they think about the problem differently.

Approach 1: Backtracking

At each position, decide whether to include the current element or skip it. Recurse through the whole array, building up a current subset, then record it when you reach the end.

def all_subsets_backtrack(arr):
"""Generate all subsets using recursive backtracking."""
result = []
def backtrack(start, current):
# Record the current subset at every call — not just at the leaf
result.append(list(current))
for i in range(start, len(arr)):
current.append(arr[i]) # include arr[i]
backtrack(i + 1, current) # recurse with next elements
current.pop() # exclude arr[i] (backtrack)
backtrack(0, [])
return result
arr = [1, 2, 3]
subsets = all_subsets_backtrack(arr)
for s in subsets:
print(s)
# Output (order may vary):
# []
# [1]
# [1, 2]
# [1, 2, 3]
# [1, 3]
# [2]
# [2, 3]
# [3]

The key insight: result.append(list(current)) is called at the top of every recursive call, so every prefix gets recorded — not just the complete branches.

Approach 2: Bitmask Enumeration

For n elements, iterate through all integers from 0 to 2ⁿ - 1. Each integer’s binary representation indicates which elements to include (bit set = include, bit not set = skip).

def all_subsets_bitmask(arr):
"""Generate all subsets using bitmask enumeration."""
n = len(arr)
result = []
for mask in range(1 << n): # 1 << n equals 2**n
subset = []
for i in range(n):
if mask & (1 << i): # check if bit i is set
subset.append(arr[i])
result.append(subset)
return result
arr = [1, 2, 3]
for s in all_subsets_bitmask(arr):
print(s)
# Output:
# []
# [1]
# [2]
# [1, 2]
# [3]
# [1, 3]
# [2, 3]
# [1, 2, 3]

For arr = [1, 2, 3] and mask 5 (binary 101): bits 0 and 2 are set, so the subset is [1, 3]. The empty set corresponds to mask 0, the full set to mask 7 (binary 111).

Using itertools for a Clean One-Liner

Python’s standard library handles this natively:

from itertools import chain, combinations
def power_set(arr):
"""Return all subsets using itertools."""
return list(chain.from_iterable(
combinations(arr, r) for r in range(len(arr) + 1)
))
arr = [1, 2, 3]
print(power_set(arr))
# Output: [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

Each result is a tuple here. Wrap with list() if you need lists.

Subsets with a Target Sum

A common variant: find all subsets whose elements sum to a target value.

def subsets_with_sum(arr, target):
"""Return all subsets that sum to target."""
result = []
def backtrack(start, current, remaining):
if remaining == 0:
result.append(list(current))
return
for i in range(start, len(arr)):
if arr[i] <= remaining: # pruning: skip if element exceeds remaining
current.append(arr[i])
backtrack(i + 1, current, remaining - arr[i])
current.pop()
backtrack(0, [], target)
return result
arr = [2, 4, 6, 3, 8, 5]
print(subsets_with_sum(arr, 10))
# Output: [[2, 8], [4, 6], [2, 3, 5]]

Complexity

ApproachTimeSpace (excluding output)
BacktrackingO(2ⁿ × n)O(n) recursion depth
BitmaskO(2ⁿ × n)O(1) extra
itertoolsO(2ⁿ × n)O(1) extra

Both generate the same 2ⁿ subsets; the n factor comes from copying each subset into the result. The bitmask approach is slightly more cache-friendly but the difference is negligible in Python.