并查集(399,990,547、1631)

如果一个问题具有传递性,则考虑使用并查集!

399.除法求值

399-除法求值

class Solution {
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        //并查集!!
        //如果一个问题具有传递性,则考虑使用并查集!!
        int equationsSize = equations.size();
        //创建并查集
        UnionFind unionFind = new UnionFind(2*equationsSize);//最差时的情况容量为2*equationsSize
        //1.预处理,将变量的值映射位数字,使得并查集的底层使用数组实现,方便编码
        Map<String, Integer> map = new HashMap<>(2*equationsSize);
        int id = 0;
        for(int i = 0;i < equationsSize;i++){
            String s1 = equations.get(i).get(0);
            String s2 = equations.get(i).get(1);
            if(!map.containsKey(s1)){
                map.put(s1, id++);
            }
            if(!map.containsKey(s2)){
                map.put(s2, id++);
            }
            //合并
            unionFind.union(map.get(s1), map.get(s2), values[i]);
        }
        //2.做查询
        int queriesSize = queries.size();
        double[] ans = new double[queriesSize];
        for(int i = 0;i < queriesSize;i++){
            String s1 = queries.get(i).get(0);
            String s2 = queries.get(i).get(1);

            Integer id1 = map.get(s1);
            Integer id2 = map.get(s2);
            if(id1 == null || id2 == null)
                ans[i] = -1.0d;
            else
                ans[i] = unionFind.isConnected(id1, id2);
        }
    return ans;
    }

    private class UnionFind{
        private int[] parent;
        //指向父节点的权值
        private double[] weight; 

        //构造函数
        public UnionFind(int size){
            parent = new int[size];
            weight = new double[size];
            //初始化父节点与权值
            for(int i = 0;i < size;i++){
                parent[i] = i;
                weight[i] = 1.0d;
            }
        }
        //合并
        public void union(int x, int y, double val){
            int rootX = find(x);
            int rootY = find(y);
            if(rootX == rootY)//属于同一连通分量
                return;
            else{
                parent[rootX] = rootY;
                weight[rootX] = val * weight[y] / weight[x];
            }
        }
        //查找&路径压缩
        public int find(int x){
            if(parent[x] != x){
                int origin = parent[x];
                parent[x] = find(parent[x]);//压缩路径
                weight[x] *= weight[origin];
            }
            return parent[x];
        }

        //回答题目
        public double isConnected(int x, int y){
            int rootX = find(x);
            int rootY = find(y);
            if(rootX != rootY)//不属于同一连通分量
                return -1.0d;
            return weight[x] / weight[y];
        }
    }
}

990.等式方程的可满足性

990

class Solution {
    public boolean equationsPossible(String[] equations) {
        //数组实现并查集
        int[] parent = new int[26];
        //初始化
        for(int i = 0;i < 26;i++){
            parent[i] = i;
        }

        for(String s : equations){
            if(s.charAt(1) == '='){
                int id1 = s.charAt(0) - 'a';
                int id2 = s.charAt(3) - 'a';
                union(parent, id1, id2);
            }
            
        }
        for(String s : equations){
            if(s.charAt(1) == '!'){//扫描所有的不等式并进行判断
                int id1 = s.charAt(0) - 'a';
                int id2 = s.charAt(3) - 'a';
                if(find(parent, id1) == find(parent, id2))//如果在一个连通分量则返回false
                    return false;
            }
        }
        return true;
    }
    //合并
    public void union(int[] parent, int x, int y){
        int rootX = find(parent, x);
        int rootY = find(parent, y);
        parent[rootX] = rootY;
    }
    //查询&路径压缩
    public int find(int[] parent, int x){
        if(parent[x] != x){
            parent[x] = find(parent, parent[x]);
        }
        return parent[x];
    }

}

547.省份数量

547

