一天一大 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
}
- 欧里几德及扩展欧里几德算法
- Hadoop数据分析平台实战——260用户数据ETL离线数据分析平台实战——260用户数据ETL
- # Hadoop离线数据分析平台实战——230项目数据存储结构设计Hadoop离线数据分析平台实战——230项目数据存储结构设计
- ECJTUACM16 Winter vacation training #5 题解&源码
- Codeforces 716A Crazy Computer
- Hadoop数据分析平台实战——240JavaSDK数据收集引擎编写离线数据分析平台实战——240JavaSDK数据收集引擎编写
- Hadoop离线数据分析平台实战——290活跃用户分析Hadoop离线数据分析平台实战——290活跃用户分析
- Codeforces 719B Anatoly and Cockroaches
- 【一起学Python】爬取前程无忧招聘信息并写入Excel
- 【一起学Python】爬取网易云歌词
- Hadoop离线数据分析平台实战——370外链信息分析Hadoop离线数据分析平台实战——370外链信息分析
- POJ 1067 取石子游戏
- 【一起学Python】STEAM游戏评测爬虫
- Open Judge 2750 鸡兔同笼
- 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 数组属性和方法
- PHP使用htmlspecialchars方法实现像ASP的Server.HTMLEncode一样的效果
- jquery.ajax()怎么把获取来的内容转为JSON,并使用。
- Spring:IOC 控制反转
- Spring:AOP 面向切面编程
- JavaScript进阶教程(5)-一文让你搞懂作用域链和闭包
- JavaScript进阶教程(6)—硬核动图让你轻松弄懂递归与深浅拷贝
- Spring:JDBC Template,声明式事务
- Spring:讲解编程题
- SpringMVC:基本应用
- SpringMVC:进阶
- SpringMVC:SSM 整合
- 12 | Tornado源码分析:BaseIOStream 对象(上)
- 数据一致性校验及数据同步,运维必看
- Windows使用scoop包管理器安装RabbitMQ
- 深入理解web协议(二):DNS、WebSocket