LeetCode 题集:哈希表

本文为 LeetCode 有关哈希表的问题。


1. Two Sum(两数之和)


问题描述

LeetCode 1 问题描述

思路与代码


题目中特别指出,可否找到时间复杂度低于 O ( n 2 ) O(n^2) O(n2) 的方法,答案即为哈希表。

代码如下:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        dic = {}
        for i in range(len(nums)):
            if target - nums[i] in dic:
                return [dic[target - nums[i]], i]

            dic[nums[i]] = i
            
        return []

运行效果:
LeetCode 1 运行效果


290. Word Pattern(单词规律)


问题描述

LeetCode 290 问题描述

思路与代码


本题思路较为简单,只需要通过 pattern 对应单词和单词对应 pattern 两个字典,判断模式是否符合即可,由于两个字典为互逆的关系,其中一个可以简化为集合。

代码如下:

class Solution:
    def wordPattern(self, pattern: str, s: str) -> bool:
        list_word = s.split(' ')

        if len(pattern) != len(list_word):  # 长度不匹配,直接返回 False
            return False

        dict_pattern, set_word = {}, set()
        for i in range(len(pattern)):
            char, word = pattern[i], list_word[i]
            if char in dict_pattern.keys():
                if word != dict_pattern[char]:
                    return False
            else:
                if word in set_word:
                    return False
                else:
                    dict_pattern[char] = word
                    set_word.add(word)

        return True

运行效果:
LeetCode 290 运行效果


49. Group Anagrams(字母异位词分组)


问题描述

LeetCode 49 问题描述

思路与代码


本题较自然的思路是,遍历每个单词,将单词的字母按字母表顺序排序,然后以排序后的字符串为 key,查询并更新类型字典,得出最终结果。

代码如下:

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        dic_type = {}
        for i in range(len(strs)):
            word = strs[i]
            word = "".join(sorted(list(word)))  # 单词按字母表顺序排序

            if word not in dic_type:
                list_new = [strs[i]]
                dic_type[word] = list_new
            else:
                dic_type[word].append(strs[i])

        list_group = list(dic_type.values())

        return list_group

运行结果看起来还不错:
LeetCode 49 运行结果 1

但其实,代码是有优化空间的。

由于对单词中的字母进行了排序操作,会增加算法的复杂度。这里可以通过将字母转化为自然数中的第 i i i 个素数( i i i 为字母序号)并累乘,使每种单词类型对应为一个数字(一系列素数的乘积),达到将排序算法中的 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的复杂度优化为 O ( n ) O(n) O(n) 的目的。

具体代码如下:

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        # 素数字典
        list_prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
        dict_prime = {chr(i + 97): list_prime[i] for i in range(26)}

        dic_group = {}
        for word in strs:
            product = 1
            for letter in word:
                product *= dict_prime[letter]

            if product in dic_group:
                dic_group[product].append(word)
            else:
                dic_group[product] = [word]

        return list(dic_group.values())

提交运行后,效果比较有趣。
编程语言选用 Python3 时,有时甚至没有明显的优化效果:
LeetCode 49 运行效果 2-1
选用 Python 时,优化效果相对较为稳定:
LeetCode 49 运行效果 2-2


205. Isomorphic Strings(同构字符串)


问题描述

LeetCode 205 问题描述

思路与代码


本题的思路也较简单,可以为两个字符串分别定义一个字母代号字典,代号即为该字母首次出现的位置,按位遍历两个字符串,只要发现某位置的字母代号不同,即为非同构,若遍历结束,则为同构。

代码如下:

class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False

        dict_letter_s, dict_letter_t = {}, {}  # 字母代号字典
        for i in range(len(s)):
            # 更新字典
            if s[i] not in dict_letter_s.keys():
                dict_letter_s[s[i]] = i
            if t[i] not in dict_letter_t.keys():
                dict_letter_t[t[i]] = i

            if dict_letter_s[s[i]] != dict_letter_t[t[i]]:  # 如果当前位的字母所对应的代号不同,则输出 False
                return False

        return True

运行效果:
LeetCode 205 运行效果 1

进一步,本体也可类似 290 题的方式优化,即将其中一个字典简化为集合,优化后的代码如下:
LeetCode 205 运行效果 2


166. Fraction to Recurring Decimal(分数到小数)


问题描述

LeetCode 166 问题描述 I
LeetCode 166 问题描述 II

思路与代码


本题很自然的思路是,通过记录余数的再现,以确定循环节。因此,可以定义一个字典用来记录余数出现的位置。此外,需要考虑两种边界情况,一种是分子为 0 时,直接输出 ‘0’;另一种是分子或分母符号为负的情况,先记录结果的正负,再取绝对值进行计算。

具体代码如下:

class Solution:
    def fractionToDecimal(self, numerator: int, denominator: int) -> str:
        str_res = ""

        # edge case 1: 分子为 0
        if not numerator:
            return '0'

        # edge case 2: 正负数
        if_neg = False
        if numerator < 0:
            if_neg = not if_neg
            numerator = abs(numerator)
        if denominator < 0:
            if_neg = not if_neg
            denominator = abs(denominator)
        str_res += '-' if if_neg else ''

        # 整数部分
        itg = int(numerator / denominator)
        rem = numerator - itg * denominator  # 余数
        str_res += str(itg)
        str_res += '.' if rem else ''

        list_fra = []
        dict_rem = {rem: 0}  # "余数: 位数" 字典
        i = 0  # 位数
        rem *= 10
        if_loop, loop_period = False, []  # 是否存在循环节,循环节的位置
        while rem:
            i += 1  # 更新位数
            if rem < denominator:  # 若余数小于分母,将其扩大 10 倍
                list_fra.append('0')
                rem *= 10
                continue

            fra = int(rem / denominator)  # 当前位的小数
            list_fra.append(str(fra))
            rem -= fra * denominator  # 更新余数
            if rem not in dict_rem.keys():  # case 1: 暂未发现循环节
                dict_rem[rem] = i
            else:  # 发现循环节,将循环节的首末位记录在字典中
                if_loop, loop_period = True, (dict_rem[rem], i)
                break
            rem *= 10  # 余数扩大 10 倍

        str_fra = ''.join(list_fra[: loop_period[0]]) + '(' + ''.join(
            list_fra[loop_period[0]: loop_period[1]]) + ')' if if_loop else ''.join(list_fra)  # 小数部分
        str_res += str_fra

        return str_res

运行效果:
LeetCode 166 运行效果


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值