再论力扣第279题--完全平方数
题目:
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...
)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例1:
输入: n = 12 输出: 3 解释: 12 = 4 + 4 + 4.
示例2:
输入: n = 13 输出: 2 解释: 13 = 4 + 9.
分析:
之前曾做过一次这道题,当时是刚学了动态规划算法,然后正好拿这个题进行练习,通过定义一个状态转移数组,也顺利的解决了,但是经过力扣的测试,运行效率十分地的感人,运行时间长达上前毫秒,可见用动态规划算法实现的程序的性能并不怎么好。正好最近在温习了广度优先算法,那么这个题也正好可以通过BFS解决,所以就再研究一下咯。并且没想到,虽然程序写的更为复杂了,但是运行时间大大的缩减了,甚至优于百分之九十以上的人的程序。好吧,废话不多说,开始进行正式的分析。
我们知道,任何一个正整数都可以由若干个完全平方数相加而得到,最简单的就是数值是几,就由多少个1相加,这是组成和的完全平方数最为多的情况。而题目中要求的是最少的情况,其实可以抽象为求最短路径。也就是说,将n设置为起点,目的地是0,而n每一次向0靠近的时候,可以向前走的距离是1~n之间的所有平方数,比如:
n = 13 目的地:0 ,设置一个计数值用于记录遍历的层数,到达0时的计数值就是最终的结果;
从13出发,作为BFS遍历的第一层,那么此时向0靠近时,可以走的距离分别由1、4、9,那么我们就都走一下试试;
分别按照这三个距离向前走,到达的点分别是12、9、4,此时这三个点算作BFS遍历的第二层;
开启遍历第二层,计数值加1,获取第二层的结点个数,分别向0靠近遍历,可走的距离依旧是小于等于各自顶点值的所有平方数,遍历完当前层的所有顶点之后,就得到了下一层的所有顶点;
继续重复上述工作,到达目的地0时,就立马返回计数值。
注意必须遍历完一层的顶点之后才能开始下一层,开启一层计数值就+1
代码:
public int numSquares(int n) { if ((int)Math.sqrt(n) * (int)Math.sqrt(n) == n){ return 1; } List<Integer> squares = generateSquare(n); LinkedList<Integer> queue = new LinkedList<>(); boolean[] isVisited = new boolean[n + 1]; isVisited[n] = true; queue.offer(n); int count = 0; while (!queue.isEmpty()){ int unum = queue.size(); count++; while (unum-- > 0){ Integer u = queue.poll(); for (Integer square : squares) { int next = u - square; if (next == 0){ return count; }else if (next < 0){ break; }else { if (isVisited[next]){ continue; }else { isVisited[next] = true; queue.offer(next); } } } } } return count; } private List<Integer> generateSquare(int n){ ArrayList<Integer> res = new ArrayList<>(); for (int i = 1; i*i <= n; i++) { res.add(i*i); } return res; }