benym的知识笔记 benym的知识笔记
🦮首页
  • Java

    • Java-基础
    • Java-集合
    • Java-多线程与并发
    • Java-JVM
    • Java-IO
  • Python

    • Python-基础
    • Python-机器学习
  • Kafka
  • Redis
  • MySQL
  • 分布式事务
  • Spring

    • SpringIOC
    • SpringAOP
🦌设计模式
  • 剑指Offer
  • LeetCode
  • 排序算法
🐧实践
  • Rpamis

    • Utils
    • Exception
    • Security
  • 归档
  • 标签
  • 目录
🦉里程碑
🐷关于
GitHub (opens new window)

benym

惟其艰难,才更显勇毅🍂惟其笃行,才弥足珍贵
🦮首页
  • Java

    • Java-基础
    • Java-集合
    • Java-多线程与并发
    • Java-JVM
    • Java-IO
  • Python

    • Python-基础
    • Python-机器学习
  • Kafka
  • Redis
  • MySQL
  • 分布式事务
  • Spring

    • SpringIOC
    • SpringAOP
🦌设计模式
  • 剑指Offer
  • LeetCode
  • 排序算法
🐧实践
  • Rpamis

    • Utils
    • Exception
    • Security
  • 归档
  • 标签
  • 目录
🦉里程碑
🐷关于
GitHub (opens new window)
  • 剑指Offer

    • 01背包问题详解
    • LeetCode-面试题17-打印从1到最大的n位数
    • LeetCode-面试题03-不修改数组找出重复的数字
    • LeetCode-面试题03-数组中重复的数字
    • LeetCode-面试题04-二维数组中的查找
    • LeetCode-面试题05-替换空格
    • LeetCode-面试题06-从尾到头打印链表
    • LeetCode-面试题09-用两个栈实现队列
    • LeetCode-面试题10-1-斐波那契数列
    • LeetCode-面试题10-2-青蛙跳台阶
    • LeetCode-面试题11-旋转数组的最小数字
    • LeetCode-面试题13-机器人的运动范围
    • LeetCode-面试题14-1-剪绳子
    • LeetCode-面试题14-2-剪绳子(大数)
    • LeetCode-面试题15-二进制中1的个数
    • LeetCode-面试题16-数值的整数次方
    • LeetCode-面试题18-删除链表的节点
    • LeetCode-面试题19-正则表达式匹配
    • LeetCode-面试题20-表示数值的字符串
    • LeetCode-面试题21-调整数组顺序使奇数位于偶数前面
    • LeetCode-面试题22-链表中倒数第k个节点
    • LeetCode-面试题24-反转链表
    • LeetCode-面试题25-合并两个排序的链表
    • LeetCode-面试题26-树的子结构
    • LeetCode-面试题27-二叉树的镜像
    • LeetCode-面试题29-顺时针打印矩阵
    • LeetCode-面试题31-栈的压入弹出序列
    • LeetCode-面试题32-1-从上到下打印二叉树
    • LeetCode-面试题32-2-从上到下打印二叉树
    • LeetCode-面试题32-3-从上到下打印二叉树
    • LeetCode-面试题35-复杂链表的复制
    • LeetCode-面试题36-二叉搜索树与双向链表
    • LeetCode-面试题37-序列化二叉树
    • LeetCode-面试题38-字符串的排列
    • LeetCode-面试题39-数组中出现次数超过一半的数字
    • LeetCode-面试题40-最小的k个数
    • LeetCode-面试题41-数据流中的中位数
    • LeetCode-面试题42-连续子数组的最大和
    • LeetCode-面试题43-1到n整数中1出现的次数
    • LeetCode-面试题44-数字序列中某一位的数字
    • LeetCode-面试题45-把数组排成最小的数
    • LeetCode-面试题47-礼物的最大价值
    • LeetCode-面试题48-最长不含重复字符的子字符串
    • LeetCode-面试题49-丑数
    • LeetCode-面试题50-第一次只出现一次的字符
    • LeetCode-面试题51-数组中的逆序对
    • LeetCode-面试题53-1-在排序数组中查找数字I
    • LeetCode-面试题33-二叉搜索树的后序遍历序列
    • LeetCode-面试题54-二叉搜索树的第k大节点
    • LeetCode-面试题55-1-二叉树的深度
    • LeetCode-面试题12-矩阵中的路径
    • LeetCode-面试题30-包含min函数的栈
    • LeetCode-面试题34-二叉树中和为某一值的路径
    • LeetCode-面试题46-把数字翻译成字符串
    • LeetCode-面试题55-2-平衡二叉树
    • LeetCode-面试题56-1-数组中数字出现的次数
    • LeetCode-面试题56-2-数组中数字出现的次数2
    • LeetCode-面试题57-和为s的两个数字
    • LeetCode-面试题58-1-翻转单词顺序
    • LeetCode-面试题58-2-左旋转字符串
    • LeetCode-面试题59-2-队列的最大值
    • LeetCode-面试题60-n个骰子的点数
    • LeetCode-面试题61-扑克牌中的顺子
    • LeetCode-面试题62-圆圈中最后剩下的数字
    • LeetCode-面试题63-股票的最大利润
    • LeetCode-面试题64-求1+2+到+n
    • LeetCode-面试题65-不用加减乘除做加法
    • LeetCode-面试题66-构建乘积数组
    • LeetCode-面试题67-把字符串转化成整数
    • LeetCode-面试题68-1-二叉搜索树的最近公共祖先
    • LeetCode-面试题68-2-二叉搜索树的最近公共祖先
    • LeetCode-面试题07-重建二叉树
    • LeetCode-面试题52-两个链表的第一个公共节点
    • LeetCode-面试题53-2-0到n-1中缺失的数字
    • LeetCode-面试题57-2-和为s的连续正数序列
    • LeetCode-面试题59-1-滑动窗口的最大值
    • LeetCode-面试题28-对称的二叉树
  • LeetCode

    • LeetCode-54-螺旋矩阵
    • LeetCode-67-二进制求和
    • LeetCode-83-删除排序链表中的重复元素
    • LeetCode-415-字符串相加
    • LeetCode-498-对角线遍历
    • LeetCode-724-寻找数组的中心索引
    • 动态规划问题——最长上升子序列(LIS)(一)
    • 动态规划问题——最长上升子序列(LIS)(二)
    • 动态规划问题——最长上升子序列(LIS)(三)
    • LeetCode-144-二叉树的前序遍历
    • LeetCode-94-二叉树的中序遍历
    • LeetCode-145-二叉树的后序遍历
    • LeetCode-53-最大子序和
    • LeetCode-392-判断子序列
    • LeetCode-303-区域和检索-数组不可变
    • LeetCode-2-两数相加
    • LeetCode-3-无重复字符的最长字串
    • LeetCode-4-寻找两个正序数组的中位数
    • LeetCode-5-最长回文字串
    • LeetCode-11-盛最多水的容器
    • LeetCode-15-三数之和
    • LeetCode-17-电话号码的字母组合
    • LeetCode-19-删除链表的倒数第N个节点
    • LeetCode-20-有效的括号
    • LeetCode-21-合并两个有序链表
    • LeetCode-22-括号生成
    • LeetCode-23-合并K个排序链表
    • LeetCode-31-下一个排列
    • LeetCode-32-最长有效括号
    • LeetCode-33-搜索旋转排序数组
    • LeetCode-34-在排序数组中查找元素的第一个和最后一个位置
    • LeetCode-39-组合总数
    • LeetCode-46-全排列
    • LeetCode-47-全排列2
    • LeetCode-51-N皇后
    • LeetCode-55-跳跃游戏
    • LeetCode-56-合并区间
    • LeetCode-62-不同路径
    • LeetCode-64-最小路径和
    • LeetCode-70-爬楼梯
    • LeetCode-72-编辑距离
    • LeetCode-75-颜色分类
    • LeetCode-76-最小覆盖字串
    • LeetCode-77-组合
    • LeetCode-78-子集
    • LeetCode-84-柱状图中最大的矩形
    • LeetCode-102-二叉树的层序遍历
    • LeetCode-104-二叉树的最大深度
    • LeetCode-105-从前序与中序遍历构造二叉树
    • LeetCode-107-二叉树的层次遍历2
    • LeetCode-114-二叉树展开为链表
    • LeetCode-121-买卖股票的最佳时机
    • LeetCode-128-最长连续序列
    • LeetCode-136-只出现一次的数字
    • LeetCode-142-环形链表2
    • LeetCode-143-重排链表
    • LeetCode-146-LRU缓存机制
    • LeetCode-152-乘积最大子数组
    • LeetCode-198-打家劫舍
    • LeetCode-199-二叉树的右视图
    • LeetCode-206-反转链表
    • LeetCode-207-课程表
    • LeetCode-215-数组中的第K个最大元素
    • LeetCode-221-最大正方形
    • LeetCode-226-翻转二叉树
    • LeetCode-236-二叉树的最近公共祖先
    • LeetCode-279-完全平方数
    • LeetCode-287-寻找重复数
    • LeetCode-300-最长上升子序列
    • LeetCode-309-最佳买卖股票时机含冷冻期
    • LeetCode-322-零钱兑换
      • LeetCode-322-零钱兑换
        • 解题思路
        • Java代码1
        • Java代码2
        • Java代码3
    • LeetCode-328-奇偶链表
    • LeetCode-347-前K个高频元素
    • LeetCode-394-字符串解码
    • LeetCode-406-根据身高重建队列
    • LeetCode-413-等差数列划分
    • LeetCode-416-分割等和子集
    • LeetCode-438-找到字符串中所有字母异位词
    • LeetCode-448-找到所有数组中消失的数字
    • LeetCode-461-汉明距离
    • LeetCode-494-目标和
    • LeetCode-538-把二叉搜索树转换为累加树
    • LeetCode-543-二叉树的直径
    • LeetCode-560-和为K的子数组
    • LeetCode-567-字符串的排列
    • LeetCode-581-最短无序连续子数组
    • LeetCode-617-合并二叉树
    • LeetCode-704-二分查找
    • LeetCode-739-每日温度
    • LeetCode-747-至少是其他数字两倍的最大数
    • LeetCode-890-查找和替换模式
    • LeetCode-1143-最长公共子序列
    • LeetCode-1247-交换字符使得字符串相同
    • LeetCode-1367-二叉树中的列表
    • LeetCode-字符串排序
    • LeetCode-面试题02.02-返回倒数第k个节点
    • LeetCode-面试题17.16-按摩师
    • 获取满足指数的最长字符串
    • 数组的最多素数个数
    • 最小字典序字符串
    • LeetCode-1-两数之和
    • LeetCode-16-最接近的三数之和
    • LeetCode-679-24点游戏
    • LeetCode-141-环形链表
    • LeetCode-155-最小栈
    • LeetCode-160-相交链表
    • 判断一棵二叉树是否为二叉搜索树和完全二叉树
    • LeetCode-169-多数元素
    • LeetCode-234-回文链表
    • LeetCode-238-除自身以外数组的乘积
    • LeetCode-283-移动零
    • LeetCode-338-比特位计数
    • LeetCode-797-所有可能的路径
  • 排序算法

    • 常见排序算法总结
    • 冒泡排序
    • 基数排序
    • 堆排序
    • 希尔排序
    • 归并排序
    • 快速排序
    • 插入排序
    • 桶排序
    • 选择排序
  • 算法
  • LeetCode
