leetcode 并查集模版题

并查集模版附在文末。
以下代码,省略模版,跑 leetcode 时记得补上。

323. 无向图中连通分量的数目

思想

连通分量的数量就是并查集内的集合数量

复杂度

时间复杂度: O ( N ) O(N) O(N)

空间复杂度: O ( N ) O(N) O(N)

代码

class Solution {
    public int countComponents(int n, int[][] edges) {
        UnionFind find = new UnionFind(n);
        for (int i = 0; i < edges.length; i++) {
            find.union(edges[i][0], edges[i][1]);
        }
        return find.sets;
    }
}

261. 以图判树

给定从 0n-1 标号的 n 个结点,和一个无向边列表(每条边以结点对来表示),请编写一个函数用来判断这些边是否能够形成一个合法有效的树结构。

示例 1:

输入: n = 5, 边列表 edges = [[0,1], [0,2], [0,3], [1,4]]
输出: true

思想

树是特殊的图,图满足树的条件是:

  1. 无环图
  2. 连通分量的数量为 1

图判定无环,可以利用并查集,边在加入前看是否属于同一个集合,如果是一个集合则形成环

复杂度

时间复杂度: O ( N ) O(N) O(N)

空间复杂度: O ( N ) O(N) O(N)

代码

class Solution {
    public boolean validTree(int n, int[][] edges) {      
        UnionFind find = new UnionFind(n);
        for (int i = 0; i < edges.length; i++) {
            if (find.isSameSet(edges[i][0], edges[i][1])) {
                return false;
            } else {
                find.union(edges[i][0], edges[i][1]);
            }
        }
        return find.sets == 1;
    }
}

737. 句子相似性 II

给定两个句子 s1, s2 (每个用字符串数组表示),和一个相似单词对的列表 pairs ,判断是否两个句子是相似的。

例如,当相似单词对是 pairs = [["great", "fine"], ["acting","drama"], ["skills","talent"]]的时候,s1 = ["great", "acting", "skills"]s2 = ["fine", "drama", "talent"] 是相似的。

注意相似关系是 具有 传递性的。例如,如果 “great” 和 “fine” 是相似的,“fine” 和 “good” 是相似的,则 “great” 和 “good” 是相似的。

而且,相似关系是具有对称性的。例如,“great” 和 “fine” 是相似的相当于 “fine” 和 “great” 是相似的。

并且,一个单词总是与其自身相似。例如,句子 s1 = ["great"], s2 = ["great"], pairs = [] 是相似的,尽管没有输入特定的相似单词对。

最后,句子只会在具有相同单词个数的前提下才会相似。

思想

相同词义的词放入一个集合,然后依次对比两个句子的单词是否是同一个集合。

复杂度

时间复杂度: O ( N + P ) O(N + P) O(N+P) N N Ns1s2 最大的大小,P 为 pairs 大小

空间复杂度: O ( P ) O(P) O(P)

代码

class Solution {
    public boolean areSentencesSimilarTwo(String[] s1, String[] s2, List<List<String>> pairs) {
        if (s1.length != s2.length) return false;

        Map<String, Integer> map = new HashMap<>();
        UnionFind find = new UnionFind(pairs.size() * 2);
        
        int cnt = 0;
        for (var pair : pairs) {
            for (var str : pair) {
                if (!map.containsKey(str)) {
                    map.put(str, cnt++);
                }
            }
            find.union(map.get(pair.get(0)), map.get(pair.get(1)));
        }

        for (int i = 0; i < s1.length; i++) {
            if (s1[i].equals(s2[i])) continue;
            if (!map.containsKey(s1[i]) || !map.containsKey(s2[i])) {
                if (!s1[i].equals(s2[i])) {
                    return false;
                }
            } else {
                if (!find.isSameSet(map.get(s1[i]), map.get(s2[i]))) {
                    return false;
                }
            }            
        }

        return true;
    }
}

附:并查集模版

class UnionFind {
    int[] parent; // parent[i] = k,i 的父亲是 k    
    int[] size; // i 所在的集合大小是多少    
    int sets; // 并查集内有多少个集合    
    int[] stack; // 数组模拟栈

    public UnionFind(int N) {
        parent = new int[N];
        size = new int[N];
        stack = new int[N];
        sets = N;
        for (int i = 0; i < N; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    // 从i开始一直往上,往上到不能再往上,代表节点,返回
    private int find(int i) {
        int top = 0; 
        while (i != parent[i]) {
            stack[top++] = i;
            i = parent[i];
        }
        for (top--; top >= 0; top--) { // 路径压缩
            parent[stack[top]] = i;
        }
        return i;
    }

    public void union(int i, int j) {
        int f1 = find(i);
        int f2 = find(j);
        if (f1 != f2) {
            int max = size[f1] > size[f2] ? f1 : f2;
            int min = max == f1 ? f2 : f1;
            size[max] += size[min];
            parent[min] = max;
            sets--;
        }
    }

    public boolean isSameSet(int i, int j) {
        return find(i) == find(j);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值