一天一大 leet(矩阵中的最长递增路径)难度:困难-Day20200726
时间:2022-07-25
本文章向大家介绍一天一大 leet(矩阵中的最长递增路径)难度:困难-Day20200726,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
题目:
给定一个整数矩阵,找出最长递增路径的长度。
对于每个单元格,你可以往上,下,左,右四个方向移动。你不能在对角线方向上移动或移动到边界外(即不允许环绕)。
示例:
- 示例 1
输入: nums =
[
[9,9,4],
[6,6,8],
[2,1,1]
]
输出: 4
解释: 最长递增路径为 [1, 2, 6, 9]。
- 示例 2
输入: nums =
[
[3,4,5],
[3,2,6],
[2,2,1]
]
输出: 4
解释: 最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。
抛砖引玉
- 之前的题目都已知起点,而且路径方向限制了只有两个方向,但是,任意单元格可以向上下左右四个方向移动且不知道起点
那把本题向已经做过的题变化一下:
- 起点:变量矩阵,分别设坐标(i,j)的点为起点
- 之前 dp 记录每个点的结果,本题相邻点的结果没有了推到关系,那指定起点查询以它开始的路线可能
思路
- 查询矩阵中所有点为起点的路线可能
- dp[i][j]存储以(i,j)为起点所有可能路线中最多节点的节点数
- 最终出现的最大可能数即为结果
实现
- 声明 dp 长宽与 matrix 一致
- 给定起点(i,j),查询其四个方向是否满足大于该点位置:
- 如果大于则,节点数= 1+以满足条件位置为起点的最多节点的节点数
- 如果小于则,该路线不通
- 给定起点查询最多节点的节点数时,起点会多次枚举,且枚举起点又设计查询满足条件的其他方位点做起点,则使用递归查询
- 递归优化,出现过的起点直接返回结果
- 终点返回计算的节点数
/**
* @param {number[][]} matrix
* @return {number}
*/
var longestIncreasingPath = function (matrix) {
let row = matrix.length,
colum = matrix[0] ? matrix[0].length : 0,
_result = 0,
dp = Array.from({ length: row }, () => Array(colum).fill(0))
// matrix长宽为0 返回结果值0
if (row === 0 || colum === 0) return _result
// 遍历枚举起点
for (let i = 0; i < row; i++) {
for (let j = 0; j < colum; j++) {
// 变量行列查询到所以可能的起点
_result = Math.max(_result, dfs(i, j))
}
}
// 指定起点坐标查询其最大递增路线
function dfs(r, c) {
// 不为0则什么已经计算
if (dp[r][c]) return dp[r][c]
// 以其自身做起点 默认节点数1
dp[r][c] = 1
// 四个方向
// 左侧
if (c - 1 >= 0 && matrix[r][c - 1] > matrix[r][c])
dp[r][c] = Math.max(dp[r][c], dfs(r, c - 1) + 1)
// 右侧
if (c + 1 < colum && matrix[r][c + 1] > matrix[r][c])
dp[r][c] = Math.max(dp[r][c], dfs(r, c + 1) + 1)
// 上方
if (r - 1 >= 0 && matrix[r - 1][c] > matrix[r][c])
dp[r][c] = Math.max(dp[r][c], dfs(r - 1, c) + 1)
// 下方
if (r + 1 < row && matrix[r + 1][c] > matrix[r][c])
dp[r][c] = Math.max(dp[r][c], dfs(r + 1, c) + 1)
// 返回指定坐标结果
return dp[r][c]
}
return _result
}
拓扑排序
按照上面思路发现其实已经枚举了已所有点为起点路线情况,
既然枚举了所有路线,那某一个节点,一定知道有多少路线包含了它,或者某一个点是否与其他点形成路线,
且已知任何一条路线的终点一定在四个方向上都不能移动的坐标
那么记录索引在四个方向上都不能移动的坐标,
再从这个点向起点反推,反推的次数最多的就查找的节点最多的路线,反推的次数就是节点数
/**
* @param {number[][]} matrix
* @return {number}
*/
var longestIncreasingPath = function (matrix) {
let dirs = [
[-1, 0], // 上方
[1, 0], // 下方
[0, -1], // 左侧
[0, 1], // 右侧
],
row = matrix.length,
colum = matrix[0] ? matrix[0].length : 0,
_result = 0,
level = Array.from({ length: row }, () => Array(colum).fill(0)),
dp = []
// matrix长宽为0 返回结果值0
if (row === 0 || colum === 0) return _result
// 计算每个单元格 四个方向上满足条件的方向数
for (let i = 0; i < row; i++) {
for (let j = 0; j < colum; j++) {
for (let k = 0; k < 4; k++) {
let r = i + dirs[k][0],
c = j + dirs[k][1]
if (
r >= 0 &&
r < row &&
c >= 0 &&
c < colum &&
matrix[r][c] > matrix[i][j]
) {
// 记录在所有路线中点(i,j)存在的数量
level[i][j]++
}
}
// 如果点(i,j)为在本路线中出现则记录坐标,作为路线终点
if (level[i][j] === 0) dp.push([i, j])
}
}
// 遍历终点集合,反推起点
while (dp.length > 0) {
// 记录遍历层数
_result++
let dpLen = dp.length
for (let x = 0; x < dpLen; x++) {
let cell = dp.shift(),
i = cell[0],
j = cell[1]
// (i,j)为终点坐标,(r,c)为满足反推条件的起点坐标
for (let k = 0; k < 4; ++k) {
let r = i + dirs[k][0],
c = j + dirs[k][1]
if (
r >= 0 &&
r < row &&
c >= 0 &&
c < colum &&
matrix[r][c] < matrix[i][j]
) {
// 遍历一层则默认以(r,c)起点的可能路线减少一个(及包含终点(i,j)的那一条)
level[r][c]--
// 如果(r,c)起点也不存在路线经过他了,那将其放置到dp中作为终点
if (level[r][c] === 0) {
dp.push([r, c])
}
}
}
}
}
return _result
}
- websocket教程(一) 非常有趣的理解websocket
- 前端插件——头像截图上传插件的使用(带后台)
- 如何减轻ajax定时触发对服务器造成的压力和带宽的压力?ajax-长轮训
- Spark源码之Standalone模式下master持久化引擎讲解
- spring整合quartz
- android沉浸式状态栏的实现
- Jayrock: JSON and JSON-RPC for .NET
- rabbitMQ教程(三) spring整合rabbitMQ代码实例
- 谈谈序列化—实体bean一定要实现Serializable接口?
- Kafka源码系列之通过源码分析Producer性能瓶颈
- 微软在动态语言支持上超越了Java?
- JAVA图片批量上传JS-带预览功能
- Netty-Websocket 根据URL路由,分发机制的实现
- DrawerLayout实现网易新闻抽屉效果
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- android音乐播放简单实现的简单示例(MediaPlayer)
- Android实现桌面悬浮小火箭效果
- Android数据库中事务操作方法之银行转账示例
- Android实现QQ登录功能
- Devtools 老师傅养成[6] - Performance 面板
- RecyclerView上拉加载封装代码
- Devtools 老师傅养成[7] - Memory 内存
- Android实现简单的城市列表功能
- Android Animation之TranslateAnimation(平移动画)
- Android自定义View实现箭头沿圆转动实例代码
- Android 中Context的使用方法详解
- Android自定义水平渐变进度条
- Android+SQLite数据库实现的生词记事本功能实例
- Android 设置颜色的方法总结
- Android支付宝支付的示例代码