🌈【LeetCode 1631. 最小体力消耗路径 】- JavaScript(并查集+Dijkstra)

210 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情


说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)

作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金

GitHub:P-J27、 CSDN:PJ想做前端攻城狮

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


【LeetCode 1631. 最小体力消耗路径 】- JavaScript(并查集+Dijkstra)

题意描述

你准备参加一场远足活动。给你一个二维rows x columns的地图 heights ,其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) (注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费体力最小的一条路径。

一条路径耗费的体力值是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。

请你返回从左上角走到右下角的最小 体力消耗值 。

img

示例 1:

输入:heights = [[1,2,2],[3,8,2],[5,3,5]] 输出:2 解释:路径 [1,3,5,3,5] 连续格子的差值绝对值最大为 2 。 这条路径比路径 [1,2,2,2,5] 更优,因为另一条路径差值最大值为 3 。

示例 2:

输入:scores = [4,5,6,5], ages = [2,1,2,1] 输出:16 解释:最佳的选择是后 3 名球员。注意,你可以选中多个同龄球员。

思路分析:

一开始还想搜索+剪枝,发现一直TLE,还以为终于不是图论变成动态规划了,仔细一看好家伙又是图论。 万万想不到这也能并查集这也能Dijkstra。敢问并查集还有什么不能做的

并查集

核心思想

  • 先构建各点之间的边列表edges。 其中由edge=[x,y,d]组成,d为点x与y差的绝对值
  • 根据边列表中的 各点差的绝对值 从小到大排序
  • 依次遍历edges, 并将各点进行连通, 当最左上与最左下 第一次连通时结束,并输出 所遍历过edges中的最大d
class UnionFind {
    constructor(n) {
        this.count = n;
        this.rank = new Array(n).fill(1);
        this.parents = new Array(n).fill(0).map((v, i) => i);
    }

    find(x) {
        return this.parents[x] === x ? x : this.find(this.parents[x]);
    }

    union(p, q) {
        const rootP = this.find(p);
        const rootQ = this.find(q);
        if (rootP === rootQ) {
            return;
        }
        if (this.rank[rootP] >= this.rank[rootQ]) {
            this.parents[rootQ] = this.parents[rootP];
            this.rank[rootP] += this.rank[rootQ];
        } else {
            this.parents[rootP] = this.parents[rootQ];
            this.rank[rootQ] += this.parents[rootP];
        }
        this.count--;
    }

    isConnected(p, q) {
        return this.find(p) === this.find(q);
    }
}
var minimumEffortPath = function(heights) {
    const m = heights.length, n = heights[0].length;
    const uf = new UnionFind(m * n)
    const edges = [];
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            const id = i * n + j;
            if (i > 0) {
                edges.push([id-n, id, Math.abs(heights[i][j] - heights[i-1][j])]);
            }
            if (j > 0) {
                edges.push([id-1, id, Math.abs(heights[i][j] - heights[i][j-1])]);
            }
        }
    }
    edges.sort((a, b) => a[2] - b[2]);
    for (const [x, y, diff] of edges) {
        uf.union(x, y);
        if (uf.isConnected(0, m * n - 1)) {
            return diff;
        }
    }
    return 0
};

Dijkstra

思路:****

  • 将二维的坐标一维化:即将(x,y)记为x*col+y;
  • 使用优先队列对最短路径进行排序上的控制;
  • Dijkstra算法构造:这道题可以想成BFS+优先队列,当然这里Dijkstra的更新条件记得要改一下。
interface PointState {
    x: number; //col
    y: number; //row
    heightFromStart: number;
}
function minimumEffortPath(heights: number[][]): number {
    const rowCount = heights.length;
    const colCount = heights[0].length;
    const queue: PointState[] = [];
    const dp: number[][] = [];
    for(let i=0;i<rowCount;i++){
        dp[i] = []
        for(let j=0;j<colCount;j++){
            dp[i][j] = Infinity 
        }
    }
    queue.push({x:0,y:0,heightFromStart:0});
    dp[0][0] = 0;
    while (queue.length > 0) {
        const curPointState = queue.shift();
        const {x,y} = curPointState;
        if (x === colCount -1 && y === rowCount - 1){
            continue;
        }
        if (curPointState.heightFromStart > dp[x][y]){
            continue;
        }
        getNeighborItem(heights,x,y).forEach((child)=>{
            const height = Math.abs(heights[x][y] - heights[child.x][child.y]);
            const childHeight = Math.max(height,curPointState.heightFromStart);
            if (childHeight < dp[child.x][child.y]){
                dp[child.x][child.y] = childHeight;
                queue.push({x:child.x,y:child.y,heightFromStart:childHeight})
            }
        })
    }
    return dp[rowCount - 1][colCount - 1];
};


function getNeighborItem(heights: number[][],x: number,y: number): {x: number;y:number}[] {
    const result = []
    const t = {x:x-1,y};
    const l = {x,y:y-1};
    const r = {x,y:y+1};
    const b = {x:x+1,y};
    function insert(p: any) {
        const xValid = p.x >=0 && p.x < heights.length;
        const yValid = p.y >=0 && p.y < heights[0].length;
        if (xValid && yValid) result.push(p) 
    }
    insert(t);insert(l);insert(r);insert(b);
    return result;
}

感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。

写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