LeetCode #368 Medium

Largest Divisible Subset

Given a set of distinct positive integers nums, return the largest subset such that for every pair (a, b) in the subset, either a % b == 0 or b % a == 0.

dynamic-programmingmathsorting
Open on LeetCode ↗
02

Intuition

The divisibility constraint is transitive in a sorted array: if a | b and b | c (where | means 'divides'), then a | c. This means a valid divisible subset, when sorted, forms a chain where each element divides the next. After sorting nums, finding the longest such chain is a variant of the Longest Increasing Subsequence (LIS) problem, replacing the 'increasing' condition with 'divides'. For each element, find the best chain ending at a previous element that divides it, extend by one, and track parent pointers to reconstruct the actual subset.

How to spot this pattern

The shape is: sorted array, find the longest chain where each element satisfies a transitive relation with the previous. LIS uses <, this uses %. Whenever a problem asks for the longest chain with a transitive property and you can sort to make the property directional, the LIS-like DP applies. Reconstruction with parent pointers is the standard way to recover the actual subsequence.

03

Approach

1

Sort the array so divisibility becomes a forward chain

Sorting ensures that if nums[j] divides nums[i], then j < i. This lets us build chains left to right. Without sorting, you would need to check all pairs in both directions.

2

DP like LIS, but with divisibility instead of inequality

Let dp[i] = length of the longest divisible chain ending at nums[i]. For each i, scan all j < i where nums[i] % nums[j] == 0. Take the best: dp[i] = max(dp[j] + 1) over all such j. Also store parent[i] = j to reconstruct the chain. Base case: dp[i] = 1 (the element alone).

3

Reconstruct the subset by following parent pointers

After filling the DP, find the index with the maximum dp value. Follow parent pointers backward to collect the chain. Reverse it (since we traced backward) and return. Time is O(n²) for the double loop. Space is O(n) for dp and parent.

04

Solution

1class Solution:
2 def largestDivisibleSubset(self, nums):
3 nums.sort()
4 n = len(nums)
5 dp = [1] * n
6 parent = [-1] * n
7 best_idx = 0
8 for i in range(1, n):
9 for j in range(i):
10 if nums[i] % nums[j] == 0 and dp[j] + 1 > dp[i]:
11 dp[i] = dp[j] + 1
12 parent[i] = j
13 if dp[i] > dp[best_idx]:
14 best_idx = i
15 result = []
16 idx = best_idx
17 while idx != -1:
18 result.append(nums[idx])
19 idx = parent[idx]
20 return result[::-1]
05

Common pitfalls

Checking nums[j] % nums[i] == 0 instead of nums[i] % nums[j] == 0

✗ Wrong
if nums[j] % nums[i] == 0:
✓ Right
if nums[i] % nums[j] == 0:

After sorting, nums[j] <= nums[i] for j < i. The condition should be 'the larger divides evenly by the smaller', which is nums[i] % nums[j] == 0. Reversing it checks if the smaller divides by the larger, which is almost never true.

Forgetting to sort the array before the DP

✗ Wrong
dp = [1] * len(nums)
for i in range(len(nums)):
✓ Right
nums.sort()
dp = [1] * len(nums)
for i in range(len(nums)):

Without sorting, a larger number might appear before a smaller one. The left-to-right scan would miss valid chains where the divisor appears later in the unsorted array.

Not tracking parent pointers for reconstruction

✗ Wrong
# only track dp[i], no parent
max_idx = dp.index(max(dp))
return [nums[max_idx]]
✓ Right
parent = [-1] * n
...
result = []
while idx != -1:
    result.append(nums[idx])
    idx = parent[idx]

The problem asks for the actual subset, not just its size. Without parent pointers, you can only return the last element of the chain, not the entire chain.

06

Edge cases

All elements are powers of 2, e.g. [1, 2, 4, 8]

Every element divides every later one. The entire sorted array is a valid subset.

All elements are prime

No element divides any other (since they are all > 1 and prime). The largest subset is any single element.

Single element

dp[0] = 1. The subset is just that element.

07

Complexity

Time
O(n²)
Space
O(n)
Double loop over the sorted array. Parent array and dp array each use O(n).