Leetcode 279. 完全平方数 (C++)(小白学习之路)

题目描述

 

279. 完全平方数

难度中等380收藏分享切换为英文关注反馈

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

示例 1:

输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.

示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

 

老实说看到这道题我第一想法应该是用动态规划做,但是只知道动态规划是利用前面的结果来进行后一步计算,实际没有写过,而这道题正好是在BFS卡片里面的,所以就用BFS来写了,总的来说跟上一道题打开转盘锁是一个套路,首先找出目标数的完全平方数组,然后数组里的每个数都是一个分支,用队列实现,代码如下:

class Solution {
public:
    vector<int>getSquares(int n) {            //找到符合要求完全平方数组
        vector<int> res;
        for (int i = 1; i * i <= n; i++)
        {
            res.push_back(i * i);
        }
        return res;
    }

    int numSquares(int n) {
        vector<int>squarearr = getSquares(n);        
        queue<int>q;                        //队列用来实现BFS
        q.push(n);
        int ans = 1;                        //这里就算目标在完全平方数组里的返回值也应该是1
        unordered_set<int> squareset(squarearr.begin(), squarearr.end());    //unordered_set 用过真的觉得好用
        unordered_set<int> visited;        //建立访问过的unordered_set,避免重复劳动
        visited.insert(n);    
        if (squareset.count(n) != 0) return 1;
        while (!q.empty())
        {
            int size = q.size();            //size为同一层或者说同一步骤下节点的个数
            ans++;                            //每多一层答案+1
            for (int i = 0; i < size; i++)
            {
                int temp2;                    //temp2用来构建临时结果
                int temp = q.front();         //temp用来储存当前出队讨论的项,用temp就避免了temp2在下面会因为-num而改变值的情况
                q.pop();
                for (int num : squarearr)    //C++11新特性,基于范围的遍历
                {
                    temp2 = temp - num;        //父节点的值减去完全平方数得到子节点的值,检查是否满足要求
                    if (squareset.count(temp2) != 0) return ans;    //满足要求,返回结果
                    if (visited.count(temp2) != 0) continue;        //访问过,continue
                    if (temp2 < 0) break;                           //小于0说明超过上界了无需继续讨论

                    q.push(temp2);
                    visited.insert(temp2);

                }
            }
        }
        return n;        //最坏情况由N个1组成


    }
};

时间复杂度:粗略估算O(n^1.5),一个n是需要遍历从1到N的所有数(实际上应该不用),n^0.5是每一个数的遍历都要遍历一次square数组

空间复杂度:O(n),主要是visited这个unordered_set的空间,另外还有squarearr,queue,都小于n,因此合并为O(n)

 

 写了几道BFS的题目之后总结一下这类题型的关键点:

1. 弄清楚起始条件和最终条件,中间是通过什么样的边来链接,比如这题的边是完全平方数组,转盘锁那道题是锁的位数*转的方向,知道这个了就找到了BFS树中的边;

2. 明确起点即终点情况的返回值,此题是1,转盘锁是0;

3. 什么时候结果+1,临时值temp的改变发生在哪一个循环里(!queue.empty()还是range(0,size)),这一步很大程度上决定了你的结果是否有问题,出错最多的地方也在这里

4. 考虑各种能跳出循环的条件,减少时间消耗, 比如此题中的continue, break

 

本题还有其他多种方法,下次尝试时将试着用动态规划写一下,还有数学结论法,简直就是降维打击;慢慢刷题吧

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值