2Brute2Best
🔥0

Largest Divisible Subset

Medium·LeetCode #368Basic Anchor
LeetCode
30-Second Mental ModelLIS-style DP with a custom 'can follow' rule: chains and reconstruction
Complexity:O(2^N · N^2)→O(N^2)
Key Insight

Sort the numbers. Let len[i] be the size of the largest valid group whose largest number is nums[i]. For each earlier j, if nums[i] % nums[j] == 0, the group ending at nums[j] can be extended by nums[i]. Keep the best one, and store its parent j. Walking the parents back from the best end gives the group.

Core Validity Invariant

A group is valid when each number divides the next one. Divisibility passes along a chain (if a | b and b | c, then a | c), so every pair in the group divides one way round.

01

Problem

You get a list of distinct positive whole numbers. Pick the largest group you can from the list, where every two numbers in the group have one that divides the other.

Return any group of the largest size. The order of the numbers in the answer does not matter.

Input

nums, a list of distinct positive whole numbers. The list is not sorted.

Output

A largest group of numbers from nums where every pair has one number dividing the other.

Example 1
Input: nums = [1, 2, 3]
Output: [1, 2]

[1, 3] is also accepted. 2 and 3 do not divide each other, so they cannot both be in the group.

Example 2
Input: nums = [1, 2, 4, 8]
Output: [1, 2, 4, 8]

Each number divides the next one, so all four numbers fit in the group.

Constraints
  • • 1 ≤ nums.length ≤ 1000
  • • 1 ≤ nums[i] ≤ 2 · 10^9
  • • All the integers in nums are unique.
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    Can 2 and 3 both be in the group? What about 2 and 4? What is the rule for a pair?

  2. 2.

    Does the order of the numbers in the list help me check the rule? What order would make it easier?

  3. 3.

    If I know the best group that ends with a number x, how can I use it for a bigger number y?

  4. 4.

    When I add y to a group, do I have to check y against every number already in the group, or is one check enough?

  5. 5.

    I know the length of the best group. How do I get the actual numbers back?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceTake or skip each number, then check every pair in each setO(2^N · N^2)O(N)
OptimalSort; len[i] is the best chain ending at nums[i]; keep parents and walk backO(N^2)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “largest subset”We want a size. Keep the best size for each ending number, and take the maximum at the end.
  • “every pair has one that divides the other”A group is a chain where each number is a multiple of the one before it. The state only needs the last number.
  • “distinct positive numbers”No repeats, so each divisor is smaller than the number it divides. Sorting turns the list into a line we can scan.
Chain in sorted order+each element extends a smaller element+maximum length, with parents to rebuild it→LIS-style DP with a custom 'can follow' rule: chains and reconstruction

This is LIS where the rule is divisibility instead of 'bigger'. In LIS, any bigger number can follow. Here a number can follow only when it is a multiple of the one before, which is a modulo test. The chain property is what makes the O(N²) loop correct: once the list is sorted, checking the last number of a chain is enough, and parent pointers rebuild the group.

10

Don't make this mistake

Pattern confusion: not sorting first
✗ for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) if (nums[i] % nums[j] == 0) ... // on the unsorted list
✓ sort(nums.begin(), nums.end()); // then the same two loops

The loop only looks left, so a divisor placed after its multiple is never used. On [4, 2, 1], the loop never links 1 to 2 or 4, so it returns 1. The right answer is 3, the chain [1, 2, 4]. Sorting puts every divisor before the numbers it divides.

Checking only the smallest number
✗ if (x % nums[0] == 0) keep x; // every number is checked against the smallest one
✓ if (x % last == 0) extend the chain ending at last; // check against the chain's last number

Dividing the smallest number is not enough to form a chain. On [1, 2, 3], every number is a multiple of 1, so this keeps all three. But 2 and 3 do not divide each other, so the right answer has size 2, for example [1, 2].

Returning the chain that ends at the last number
✗ return the chain that ends at nums[n - 1];
✓ return the chain that ends at the index with the largest len[i];

The best group does not have to end at the largest number. On [1, 2, 4, 8, 9], the chain ending at 9 is just [1, 9] (size 2). The best group is [1, 2, 4, 8] (size 4).

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like DP — Longest Increasing Subsequence translate into production architectures and low-level memory mechanics.