【数据结构与算法】不同路径 III:使用哈密尔顿路径算法实现
【数据结构与算法】不同路径 III:使用哈密尔顿路径算法实现
Java
不同路径 III
https://leetcode-cn.com/problems/unique-paths-iii/
解题思路
使用哈密尔顿路径的方法解决。
图的深度优先遍历,在遍历时通过left变量记录所有可走的方块有没有被遍历了,如果发现全部遍历过了,并且是在出口了,那么就认为我们找到了一条哈密尔顿路径,返回1,这样多个遍历路径合并的最终结果就是问题的解。有个比较神奇的地方,就是为啥只使用一次深度优先遍历就能找到所有路径?这应该归功于深度优先遍历的特点,可以尝试把深度优先遍历的遍历树画出来,每一个叶子结点到根结点的路径就是我们深度遍历到不同终点的路径了,而每个叶子结点之间的遍历互不影响~
本算法我认为有以下几点是比较特别的:
- 遍历时用到回溯思想,即遍历完了之后在本次遍历路径肯定不用再去判断是否被访问了,所以要给其他遍历路径留活路,把它设置为未访问。
- 使用left变量记录被访问的顶点的个数,不需要在判断时再去遍历isVisited数组有没有被全部访问了,这里有点奇怪的是left不需要通过减一进行回溯吗?答案是肯定的,因为我们方法传参的特性,你递归调用后在被调用方法里加了一,但是调用方法中的left不会变,因为letf是基本数据类型,不会影响到调用方法中的值。
- 巧妙利用了深度优先遍历遍历树的特性,一开始看到问题我还在想是不是要把找到的每条路径保存下来,用于后面的去重?结果发现深度遍历的遍历树中两条遍历路径是不会相互影响的,所以你只需要遍历所有的遍历树,然后把满足条件的叶子结点记录下来并累加到结果中就可以了。
本算法对递归的使用、图的深度优先遍历、图的建模和对哈密尔顿路径的理解都是不错的锻炼。
还可以尝试把所有哈密尔顿路径输出。
代码
class Solution {
boolean[][] isVisited; // 访问数组
int[][] grid; // 格子的引用
int R, C; // 行数,列数
int start, end; // 开始位置,结束位置
int[][] dir4 = { // 上,下,左,右 4方向变量
{1,0},
{0,-1}, {0,1},
{-1,0}
};
public int uniquePathsIII(int[][] grid) {
this.grid = grid;
R = grid.length; // 行
C = grid[0].length; // 列
isVisited = new boolean[R][C];
int left = 0;
// 扫描整个地图,判断一共有多少个可以走的格子,获取起点和终点
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (grid[i][j] == 0) {
left++;
} else if (grid[i][j] == 1) {
start = i * C + j;left++;
} else if (grid[i][j] == 2) {
end = i * C + j;left++;
}
}
}
// 深度优先遍历来获取路径数量
return dfs(start, left);
}
private int dfs(int v, int left) {
int x = v / C;
int y = v % C;
isVisited[x][y] = true;
left--;
if (left == 0) {
isVisited[x][y] = false; // 回溯,给其他路径留活路
if(v == end) {
return 1;
}
return 0; // 找到了一条哈密尔顿路径
}
int res = 0;
// 获取相邻顶点
for (int[] ints : dir4) {
int x1 = x + ints[0];
int y1 = y + ints[1];
if (x1 >= 0 && y1 >= 0 && x1 < R && y1 < C && grid[x1][y1] != -1 && !isVisited[x1][y1]) {
res += dfs(x1 * C + y1, left);
}
}
isVisited[x][y] = false; // 回溯
return res;
}
}
【数据结构与算法】不同路径 III:使用哈密尔顿路径算法实现的更多相关文章
- 旅行商问题(TSP)、最长路径问题与哈密尔顿回路之间的联系(归约)
一,旅行商问题与H回路的联系(H回路 定义为 哈密尔顿回路) 旅行商问题是希望售货员恰好访问每个城市一次,最终回到起始城市所用的费用最低,也即判断图中是否存在一个费用至多为K的回路.(K相当于图中顶点 ...
- Leetcode 980. 不同路径 III
980. 不同路径 III 显示英文描述 我的提交返回竞赛 用户通过次数42 用户尝试次数43 通过次数46 提交次数60 题目难度Hard 在二维网格 grid 上,有 4 种类型的方格: 1 ...
- 【算法】Dijkstra算法(单源最短路径问题)(路径还原) 邻接矩阵和邻接表实现
Dijkstra算法可使用的前提:不存在负圈. 负圈:负圈又称负环,就是说一个全部由负权的边组成的环,这样的话不存在最短路,因为每在环中转一圈路径总长就会边小. 算法描述: 1.找到最短距离已确定的顶 ...
- Leetcode之深度优先搜索&回溯专题-980. 不同路径 III(Unique Paths III)
Leetcode之深度优先搜索&回溯专题-980. 不同路径 III(Unique Paths III) 深度优先搜索的解题详细介绍,点击 在二维网格 grid 上,有 4 种类型的方格: 1 ...
- [POJ 2821]TN's Kindom III(任意长度循环卷积的Bluestein算法)
[POJ 2821]TN's Kindom III(任意长度循环卷积的Bluestein算法) 题面 给出两个长度为\(n\)的序列\(B,C\),已知\(A\)和\(B\)的循环卷积为\(C\),求 ...
- [PHP]算法-二叉树中和为某一值的路径的PHP实现
二叉树中和为某一值的路径: 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.(注意: 在返回值的li ...
- 经典算法题每日演练——第十六题 Kruskal算法
原文:经典算法题每日演练--第十六题 Kruskal算法 这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的. 一:思想 若存在M={0, ...
- 【啊哈!算法】算法6:只有五行的Floyd最短路算法
暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. 上图中有 ...
- 【坐在马桶上看算法】算法6:只有五行的Floyd最短路算法
暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. 上图中有 ...
- 【十大算法实现之naive bayes】朴素贝叶斯算法之文本分类算法的理解与实现
关于bayes的基础知识,请参考: 基于朴素贝叶斯分类器的文本聚类算法 (上) http://www.cnblogs.com/phinecos/archive/2008/10/21/1315948.h ...
随机推荐
- G1原理—6.G1垃圾回收过程之Full GC
大纲 1.FGC的一些前置处理 2.FGC的整体流程 3.传统FGC只能串行化 + G1的FGC可以并发化 4.并行化FGC之并行标记 + 任务窃取 5.并行化FGC之跨分区压缩 + 避免对象跨分区 ...
- Docker npm install:npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY 。。reason: unable to get local issuer certificate 解决办法
这个是需要证书导致无法连接,临时解决办法是设置 npm set strict-ssl=false 在 Dockerfile文件里的 RUN npm install 之前添加 RUN npm set ...
- c# yield return
这个函数在处理循环时可以每生成一个数据就返回一个数据让主函数进行处理: static void Main(string[] args) { foreach (var item in GetNumber ...
- 深入LinkedBlockingQueue实现原理
学习BlockingQueue之LinkedBlockingQueue实现原理 一:概念 LinkedBlockingQueue是一个用链表实现的有界阻塞队列.此队列的默认和最大长度为 Integ ...
- 史上最全Redis面试49题(含答案):哨兵+复制+事务+集群+持久化等
Redis主要有哪些功能? 1.哨兵(Sentinel)和复制(Replication) Redis服务器毫无征兆的罢工是个麻烦事,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制. ...
- LeetCode 未验证规则
1. for循环容易忘记break,很多提交不过,都是因为这个 2. 左右指针,思考起来比较复杂,但是以代码容易理解.code精简为主,不要在乎省下一两次运算时间 3. 左右指针,有时候以left & ...
- codeblocks快捷键注释
ctrl+shift+c可以快速注释掉多行. ctrl+shift+x可以取消注释
- oracle配置SGA参数不当导致不能正确启动数据库实例处理
原因:生成环境数据库想要增加数据库内存配置参数SGA_TARGET增加到42G,但是没有配置SGA_MAX_SIZE参数值,导致SHUTDOWN IMMEDIATE停止数据库,再STARTUP启动数据 ...
- uni-app封装网络请求promise
在项目的根目录下,创建http文件夹. 然后在创建request.js文件 文件代码如下 export function apiapi(myurl,myget,mydata,tou="Acc ...
- 闲话 717 - LGV 引理的小应用
这是我们的某一天的联考题目: \(n\le 500\). 显然使用平面图完美匹配计数可以获得 \(O(n^6)\),但是有一种神秘的对路径的双射.当时我们都认为这是超级人类智慧,但是今天看书发现是书上 ...