benym
2020-08-21
目录

LeetCode-322-零钱兑换

# LeetCode-322-零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1
1
2
3
1
2
3

示例2:

输入: coins = [2], amount = 3
输出: -1
1
2
1
2

说明: 你可以认为每种硬币的数量是无限的。

# 解题思路

摘自官方题解https://leetcode-cn.com/problems/coin-change/solution/322-ling-qian-dui-huan-by-leetcode-solution/

方法1、回溯:

用S代表总金额,ci是第i枚硬币的面值,xi是面值为ci的硬币数量,由于xi*ci不能超过S,可以得出xi的取值范围[0,S/xi]

一个简单的解决方案是枚举每个硬币数量子集[x0,...,xn-1]。如果满足上述约束条件,计算硬币数量总和并返回所有子集中的最小值

for循环每一个硬币,选择0个1面值硬币,判断当前选择情况*面值是否小于等于总面值S,进入下层递归选择硬币应该固定1面值,选择2面值,idxCoin+1,总面值应该减去当前选择的硬币个数乘以面值数,即

amount - i * coins[idxCoin],选择0个2面值硬币,进行判断...依次列推。固定某一面值选择数,深度优先穷举后续面值可能的选择数目,且硬币选择数目范围在[0,S/xi]

由于有重复计算,所以回溯的效率并不是很高

