当前位置: 首页 > news >正文

LeetCode322.零钱兑换

文章目录

  • 题目描述
  • 解题思路
    • 递归
    • 记忆化搜索
    • 动态规划
      • 另一种实现

题目描述

https://leetcode.cn/problems/coin-change/description/?envType=study-plan-v2&envId=top-interview-150
给你一个整数数组 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

提示:

1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

解题思路

  1. 这种找路径,找方法的题一般可以使用回溯法来解决,回溯法也可以说是树形图法,解题的时候使用类似于树状图的结构,使用 自顶而下 的方法。

  2. 而在回溯法中,如果含有很多的重复的计算的时候,就可以使用记忆化的搜索,将可能出现的重复计算大状态使用一个数组来保存其值,在进行重复的计算的时候,就可以直接的调用数组中的值,较少了不必要的递归。

  3. 使用了记忆化搜索后,一般还可以进行优化,在记忆化搜索的基础上,变成 自底而上 的动态规划。

在这里插入图片描述

递归

使用递归就是关键是知道递归函数是用来干什么的,但这里使用递归会超时,因为有太多的重复计算。

class Solution {int res = Integer.MAX_VALUE;public int coinChange(int[] coins, int amount) {if(coins.length == 0){return -1;}findWay(coins,amount,0);// 如果没有任何一种硬币组合能组成总金额,返回 -1。if(res == Integer.MAX_VALUE){return -1;}return res;}public void findWay(int[] coins,int amount,int count){if(amount < 0){return;}if(amount == 0){res = Math.min(res,count);}for(int i = 0;i < coins.length;i++){findWay(coins,amount-coins[i],count+1);}}
}

记忆化搜索

记忆化搜索其实就是在递归的时候,把每个状态保存起来,这样我们递归过程中使用到的重复的结果的时候,不需要在去重新计算,直接从数组中取值,就可以加快速度了。
在这里插入图片描述

从图中可以看出,里面有很多的重复节点,我们使用memo[]来保存节点,memo[n]表示钱币n可以被换取的最少的硬币数,不能换取的话赋值-1。
findway函数的目的是为了找到amount数量的零钱可以兑换的最少硬币数量,返回其值int。
在进行递归的时候,mome[n]被复制了,就不用在继续递归了,可以直接的调用。

class Solution {int[] memo;public int coinChange(int[] coins, int amount) {if(coins.length == 0){return -1;}memo = new int[amount];return findWay(coins, amount);}// memo[n] 表示钱币n可以被换取的最少的硬币数,不能换取就为-1// findWay函数的目的是为了找到 amount数量的零钱可以兑换的最少硬币数量,返回其值intpublic int findWay(int[] coins, int amount){if(amount < 0){return -1;}if(amount == 0){return 0;}// 记忆化的处理,memo[n]用赋予了值,就不用继续下面的循环// 直接的返回memo[n] 的最优值if(memo[amount - 1] != 0){return memo[amount - 1];}int min = Integer.MAX_VALUE;for(int i=0; i < coins.length; i++){int res = findWay(coins, amount - coins[i]);if(res >= 0 && res < min){min = res + 1;// 加1,是为了加上得到res结果的那个步骤中,兑换的一个硬币}}memo[amount - 1] = (min == Integer.MAX_VALUE? -1 : min);return memo[amount - 1];}
}

动态规划

记忆化搜索是在递归的基础上,进行优化。先从memo[amount - 1]开始,从上到下。
动态规划是从memo[0]开始,从下到上。

class Solution {public int coinChange(int[] coins, int amount) {// 自底向上的动态规划if(coins.length == 0){return -1;}// memo[n]的值: 表示的凑成总金额为n所需的最少的硬币个数int[] memo = new int[amount + 1];memo[0] = 0;for(int i=1; i<= amount; i++){int min = Integer.MAX_VALUE;for(int j=0; j<coins.length; j++){if(i - coins[j] >= 0 && memo[i-coins[j]] < min){min = memo[i - coins[j]] + 1;}}memo[i] = min;}return memo[amount] == Integer.MAX_VALUE ? -1 : memo[amount];}
}

另一种实现

memo[i]有两种实现方式,取两者的最小值

