跳转至

代码-哈希表

概述

哈希表通过哈希函数将元素映射到特定数字,从而可以实现\(O(1)\)的时间复杂度索引元素。

题目

242.有效的字母异位词

image-20231129193731392

题解:

  • 第一种思路:因为只有26个字母,所以构建一个长为26的数字记录a-z的个数即可,先遍历一个字符串记录个数,然后遍历另一个字符串减小个数;
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        res = [0] * 26
        for c in s:
            res[ord(c) - ord('a')] += 1
        for c in t:
            res[ord(c) - ord('a')] -= 1
        for i in range(26):
            if res[i] != 0:
                return False
        return True
  • 第二种思路:直接用dict或者collections库中的defacultdict或者Counter类解决
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        return Counter(s) == Counter(t)

349.两个数组的交集

image-20231129194537687

题解:

  • 用集合求解
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        return list(set(nums1) & set(nums2))

202/快乐数

image-20231129200226602

题解:

  • 求出数字各个位数平方之和之后,用数组记录即可

  • 其中求数字各位数之和可以用求余,也可以把数字转换成字符串

class Solution:
    def isHappy(self, n: int) -> bool:
        def calc_squre(n):
            # return sum([int(c) for c in str(n)])
            r = 0
            while n:
                r += ((n % 10) ** 2)
                n = n // 10 
            return r
        record = []
        while n:
            record.append(n)
            n = calc_squre(n)
            if n == 1:
                return True
            elif n in record:
                return False 

1.两数之和

image-20231129202059922

题解:

  • 使用哈希表记录已经便利的数及其下标,并且搜索目标值与当前遍历数字的差是否在哈希表中
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        res = {}
        for i in range(len(nums)):
            if target - nums[i] in res:
                return [res[target - nums[i]], i]
            res[nums[i]] = i

        # 双指针
        # 对输入列表进行排序
        nums_sorted = sorted(nums)

        # 使用双指针
        left = 0
        right = len(nums_sorted) - 1
        while left < right:
            current_sum = nums_sorted[left] + nums_sorted[right]
            if current_sum == target:
                # 如果和等于目标数,则返回两个数的下标
                left_index = nums.index(nums_sorted[left])
                right_index = nums.index(nums_sorted[right])
                if left_index == right_index:
                    # 当两个数相等时,index方法找到的是第一次出现的下标
                    right_index = nums[left_index + 1: ].index(nums_sorted[right]) + left_index + 1
                return [left_index, right_index]
            elif current_sum < target:
                # 如果总和小于目标,则将左侧指针向右移动
                left += 1
            else:
                # 如果总和大于目标值,则将右指针向左移动
                right -= 1
  • 使用双端指针搜索,这需要首先对数组进行排序,同时需要注意如果找到的两个数字相等,那么用index方法从list中索引时返回的是第一次出现的位置,所以需要进一步索引
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        sort_nums = sorted(nums)
        i, j = 0, len(nums) - 1
        num_sum = 0
        while i < j:
            num_sum = sort_nums[i] + sort_nums[j]
            if num_sum == target:
                id_i = nums.index(sort_nums[i])
                id_j = nums.index(sort_nums[j])
                if id_i == id_j:
                    id_j = nums[id_i + 1: ].index(sort_nums[j]) + id_i + 1
                return [id_i, id_j]
            elif num_sum < target:
                i += 1
            else:
                j -= 1

454.四数相加II

image-20231129203758894

题解:

  • 用哈希表记录两个数组的两数之和的个数,然后遍历另外两个数组,如果这两个数组的数字和的负数出现在哈希表中,结果加上哈希表的计数。
class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        sum12 = {}
        for n1 in nums1:
            for n2 in nums2:
                if (n1 + n2) in sum12:
                    sum12[n1 +n2] += 1
                else:
                    sum12[n1 + n2] = 1
        cnt = 0
        for n3 in nums3:
            for n4 in nums4:
                if -(n3 + n4) in sum12:
                    cnt += sum12[-(n3 + n4)]
        return cnt 

383.赎金信

image-20231129205141880

题解:

  • 记录reasomNote的字母个数,然后与magazine对比即可,可以采用数组/字典/defacultdict/Counter
# 使用Counter
class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        a, b = Counter(ransomNote), Counter(magazine)
        for k, v in a.items():
            if (k not in b) or (k in b and v > b[k]):
                return False
        return True

# 使用字典
class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        counts = {}
        for c in magazine:
            counts[c] = counts.get(c, 0) + 1
        for c in ransomNote:
            if c not in counts or counts[c] == 0:
                return False
            counts[c] -= 1
        return True

15.三数之和

image-20231129205825399

image-20231129205842923

题解:

  • 双指针:首先确定三元组左边元素,再对右边区间进行双指针遍历,需要注意各种去重逻辑
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        nums.sort()

        for i in range(len(nums)):
            # 如果第一个元素已经大于0,不需要进一步检查
            if nums[i] > 0:
                return result

            # 跳过相同的元素以避免重复
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            left = i + 1
            right = len(nums) - 1

            while right > left:
                sum_ = nums[i] + nums[left] + nums[right]

                if sum_ < 0:
                    left += 1
                elif sum_ > 0:
                    right -= 1
                else:
                    result.append([nums[i], nums[left], nums[right]])

                    # 跳过相同的元素以避免重复
                    while right > left and nums[right] == nums[right - 1]:
                        right -= 1
                    while right > left and nums[left] == nums[left + 1]:
                        left += 1

                    right -= 1
                    left += 1

        return result

18.四数之和

image-20231129212223280

题解:

  • 15.三数之和思路相同,此时除了双指针外,还需要引入额外两个循环遍历
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        result = []
        for i in range(n):
            if nums[i] > target and nums[i] > 0 and target > 0: # 剪枝(可省)
                break
            if i > 0 and nums[i] == nums[i-1]: # 去重
                continue
            for j in range(i+1, n):
                if nums[i] + nums[j] > target and target > 0: #剪枝(可省)
                    break
                if j > i+1 and nums[j] == nums[j-1]: # 去重
                    continue
                left, right = j+1, n-1
                while left < right:
                    s = nums[i] + nums[j] + nums[left] + nums[right]
                    if s == target:
                        result.append([nums[i], nums[j], nums[left], nums[right]])
                        while left < right and nums[left] == nums[left+1]:
                            left += 1
                        while left < right and nums[right] == nums[right-1]:
                            right -= 1
                        left += 1
                        right -= 1
                    elif s < target:
                        left += 1
                    else:
                        right -= 1
        return result