Java面试-动态规划与组合数
最近在刷力扣上的题目,刷到了65不同路径,当初上大学的时候,曾在hihocoder上刷到过这道题目,但是现在已经几乎全忘光了,大概的知识点是动态规划,如今就让我们一起来回顾一下。
从题目说起
题目原文是:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向右 -> 向下
- 向右 -> 向下 -> 向右
- 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
正向思路
我们先按照正常思路来想一下,当你处于起点时,你有两个选择,向右或者向下,除非你处于最下面一排或者最右边一列,那你只有一种选择(比如处于最下面一排,你只能往右),其他位置,你都有两种选择。
因此,我们就根据这个思路,可以写出代码:
class Solution {
public int uniquePaths(int m, int n) {
// 特殊情况:起点即终点
if (m == 1 && n == 1) {
return 1;
}
// 当前处于(1,1),终点为(m,n)
return walk(1, 1, m, n);
}
public int walk(int x, int y, int m, int n){
// 已经处于终点
if (x >= m && y >= n) {
return 0;
}
// 处于最下面一排或者最右边一列
if (x >= m || y >= n) {
return 1;
}
// 往下走,有多少种走法
int down = walk(x, y + 1, m, n);
// 往右走,有多少种走法
int right = walk(x + 1, y, m, n);
// 从当前(x,y)出发,走到(m,n),共有多少种走法
return down + right;
}
}
优化
我们考虑一下,这种写法,有没有可以优化的地方。
你们应该一眼就发现,walk
方法的第一个判断if (x >= m && y >= n)
,永远都不可能为true
,因为下一个判断if (x >= m || y >= n)
就已经是临界点情况,直接就已经有返回值,根本不可能达到x >= m && y >= n
的情况。因此,该判断可以删除。
假设我们从(1,1)的位置出发,终点是(3,3),那么到达(2,2)这个中间点的话有几种走法呢?两种,先到(1,2)再到(2,2),或者,先到(2,1)再到(2,2)。
因此,如果根据我们上面的写法,从(2,2)到终点(3,3),我们会算两次,虽然这样的思路本身是正确,但这样的情况应该是可以优化的。因为从(1,1)到(3,3),一共只有6种路径,但已经有2条是重复的路径了,那么随着m
与n
越来越大,中间点会越来越多,那么重复的路径也会越来越多。
这就是前面的选择
对于后面的选择
会有影响,即使后面的选择
相同,但由于前面的选择
不同,从而也被认为是不同的选择。
很明显,后面的选择
更加唯一,如果我们先在后面做出选择,那么就可以减少重复计算的次数。因此,我们可以试试反向思路。
反向思路
如果我们不是从起点出发,而是从终点倒退到起点开始算的话。假设终点是(3,3),它只能由(2,3)和(3,2)直接到达,(2,3)也只能由(2,2)和(1,3)直接到达,(1,3)只能由(1,2)直接到达,(1,2)只能由(1,1)直接到达,因此(1,3)只能由(1,1)直达。
我们可以得出规律:除了最左边一列和最上面一排的点,只能由起点(1,1)直达以外,其他的点(x,y)都是由(x-1,y)和(x,y-1)两个点直接到达的。
因此,根据这个思路,我们可以写出代码:
class Solution {
public int uniquePaths(int m, int n) {
int[][] result = new int[m][n];
int j;
for (int i = 0; i < m; i++) {
for (j = 0; j < n; j++) {
if (i == 0 || j == 0) {
// 最上面一排的点和最左边一列的点,只能由(1,1)到达
result[i][j] = 1;
} else {
// 其他的点都可以由左边的点和上面的点到达
result[i][j] = result[i - 1][j] + result[i][j - 1];
}
}
}
return result[m - 1][n - 1];
}
}
其实这样的想法就已经是动态规划
的范畴了,我们看看维基上的定义
动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
一开始我感觉很像分治法
,因为都需要将一个大问题分解为子问题,但分治法
最终会将子问题合并,但动态规划
却不用。
优化
我们考虑一下,这种写法,有没有可以优化的地方。
首先是空间上的优化,我们一定要用二维数组吗?可以用一维数组代替吗?
答案是肯定的,因为每个点的计算只和左边与上边相邻的点有关,因此,不需要更加久远的点。
一维数组
假如只用一维数组,那么只需要存储上一排的结果,如果计算到下一排的时候,则依次替换,代码为:
class Solution {
public int uniquePaths(int m, int n) {
int[] dp = new int[m];
int j;
for(int i = 0; i < n; i++) {
for(j = 0; j < m; j++) {
if(j == 0) {
dp[j] = 1;
}
else {
// 其他的点都可以由左边的点和上面的点到达
dp[j] += dp[j-1];
}
}
}
return dp[m-1];
}
}
这样的优化,差不多就结束了。那我们是否可以从思路上进行优化呢?
组合数
因为我们只有向右或向下两种选择,而我们一共要走的路径其实是(m-n-2)
,其中有(m-1)
的路径是向右,(n-1)
的路径是向下,其实可以转变为:
从
(m-n-2)
中挑出(m-1)
,即组合数C((m-n-2), (m-1))
的值
那么我们可以写出代码:
class Solution {
public int uniquePaths(int m, int n) {
// 用double,因为计算出的数值会很大
double num = 1, denom = 1;
// 找出更小的数,这样可以减少计算次数和计算出的数值
int small = m > n ? n : m;
for (int i = 1; i <= small - 1; ++i) {
num *= m + n - 1 - i;
denom *= i;
}
return (int)(num / denom);
}
}
总结
以上就是我做这道题的一些思路和想法了,虽然题目本身不难,但可以讨论的点还是很多的,如果大家有什么疑问,欢迎在下方留言。
有兴趣的话可以关注我的公众号,说不定会有意外的惊喜。
Java面试-动态规划与组合数的更多相关文章
- Java面试 32个核心必考点完全解析
目录 课程预习 1.1 课程内容分为三个模块 1.2 换工作面临问题 1.3 课程特色 课时1:技术人职业发展路径 1.1 工程师发展路径 1.2 常见技术岗位划分 1.3 面试岗位选择 1.4 常见 ...
- Java面试知识点汇总
Java面试知识点汇总 置顶 2019年05月07日 15:36:18 温柔的谢世杰 阅读数 21623 文章标签: 面经java 更多 分类专栏: java 面试 Java面试知识汇总 版权声明 ...
- JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结
我是一名java开发人员,hibernate以及mybatis都有过学习,在java面试中也被提及问道过,在项目实践中也应用过,现在对hibernate和mybatis做一下对比,便于大家更好的理解和 ...
- 转:最近5年133个Java面试问题列表
最近5年133个Java面试问题列表 Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来 ...
- java面试宝典(蓝桥学院)
Java面试宝典(蓝桥学院) 回答技巧 这套面试题主要目的是帮助那些还没有java软件开发实际工作经验,而正在努力寻找java软件开发工作的学生在笔试/面试时更好地赢得好的结果.由于这套试题涉及的范围 ...
- JAVA面试精选【Java基础第一部分】
这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了.对于那些正打算找工作JAVA软件开发工作的童 ...
- Java面试必备知识
JAVA面试必备知识 第一,谈谈final, finally, finalize的区别. 第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可 ...
- java面试和笔试大全 分类: 面试 2015-07-10 22:07 10人阅读 评论(0) 收藏
2.String是最基本的数据类型吗? 基本数据类型包括byte.int.char.long.float.double.boolean和short. java.lang.String类是final类型 ...
- 近5年133个Java面试问题列表
Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来越高级,面试官问的问题也更深入. 在我 ...
随机推荐
- Spring注解浅入浅出——不吹牛逼不装逼
Spring注解浅入浅出——不吹牛逼不装逼 前情提要 上文书咱们说了<Spring浅入浅出>,对Spring的核心思想看过上篇的朋友应该已经掌握了,此篇用上篇铺垫,引入注解,继续深入学习. ...
- 夯实Java基础(三)——面向对象之继承
1.继承概述 继承是Java面向对象的三大特征之一,是比较重要的一部分,与后面的多态有着直接的关系.继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法, ...
- 数据结构之稀疏矩阵C++版
//只是简单的演示一下,这个实际运用视乎不怎么多,所以java版不再实现 /* 希疏矩阵应用于对数据的压缩,仅仅保留不为0的数据 稀疏矩阵的转置,可以由多种方式,下面演示的稍显简单,时间复杂度略高O( ...
- 逆向破解之160个CrackMe —— 004-005
CrackMe —— 004 160 CrackMe 是比较适合新手学习逆向破解的CrackMe的一个集合一共160个待逆向破解的程序 CrackMe:它们都是一些公开给别人尝试破解的小程序,制作 c ...
- 使用 RxJava 进行嵌套串行网络请求的一种方法
需求 有这样一个列表数据,它包含了商店+订单的信息,获取订单列表时,订单实体中会包含商店的 ID,而列表显示时需要商店的名称和 logo,这时候就需要进行嵌套串行网络请求了. 关键词 flatMap ...
- Android Pie 私人 DNS 使用教程
本文首发于:微信公众号「运维之美」,公众号 ID:Hi-Linux. 「运维之美」是一个有情怀.有态度,专注于 Linux 运维相关技术文章分享的公众号.公众号致力于为广大运维工作者分享各类技术文章 ...
- 帝国CMS(EmpireCMS) v7.5 前台XSS漏洞分析
帝国CMS(EmpireCMS) v7.5 前台XSS漏洞分析 一.漏洞描述 该漏洞是由于javascript获取url的参数,没有经过任何过滤,直接当作a标签和img标签的href属性和src属性输 ...
- MYSQL--表与表之间的关系、修改表的相关操作
表与表之间的操作: 如果所有信息都在一张表中: 1.表的结构不清晰 2.浪费硬盘空间 3.表的扩展性变得极差(致命的缺点) 确立表与表之间的关系.一定要换位思考(必须在两者考虑清楚之后才能得出结论) ...
- MySQL--单表查询、多表查询简单概述
表的前期准备: create table emp( id int not null unique auto_increment, name ) not null, sex enum('male','f ...
- springboot+mybatis+druid+atomikos框架搭建及测试
前言 因为最近公司项目升级,需要将外网数据库的信息导入到内网数据库内.于是找了一些springboot多数据源的文章来看,同时也亲自动手实践.可是过程中也踩了不少的坑,主要原因是我看的文章大部分都是s ...