Medium
题目描述
给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:
answer[i] % answer[j] == 0,或answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。
示例 1:
输入:nums = [1,2,3]
输出:[1,2]
解释:[1,3] 也会被接受。
示例 2:
输入:nums = [1,2,4,8]
输出:[1,2,4,8]
提示:
1 <= nums.length <= 10001 <= nums[i] <= 2 * 10^9nums中的所有整数 互不相同
解题思路
这道题的核心思想是利用动态规划和排序的性质。
首先观察题目要求:子集中任意两个数都要满足整除关系。这意味着如果我们将子集按升序排列,那么相邻的数字也必须满足整除关系,即 a[i] % a[i-1] == 0。
算法步骤:
排序:将数组排序,这样可以保证如果
a < b < c且b % a == 0, c % b == 0,那么c % a == 0也成立(整除的传递性)。动态规划:
dp[i]表示以nums[i]结尾的最长整除子集的长度parent[i]记录前驱元素的索引,用于构造最终答案
状态转移:对于每个位置
i,检查所有之前的位置j,如果nums[i] % nums[j] == 0,则可以将nums[i]加入到以nums[j]结尾的子集中。路径重建:通过
parent数组反向构造最长的整除子集。
时间复杂度主要来自双重循环,空间复杂度用于存储 dp 和 parent 数组。
代码实现
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<int> dp(n, 1);
vector<int> parent(n, -1);
int maxLen = 1;
int maxIndex = 0;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] % nums[j] == 0 && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
parent[i] = j;
}
}
if (dp[i] > maxLen) {
maxLen = dp[i];
maxIndex = i;
}
}
vector<int> result;
int curr = maxIndex;
while (curr != -1) {
result.push_back(nums[curr]);
curr = parent[curr];
}
reverse(result.begin(), result.end());
return result;
}
};
class Solution:
def largestDivisibleSubset(self, nums: List[int]) -> List[int]:
n = len(nums)
nums.sort()
dp = [1] * n
parent = [-1] * n
max_len = 1
max_index = 0
for i in range(1, n):
for j in range(i):
if nums[i] % nums[j] == 0 and dp[j] + 1 > dp[i]:
dp[i] = dp[j] + 1
parent[i] = j
if dp[i] > max_len:
max_len = dp[i]
max_index = i
result = []
curr = max_index
while curr != -1:
result.append(nums[curr])
curr = parent[curr]
result.reverse()
return result
public class Solution {
public IList<int> LargestDivisibleSubset(int[] nums) {
int n = nums.Length;
Array.Sort(nums);
int[] dp = new int[n];
int[] parent = new int[n];
Array.Fill(dp, 1);
Array.Fill(parent, -1);
int maxLen = 1;
int maxIndex = 0;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] % nums[j] == 0 && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
parent[i] = j;
}
}
if (dp[i] > maxLen) {
maxLen = dp[i];
maxIndex = i;
}
}
List<int> result = new List<int>();
int curr = maxIndex;
while (curr != -1) {
result.Add(nums[curr]);
curr = parent[curr];
}
result.Reverse();
return result;
}
}
var largestDivisibleSubset = function(nums) {
nums.sort((a, b) => a - b);
const n = nums.length;
const dp = new Array(n).fill(1);
const parent = new Array(n).fill(-1);
let maxLen = 1;
let maxIndex = 0;
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] % nums[j] === 0 && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
parent[i] = j;
}
}
if (dp[i] > maxLen) {
maxLen = dp[i];
maxIndex = i;
}
}
const result = [];
let current = maxIndex;
while (current !== -1) {
result.unshift(nums[current]);
current = parent[current];
}
return result;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 需要双重循环遍历数组进行状态转移 |
| 空间复杂度 | O(n) | 需要 dp 和 parent 数组存储状态信息 |