class Solution {
    public int findCircleNum(int[][] isConnected) {
        //问题具有传递性,考虑使用并查集
        int n = isConnected.length;
        int[] parent = new int[n];
        //初始化
        for(int i = 0;i < n;i++){
            parent[i] = i;
        }
        Set<Integer> set = new HashSet<>();
        for(int i = 0;i < n;i++){
            for(int j = 0;j < n;j++){
                if(isConnected[i][j] == 1 && i != j){
                    union(parent, i, j);
                }
            }
        }
        int ans = 0;
        for(int i = 0;i < n;i++){
            if(parent[i] == i)//只有根节点才会指向自己!
                ans++;
        }
            
        return ans;
    }
    //合并
    public void union(int[] parent, int x, int y){
        parent[find(parent, x)] = find(parent, y);
    }
    //查询&路径压缩
    public int find(int[] parent, int x){
        if(parent[x] != x){
            parent[x] = find(parent, parent[x]);
        }
        return parent[x];
    }
}

1631.最小体力值消耗路径

1并查集

class Solution {
    public int minimumEffortPath(int[][] heights) {
        //并查集,从小到大依次加入边,当加入的边使得左上角和右下角联通时,该变得权值即为答案
        int m = heights.length, n = heights[0].length;
        List<int[]> edges = new ArrayList<>();
        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                int id = i * n + j;//对节点编码
                if(i < m-1)
                    edges.add(new int[]{id, id+n, Math.abs(heights[i+1][j] - heights[i][j])});
                if(j < n-1)
                    edges.add(new int[]{id, id+1, Math.abs(heights[i][j+1] - heights[i][j])});
            }
        }
        Collections.sort(edges, new Comparator<int[]>(){//按边权值从小到大排序
            public int compare(int[] edge1, int[] edge2){
                return edge1[2] - edge2[2];
            }
        });
        UnionFind uf = new UnionFind(m*n);
        for(int[] edge : edges){
            int id1 = edge[0], id2 = edge[1], weight = edge[2];
            uf.union(id1, id2);
            if(uf.connected(0, m*n-1)){
                return weight;
            }
        }
        return 0;
    }

    class UnionFind{
        private int[] parent;

        public UnionFind(int n){
            parent = new int[n];
            for(int i = 0;i < n;i++){
                parent[i] = i;
            }
        }

        public void union(int x, int y){
            parent[find(x)] = parent[find(y)];
        }

        public int find(int x){
            if(x != parent[x]){
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }
        public boolean connected(int x, int y){
            return find(x) == find(y);
        }
    }
}

最短路径算法—dijkstra算法变形

class Solution {
    //求最短路径, dijkstra算法变形
    int[][] dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}};//定义4个方向
    public int minimumEffortPath(int[][] heights) {
        int m = heights.length, n = heights[0].length;
        //定义优先级队列,用于每次选出最近进行bfs
        PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>(){
            public int compare(int[] edge1, int[] edge2){
                return edge1[2] - edge2[2];
            }
        });
        pq.offer(new int[]{0,0,0});//加入原点
        int[] dist = new int[m*n];//定义距离数组
        Arrays.fill(dist, Integer.MAX_VALUE);//初始化距离为无穷大
        dist[0] = 0;
        boolean[] seen = new boolean[m*n];//标记遍历过的节点
        while(!pq.isEmpty()){
            int[] edge = pq.poll();//每次选出距离最近的点作为跳板向外遍历
            int x = edge[0], y = edge[1], d = edge[2];
            int id = x*n+y;
            if(seen[id])
                continue;
            if(x == m-1 && y == n-1)
                break;
            seen[id] = true;
            for(int i = 0;i < 4;i++){//通过该点遍历4个方向
                int nx = x + dirs[i][0], ny = y + dirs[i][1];
                if(nx >= 0 && nx < m && ny >= 0 && ny < n && Math.max(d, Math.abs(heights[x][y] - heights[nx][ny])) < dist[nx*n+ny]){//这里的Math.max(d, Math.abs(heights[x][y] - heights[nx][ny]))就是本题中的最小体力值消耗
                    dist[nx*n+ny] = Math.max(d, Math.abs(heights[x][y] - heights[nx][ny]));
                    pq.offer(new int[]{nx, ny, dist[nx*n+ny]});
                }
            }
        }
        return dist[m*n-1]; 
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值