Factor Combinations
Medium · Backtracking · Explanation and code example
Backtracking
How it works
To find all unique factor combinations of n, we use backtracking. At each step, we take the last factor in our current list and try to split it into two smaller factors. By only considering factors greater than or equal to the previous one, we avoid generating duplicate combinations like [2, 6] and [6, 2].
The key insight is that if we have a product, we only need to try factors up to the square root of that product. If i divides the product, then both i and product/i are factors, and we recursively continue with product/i.
Steps
- Start with
factors = [n]and an empty result list. - In the backtracking function:
- If
factorshas more than one element, it represents a valid combination, so add a copy to the result. - Pop the last factor (call it
lastFactor). - Determine the starting point: use
2iffactorsis empty, otherwise use the last remaining factor. - For each
ifrom the starting point up tosqrt(lastFactor):- If
idivideslastFactor, push bothiandlastFactor/i, recurse, then pop both to backtrack.
- If
- Restore
lastFactortofactorsbefore returning.
- If
- Return the result list.
Complexity
-
Time complexity:
O(n^{1.5}) -
Space complexity:
O(log (n))
Where
nis the input integern.
Things to watch for
Generating Duplicate Combinations
A common mistake is generating duplicate factor combinations like [2, 6] and [6, 2]. To avoid this, always ensure factors are added in non-decreasing order by only considering factors greater than or equal to the previous factor in the current combination.
Including 1 or n as Factors
The problem explicitly excludes 1 and n itself as valid factors. Forgetting this constraint leads to including trivial factorizations like [1, n] or just [n] in the result. Always start factor candidates from 2 and stop before reaching n.
Inefficient Factor Search
Searching for factors up to n instead of sqrt(n) leads to unnecessary iterations. Since factors come in pairs (if i divides n, then n/i also divides n), you only need to check up to the square root of the current product to find all factor pairs efficiently.
Python code example
from typing import List
class Solution:
def _backtracking(self, factors: List[int], ans: List[List[int]]) -> None:
# Got a solution.
if len(factors) > 1:
ans.append(factors.copy())
last_factor = factors.pop()
i = 2 if not factors else factors[-1]
while i <= last_factor // i:
if last_factor % i == 0:
# Add i and last_factor // i.
factors.append(i)
factors.append(last_factor // i)
self._backtracking(factors, ans)
# Remove the last 2 elements in factors to restore it after the recursion returns.
factors.pop()
factors.pop()
i += 1
# Add last_factor back to factors to restore it.
factors.append(last_factor)
def getFactors(self, n: int) -> List[List[int]]:
ans = []
self._backtracking([n], ans)
return ans
C++ code example
class Solution {
void backtracking(vector<int>& factors, vector<vector<int>>& ans) {
// Got a solution,
if (factors.size() > 1) {
ans.push_back(factors);
}
const int lastFactor = factors.back();
factors.pop_back();
for (int i = factors.empty() ? 2 : factors.back(); i <= lastFactor / i; ++i) {
if (lastFactor % i == 0) {
// Add i and lastFactor / i.
factors.push_back(i);
factors.push_back(lastFactor / i);
backtracking(factors, ans);
// Remove the last 2 elements in factors to restore it after the recursion returns
factors.pop_back();
factors.pop_back();
}
}
// Add lastFactor back to factors to restore it.
factors.push_back(lastFactor);
}
public:
vector<vector<int>> getFactors(int n) {
vector<int> factors = {n};
vector<vector<int>> ans;
backtracking(factors, ans);
return ans;
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.