LeetCode上最小体力消耗路径,思路参考了官方题解,记录下自己的理解过程吧
本月不知道做的,直接先看并查集
这里传入的参数设为[[4,3,4,10,5,5,9,2],[10,8,2,10,9,7,5,6],[5,8,10,10,10,7,4,2],[5,1,3,1,1,3,1,9],[6,4,10,6,10,9,4,6]]
,接收的是一个二维数组,可以画出如下表格括号内为该点的高度
要从编号0点走到编号39点,求之间消耗的最小体力。根据题意,最小体力的消耗应该是路径上相邻格子之间高度差绝对值的最大值决定的,就是连通路径上最高点和最低点的差值。
这里看这张图可以知道,假设每个点只能向右和向下连接,比如0点就可以和1/8点连接,连接的时候需要的体力是1/6,那么可以遍历整张图,生成一个每个点到另外点的一个数组。还要注意最后一行以及最后一列只能向右或者只能向下,这种情况应该过滤掉。
for(let j=0;j<col;j++) {
let id = i*col + j
if(i!== row - 1) {
nodesmap.push([id,id+col,Math.abs(heights[i][j] - heights[i+1][j])])
}
if(j!== col - 1) {
nodesmap.push([id,id+1,Math.abs(heights[i][j] - heights[i][j+1])])
}
}
}
会写出这样一个双重循环,从[0,0]点开始循环,当前点的编号就是id = i*col + j
,每个数组中保存的数据是[当前点序号,连接点序号,两者之间体力消耗的绝对值]
这里需要求得一个最小值,可以理解为一个贪心算法,每次都连接体力消耗最小的,直到两点连接上了,那么此时消耗的体力就是最小体力
根据消耗体力排序nodesmap.sort((a,b) => {return a[2] - b[2]})
那么在最后通过并查集连接的时候从这个数组的第一个开始连接,直到判断到
find(nodes[0]) === find(nodes[row*col - 1])
即0点和最后一点的parent
属性相等就是表示连接了先连接体力消耗为0的数据
连接体力消耗为1的数据,可以发现此时star已经进入路径了,但是还没有和end连接
这样不断的循环整个数组直到把39连入的时候,那个时候消耗的体力就是最小体力
// 定义一个节点
class node {
constructor (i) {
// 将自己的父亲设为自己
this.parent = this
// val属性记录下当前节点在accounts中的位置
this.val = i
}
}
// 定义查找函数,不断查找父节点,直到父节点等于本身
const find = function (x) {
while(x !== x.parent) {
x = x.parent
}
return x
}
var minimumEffortPath = function(heights) {
// 传入的是个二维数组,需要保存行列的数据
// 有多少行
let row = heights.length
// 有多少列
let col = heights[0].length
// 结果
console.log(row*col);
let res = 0
// 创建对应多个节点数组
let nodes = Array(row*col).fill(0).map((x,i) => new node(i))
// 改造二维数组
// map 用于存放改造后的数组
let nodesmap = []
// 填充这个map,每个子数组对应的是[当前点,下一个点,两个点之间的]
for(let i =0;i<row;i++) {
for(let j=0;j<col;j++) {
// 每个点可以往下和往右走
// 那么最右和最下排的点只能往下和往右,只有一个方向,那么对应的就是j=col-1列以及i=row-1行
// 除去这两排其他点都要记录和向下以及向右的点的距离
// 对应的当前点在nodes数组中的id
let id = i*col + j
if(i!== row - 1) {
// 当前节点向下连接,即在同一行上
// 当前节点和下一行同位置的节点连接
nodesmap.push([id,id+col,Math.abs(heights[i][j] - heights[i+1][j])])
}
if(j!== col - 1) {
// 当前节点向右连接,即在同一列上
// 当前节点和下一列同行连接
nodesmap.push([id,id+1,Math.abs(heights[i][j] - heights[i][j+1])])
}
}
}
// 为了计算更短的体力消耗,就要根据map内子数组的第3位从小到大排序
nodesmap.sort((a,b) => {return a[2] - b[2]})
// 那么nodesmap就是根据体力从小到大排序的,之后一个个相连,知道左上和右下点的parent属性相等的时候
// 那么就是两点相连了
console.log(nodesmap);
for(let i = 0;i<nodesmap.length; i++) {
let [x,y,l] = nodesmap[i]
res = l
// 连接xy点
find(nodes[y]).parent = find(nodes[x])
if(find(nodes[0]) === find(nodes[row*col - 1])) {
// 如果左上和右下的parent相等,那么就是连通了
break
}
}
return res
}