🧠
Brute Force (Pure Recursion / Nested Loops / etc.)
💡 Starting with brute force helps understand the problem deeply by exploring all subsets and checking divisibility, even though it's inefficient. Think of it as trying every possible team combination to find the best fit, which is simple but slow.
Intuition
Try every subset of nums and check if it satisfies the divisibility condition. Keep track of the largest valid subset found.
Algorithm
- Generate all subsets of nums using recursion or bitmask.
- For each subset, check if every pair satisfies the divisibility condition.
- Keep track of the largest valid subset found so far.
- Return the largest valid subset after checking all.
💡 This approach is conceptually simple but hard to implement efficiently because the number of subsets grows exponentially.
from typing import List
def largestDivisibleSubset(nums: List[int]) -> List[int]:
n = len(nums)
nums.sort()
max_subset = []
def is_divisible_subset(subset):
for i in range(len(subset)):
for j in range(i + 1, len(subset)):
if subset[j] % subset[i] != 0:
return False
return True
def backtrack(start, path):
nonlocal max_subset
if len(path) > len(max_subset):
if is_divisible_subset(path):
max_subset = path[:]
for i in range(start, n):
path.append(nums[i])
backtrack(i + 1, path)
path.pop()
backtrack(0, [])
return max_subset
# Driver code
if __name__ == '__main__':
print(largestDivisibleSubset([1, 2, 3])) # Expected: [1, 2] or [1, 3]
Line Notes
nums.sort()Sorting helps to check divisibility in ascending order, simplifying checks.
def is_divisible_subset(subset):Helper to verify if current subset satisfies divisibility condition.
for i in range(len(subset))Check every pair in the subset for divisibility to ensure the subset is valid.
if len(path) > len(max_subset):Update max subset only if current path is longer and valid to keep track of largest subset.
import java.util.*;
public class LargestDivisibleSubset {
public static List<Integer> largestDivisibleSubset(int[] nums) {
Arrays.sort(nums);
List<Integer> maxSubset = new ArrayList<>();
backtrack(nums, 0, new ArrayList<>(), maxSubset);
return maxSubset;
}
private static void backtrack(int[] nums, int start, List<Integer> path, List<Integer> maxSubset) {
if (path.size() > maxSubset.size() && isDivisibleSubset(path)) {
maxSubset.clear();
maxSubset.addAll(path);
}
for (int i = start; i < nums.length; i++) {
path.add(nums[i]);
backtrack(nums, i + 1, path, maxSubset);
path.remove(path.size() - 1);
}
}
private static boolean isDivisibleSubset(List<Integer> subset) {
for (int i = 0; i < subset.size(); i++) {
for (int j = i + 1; j < subset.size(); j++) {
if (subset.get(j) % subset.get(i) != 0) return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(largestDivisibleSubset(new int[]{1, 2, 3})); // Expected: [1, 2] or [1, 3]
}
}
Line Notes
Arrays.sort(nums);Sort to simplify divisibility checks in ascending order.
if (path.size() > maxSubset.size() && isDivisibleSubset(path))Update max subset only if current path is longer and valid to track largest subset.
for (int i = start; i < nums.length; i++)Try adding each remaining number to current subset to explore all subsets.
path.remove(path.size() - 1);Backtrack by removing last added element to explore other subsets.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool isDivisibleSubset(const vector<int>& subset) {
for (int i = 0; i < (int)subset.size(); i++) {
for (int j = i + 1; j < (int)subset.size(); j++) {
if (subset[j] % subset[i] != 0) return false;
}
}
return true;
}
void backtrack(const vector<int>& nums, int start, vector<int>& path, vector<int>& maxSubset) {
if ((int)path.size() > (int)maxSubset.size() && isDivisibleSubset(path)) {
maxSubset = path;
}
for (int i = start; i < (int)nums.size(); i++) {
path.push_back(nums[i]);
backtrack(nums, i + 1, path, maxSubset);
path.pop_back();
}
}
vector<int> largestDivisibleSubset(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<int> maxSubset, path;
backtrack(nums, 0, path, maxSubset);
return maxSubset;
}
int main() {
vector<int> nums = {1, 2, 3};
vector<int> res = largestDivisibleSubset(nums);
for (int x : res) cout << x << ' ';
cout << '\n'; // Expected: 1 2 or 1 3
return 0;
}
Line Notes
sort(nums.begin(), nums.end());Sort to ensure divisibility checks are easier and consistent.
if ((int)path.size() > (int)maxSubset.size() && isDivisibleSubset(path))Update max subset only if current path is longer and valid to track largest subset.
path.push_back(nums[i]);Add current number to subset before recursive call to explore subsets.
path.pop_back();Remove last number to backtrack and try other subsets.
function largestDivisibleSubset(nums) {
nums.sort((a, b) => a - b);
let maxSubset = [];
function isDivisibleSubset(subset) {
for (let i = 0; i < subset.length; i++) {
for (let j = i + 1; j < subset.length; j++) {
if (subset[j] % subset[i] !== 0) return false;
}
}
return true;
}
function backtrack(start, path) {
if (path.length > maxSubset.length && isDivisibleSubset(path)) {
maxSubset = path.slice();
}
for (let i = start; i < nums.length; i++) {
path.push(nums[i]);
backtrack(i + 1, path);
path.pop();
}
}
backtrack(0, []);
return maxSubset;
}
// Test
console.log(largestDivisibleSubset([1, 2, 3])); // Expected: [1, 2] or [1, 3]
Line Notes
nums.sort((a, b) => a - b);Sort to make divisibility checks straightforward and consistent.
if (path.length > maxSubset.length && isDivisibleSubset(path))Update max subset only if current path is longer and valid to track largest subset.
path.push(nums[i]);Add current element before recursive exploration to build subsets.
path.pop();Backtrack by removing last element to try other subsets.
TimeO(n * 2^n * n^2)
SpaceO(n)
There are 2^n subsets, each subset can be up to size n, and checking divisibility takes O(n^2) in worst case.
💡 For n=20, this means over a million subsets and billions of checks, which is impractical. This approach is mainly for understanding the problem.
Interview Verdict: TLE
This approach is too slow for large inputs but helps understand the problem and correctness.