队列&完全平方数问题&动态规划&BFS --转载

队列&完全平方数问题&动态规划&BFS

近在做力扣学习学习数据结构与算法,评论区看到解决方案在此记录下。

在这里插入图片描述

1,BFS解决

这题让求的是若干个平方数的和等于n,并且平方数的个数最少。首先我们可以把它想象成为一颗m叉树,树的每一个节点的值都是平方数的和,如下图所示。
在这里插入图片描述每一个节点的值都是从根节点到当前节点的累加。而平方数的个数其实就是遍历到第几层的时候累加和等于target。我们只需要一层一层的遍历,也就是常说的BFS,当遇到累加的和等于target的时候直接返回当前的层数即可。
我们知道二叉树的BFS遍历像下面这样
在这里插入图片描述

public void levelOrder(TreeNode tree) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(tree);
        int level = 0;//统计有多少层
        while (!queue.isEmpty()) {
            //每一层的节点数
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                //打印节点
                System.out.println(node.val);
                if (node.left != null)
                    queue.add(node.left);
                if (node.right != null)
                    queue.add(node.right);
            }
            level++;
            //打印第几层
            System.out.println(level);
        }
    }

最终代码:

//BFS
	public int numSquares(int n) {
		Queue<Integer> queue = new LinkedList<>();
		queue.offer(0);
		//记录访问过的节点值
		Set<Integer> visited = new HashSet<>();
		visited.add(0);
		 //树的第几层
		int min = 0;
		while(!queue.isEmpty()){
			min++;
			int size = queue.size();
			//遍历当前层的所有节点
			for(int i=0;i<size;i++){
				int value = queue.poll();
				// 开始枚举所有满足条件的j,使j*j<=n.
				// 类比二叉树的left/right节点或者二维网格的上下左右四个方向
				//访问当前节点的子节点,类比于二叉树的左右子节点
				for(int j=1;j*j<=n;j++){
					int nValue = value + j*j;
					// 如果新值大于目标值,则停止内层循环
					if(nValue > n){
						break;
					}
					// 如果新值已经遍历过,则跳到下一个循环继续
					if(visited.contains(nValue)){
						continue;
					}
					// 如果新值等于目标值,则直接返回当前层数
					if(nValue == n){
						return min;
					}
					// 否则,将新值加入到队列中并标记已遍历,为下一层的遍历做准备
					queue.offer(nValue);
					visited.add(nValue);
				}
			}
		}
		return min;
	}

作者:数据结构和算法
链接:https://leetcode-cn.com/leetbook/read/queue-stack/kfgtt/?discussion=xgcYsh
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2,动态规划解决

这题除了使用BFS以外,还可以使用动态规划解决。

定义数组dp[],其中dp[i]表示的是当n等于i的时候完全平方数的最少数量。比如dp[12]表示当n等于12的时候完全平方数的最少数量。

比如当n等于60的时候,他的值是dp[60],但是60还可以由11加上7的平方组成,我们还可以改为dp[11]+1,取最小的即可,即
dp[60]=min(dp[60],dp[11]+1)

实际上60还可以由24加上6的平方组成……我们只需要找出所有的可能组合并记录最小的值即可。

所以递推公式我们很容易找出来

dp[i] = Math.min(dp[i], dp[i - j * j] + 1);

那么初始条件是什么呢,我们默认任何正整数都是由1的平方组成,即dp[i]=i,也就是最大值,然后再通过递推公式找出最小值即可。

最后我们再来看下代码

//动态规划
	public int numSquares2(int n) {
		// 定义dp,dp[i]代表输入为i时,返回和为 i 的完全平方数的 最少数量
		int[] dp = new int[n+1];
		dp[0] = 0;
		for(int i=1;i<=n;i++){
			//最坏的情况都是由1的平方组成  dp[12] =12个数字
			dp[i] = i;
			for(int j=1;j*j<=i;j++){
				//动态规划公式
				//exp dp[60]=min(dp[60],dp[11]+1)   60=11+7*7
				dp[i] = Math.min(dp[i],dp[i-j*j]+1);
			}
		}
		return dp[n];
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值