方法2、动态规划-自上而下:

利用动态规划,改进上面的指数时间复杂度的解,定义

  • F(S):组成金额S所需的最少硬币数量
  • [c0,...,cn-1]:可选的n枚硬币面额值

这个问题有一个最优的子结构性质,这是解决动态规划问题的关键。最优解可以从其子问题的最优解构造出来。如何将问题分解成子问题?

假设我们知道F(S),即组成金额S最少的硬币数,最后一枚硬币的面值是C。那么由于问题的最优子结构,转移方程应为:

F(S)=F(S-C)+1

但我们不知道最后一枚硬币的面值是多少,所以我们需要枚举每个硬币面额值c0,c1,c2,...,cn-1并选择其中的最小值。下列递推关系成立:

image-20200821132154572

在上面的递归树中,可以发现有许多子问题被多次计算。例如,F(1)被计算了13次。为了避免重复的计算,我们将每个子问题的答案存在一个数组中进行记忆化,如果下次还要计算这个问题的值直接从数组中去除返回即可,这样能保证每个子问题最多只被计算一次。

方法3、动态规划-自下而上:

采用自下而上的方式进行思考,仍定义F(i)为组成金额i所需最少的硬币数量,假设在计算F(i)之前,我们已经计算出F(0)-F(i-1)的答案,则F(i)对应的转移方程为

