力扣小白刷题之279题完全平方数

题目描述

给定正整数 n,找到若干个完全平方数(比如1,4,9,16,…),使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

思路一:BFS

参考:https://leetcode-cn.com/problems/perfect-squares/solution/dong-tai-gui-hua-bfs-zhu-xing-jie-shi-python3-by-2/

可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。
要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。
力扣小白刷题之279题完全平方数_第1张图片
使用BFS顺序遍历每一层,当节点差出现 0 时,此时一定是最短的路径。

第一层依次减去一个平方数得到第二层,第二场依次减去一个平方数得到第三层。直到某一层出现了 0,此时的层数就是我们要找的平方数和的最小个数。

如图中绿色所示,此时节点为 0,表示根节点可以由路径上的平方数 {1,1,9}构成,返回此时的路径长度 3,后续不再执行。
而图中的红色节点表示,此节点值在之前已经出现,不需要再计算,一定不会是最短路径,最短路径还未出现。

步骤

借助队列实现BFS(层次遍历)

  1. 初始化队列queue = {n},访问元组 visited = { },初始化路径长度(层数) level = 0;
  2. 循环条件,队列不为空:
    • level++,因为循环依次,意味着一层中的节点已经遍历完,所以路径长度需要加1
    • 定义当前层中的节点数 size = queue.size(),遍历当前层中的所有节点
      • cur 为队首元素
      • 遍历所有 小于 n 的平方数 s
      • next = cur - s
      • 如果 next == 0 ,返回当前的路径长度level
      • 如果 !visited.contains(next) is true,表示当前节点未出现过,将当前节点入队并在访问数组中加入。

BFS代码力扣小白刷题之279题完全平方数_第2张图片

代码优化

把visited数组从HashSet改成boolean数组marked,这样只需要boolean数组判断是否已经访问,节省了大量HashSet操作。
力扣小白刷题之279题完全平方数_第3张图片

生成平方数序列的代码 力扣小白刷题之279题完全平方数_第4张图片

思路2 :动态规划

参考了:https://leetcode-cn.com/problems/perfect-squares/solution/hua-jie-suan-fa-279-wan-quan-ping-fang-shu-by-guan/

还是需要一段创建平方数序列的代码
然后动态规划:

  1. 首先创建 长度为 n + 1 的 dp 数组,每个位置的默认初始化值都为 0,dp[i] 表示 i 最少可以由几个完全平方数构成。

  2. 如果 n 为 0 ,则结果为 0,会直接return dp[0] = 0

  3. 对数组进行遍历,下标为 i,每次都将当前数字先更新为最大的结果,即 dp[i] = i。

  4. 动态转移方程:dp[i] = min(dp[i], dp[i - j * j] + 1),i 表示当前数字,j * j 表示平方数

    公式推导:(参考了:https://leetcode-cn.com/problems/perfect-squares/solution/pythonti-jie-xiang-xi-yi-dong-de-dpjie-da-by-xiao-/)

    因为 i = j * j + b,所以可以得出:

dp[i] = 1 + dp[i - j * j]
  • 公式含义
    1. 因为我们设定的 dp[i] 为和为 i 的完全平方数的最少个数,等于当前的这个平方数加上其余的平方数的个数,上面公式的中 1 代表的是个数 1,指的是 j * j 这当前的平方数,而dp[i - j * j] 指的则是剩余的平方数的个数
    2. 由于我们每次计算是先把以前的数的最小平方数目计算出来了,所以只需循环的更新最小的那个完全平方数就好。
  1. 时间复杂度:O(n*sqrt(n)),sqrt为平方根
    空间复杂度:O(n)

代码

力扣小白刷题之279题完全平方数_第5张图片

代码优化力扣小白刷题之279题完全平方数_第6张图片

你可能感兴趣的:(bfs,动态规划)