跳至主要內容

368. 最大整除子集


368. 最大整除子集

🟠   🔖  数组 数学 动态规划 排序  🔗 力扣open in new window LeetCodeopen in new window

题目

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.

Example 1:

Input: nums = [1,2,3]

Output: [1,2]

Explanation: [1,3] is also accepted.

Example 2:

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

Output: [1,2,4,8]

Constraints:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 2 * 10^9
  • All the integers in nums are unique.

题目大意

给你一个由 无重复 正整数组成的集合 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 <= 1000
  • 1 <= nums[i] <= 2 * 10^9
  • nums 中的所有整数 互不相同

解题思路

  1. 排序数组

    • nums 进行升序排序,以确保在后续遍历中可以利用整除性质。
  2. 动态规划求解

    • 定义 dp[i] 表示以 nums[i] 结尾的最大整除子集的长度。
    • 通过额外的 parent[i] 数组记录前驱节点索引,从而避免回溯时重复判断整除条件。
    • 使用双层循环,遍历 nums 中的每一对元素:
      • 对于每个 nums[i],遍历 nums[0]nums[i - 1],如果满足 nums[i] % nums[j] === 0
      • 则更新 dp[i] = max(dp[i], dp[j] + 1),表示通过 nums[j] 扩展出更长的子集。
      • 记录路径 parent[i] = j,以便回溯生成结果。
  3. 回溯构建结果

    • 根据 dp 数组找到最大整除子集的终点索引 maxIdx
    • 通过回溯 parent 数组直接生成结果子集。
    • parent[maxIdx] 依次回溯,将符合条件的元素加入结果子集。
  4. 返回结果

    • 最后返回子集 res

复杂度分析

  • 时间复杂度O(n^2),外层遍历每个元素,内层检查所有前驱节点,形成二重循环。
  • 空间复杂度O(n),需要额外的 dpparent 数组。

代码

/**
 * @param {number[]} nums
 * @return {number[]}
 */
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 maxSize = 1,
		maxIdx = 0;
	for (let i = 1; i < n; i++) {
		for (let j = 0; j < i; j++) {
			if (nums[i] % nums[j] == 0 && dp[i] < dp[j] + 1) {
				dp[i] = dp[j] + 1;
				parent[i] = j;
			}
		}
		if (dp[i] > maxSize) {
			maxSize = dp[i];
			maxIdx = i;
		}
	}

	let res = [];
	// 直接通过 parent 数组回溯
	for (let i = maxIdx; i >= 0; i = parent[i]) {
		res.push(nums[i]);
		if (parent[i] == -1) break;
	}
	return res;
};