使用并查集计算婴儿名字频度

我去,这个题对于刚学并查集的我来说还是挺难的,记录一下!(这个查询方法太慢了)

from collections import defaultdict

# names = ["John(15)", "Jon(12)", "Chris(13)", "Kris(4)", "Christopher(19)"]
# synonyms = ["(Jon,John)", "(John,Johnny)", "(Chris,Kris)", "(Chris,Christopher)"]


class UnionFind:

    def __init__(self, names_: []):
        self.parent = {}
        # 初始化数组
        for name in names_:
            # parent中存放name(key)和name(value)的是以键值对方式存放的
            # 这样一来就可以和以数组的方式i和arr[i]并查方式一样了,也就是说
            # 现在我们可以以key作为i(即节点本身的信息),以value作为arr[i](即节点的根节点信息)方式存放
            # 初始化时key为本身(i),value也为本身(arr[i])
            self.parent[name] = name

    def find(self, p):
        """
        查找 p 节点的根节点
        :param p:待查找的节点
        :return:返回根节点的标记(这里即字典中的value)
        """
        while p != self.parent[p]:
            # 查找当前节点的爷爷节点的组别
            p = self.parent[self.parent[p]]

        return p

    def union(self, p, q):
        """
        合并操作
        :param p:
        :param q:
        :return:
        """
        # 如果要合并的元素有一个不在parent列表中,则结束
        if p not in self.parent or q not in self.parent:
            return
        # 判断p和q是否指向相同的根节点
        p_root = self.find(p)
        q_root = self.find(q)
        if p_root == q_root:
            return
        # 如果p所在的树小于q所在的树,则将p所在的树合并到q所在的树
        # TODO:这里字典的操作和数组有区别
        if p_root < q_root:
            # 更改q_root对应的value值,即将小树挂载到大树上
            self.parent[q_root] = p_root
        if p_root > q_root:
            self.parent[p_root] = q_root


class Solution(object):
    """
    我们将name(每一个name,例如john和jon,以及其对应的次数都存放在freq_map中)
    将name放在并查集中合并,然后再赋予 “相同” name的次数,存放在 res_map 中
    """

    def trulyMostPopular(self, names, synonyms)->list:
        """
        :param names:
        :param synonyms:
        :return:
        """
        # 以defaultdict第一字典时当字典里的key不存在但被查找时,返回的是一个默认值
        freq_map = defaultdict(int)
        # 将数据解析出来
        for freq_name in names:
            # names:"john(12)"这种数据格式
            name, freq_str = (part.strip(")") for part in freq_name.split("("))
            # 把name和对应的名字的频率放入字典
            freq_map[name] = int(freq_str)

        # 然后初始化并查集,dict/defaultdict.keys()返回一个字典所有的键
        uf = UnionFind(freq_map.keys())

        # 然后是并操作,synonyms保存的是“相同”name的列表,解析出来后立即将其连接起来
        # 并且这些name也在names列表中,并且已经先初始化了uf对象
        for pair_str in synonyms:
            a, b = (n.strip(")").strip("(") for n in pair_str.split(","))
            uf.union(a, b)

        # 生成最终结果
        result = []
        res_map = defaultdict(int)

        # freq_map中存放的是name和对应的频次例如:john: 12
        for name_, freq in freq_map.items():
            # res_map[uf.find(name_)] += freq,
            # 其中uf.find(name_)是找到当前节点的根节点,
            res_map[uf.find(name_)] = res_map[uf.find(name_)] + freq

        for name_, freq in res_map.items():
            # result保存的结果应当是john(12)这种形式的,所以下面append操作里面的是这种形式
            result.append("{}({})".format(name_, freq))

        return result


if __name__ == "__main__":
    names = ["John(15)", "Jon(12)", "Chris(13)", "Kris(4)", "Christopher(19)"]
    synonyms = ["(Jon,John)", "(John,Johnny)", "(Chris,Kris)", "(Chris,Christopher)"]
    s = Solution()
    result_ = s.trulyMostPopular(names, synonyms)

    print(result_)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值