# 前言

本题是寻找最短路径问题，可以分别采用【并查集/Kruskal】、【二分dfs/bfs】、【Dijkstra】进行解决
**并查集**
并查集中首先采用 Kruskal 最小生成树方法，以相邻结点的高度差作为权值，然后根据权值从小到大的顺序构图，直到左上角结点和右下角结点连通，break，输出结果。
**二分法**
图的问题也可以采用dfs和bfs进行求解，题目注释里给出高度值的范围为[1,1000000]，因此结果最大为100000，可以采用二分法进行夹逼求得结果。注意：如果不采用二分法，而是采用传统回溯法，会导致超时
**dijkstra**
最短路径问题，可以联想到使用 dijkstra 算法进行求解

各解法代码及注释如下

* []

```并查集
class Solution {
    public int minimumEffortPath(int[][] heights) {
        int rows = heights.length;
        int cols = heights[0].length;
        //Kruskal构造连边
        List<int[]> edges = new LinkedList<>();
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                int id = i*cols+j;
                if(i<rows-1){
                    edges.add(new int[]{id,id+cols,Math.abs(heights[i][j]-heights[i+1][j])});
                }
                if(j<cols-1){
                    edges.add(new int[]{id,id+1,Math.abs(heights[i][j]-heights[i][j+1])});
                }
            }
        }
        //根据结点之间的权值进行排序
        Collections.sort(edges,new Comparator<int[]>(){
            public int compare(int[] o1,int[] o2){
                return o1[2] - o2[2];
            }
        });
        int ans = 0;
        //从小到大连通结点
        UnionFind uf = new UnionFind(rows*cols);
        for(int i=0;i<edges.size();i++){
            int[] temp = edges.get(i);
            int x = temp[0];
            int y = temp[1];
            int dp = temp[2];
            if(uf.find(x)!=uf.find(y)){
                uf.union(x,y);
                ans = dp;
            }
            //左上角结点和右下角结点连通
            if(uf.find(0)==uf.find(rows*cols-1)){
                break;
            }
        }
        return ans;
    }
}
//并查集
class UnionFind{
    int[] f;
    public UnionFind(int n){
        f = new int[n];
        for(int i=0;i<n;i++){
            f[i] = i;
        }
    }
    //查
    public int find(int x){
        if(f[x]!=x){
            f[x] = find(f[x]);
        }
        return f[x];
    }
    //并
    public void union(int x,int y){
        if(find(x)!=find(y)){
            f[find(x)] = find(y);
        }
    }
}
```

* []

```二分法dfs
class Solution {
    int[][] directions = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
    int ans = Integer.MAX_VALUE;
    int rows;
    int cols;    
    public int minimumEffortPath(int[][] heights) {
        rows = heights.length;
        cols = heights[0].length;
        int left = 0;
        int right = 1000000;
        while(left<right){
            int mid = (right+left)/2;
            //更新左右边界
            if(dfs(heights,0,0,mid,new boolean[rows][cols])){
                right = mid;
            }else{
                left = mid+1;
            }
        }
        return left;
    }
    //dfs寻找是否存在一条连通左上角和右下角结点的道路
    public Boolean dfs(int[][] heights,int row,int col,int mid,boolean[][] visited){ 
        if(row==rows-1 && col==cols-1){
            return true;
        }
        visited[row][col] = true;
        for(int[] dir:directions){
            int newRow = row + dir[0];
            int newCol = col + dir[1];
            if(newRow>=0&&newRow<rows&&newCol>=0&&newCol<cols && !visited[newRow][newCol] && 
            Math.abs(heights[newRow][newCol]-heights[row][col])<=mid){
                if(dfs(heights,newRow,newCol,mid,visited)) return true;
            }
        }
        return false;
    }
}
```

* []

```二分法bfs
class Solution {
    public int minimumEffortPath(int[][] heights) {
        int[][] directions = new int[][]{{1,0},{-1,0},{0,1},{0,-1}}; 
        int rows = heights.length;
        int cols = heights[0].length;
        int left = 0,right = 1000000;
        while(left<right){
            int mid = (left+right)/2;
            boolean[][] visited = new boolean[rows][cols];
            Queue<int[]> queue = new LinkedList<>();
            queue.offer(new int[]{0,0});
            visited[0][0] = true;
            while(!queue.isEmpty()){
                int[] temp = queue.poll();
                int x = temp[0];
                int y = temp[1];
                for(int[] dir:directions){
                    int nx = x+dir[0];
                    int ny = y+dir[1];
                    if(nx>=0 && nx<rows && ny>=0 && ny<cols && !visited[nx][ny] 
                    && Math.abs(heights[nx][ny]-heights[x][y])<=mid){
                        queue.offer(new int[]{nx,ny});
                        visited[nx][ny] = true;
                    }
                }
            }
            if(visited[rows-1][cols-1]){
                right = mid;
            }else{
                left = mid+1;
            }
        }
        return left;
    }
}
```

* []

```dijkstra
class Solution {
    public int minimumEffortPath(int[][] heights) {
        int[][] directions = new int[][]{{1,0},{-1,0},{0,1},{0,-1}}; 
        int rows = heights.length;
        int cols = heights[0].length;
        //优先队列，最小的先出来
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>(){
            public int compare(int[] o1,int[] o2){
                return o1[2] - o2[2];
            }
        });
        boolean[][] visited = new boolean[rows][cols];
        int[] dist = new int[rows*cols];
        Arrays.fill(dist,Integer.MAX_VALUE);
        dist[0] = 0;
        queue.offer(new int[]{0,0,0});
        while(!queue.isEmpty()){
            int[] temp = queue.poll();
            int x = temp[0];
            int y = temp[1];
            int dp = temp[2];
            if(visited[x][y]) continue;
            if(x==rows-1 && y==cols-1){
                break;
            }
            //弹出来之后再进行标记，确定此刻为最小
            visited[x][y] = true;
            for(int[] dir:directions){
                int nx = x+dir[0];
                int ny = y+dir[1];
                if(nx>=0 && nx<rows && ny>=0 && ny<cols
                && Math.max(dp,Math.abs(heights[nx][ny]-heights[x][y]))<=dist[nx*cols+ny]){
                    dist[nx*cols+ny] = Math.max(dp,Math.abs(heights[nx][ny]-heights[x][y]));
                    queue.offer(new int[]{nx,ny,dist[nx*cols+ny]});
                }
            }
        }
        return dist[rows*cols-1];
    }
}
```

