😇721 account merge

https://leetcode.com/problems/accounts-merge/

感觉是很好的union find的题目,union的过程是你认为两个group需要union然后主动call union才会union,一般是by size,size相等的时候谁当组长其实不确定的,然后注意这里每一组account天然就是一个group,对应一个group id,之后可以通过这个反向找到一开始的accounts

这里最关键的是把emails展平成email->idx的形式,如果email已经存在就union当前idx和已经存在的idx,最后生成答案是通过email的idx找到对应的leader对应的account

class Solution {
    private class Union {
        int[] representatives;
        int[] size;
        
        public Union(int n) {
            size = new int[n];
            representatives = new int[n];
            for (int i = 0; i < n; i++) {
                representatives[i] = i;
                size[i] = 1;
            }
        }
        public int find(int a) {
            if (representatives[a] == a) {
                return a;
            } 
            representatives[a] = find(representatives[a]);
            return representatives[a];
        }
        //注意union不是一个自发的过程,是你点名要union这两个partition它们俩才会union
        public void union(int a, int b) {
            int ar = find(a);
            int br = find(b);
            if (size[ar] >= size[br]) {
                size[ar] += size[br];
                representatives[br] = ar;
            } else {
                size[br] += size[ar];
                representatives[ar] = br;
            }
        }
    }
    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        int n = accounts.size();
        Union uni = new Union(n);
        Map<String, Integer> map = new HashMap<>();
        List<List<String>> ans = new ArrayList<>();
        for (int i = 0; i < accounts.size(); i++) {
            List<String> account = accounts.get(i);
            for (int j = 1; j < account.size(); j++) {
                String email = account.get(j);
                if (!map.containsKey(email)) {
                    map.put(email, i);
                } else {
                    //总是与第一次遇到email时的account去union
                    uni.union(map.get(email), i);
                }
            }
        }
        // group id -> emails under the group
        // 因为可能有重名所以名字不能作为key,用group id是最好的选择
        Map<Integer, Set<String>> groups = new HashMap<>();
        for (int i = 0; i < accounts.size(); i++) {
            List<String> account = accounts.get(i);
            //这里相当于遍历了union所有的group
            int group = uni.find(i);
            Set<String> set = groups.get(group);
            if (set == null) {
                set = new HashSet<>();
            }
            groups.put(group, set);
            for (int j = 1; j < account.size(); j++) {
                String email = account.get(j);
                set.add(email);
            }
        }

        for (Map.Entry<Integer, Set<String>> entry : groups.entrySet()) {
            int group = entry.getKey();
            Set<String> emails = entry.getValue();
            List<String> account = new ArrayList<>(emails);
            Collections.sort(account);
            account.add(0, accounts.get(group).get(0));
            ans.add(account);
        }
        return ans;
    }
}

注意下面的写法会出问题

class Union {
        int[] leaders;
        public Union(int n) {
            this.leaders = new int[n];
            for (int i = 0; i < n; i++) {
                this.leaders[i] = i;
            }
        }
        public int union(int l, int r) {
            int leaderl = find(l);
            int leaderr = find(r);
            if (leaderl == leaderr) {
                return 0;
            }
            leaders[r] = leaderl;
            return 1;
        }

        public int find(int i) {
            if (leaders[i] == i) {
                return i;
            }
            leaders[i] = find(leaders[i]);
            return leaders[i];
        }
    }

如以下case:

union 1 2 leader: 1
union 1 3 leader: 1
union 0 4 leader: 0
union 3 4 leader: 1

这是由于4的leader被更新成了0,但是4的leader并非它自己而是1,可以把union看成一个森林也就是一堆树的集合,union两棵树一定要找到它的root来union,在经过find的path compression之后,每棵树都被扁平化了,也就是说1一定是root,我们要做的是把1也就是root的leader设成0而不是把4的leader设成0:

class Union {
        int[] leaders;
        public Union(int n) {
            this.leaders = new int[n];
            for (int i = 0; i < n; i++) {
                this.leaders[i] = i;
            }
        }
        public int union(int l, int r) {
            int leaderl = find(l);
            int leaderr = find(r);
            if (leaderl == leaderr) {
                return 0;
            }
            leaders[leaderr] = leaderl;
            return 1;
        }

        public int find(int i) {
            if (leaders[i] == i) {
                return i;
            }
            leaders[i] = find(leaders[i]);
            return leaders[i];
        }
    }

Last updated