[LeetCode] 322.零钱兑换 五种方法讲解
322.零钱兑换 五种方法讲解
文章目录
- 322.零钱兑换 五种方法讲解
- 1 问题描述
- 2 问题分析
- 3 解决策略
- 3.1 递归-暴力解决
- 3.2 递归-加入存储
- 3.3 BFS
- 3.4 动态规划-自上而下
- 3.5 动态规划-自下而上
1 问题描述
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
2 问题分析
给人的第一直接就是让程序去尝试所有的可能性,然后将其中所需硬币数量最少的结果进行返回。当需要尝试所有可能性的时候,我们往往会用到树(Tree)这种数据结构。我们假设以coins=[1,2,5], amount=11
为例子,试图去用树对这个问题进行建模。我们假定每个节点的数字代表的是当前的金额。
可以看到,从根节点11出发,我们有三个硬币,所以有三个分支,而每一个金额又可以对应新的三个节点,如果节点的值为0,也就是说当前的金额就是0,说明任务完成了,我们成功凑出了金额,如果小于0,那就说明当前分支不能够凑出该金额。
3 解决策略
3.1 递归-暴力解决
根据上面的分析,发现这个问题很容易可以被拆解成递归的方式去解决,每个子问题就是找到当前金额所需的最少硬币数量,如果当前金额为0,那么就返回当前硬币的数量,这是递归的退出条件之一,除此之外,当金额小于0时,也需要返回值。考虑到所需要的硬币数量不可能大于amount + 1
,在金额小于0时,说明不可以凑成我们想要的数,那么我们就返回一个amount + 1
来表示当前的分支是不能凑成所需金额的。
class Solution(object):def coinChange(self, coins, amount):def helper(coins, curAmount, numOfCoins):globalMin = amount + 1if curAmount == 0:return numOfCoinsif curAmount < 0:return amount + 1for coin in coins:curMin = helper(coins, curAmount - coin, numOfCoins + 1)globalMin = min(curMin, globalMin)return globalMinreturn helper(coins, amount, 0)
时间复杂度:O(nS)O(n^S)O(nS), 其中 S 是要凑的金额数amount
,而 n 是所拥有的硬币数量。这是因为考虑到树结构最多的层数不会超过SSS,而分支的数量则取决于硬币的个数。
空间复杂度:O(S)O(S)O(S),因为至多需要存储SSS个值来得到amount
。
3.2 递归-加入存储
如果细心的话会发现,在计算过程中有很多重复计算的部分,所以我们可以加入存储,如果当前的金额已经被计算出来,那我们就直接返回结果即可。
class Solution(object):def coinChange(self, coins, amount):self.cache = {}def helper(coins, curAmount):if curAmount in self.cache:return self.cache[curAmount]globalMin = amount + 1if curAmount == 0:return 0if curAmount < 0:return amount + 1for coin in coins:curMin = helper(coins, curAmount - coin)if curMin == globalMin:continueglobalMin = min(curMin + 1, globalMin)self.cache[curAmount] = globalMinreturn globalMinresult = helper(coins, amount)return -1 if result == amount + 1 else result
这样做之后时间复杂度就可以减少到O(Sn)O(Sn)O(Sn),这是因为每一次的计算步骤至多需要SSS次运算,而我们每次都需要计算所有不同面额的硬币的可能性,所以一共需要计算两者的乘积。
3.3 BFS
上述两种递归方法更偏向于深度优先遍历,但是就我们的问题而言,其实并不需要这样做,实际上,如果使用BFS遍历的方法,当我们找到第一次amount
为0的时候,就已经代表我们找到了结果,因为当前树的层数一定是最少的。
import collections
class Solution(object):def coinChange(self, coins, amount):q = collections.deque([(amount, 0)])seen = set([amount])while q:curAmount, numOfCoins = q.popleft()if curAmount == 0:return numOfCoinsfor coin in coins:if curAmount - coin >= 0 and curAmount - coin not in seen:q.append((curAmount - coin, numOfCoins + 1))seen.add(curAmount - coin)return -1
3.4 动态规划-自上而下
如果从动态规划的角度来考虑问题,还是以老例子为例,想要求解F(11)F(11)F(11),那其实就是求解min(F(11−coin1)+1,F(11−coin2)+1,F(11−coin3)+1)min(F(11-coin_1)+1, F(11-coin_2)+1,F(11-coin_3)+1)min(F(11−coin1)+1,F(11−coin2)+1,F(11−coin3)+1),而当中的每一项都可以被继续分解,直到F(0)F(0)F(0)。而F(0)F(0)F(0)的意思是凑出0元所需的硬币个数,结果显然是0。
class Solution(object):def coinChange(self, coins, amount):seen = {}def helper(curAmount):if curAmount in seen:return seen[curAmount]if curAmount == 0:return 0if curAmount < 0:return amount + 1minNumOfCoins = amount + 1for coin in coins:minNumOfCoins = min(helper(curAmount - coin) + 1, minNumOfCoins)seen[curAmount] = minNumOfCoinsreturn minNumOfCoinsresult = helper(amount)return result if result != amount + 1 else -1
3.5 动态规划-自下而上
有了上面的思路,我们同样可以从反方向来考虑这个问题,先给定F(0)F(0)F(0),然后再去求解F(1)F(1)F(1),F(2)F(2)F(2),以此类推,直到F(amount)F(amount)F(amount)为止。
import collections
class Solution(object):def coinChange(self, coins, amount):q = collections.deque([(amount, 0)])seen = set([amount])while q:curAmount, numOfCoins = q.popleft()if curAmount == 0:return numOfCoinsfor coin in coins:if curAmount - coin >= 0 and curAmount - coin not in seen:q.append((curAmount - coin, numOfCoins + 1))seen.add(curAmount - coin)return -1
[LeetCode] 322.零钱兑换 五种方法讲解相关推荐
- leetcode: 322.零钱兑换
322.零钱兑换 来源:力扣(LeetCode) 链接: https://leetcode.cn/problems/coin-change/ 给你一个整数数组 coins ,表示不同面额的硬币:以及一 ...
- LeetCode 322. 零钱兑换
322. 零钱兑换 难度 中等 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amount ,表示总金额. 计算并返回可以凑成总金额所需的 最少的硬币个数 .如果没有任何一种硬币组 ...
- LeetCode 322. 零钱兑换(DP)
文章目录 1. 题目信息 2. 解题 2.1 回溯穷举 2.2 动态规划 1. 题目信息 给定不同面额的硬币 coins 和一个总金额 amount. 编写一个函数来计算可以凑成总金额所需的最少的硬币 ...
- Java实现 LeetCode 322 零钱兑换
322. 零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输 ...
- golang力扣leetcode 322.零钱兑换
322.零钱兑换 322.零钱兑换 题解 代码 322.零钱兑换 322.零钱兑换 题解 //state: dp[i]金额为i时所需最少硬币个数 //function: dp[i]=dp[i-n]+1 ...
- Leetcode.322 零钱兑换
索引iii表示 amountamountamount 金额,dp[i]dp[i]dp[i] 表示最少 coinscoinscoins 个数. 递归 class Solution {int res = ...
- Leetcode 322.零钱兑换
Time: 20190906 Type: Medium 题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币 ...
- leetcode 322. 零钱兑换 思考分析
目录 1.题目 2.思路分析 3.参考链接 1.题目 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总 ...
- 99. Leetcode 322. 零钱兑换 (动态规划-完全背包)
步骤一.确定状态: 确定dp数组及下标含义 dp数组是个amout+1的数组,dp[j]表示的是装满容量为j的背包所需要的最 少物品的个数 步骤二.推断状态方程: 对于当前物品i, 有两种选择决定了d ...
最新文章
- SDH/E1/T1/E3/T3/STM/TDM相关名词介绍
- 深度学习之好的博客文章
- CISCO SECURE ACCESS CONTROL SERVER
- 004-2-拟合,drop-out
- elasticsearch mapping之fields
- 遇上浏览器跨域问题怎么办?
- Bulma - 基于 Flexbox 的现代化的 CSS 框架
- 【R图秀-6】地震来了
- 【Unity3D基础2-2】认识Unity3D引擎
- deepinv2 添加打印机_Deepin系统上安装使用HP惠普打印机的方法
- 【计算理论】计算理论总结 ( 非确定性有限自动机 NFA 转为确定性有限自动机 DFA ) ★★
- antd表格分页设置
- H265 CTU、CU、PU、TU划分的特点及要求
- fiddler--HTTP协议调试工具
- 正则表达式匹配,2位数字,单个字符-华图在线试题修改
- 计算机网络的安全性在哪里,计算机网络面临的安全性威胁可分为哪两类分别包含..._安全工程师_帮考网...
- 鸿蒙系统别的手机厂商可以用,鸿蒙有望适配国内其他手机厂商,别再黑了
- woo语言实现 m3u8流媒体视频文件 下载并播放
- Android R模块编译framework
- 数据通信与网络(五)