Skip to main content

368.最大的可整除子集

标签: dynamic-programming, sort

难度: Medium

通过率: 45.75%

原题链接: https://leetcode.com/problems/largest-divisible-subset/description/

题目描述

给定一个由不同正整数组成的集合 numsnums,返回一个最大的子集 answeranswer,使得在这个子集中每一对元素 (answer[i],answer[j])(answer[i], answer[j]) 满足:

  • answer[i]%answer[j]==0answer[i] \% answer[j] == 0,或
  • answer[j]%answer[i]==0answer[j] \% answer[i] == 0

若有多个解,返回其中任意一个。

解题思路

这个问题可以通过动态规划来解决。首先,将数组 numsnums 进行排序,这可以确保在检查可整除性时,较大的数字出现在较小的数字后面。接下来,我们创建一个数组 dpdp,其中 dp[i]dp[i] 表示以 nums[i]nums[i] 为结尾的最大可整除子集的长度。同时,我们还需要一个跟踪数组 parentparent 来构建结果子集。

使用两重循环遍历数组:对于每一个 nums[i]nums[i],我们查看之前的所有元素 nums[j]nums[j]j<ij < i),如果 nums[i]%nums[j]==0nums[i] \% nums[j] == 0,则 nums[i]nums[i] 可以加入以 nums[j]nums[j] 为结尾的最大子集中。这时,我们就更新 dp[i]=dp[j]+1dp[i] = dp[j] + 1 。同时更新 parent[i]=jparent[i] = j 来记录 nums[j]nums[j]nums[i]nums[i] 的前继节点。

最后,找到 dpdp 数组中的最大值,这就是最大可整除子集的长度,通过 parentparent 数组从后往前追踪,就可以构建出这个子集。

代码实现

def largestDivisibleSubset(nums):
# 如果数组为空,直接返回空列表
if not nums:
return []

n = len(nums)
nums.sort()
# dp[i] 表示以 nums[i] 为结尾的最大可整除子集的大小
dp = [1] * n
# parent 用于重构子集
parent = [-1] * n

max_size = 0
max_index = -1

# 动态规划以找到最大的子集
for i in range(n):
for j in range(i):
if nums[i] % nums[j] == 0:
if dp[j] + 1 > dp[i]:
dp[i] = dp[j] + 1
parent[i] = j
if dp[i] > max_size:
max_size = dp[i]
max_index = i

# 通过 parent 数组来重构结果子集
result = []
while max_index != -1:
result.append(nums[max_index])
max_index = parent[max_index]

return result[::-1]

复杂度分析

时间复杂度为 O(n2)O(n^2),因为我们需要对每一对可能的数对进行检查。此外,排序操作为 O(nlogn)O(n \log n),所以总的时间复杂度为 O(n2)O(n^2)

空间复杂度为 O(n)O(n),用于存储 dpdpparentparent 数组。