Taro Logo

Largest Divisible Subset

Medium
Apple logo
Apple
3 views
Topics:
Dynamic Programming

Given a set of distinct positive integers nums, return the largest subset answer such that every pair (answer[i], answer[j]) of elements in this subset satisfies:

  • answer[i] % answer[j] == 0, or
  • answer[j] % answer[i] == 0

If there are multiple solutions, return any of them.

For example:

nums = [1,2,3]

Possible outputs are [1,2] or [1,3].

As another example:

nums = [1,2,4,8]

The only possible output is [1,2,4,8].

Describe an algorithm to find the largest divisible subset. What is the time and space complexity of your solution? What are the edge cases to consider? Provide code for your solution.

Solution


Brute Force Approach

A naive approach would be to generate all possible subsets of the input array nums and then check if each subset satisfies the given condition. We iterate through all subsets, and for each subset, we check every pair of elements to see if they meet the divisibility requirement. The largest subset that satisfies the condition is the answer.

Code (Python):

def largest_divisible_subset_brute_force(nums):
    def check_subset(subset):
        for i in range(len(subset)):
            for j in range(i + 1, len(subset)):
                if not (subset[i] % subset[j] == 0 or subset[j] % subset[i] == 0):
                    return False
        return True

    from itertools import combinations

    largest_subset = []
    for i in range(1, len(nums) + 1):
        for subset in combinations(nums, i):
            subset = list(subset)
            if check_subset(subset):
                if len(subset) > len(largest_subset):
                    largest_subset = subset
    return largest_subset

Time Complexity: O(2n * n2), where n is the number of elements in nums. Generating all subsets takes O(2n) time, and checking each subset for the divisibility condition takes O(n2) time in the worst case.

Space Complexity: O(n), primarily due to the space used to store the subsets.

Optimal Solution: Dynamic Programming

A more efficient approach uses dynamic programming. First, sort the input array nums. Then, create two arrays: dp and predecessor, both of the same length as nums. dp[i] stores the length of the largest divisible subset ending at nums[i]. predecessor[i] stores the index of the preceding element in the largest divisible subset ending at nums[i]. Iterate through the sorted nums, and for each nums[i], iterate through the preceding elements nums[j] (where j < i). If nums[i] is divisible by nums[j], it means we can extend the divisible subset ending at nums[j] by adding nums[i]. Update dp[i] and predecessor[i] accordingly. After filling the dp and predecessor arrays, find the index with the maximum value in dp. Backtrack from that index using the predecessor array to construct the largest divisible subset.

Code (Python):

def largest_divisible_subset(nums):
    nums.sort()
    n = len(nums)
    dp = [1] * n
    predecessor = [-1] * n
    max_index = 0

    for i in range(n):
        for j in range(i):
            if nums[i] % nums[j] == 0:
                if dp[i] < dp[j] + 1:
                    dp[i] = dp[j] + 1
                    predecessor[i] = j
        if dp[i] > dp[max_index]:
            max_index = i

    result = []
    curr = max_index
    while curr != -1:
        result.append(nums[curr])
        curr = predecessor[curr]

    return result

Time Complexity: O(n2), where n is the number of elements in nums. The nested loops iterate through all possible pairs of elements.

Space Complexity: O(n), for the dp and predecessor arrays, and the result list.

Edge Cases:

  • Empty input array: Return an empty list.
  • Single element array: Return the array itself.
  • Multiple solutions: The algorithm will return one of the possible largest divisible subsets.