当前位置: 首页 > news >正文

Swift 解法详解:LeetCode 368《最大整除子集》

在这里插入图片描述
在这里插入图片描述

文章目录

    • 摘要
    • 描述
    • 题解答案
    • 题解代码分析
      • 代码拆解
    • 示例测试及结果
    • 时间复杂度
    • 空间复杂度
    • 总结

摘要

有时候我们会遇到这样的问题:给定一堆数,如何从中挑出一个子集,让这个子集里的每一对数都能互相整除?题目要求我们找出最大的这样一个子集。

这个问题看起来有点像是在“组团”,条件是必须能整除。其实背后是一个动态规划问题,用来锻炼我们对“子问题递推”的理解。

描述

题目给了一个数组 nums,里面是一些互不相同的正整数。我们要找出一个最大的子集 answer,使得里面的任意两个数 ab 满足:

  • a % b == 0,或者
  • b % a == 0

换句话说,要么一个能被另一个整除,要么反过来也能被整除。

示例 1:

输入: [1,2,3]
输出: [1,2]
解释: 其实 [1,3] 也对,因为 3 % 1 == 0。

示例 2:

输入: [1,2,4,8]
输出: [1,2,4,8]

题解答案

直观的思路是:

  1. 如果一个数可以整除另一个数,那它们更可能属于同一个子集。
  2. 子集要尽量大,所以我们需要在多个候选方案中找到“最长的那条链”。

动态规划的做法

  • 先把数组排序,因为小数更容易整除大数。

  • 用一个数组 dp[i] 表示以 nums[i] 结尾的最大整除子集的长度。

  • 遍历每个 nums[i],再和前面的数 nums[j] 去比较:

    • 如果 nums[i] % nums[j] == 0,说明能放在一起,那就更新 dp[i] = max(dp[i], dp[j] + 1)
  • 同时用一个 parent 数组来记录前驱,方便最后回溯出完整的子集。

题解代码分析

下面是 Swift 的实现:

import Foundationclass Solution {func largestDivisibleSubset(_ nums: [Int]) -> [Int] {if nums.isEmpty { return [] }let nums = nums.sorted()let n = nums.countvar dp = Array(repeating: 1, count: n) // 每个数至少能独立成一个集合var parent = Array(repeating: -1, count: n) // 用来回溯路径var maxLen = 1var maxIndex = 0for i in 1..<n {for j in 0..<i {if nums[i] % nums[j] == 0 {if dp[j] + 1 > dp[i] {dp[i] = dp[j] + 1parent[i] = j}}}if dp[i] > maxLen {maxLen = dp[i]maxIndex = i}}// 回溯答案var result = [Int]()var k = maxIndexwhile k != -1 {result.append(nums[k])k = parent[k]}return result.reversed()}
}// MARK: - Demo 演示
let solution = Solution()
print(solution.largestDivisibleSubset([1, 2, 3]))    // [1, 2] 或 [1, 3]
print(solution.largestDivisibleSubset([1, 2, 4, 8])) // [1, 2, 4, 8]
print(solution.largestDivisibleSubset([3, 5, 10, 20, 21])) // [5, 10, 20]

代码拆解

  1. 排序

    • 把数组排好序,保证前面的数比后面的小,便于判断整除关系。
  2. 动态规划数组 dp

    • dp[i] 表示以 nums[i] 结尾的最大整除子集的长度。
  3. 前驱数组 parent

    • parent[i] 记录了 nums[i] 前面可以接上的数字位置,用来回溯结果。
  4. 两层循环

    • 外层 i 表示当前要计算的数。
    • 内层 j 遍历比它小的数,看能不能整除。
  5. 回溯结果

    • 从最大长度的下标开始,不断回溯 parent,拼成最终的子集。

示例测试及结果

运行 Demo,结果如下:

输入: [1, 2, 3]
输出: [1, 2]   // 或 [1, 3]输入: [1, 2, 4, 8]
输出: [1, 2, 4, 8]输入: [3, 5, 10, 20, 21]
输出: [5, 10, 20]

这个结果是合理的,比如 [5, 10, 20] 就是一条完整的整除链:

  • 10 % 5 == 0
  • 20 % 10 == 0

时间复杂度

  • 外层循环 i 最多跑 n 次,内层循环 j 也最多 n 次。
  • 所以时间复杂度是 O(n²)
  • n <= 1000 的范围内,这个复杂度是可接受的。

空间复杂度

  • 使用了 dpparent 两个数组,各占 O(n)
  • 回溯的结果也是 O(n)。
  • 所以整体空间复杂度是 O(n)

总结

这道题的关键在于 先排序,再动态规划

  • 排序后更容易保证“整除链”的关系。
  • 动态规划帮我们一步步延长最长的整除子集。

如果用现实场景来打个比方:

  • 你可以把每个数想象成一个“圈子”,只有能整除的才能成为同一个圈子。
  • 我们要找的,就是那个能容纳最多成员的“最大圈子”。

这类题目训练了我们对 递推关系 的思考能力,也再次证明了排序在动态规划里的重要作用。

http://www.xdnf.cn/news/1421587.html

相关文章:

  • 【牛客JZ31】—栈的压入弹出序列判断算法详解
  • FPGA中的亚稳态与跨时钟域数据撕裂现象
  • 眼底病害图像分类数据集
  • MYSQL速通(4/5)
  • KL Loss
  • Python OpenCV图像处理与深度学习:Python OpenCV图像滤波入门
  • [系统架构设计师]论文(二十三)
  • 基于SpringBoot+MYSQL开发的师生成果管理系统
  • 美术馆预约小程序|基于微信小程序的美术馆预约平台设计与实现(源码+数据库+文档)
  • zotero.sqlite已损坏
  • 第9篇:监控与运维 - 集成Actuator健康检查
  • 『C++成长记』vector模拟实现
  • 车载总线架构 --- 车载LIN总线传输层概述
  • 百胜软件获邀出席第七届中国智慧零售大会,智能中台助力品牌零售数智变革
  • C++ 虚继承:破解菱形继承的“双亲困境”
  • 【macOS】垃圾箱中文件无法清理的--特殊方法
  • Linux | 走进网络世界:MAC、IP 与通信的那些事
  • PyTorch 实战(3)—— PyTorch vs. TensorFlow:深度学习框架的王者之争
  • mysql中如何解析某个字段是否是中文
  • 攻防演练笔记
  • Frida Hook API 转换/显示堆栈
  • 【数学建模学习笔记】缺失值处理
  • 数学分析原理答案——第七章 习题13
  • 文件夹上传 (UploadFolder)
  • crypto-babyrsa(2025YC行业赛)
  • 【系统架构师设计(8)】需求分析之 SysML系统建模语言:从软件工程到系统工程的跨越
  • 【机器学习学习笔记】numpy基础2
  • 基于 HTML、CSS 和 JavaScript 的智能图像边缘检测系统
  • ESB 走向黄昏,为什么未来属于 iPaaS?
  • 【第十一章】Python 队列全方位解析:从基础到实战