算法——婴儿名字

每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。有些名字有多种拼法,例如,John 和 Jon 本质上是相同的名字,但被当成了两个名字公布出来。给定两个列表,一个是名字及对应的频率,另一个是本质相同的名字对。设计一个算法打印出每个真实名字的实际频率。注意,如果 John 和 Jon 是相同的,并且 Jon 和 Johnny 相同,则 John 与 Johnny 也相同,即它们有传递和对称性。
在结果列表中,选择字典序最小的名字作为真实名字。
添加链接描述

解题思路:

  1. 首先通过哈希表建立并查集,哈希表的键值对都是字符串,然后将一个相连的并查集合并;
  2. 利用一个哈希表进行计数,计数的时候将值都累加到根元素。
  3. 最后进行输出,输出的时候从头遍历,如果当前元素是一个根元素,那就输出。

注意点:
1.在合并的时候,为了满足题意,将字典序小的根元素作为新的根元素。

这里用到的并查集的模板进行编写程序。一般并查集都是利用数字最为元素标识,模板操作如下:

List<Integer> p = new ArrayList<>();
// 查找根
public int find(int x){
    if(p.get(x) != x){
        p.set(x, find(p.get(x)));
    }
    return p.get(x);
}

//合并
p.set(find(a), find(b));

其实,不只是数字,只要是可以产生映射的元素都是可以用并查集来实现的。本题,就是通过哈希表将字符串进行映射,构造并查集。

题目代码:

class Solution {
    Map<String, String> map;
    public String[] trulyMostPopular(String[] names, String[] synonyms) {
        Map<String, Integer> cnt = new HashMap<>();
        map = new HashMap<>();

        for(String name : names) {
            int i = 0;
            while(name.charAt(i) != '(') i++;
            map.put(name.substring(0, i), name.substring(0, i));
        }  

        for(String name : synonyms) {
            String[] temp = name.split(",");
            String x = temp[0].substring(1, temp[0].length());
            String y = temp[1].substring(0, temp[1].length() - 1);

            if(!map.containsKey(x)) map.put(x, x);
            if(!map.containsKey(y)) map.put(y, y);

            // 获得两个集合的根
            String fx = find(x);
            String fy = find(y);

            // 合并两个并查集,将字典序小的根作为新的根
            if(!fx.equals(fy)){
                if(fx.compareTo(fy) > 0) map.put(fx, fy);
                else map.put(fy, fx);
            }
        }

        for(String name : names) {
            int i = 0;
            while(name.charAt(i) != '(') i++;

            // 将数值都累加到根的位置
            String root = find(name.substring(0, i));
            cnt.put(root, cnt.getOrDefault(root, 0) + Integer.parseInt(name.substring(i + 1, name.length() - 1)));
        }

        List<String> res = new ArrayList<>();

        for(String name : names) {
            int i = 0;
            while(name.charAt(i) != '(') i++;

            String root = find(name.substring(0, i));
            
            // 只输出根
            if(!root.equals(name.substring(0, i))) continue;

            res.add(root + "(" + String.valueOf(cnt.get(root)) + ")");
        }

        return res.toArray(new String[res.size()]);
    }

    // 查根
    public String find(String x) {
        if(!map.get(x).equals(x)) {
            map.put(x, find(map.get(x)));
        }

        return map.get(x);
    }
}
原文地址:https://www.cnblogs.com/lippon/p/14117634.html