Skip to content

Combinations

Given two integers n and k, return all possible combinations of k numbers chosen from the range [1, n].

You may return the combinations in any order.

InputOutputExplanation
n=4, k=2[[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]]6 unique combinations of 2 from 4
n=1, k=1[[1]]Only one way to choose 1 from 1
n=5, k=310 combinationsC(5,3) = 10 total combinations
  • 1 <= n <= 20
  • 1 <= k <= n
ApproachTimeSpaceImplementationBest When
BacktrackingO(C(n,k) * k)O(C(n,k) * k)Recursive, intuitiveWant clear, readable recursive solution
IterativeO(C(n,k) * k)O(C(n,k) * k)Iterative, uses state machinePrefer avoiding recursion overhead
  • Pick Backtracking if you are learning the decision tree pattern.
  • Pick Iterative if you prefer avoiding recursion or want to understand the next-permutation technique.
Most Common
Backtracking
O(C(n,k) * k) time · O(C(n,k) * k) space
Iterative
Next Combination
O(C(n,k) * k) time · O(C(n,k) * k) space
★ Recommended

Use a decision tree where at each level we choose whether to include a number or not. Start from 1, and at each number decide whether to include it in the current combination. If we include it, recursively build combinations for the remaining k-1 slots from the remaining numbers.

The key insight is choose, explore, unchoose — this allows us to explore all possibilities without managing a separate “used” set.

⏱ Time O(C(n,k) * k) C(n,k) combos, k per combo 💾 Space O(C(n,k) * k) Result storage

Input: n = 4, k = 2

LevelCurrentExploreDecision
0[]Start from 1Choose 1?
1[1]Continue from 2Choose from [2,3,4]?
2[1,2]k=2 reachedSave [1,2]
2[1,3]k=2 reachedSave [1,3]
2[1,4]k=2 reachedSave [1,4]
1[2]Continue from 3Choose from [3,4]?
2[2,3]k=2 reachedSave [2,3]
2[2,4]k=2 reachedSave [2,4]
1[3]Continue from 4Choose 4?
2[3,4]k=2 reachedSave [3,4]
function combinationsBacktracking(n, k):
result = []
function backtrack(start, current):
if len(current) == k:
result.append(copy of current)
return
for i from start to n:
current.append(i)
backtrack(i + 1, current)
current.pop()
backtrack(1, [])
return result
combinations_backtracking.py
from typing import List
def combinations_backtracking(n: int, k: int) -> List[List[int]]:
"""
Generate all combinations of k numbers from 1 to n using backtracking.
Time: O(C(n,k) * k), Space: O(C(n,k) * k) for result
"""
result = []
def backtrack(start: int, current: List[int]) -> None:
# Base case: we've selected k numbers
if len(current) == k:
result.append(current[:]) # Make a copy
return
# Explore: try each remaining number
for i in range(start, n + 1):
# Choose
current.append(i)
# Explore
backtrack(i + 1, current)
# Unchoose
current.pop()
backtrack(1, [])
return result
print(combinations_backtracking(4, 2)) # [[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]]
print(combinations_backtracking(1, 1)) # [[1]]
print(combinations_backtracking(5, 3)) # 10 combinations
🎯 Interview Favourite

Generate combinations iteratively using a state machine approach. Start with the first k numbers [1, 2, ..., k]. At each step, find the rightmost number that can be incremented (one that is not yet at its maximum value), increment it, and reset all numbers to its right.

This approach mimics the “next combination” concept, similar to how you would manually enumerate combinations on paper.

⏱ Time O(C(n,k) * k) C(n,k) combos, k per combo 💾 Space O(C(n,k) * k) Result storage

Input: n = 4, k = 2

ComboActionNext
[1,2]Save, then find rightmost incrementable: index 1Increment combo[1] from 2 to 3
[1,3]Save, then find rightmost incrementable: index 1Increment combo[1] from 3 to 4
[1,4]Save, then find rightmost incrementable: index 1, but 4 is maxBacktrack to index 0
[2,3]Increment combo[0] from 1 to 2, reset combo[1] to 3Save, find rightmost
[2,4]Save, then find rightmost incrementable: index 1Done after processing all
[3,4]Continue until rightmost element reaches n
function combinationsIterative(n, k):
result = []
combo = [1, 2, ..., k]
while true:
result.append(copy of combo)
# Find the rightmost number that can be incremented
i = k - 1
while i >= 0 and combo[i] == n - k + i + 1:
i--
if i < 0:
break
# Increment and reset
combo[i]++
for j from i+1 to k:
combo[j] = combo[j-1] + 1
return result
combinations_iterative.py
from typing import List
def combinations_iterative(n: int, k: int) -> List[List[int]]:
"""
Generate all combinations of k numbers from 1 to n using iterative approach.
Time: O(C(n,k) * k), Space: O(C(n,k) * k) for result
"""
result = []
combo = list(range(1, k + 1))
while True:
result.append(combo[:])
# Find the rightmost number that can be incremented
i = k - 1
while i >= 0 and combo[i] == n - k + i + 1:
i -= 1
if i < 0:
break
# Increment and reset
combo[i] += 1
for j in range(i + 1, k):
combo[j] = combo[j - 1] + 1
return result
print(combinations_iterative(4, 2)) # [[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]]
print(combinations_iterative(1, 1)) # [[1]]
print(combinations_iterative(5, 3)) # 10 combinations
✓ Simple

A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.

⏱ Time O(n) Optimized complexity 💾 Space O(1) Efficient space usage
function approach3():
// Implementation approach goes here
combinations_space_optimized.py
"""
Solution for Combinations
"""
def solve():
"""Implementation for approach 3 of Combinations"""
pass
if __name__ == "__main__":
print("Solution ready")