题目描述
给你一个整数 n ,返回和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
输入:
n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:
输入:
n = 13
输出:2
解释:13 = 4 + 9
提示:
1 <= n <= 10^4
动态规划
核心思路
动态规划的思路是创建一个数组 f,其中 f[i] 表示组成数字 i 所需的最少完全平方数的数量。初始化时,f[0] = 0(因为 0 不需要任何平方数),然后对于每个 i 从 1 到 n,我们遍历所有可能的完全平方数 j * j(其中 j 从 1 开始,直到 j * j <= i),并更新 f[i] 为 f[i - j * j] + 1 的最小值。
这种方法确保了对于每个数字 i,我们都能找到最优解。时间复杂度为 O(n * sqrt(n)),空间复杂度为 O(n)。
代码实现
1 | public int numSquares(int n) { |
复杂度分析
- 时间复杂度:
O(n * sqrt(n)),因为对于每个数字i,内层循环最多运行sqrt(i)次。 - 空间复杂度:
O(n),需要长度为n + 1的数组来存储状态。
数学方法(四平方和定理)
核心思路
四平方和定理指出,每个正整数都可以表示为最多四个完全平方数的和。利用这一定理,我们可以通过数学推导来减少计算:
- 如果
n本身是完全平方数,直接返回1。 - 如果
n满足公式n = 4^k * (8m + 7),那么它必须由四个完全平方数组成,返回4。 - 检查
n是否可以由两个完全平方数组成,即遍历i从1到sqrt(n),检查n - i * i是否为完全平方数。 - 如果以上都不满足,则返回
3。
这种方法的时间复杂度为 O(sqrt(n)),空间复杂度为 O(1),效率更高。
代码实现
1 | public int numSquares(int n) { |
复杂度分析
- 时间复杂度:
O(sqrt(n)),因为主要开销在检查两个平方数时循环最多sqrt(n)次。 - 空间复杂度:
O(1),只使用了常数级别的额外空间。
总结
动态规划方法直观易懂,适用于一般范围的 n,但当 n 较大时可能效率较低。数学方法基于四平方和定理,效率更高,适合大数情况。在实际应用中,可以根据问题规模选择合适的方法。