题目大意:
完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n =12输出: 3 解释:12 = 4 + 4 + 4.
示例 2:
输入: n =13输出: 2 解释:13 = 4 + 9.
如果想查看本题目是哪家公司的面试题,请参考以下免费链接: https://leetcode.jp/problemdetail.php?id=279
解题思路分析:
本题需要找到一个或多个完全平方数,保证他们的和为n。首先我们考虑,完全平方数肯定是正数并且小于等于n,因此我们需要找到小于等于n的所有完全平方数。首先求出n开方后得到的数字,即sqrt(n),sqrt(n)的平方肯定是小于等于n最大的那个完全平方数。因此,1到sqrt(n)所有整数的平方即是小于等于n的所有完全平方数。举个例子,比如n等于17,17开方,即sqrt(17)等于4,1到4所有数字的平方分别为1,4,9,16。我们将上述所有的完全平方数保存至一个数组。
得到了所有完全平方数之后,问题就转化为,我们是否能在一些数字中,找到某些数字之和等于n(数字可以重复使用)。由于完全平方数中一定包含数字1,因此,对于任意一个数字n,我们能够保证至少找到一个答案,即n个1相加等于n。不过题目要求找到完全平方数个数最小的解,这就需要我们去尝试每一种组合。
这样题目就转化为搜索题,我们将每个完全平方数看作是一个节点,每个节点之间都是互相连通的,只要我们能够找到一条最短路径,其路径上节点和等于n即可。因此本题可以使用bfs或bfs两种思路进行查找。两种解法思路基本一致,先看bfs广度优先搜索的解法。
解法一:bfs广度优先搜索
bfs的起点是每一个完全平方数,我们将其加入到Queue,bfs每次向外扩散一步,查看是否存在和等于n的数值,如果存在,返回当前步数,否则继续向外扩散一步,扩散时,凡是已经计算过的和或者当前和大于n的路径可以跳过。
实现代码:
public int numSquares(int n) {
// bfs用的Queue
Queue<Integer> q = new LinkedList<>();
// 记录以经计算过的sum
boolean[] visited =new boolean[n+1];
// 将n开方
int max=(int)Math.sqrt(n);
int[] arr = new int[max];
// 将1到max所有数字的平方加入到数组,
// 找到所有小于等于n的完全平方数
for(int i=max;i>=1;i--){
arr[i-1]=i*i;
q.offer(arr[i-1]);
}
// 返回结果
int res=0;
// 以下是标准bfs
while(q.size()>0){
int size =q.size();
// 当前步数加一
res++;
while(size>0){
// 当前路径和
int cur=q.poll();
// 如果和等于n,返回步数
if(cur==n) return res;
size--;
// 用当前路径和尝试加上每一个完全平方数
for(int i=max-1;i>=0;i--){
// 当前和
int sum=cur+arr[i];
// 如果sum小于等于n并且该sum没被计算过
if(sum<=n&&!visited[sum]){
// 将sum添加到Queue
q.offer(sum);
// 当前sum标记为已被计算过
visited[sum]=true;
}
}
}
}
return res;
} 本解法执行时间为77ms。
Runtime: 77 ms, faster than 18.60% of Java online submissions for Perfect Squares.
Memory Usage: 55.1 MB, less than 9.72% of Java online submissions for Perfect Squares.
解法二:dfs深度优先搜索
dfs与bfs类似,只不过采用递归加memo数组形式而已。
实现代码:
int memo[]; // 记忆数组
public int numSquares(int n) {
// 初始化记忆数组,
// memo[i]表示从数字i加到n需要多少个完全平方数
memo=new int[n+1];
// 与bfs解法相同,先找出所有完全平方数
int max=(int)Math.sqrt(n);
int[] arr = new int[max];
for(int i=1;i<=max;i++) arr[i-1]=i*i;
// dfs递归求解,也就是求解memo[0]
return dfs(arr,0,n);
}
int dfs(int[] arr,int sum,int n){
// 如果当前和等于n,返回0。(从n加到n需要0个完全平方数)
if(sum==n) return 0;
// 如果记忆数组中存在当前解,直接返回
if(memo[sum]>0) return memo[sum];
// 返回结果(默认为n,因为最多需要n次,使用n个1相加)
int res=n;
// 循环每一个完全平方数
for(int i=arr.length-1;i>=0;i--){
// 使用一个完全平方数加上当前sum
int s=sum+arr[i];
// 如果和小于等于n,递归求解(求memo[s])
if(s<=n){
// memo[s]加一为当前解,并更新所有情况的最优解
res=Math.min(res,dfs(arr,s,n)+1);
}
}
// 将当前的最优解保存至memo记忆数组
memo[sum]=res;
return res;
} 本解法执行时间为77ms。
Runtime: 77 ms, faster than 18.68% of Java online submissions for Perfect Squares.
Memory Usage: 43.4 MB, less than 13.89% of Java online submissions for Perfect Squares.
本网站文章均为原创内容,并可随意转载,但请标明本文链接如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/leetcode-279-perfect-squares-解题思路分析/
View Comments (1)
I'm gone to say to my little brother, that he should also go to see
this blog on regular basis to obtain updated from most up-to-date gossip.