LeetCode-279. 完全平方数-Java-medium

题目链接

法一(动态规划)-较快
    /**
     * 法一(动态规划)-较快
     * 1. 思路
     * (1)本题可看做完全背包问题
     * (2)完全平方数就是物品(可以无限件使用),凑个正整数n就是背包,问凑满这个背包最少有多少物品
     * (3)背包容量是 n,物品是从 1 - √n,价值是从 1 - √n的完全平方
     * 2. 步骤
     * (1)确定dp数组以及下标的含义
     *     dp[i]表示通过平方数组成i所需要完全平方数的最少价值
     * (2)确定递推公式
     *     dp[i]可由dp[i - j * j]推出,dp[i - j * j] + 1便可以凑成dp[i]
     *     需要选择最小的dp[i],故dp[i] = min(dp[i], dp[i - j * j] + 1)
     * (3)确定dp数组如何初始化
     *     dp[0]初始化为0,dp[i](i>0)初始化为最大值,这样dp[i]在递推的时候才不会被初始值覆盖
     * (4)确定遍历顺序
     *     可以外层for遍历背包,内层for遍历物品
     * 3. 复杂度
     * (1)时间复杂度 0(n * √n)
     * (2)空间复杂度 0(n)
     *
     * @param n
     * @return
     */
    public int numSquares(int n) {
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        for (int i = 1; i <= n; i++) { // 遍历背包,依次求出1, 2...直到n的解
            for (int j = 1; j * j <= i; j++) { // 遍历物品,依次减去一个平方数
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
            }
        }
        return dp[n];
    }
法二(BFS)-较快
    /**
     * 法二(BFS)-较快
     * 1. 思路
     * (1)一层一层的算,第一层依次减去一个平方数得到第二层,第二层依次减去一个平方数得到第三层...
     * (2)直到某一层出现了0,此时的层数就是我们要找到平方数和的最小个数
     * 2. 复杂度
     * (1)时间复杂度 0(n^(h/2)),h是可能发生的最大递归次数
     * (2)空间复杂度 0((√n)^h),这也是在h级可以出现的最大节点数
     *
     * @param n
     * @return
     */
    public int numSquares_2(int n) {
        Queue<Integer> queue = new LinkedList<>();
        Set<Integer> visited = new HashSet<>(); // 记录重复的解
        int level = 0;
        queue.add(n);
        while (!queue.isEmpty()) {
            int size = queue.size();
            level++;
            for (int i = 0; i < size; i++) {
                int cur = queue.poll();
                for (int j = 1; j * j <= cur; j++) {
                    int next = cur - j * j;
                    if (next == 0) {
                        return level;
                    }
                    if (!visited.contains(next)) {
                        queue.offer(next);
                        visited.add(next);
                    }
                }
            }
        }
        return -1;
    }
法三(DFS)-最慢
    /**
     * 考虑所有的分解方案,找出最小的解
     *
     * @param n
     * @param map
     * @return
     */
    private int dfs(int n, Map<Integer, Integer> map) {
        if (map.containsKey(n)) { // 跳过重复的解
            return map.get(n);
        }
        if (n == 0) {
            return 0;
        }
        int count = Integer.MAX_VALUE;
        for (int i = 1; i * i <= n; i++) {
            count = Math.min(count, dfs(n - i * i, map) + 1);
        }
        map.put(n, count);
        return count;
    }

    /**
     * 法三(DFS)-最慢
     * 一个一个的找,一直做减法,直到减到0算作找到一个解
     *
     * @param n
     * @return
     */
    public int numSquares_3(int n) {
        return dfs(n, new HashMap<>());
    }
法四(数学)-最快
    /**
     * 判断是否是平方数
     *
     * @param n
     * @return
     */
    private boolean isSquare(int n) {
        int sqrt = (int) Math.sqrt(n);
        return sqrt * sqrt == n;
    }

    /**
     * 法四(数学)-最快
     * 1. 思路
     * (1)四平方和定理:任何一个整数都可以表示为不超过4个数的平方和
     * (2)推论:当且仅当n=4^a*(8b+7)时,n恰好可以表示为4个数的平方和,a和b都是非负整数
     * 2. 复杂度
     * (1)时间复杂度 0(√n)
     * (2)空间复杂度 0(1)
     *
     * @param n
     * @return
     */
    public int numSquares_4(int n) {
        if (isSquare(n)) { // 考虑答案是不是1
            return 1;
        }
        int temp = n;
        while (temp % 4 == 0) { // 考虑答案是不是4,也就是判断n是不是等于4^a*(8b+7)
            temp /= 4;
        }
        if (temp % 8 == 7) {
            return 4;
        }
        for (int i = 1; i * i < n; i++) { // 然后考虑答案是不是2
            if (isSquare(n - i * i)) { // 当前数依次减去一个平方数,判断得到的差是不是平方数
                return 2;
            }
        }
        return 3; // 以上情况都排除的话,答案就是3
    }
本地测试
        /**
         * 279. 完全平方数
         */
        lay.showTitle(279);
        Solution279 sol279 = new Solution279();
        System.out.println(sol279.numSquares(12));
        System.out.println(sol279.numSquares_2(12));
        System.out.println(sol279.numSquares_3(12));
        System.out.println(sol279.numSquares_4(12));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值