image-20200821153508353

其中cj代表的是第j枚硬币的面值,即我们枚举最后一枚硬币面额是cj,那么需要从i-cj这个金额的状态F(i-cj)转移过来,再算上枚举的这个硬币数量1的贡献,由于要硬币数量最少,所以F(i)为:前面能转移过来的状态的最小值加上枚举的硬币数量1。

例子1:假设

coins = [1,2,5],amount=11
1
1

则,当i==0时无法用硬币组成,为0。当i<0时,忽略F(i)

F(i) 最小硬币数量
F(0) 0 //金额为0不能由硬币组成
F(1) 1 //F(1)=min(F(1-1),F(1-2),F(1-5))+1=1
F(2) 1 //F(2)=min(F(2-1),F(2-2),F(2-5))+1=1
F(3) 2 //F(3)=min(F(3-1),F(3-2),F(3-5))+1=2
F(4) 2 //F(4)=min(F(4-1),F(4-2),F(4-5))+1=2
... ...
F(11) 3 //F(11)=min(F(11-1),F(11-2),F(11-5))+1=3

我们可以看到问题的答案是通过子问题的最优解得到的

例子2:假设

coins = [1,2,3],amount = 6
1
1

image-20200821155315909

# Java代码1

public class LeetCode322 {
    public static void main(String[] args) {
        int[] coins = new int[]{1, 2, 5};
        int amount = 11;
        System.out.println(coinChange(coins, amount));
    }

    public static int coinChange(int[] coins, int amount) {
        return coinChanges(0, coins, amount);
    }

    private static int coinChanges(int idxCoin, int[] coins, int amount) {
        if (amount == 0)
            return 0;
        if (idxCoin < coins.length && amount > 0) {
            int maxVal = amount / coins[idxCoin];
            int minCost = Integer.MAX_VALUE;
            for (int i = 0; i <= maxVal; i++) {
                if (i * coins[idxCoin] <= amount) {
                    int res = coinChanges(idxCoin + 1, coins, amount - i * coins[idxCoin]);
                    // 说明需要更新
                    if (res != -1) {
                        minCost = Math.min(minCost, res + i);
                    }
                }
            }
            return (minCost == Integer.MAX_VALUE) ? -1 : minCost;
        }
        return -1;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# Java代码2

class Solution {
    public int coinChange(int[] coins, int amount) {
        if (amount < 1) return 0;
        return coinChanges(amount, coins, new int[amount]);
    }

    private static int coinChanges(int amount, int[] coins, int[] count) {
        if (amount < 0) return -1;
        if (amount == 0) return 0;
        if (count[amount - 1] != 0) return count[amount - 1];
        int min = Integer.MAX_VALUE;
        for (int coin : coins) {
            int res = coinChanges(amount - coin, coins, count);
            if (res >= 0 && res < min) {
                min = 1 + res;
            }
        }
        count[amount - 1] = (min == Integer.MAX_VALUE) ? -1 : min;
        return count[amount - 1];
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Java代码3

import java.util.Arrays;

public class LeetCode322_DP {
    public static void main(String[] args) {
        int[] coins = new int[]{1, 2, 5};
        int amount = 11;
        System.out.println(coinChange(coins, amount));
    }

    public static int coinChange(int[] coins, int amount) {
        int max = amount + 1;
        int[] dp = new int[max];
        // 因为需要比较最小,所以初始化数组为最大值
        Arrays.fill(dp, max);
        // 没有数值为0的硬币
        dp[0] = 0;
        // 自底向上
        for (int i = 1; i <= amount; i++) {
            // 遍历硬币面值cj
            for (int j = 0; j < coins.length; j++) {
                if (coins[j] <= i) {
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
编辑 (opens new window)
#数组#DP#回溯#Java#Medium
上次更新: 2022/11/18, 11:15:10
LeetCode-309-最佳买卖股票时机含冷冻期
LeetCode-328-奇偶链表

← LeetCode-309-最佳买卖股票时机含冷冻期 LeetCode-328-奇偶链表→

最近更新
01
SpringCache基本配置类
05-16
02
DSTransactional与Transactional事务混用死锁场景分析
03-04
03
Rpamis-security-原理解析
12-13
更多文章>
Theme by Vdoing | Copyright © 2018-2024 benym | MIT License
 |   |   | 
渝ICP备18012574号 | 渝公网安备50010902502537号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式