  1. 包含当前的coins[i], 那么剩余钱就是i - coins[i], 这种操作要兑换的硬币数是memo[i -coins[j]] + 1
  2. 不包含当前的coins[i],那么要兑换的硬币数就是memo[i]
class Solution {public int coinChange(int[] coins, int amount) {// 自底向上的动态规划if(coins.length == 0){return -1;}// memo[n]的值: 表示的凑成总金额为n所需的最少的硬币个数int[] memo = new int[amount + 1];// 给memo赋初值,最多的硬币数就是全部使用面值1的硬币进行换// amount + 1 是不可能达到的换取数量,于是使用其进行填充Arrays.fill(memo, amount + 1);memo[0] = 0;for(int i=1; i<=amount; i++){for(int j=0; j<coins.length;j++){if(i - coins[j] >= 0){// memo[i]有两种实现的方式,// 一种是包含当前的coins[i],那么剩余钱就是 i-coins[i],这种操作要兑换的硬币数是 memo[i-coins[j]] + 1// 另一种就是不包含,要兑换的硬币数是memo[i]memo[i] = Math.min(memo[i], memo[i-coins[j]] + 1);}}}return memo[amount] == (amount + 1)? -1 : memo[amount];}
}

题解参考:
作者:sugar
链接:https://leetcode.cn/problems/coin-change/solutions/137661/javadi-gui-ji-yi-hua-sou-suo-dong-tai-gui-hua-by-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章:

  • 结构体(c++语言)
  • PDF分页处理:技术与实践
  • 千益畅行,共享旅游卡,满足您多样化的同行出行需求
  • Web考试前端等级:深度剖析与实战攻略
  • 搭建python虚拟环境,并在VSCode中使用
  • 让你的TypeScript代码更优雅,这10个特性你需要了解下
  • htb-linux-9-sense
  • Web安全:Web体系架构存在的安全问题和解决方案
  • Debian13将正式切换到基于内存的临时文件系统
  • 中电金信:产教联合共育人才 AFAC2024金融智能创新大赛启动
  • k8s之deployments相关操作
  • 【图 - 遍历(BFS DFS)】深度优先搜索算法(Depth First Search), 广度优先搜索算法(Breadth First Search)
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • 安装ps提示缺少dll文件是怎么回事,哪种解决方法更推荐
  • 【计算机视觉】数字图像处理基础:以像素为单位的图像基本运算(点运算、代数运算、逻辑运算、几何运算、插值)
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【刷算法】从上往下打印二叉树
  • 10个最佳ES6特性 ES7与ES8的特性
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • CSS相对定位
  •  D - 粉碎叛乱F - 其他起义
  • FineReport中如何实现自动滚屏效果
  • JAVA 学习IO流
  • JavaScript 奇技淫巧
  • JavaScript对象详解
  • Logstash 参考指南(目录)
  • Shell编程
  • 包装类对象
  • 机器学习中为什么要做归一化normalization
  • 排序算法学习笔记
  • 区块链分支循环
  • 日剧·日综资源集合(建议收藏)
  • 为视图添加丝滑的水波纹
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • kubernetes资源对象--ingress
  • 国内开源镜像站点
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #ubuntu# #git# repository git config --global --add safe.directory
  • (C++17) std算法之执行策略 execution
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (六)vue-router+UI组件库
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • (十三)Java springcloud B2B2C o2o多用户商城 springcloud架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)...
  • (文章复现)基于主从博弈的售电商多元零售套餐设计与多级市场购电策略
  • (已解决)什么是vue导航守卫
  • (译) 函数式 JS #1:简介
  • **python多态
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .naturalWidth 和naturalHeight属性,
  • .Net CF下精确的计时器
  • .NET 材料检测系统崩溃分析
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET序列化 serializable,反序列化
  • .NET业务框架的构建
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复