[算法练习及思路-程序员面试金典(Java解法)]No90.婴儿名字

题号:no90

题目名:婴儿名字
原题URL:https://leetcode-cn.com/problems/baby-names-lcci/
题目描述

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

在结果列表中,选择字典序最小的名字作为真实名字。

示例

示例 1:

输入:names = ["John(15)","Jon(12)","Chris(13)","Kris(4)","Christopher(19)"], synonyms = ["(Jon,John)","(John,Johnny)","(Chris,Kris)","(Chris,Christopher)"]
输出:["John(27)","Chris(36)"]

限制
  • names.length <= 100000
思路

1.用散列+并查集来解决

2.先将所有名字及对应的每个名字的数量用散列存储起来

3.将所有应该在一起的集合进行存储,这里要注意,map.get得到的list并不是真正的指针,而是指针的副本,需要再重新去map里赋值

4.并查集+字典排序完成该题

解题代码
public class Solution {
        public String[] trulyMostPopular(String[] names, String[] synonyms) {
            //遍历名字数组保留名字-个数散列
            Map<String,Integer> name2nums = new HashMap<>();
            //保留名字并查集
            Map<String,List<String>> name2List = new HashMap<> ();
            //存下所有并查集
            List<String> nameList = new ArrayList<>();
            for (int i = 0; i < names.length; i++) {
                int leftBucket = names[i].indexOf('(');
                int rightBucket = names[i].indexOf(')');
                name2nums.put(names[i].substring(0, leftBucket),Integer.parseInt(names[i].substring(leftBucket+1,rightBucket)));
            }
            for (String synonym : synonyms) {
                //拿到同组的两个名字
                String str1 = synonym.substring(synonym.indexOf('(')+1, synonym.indexOf(','));
                String str2 = synonym.substring(synonym.indexOf(',')+1, synonym.indexOf(')'));
                List<String> list1 = name2List.get(str1);
                List<String> list2 = name2List.get(str2);
                //如果这两个名字都没有集合,那么就新建一个集合加入
                if(list1==null&&list2==null) {
                    List<String> list = new ArrayList<>();
                    list.add(str1);
                    list.add(str2);
                    name2List.put(str1,list);
                    name2List.put(str2,list);
                }else {
                    //如果第一个为空
                    if(list1==null) {
                        list2.add(str1);
                        name2List.put(str1,list2);
                    } else if(list2==null) {
                        //如果第二个为空
                        list1.add(str2);
                        name2List.put(str2,list1);
                    } else {
                        //如果两个都不为空
                        List<String> newList = new ArrayList<> (list1);
                        for (String s : list2) {
                            if(!newList.contains(s)) newList.add(s);
                        }
                        for (String s : newList) {
                            name2List.put(s,newList);
                        }
                    }
                }

            }
            for (Map.Entry<String, Integer> entry : name2nums.entrySet()) {
                //如果没有值,说明值被清空了
                Integer value = entry.getValue();
                if(value ==null) continue;
                //如果没有并查集,直接将这个数插入
                StringBuilder sb = new StringBuilder();
                String key = entry.getKey();
                List<String> conbineList = name2List.get(key);
                if(conbineList ==null) {
                    sb.append(key).append('(').append(value).append(')');
                    nameList.add(sb.toString());
                }else {
                    //如果有并查集,字典排序
                    conbineList.sort((s1,s2)->s1.compareTo(s2));
                    int sum = 0;
                    sb.append(conbineList.get(0)).append('(');
                    for (String s : conbineList) {
                        Integer integer = name2nums.get(s);
                        if(integer!=null) {
                            sum+=integer;
                            //加和后将这个key置位null,下次就不会用这个键了
                            name2nums.put(s,null);
                        }
                    }
                    sb.append(sum).append(')');
                    nameList.add(sb.toString());
                }
            }
            return nameList.toArray(new String[0]);
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值