力扣刷题笔记 哈希表及其相关题目(含ACM模式)

 《代码随想录》学习笔记,原链接:https://programmercarl.com/

哈希表理论基础

242.有效的字母异位词(使用数组哈希表)

383.赎金信

49.字母异位词分组

438.找到字符串中所有字母异位词

349.两个数组的交集 (使用集合set哈希表)

 350.两个数组的交集II

202.快乐数

1.两数之和

454.四数相加II

15.三数之和 

18.四数之和


哈希表理论基础

哈希表(Hash Table),用来快速判断某个元素是否在集合中出现过(当题目中出现需要判断满足某个条件的某个元素/某些元素是否出现过的场景,第一时间想到哈希法),即适用于与“查询”相关的操作。

哈希表采用的数据结构包括以下三类:

  • 数组:在哈希值和哈希表的大小范围都比较小的时候使用(优先使用,数组是最直接、运算速度最快的)。
  • 集合set:在哈希值和哈希表的大小范围都比较大的时候使用。
  • 匹配map:如果每个key都需要保存其对应的value时使用。

但是,哈希表也是牺牲时间换取空间的方法,因为要使用额外的数组、set或者map来存放数据,才能实现快速查找。

242.有效的字母异位词(使用数组哈希表)

  • 核心代码模式
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        record = [0] * 26   # 记录字符串26个字母分别出现的次数

        for letter in s:
            record[ord(letter) - ord("a")] += 1 # 统计字符串s中每个字母出现的次数,ord()表示取字符的ASCII码
        
        for letter in t:
            record[ord(letter) - ord("a")] -= 1 # 统计字符串s中每个字母出现的次数,ord()表示取字符的ASCII码
        
        for i in range(26): # 判断s和t中每个字母出现的次数是否相同
            if record[i] != 0:
                return False
        
        return True
  • ACM模式
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        record = [0] * 26  # 记录字符串26个字母分别出现的次数

        for letter in s:
            record[ord(letter) - ord("a")] += 1  # 统计字符串s中每个字母出现的次数,ord()表示取字符的ASCII码

        for letter in t:
            record[ord(letter) - ord("a")] -= 1  # 统计字符串s中每个字母出现的次数,ord()表示取字符的ASCII码

        for i in range(26):  # 判断s和t中每个字母出现的次数是否相同
            if record[i] != 0:
                return False

        return True

# 输入字符串s和t
s = input("输入小写字母字符串s:")
t = input("输入小写字母字符串t:")

# 有效的字母异位词
solution = Solution()
result = solution.isAnagram(s, t)
print(result)

383.赎金信

  • 核心代码模式
class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        record = [0] * 26   # 构建哈希表数组,记录26个字母分别出现的次数

        # 统计magazine中各个字母出现的次数
        for letter in magazine:
            record[ord(letter) - ord("a")] += 1
        
        # 统计ransomNote中每个字母出现的次数
        for letter in ransomNote:
            record[ord(letter) - ord("a")] -= 1

        for i in range(26): # 判断ransomNote能不能由magazine里面的字符构成
            if record[i] < 0:   # ransomNote中有元素不能在magazine中找到
                return False
        
        return True
  • ACM模式
class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        record = [0] * 26  # 构建哈希表数组,记录26个字母分别出现的次数

        # 统计magazine中各个字母出现的次数
        for letter in magazine:
            record[ord(letter) - ord("a")] += 1

        # 统计ransomNote中每个字母出现的次数
        for letter in ransomNote:
            record[ord(letter) - ord("a")] -= 1

        for i in range(26):  # 判断ransomNote能不能由magazine里面的字符构成
            if record[i] < 0:  # ransomNote中有元素不能在magazine中找到
                return False

        return True

# 输入字符串s和t
magazine = input("输入小写字母字符串magazine :")
ransomNote = input("输入小写字母字符串ransomNote:")

# 赎金信
solution = Solution()
result = solution.canConstruct(ransomNote, magazine)
print(result)

49.字母异位词分组

  • 核心代码模式
class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        record = {}

        for string in strs:
            key = ''.join(sorted(string))   # sorted函数返回的是一个已排序的字母列表,而不是一个字符串,需要使用join()函数转换回字符串

            # 将排序后的字符串作为key,原字符串构成的列表作为value
            if key not in record:
                record[key] = [string]  # 注意,需要将string初始化成列表元素
            else:
                record[key].append(string)

        result = list(record.values())      # 字典的values()方法返回一个dict_values类型的对象,包含字典中所有值,但是该对象需要使用list()函数转换为列表。
        
        return result

【注1】key = ''.join(sorted(string))   # sorted函数返回的是一个已排序的字母列表,而不是一个字符串,需要使用join()函数转换回字符串。

【注2】 result = list(record.values())      # 字典的values()方法返回一个dict_values类型的对象,包含字典中所有值,但是该对象需要使用list()函数转换为列表。

  • ACM模式
class Solution:
    def groupAnagrams(self, strs):
        record = {}

        for string in strs:
            key = ''.join(sorted(string))  # sorted函数返回的是一个已排序的字母列表,而不是一个字符串,需要使用join()函数转换回字符串

            # 将排序后的字符串作为key,原字符串构成的列表作为value
            if key not in record:
                record[key] = [string]  # 注意,需要将string初始化成列表元素
            else:
                record[key].append(string)

        result = list(record.values())  # 字典的values()方法返回一个dict_values类型的对象,包含字典中所有值,但是该对象需要使用list()函数转换为列表。

        return result

strs = []
# 输入字符串列表
while True:
    string = input("输入字符串元素(按回车结束输入):")
    if string == "":
        break
    strs.append(string)

# 字母异位词分组
solution = Solution()
result = solution.groupAnagrams(strs)
print(result)

438.找到字符串中所有字母异位词

  • 核心代码模式
from collections import Counter

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        result = []
        lenth = len(p)
        p_count = Counter(p)
        s_count = Counter(s[: lenth - 1])

        for i in range(lenth - 1, len(s)):
            s_count[s[i]] += 1
            if s_count == p_count:
                result.append(i - lenth + 1)
            s_count[s[i - lenth + 1]] -= 1
            if s_count[s[i - lenth + 1]] == 0:
                del s_count[s[i - lenth + 1]]

        return result
  • ACM模式
from collections import Counter

class Solution:
    def findAnagrams(self, s, p):
        result = []
        lenth = len(p)
        p_count = Counter(p)
        s_count = Counter(s[: lenth - 1])

        for i in range(lenth - 1, len(s)):
            s_count[s[i]] += 1
            if s_count == p_count:
                result.append(i - lenth + 1)
            s_count[s[i - lenth + 1]] -= 1
            if s_count[s[i - lenth + 1]] == 0:
                del s_count[s[i - lenth + 1]]

        return result

strs = []
# 输入字符串p和s
s = input("输入字符串s:")
p = input("输入字符串p:")

# 找到字符串中所有字母异位词
solution = Solution()
result = solution.findAnagrams(s, p)
print(result)

349.两个数组的交集 (使用集合set哈希表)

 

由于题目要求输出结果中的每个元素是唯一的,因此需要对于结果进行去重操作。而由于哈希值没有做限制,其数字范围很大,不适合使用数组来作为哈希表,因此使用集合set来作为哈希表。

本题逻辑如下:

  • 核心代码模式 
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        record = set() # set哈希表,存放nums1中的去重元素
        result = set()  # 存放结果

        for num in nums1:   # 记录nums1中出现的数字
            if num not in record:
                record.add(num)
        
        for num in nums2:   # 寻找两个数组的交集
            if num in record:
                result.add(num)
                record.remove(num)
            
        return list(result) # 注意返回类型提示为“list”类型

        # 可以直接使用下面这一行代码,即可得到结果
        # return list(set(nums1).intersection(set(nums2)))

【注1】哈希表选择数组和集合的情况分析:

  • 如果没有限制数值的大小,而且哈希表的值比较少,比较分散,跨度非常大,则使用数组就会造成空间的极大浪费。 
  • 但是,set会把数值映射到key上都要进行哈希运算,直接使用set相对于数组,不仅占用空间大,而且速度比数组更慢。

因此,遇到问题,不能无脑全部使用set。

【注2】关于Python中的集合set的一些知识记录:

 

 

  • ACM模式
class Solution:
    def intersection(self, nums1, nums2):
        record = set()  # set哈希表,存放nums1中的去重元素
        result = set()  # 存放结果

        for num in nums1:  # 记录nums1中出现的数字
            if num not in record:
                record.add(num)

        for num in nums2:  # 寻找两个数组的交集
            if num in record:
                result.add(num)
                record.remove(num)

        return list(result)  # 注意返回类型提示为“list”类型

        # 可以直接使用下面这一行代码,即可得到结果
        # return list(set(nums1).intersection(set(nums2)))

# 输入正整数数组nums1和nums2
nums1 = list(map(int, input("输入整数数组nums1:").split(",")))
nums2 = list(map(int, input("输入整数数组nums2:").split(",")))

# 两个数组的交集
solution = Solution()
result = solution.intersection(nums1, nums2)
print(result)

 350.两个数组的交集II

  • 核心代码模式
class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        record = {} # 创建空字典哈希表
        result = [] # 记录交集结果

        for num in nums1:   # 统计nums1中的字母个数
            # num = str(num)    # 这句可加可不加
            if num not in record:
                record[num] = 0
            record[num] += 1
        
        for num in nums2:   # 寻找两个数组的交集
            # num = str(num)    # 这句可加可不加
            if num in record and record[num] > 0:   # 首先需要判断num是否在record中
                result.append(int(num))
                record[num] -= 1
        
        return result

 【注】关于Python中的集合set的一些知识记录:

  • ACM模式
class Solution:
    def intersect(self, nums1, nums2):
        record = {}  # 创建空字典哈希表
        result = []  # 记录交集结果

        for num in nums1:  # 统计nums1中的字母个数
            # num = str(num)    # 这句可加可不加
            if num not in record:
                record[num] = 0
            record[num] += 1

        for num in nums2:  # 寻找两个数组的交集
            # num = str(num)    # 这句可加可不加
            if num in record and record[num] > 0:  # 首先需要判断num是否在record中
                result.append(int(num))
                record[num] -= 1

        return result

# 输入正整数数组nums1和nums2
nums1 = list(map(int, input("输入整数数组nums1:").split(",")))
nums2 = list(map(int, input("输入整数数组nums2:").split(",")))

# 两个数组的交集II
solution = Solution()
result = solution.intersect(nums1, nums2)
print(result)

202.快乐数

  • 核心代码模式 
class Solution:
    def isHappy(self, n: int) -> bool:
        result = []
        summation = n

        while True:
            nums = list(map(int, str(summation)))   # 将summation按照每一位,拆分成整数列表
            summation = 0       # 每次都要对summation重新赋值初始化

            for i in nums:      # 计算当前每个位置上的数字的平方和
                summation += (i ** 2) 

            # 如果无限循环,说明肯定在某个时刻,summation出现了循环,则返回False
            if summation in result:
                return False
            # 如果summation的结果为1,说明是快乐数
            if summation == 1:
                return True

            result.append(summation)    # 统计已经出现过的平方和

【注】 关于“list(map(int, str(summation)))”的分析:

summation = 12345
print(summation, type(summation))   # 输出12345 <class 'int'>

result1 = str(summation)
print(result1, type(result1))   # 输出12345 <class 'str'>

# map()函数用于将一个函数,应用到某个可迭代对象的所有元素上,并返回一个新的可迭代对象。
result2 = map(int, str(summation))
print(result2, type(result2))   # 输出<map object at 0x0000016420FAC6C8> <class 'map'>

# 使用for循环访问这个新的可迭代对象的元素值
for i in result2:
    # 1 <class 'int'>
    # 2 <class 'int'>
    # 3 <class 'int'>
    # 4 <class 'int'>
    # 5 <class 'int'>
    print(i, type(i))   

result3 = list(map(int, str(summation)))
print(result3, type(result3))   # [1, 2, 3, 4, 5] <class 'list'>
  •  ACM模式
class Solution:
    def isHappy(self, n):
        result = []
        summation = n

        while True:
            nums = list(map(int, str(summation)))   # 将summation按照每一位,拆分成整数列表
            summation = 0       # 每次都要对summation重新赋值初始化

            for i in nums:      # 计算当前每个位置上的数字的平方和
                summation += (i ** 2)

            # 如果无限循环,说明肯定在某个时刻,summation出现了循环,则返回False
            if summation in result:
                return False
            # 如果summation的结果为1,说明是快乐数
            if summation == 1:
                return True

            result.append(summation)    # 统计已经出现过的平方和

# 输入n
n = int(input("输入n:"))

# 快乐数
solution = Solution()
result = solution.isHappy(n)
print(result)

1.两数之和

 

  •  核心代码模式
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        record = {}     # 使用字典哈希表,记录已经访问的元素

        for i in range(len(nums)):
            if target - nums[i] not in record:      # 将已经访问过的元素记录到字典里
                record[nums[i]] = i
            else:    
                return [record[target - nums[i]], i]    # 记录满足条件的两个数字的下标

【注】关于本题的几个问题:

 

  • ACM模式
class Solution:
    def twoSum(self, nums, target):
        record = {}     # 使用字典哈希表,记录已经访问的元素

        for i in range(len(nums)):
            if target - nums[i] not in record:      # 将已经访问过的元素记录到字典里
                record[nums[i]] = i
            else:
                return [record[target - nums[i]], i]    # 记录满足条件的两个数字的下标

# 输入整数列表nums和目标和target
nums = list(map(int, input("nums:").split(",")))
target = int(input("target:"))

# 两数之和
solution = Solution()
result = solution.twoSum(nums, target)
print(result)

454.四数相加II

  •  核心代码模式
class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        record = {}
        count = 0

        # 计算数组nums1和nums2中的两个元素之和,并将元素和以及出现次数保存到record
        for i in range(len(nums1)):
            for j in range(len(nums2)):
                temp_sum = nums1[i] + nums2[j] 
                if temp_sum not in record:
                    record[temp_sum] = 0
                record[temp_sum] += 1
        
        # 判断nums3和nums4中是否满足四数之和等于0
        for k in range(len(nums3)):
            for l in range(len(nums4)):
                temp_sum = nums3[k] + nums4[l]
                if 0 - temp_sum in record:
                    count += record[0 - temp_sum]   # 此处注意不能写count += 1
        
        # 返回满足四数之和等于0的组合个数
        return count
  • ACM模式
class Solution:
    def fourSumCount(self, nums1, nums2, nums3, nums4):
        record = {}
        count = 0

        # 计算数组nums1和nums2中的两个元素之和,并将元素和以及出现次数保存到record
        for i in range(len(nums1)):
            for j in range(len(nums2)):
                temp_sum = nums1[i] + nums2[j]
                if temp_sum not in record:
                    record[temp_sum] = 0
                record[temp_sum] += 1

        # 判断nums3和nums4中是否满足四数之和等于0
        for k in range(len(nums3)):
            for l in range(len(nums4)):
                temp_sum = nums3[k] + nums4[l]
                if 0 - temp_sum in record:
                    count += record[0 - temp_sum]  # 此处注意不能写count += 1

        # 返回满足四数之和等于0的组合个数
        return count

# 输入整数列表nums
nums1 = list(map(int, input("nums1:").split(",")))
nums2 = list(map(int, input("nums2:").split(",")))
nums3 = list(map(int, input("nums3:").split(",")))
nums4 = list(map(int, input("nums4:").split(",")))

# 四数之和II
solution = Solution()
result = solution.fourSumCount(nums1, nums2, nums3, nums4)
print(result)

15.三数之和 

  • 核心代码模式
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()   # 对原数组进行升序排序
        result = [] # 统计满足条件的三元组

        for i in range(len(nums) - 2):
            if nums[i] > 0:   # 升序数组中,i为最小值,如果i>0,则三数之和一定大于0
                break
            
            if i > 0 and nums[i] == nums[i - 1]:    # i去重
                continue 

            j, k = i + 1, len(nums) - 1 # 三指针法
            
            while j < k:    # 不能相等,否则只有两个数
                summation = nums[i] + nums[j] + nums[k] # 计算三数之和,不能将这一步放出while循环

                if summation < 0:
                    j += 1  # 增大summation
                elif summation > 0:
                    k -= 1  # 减小summation
                else:
                    result.append([nums[i], nums[j], nums[k]])  # 记录这个三元组
                    j += 1
                    k -= 1
                    while j < k and nums[j] == nums[j - 1]: # j去重,注意要保证j < k
                        j += 1
                    while j < k and nums[k] == nums[k + 1]: # k去重,注意要保证j < k
                        k -= 1
                    
        return result   # 返回结果三元组

【注】 此题如果向上面一样使用哈希法,需要套两层for循环。虽然也可以运行通过不会超时,但是这种方法很难进行剪枝和去重操作。而且,本题只需要返回元素值,而不需要返回下标,因此,使用先将数组进行排序,之后使用双指针法。方法原理图如下(具体逻辑见代码):

该方法在能够实现功能的前提下,方便进行去重和剪枝操作。

  • ACM模式
class Solution:
    def threeSum(self, nums):
        nums.sort()  # 对原数组进行升序排序
        result = []  # 统计满足条件的三元组

        for i in range(len(nums) - 2):
            if nums[i] > 0:  # 升序数组中,i为最小值,如果i>0,则三数之和一定大于0
                break

            if i > 0 and nums[i] == nums[i - 1]:  # i去重
                continue

            j, k = i + 1, len(nums) - 1  # 三指针法

            while j < k:  # 不能相等,否则只有两个数
                summation = nums[i] + nums[j] + nums[k]  # 计算三数之和,不能将这一步放出while循环

                if summation < 0:
                    j += 1  # 增大summation
                elif summation > 0:
                    k -= 1  # 减小summation
                else:
                    result.append([nums[i], nums[j], nums[k]])  # 记录这个三元组
                    j += 1
                    k -= 1
                    while j < k and nums[j] == nums[j - 1]:  # j去重,注意要保证j < k
                        j += 1
                    while j < k and nums[k] == nums[k + 1]:  # k去重,注意要保证j < k
                        k -= 1

        return result  # 返回结果三元组

# 输入整数列表nums
nums = list(map(int, input("nums:").split(",")))

# 三数之和
solution = Solution()
result = solution.threeSum(nums)
print(result)

 18.四数之和

  • 核心代码模式
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums = sorted(nums)     # 排序
        result = []

        for a in range(len(nums)):
            if target > 0 and nums[a] > target: # 剪枝
                break
            
            if a > 0 and nums[a] == nums[a - 1]:  # 去重,注意一定要加a > 0判断
                continue
            
            for b in range(a + 1, len(nums)):
                if target > 0 and nums[a] + nums[b] > target:   # 剪枝
                    break
                
                if b > a + 1 and nums[b] == nums[b - 1]:  # 去重,注意一定要加b > a + 1判断
                    continue

                slow, fast = b + 1, len(nums) - 1   # 双指针
                while slow < fast:
                    summation = nums[a] + nums[b] + nums[slow] + nums[fast] # 求和

                    if summation < target:
                        slow += 1
                    elif summation > target:
                        fast -= 1
                    else:
                        result.append([nums[a], nums[b], nums[slow], nums[fast]])   # 保存结果
                        slow += 1
                        fast -= 1
                        while slow < fast and nums[slow] == nums[slow - 1]: # 去重,一定要加slow < fast判断
                            slow += 1
                        while slow < fast and nums[fast] == nums[fast + 1]: # 去重,一定要加slow < fast判断
                            fast -= 1
                    
        return result

【注1】对于a和b在进行去重操作时,一定要进行“当前位置不是初始化的位置”的判断,否则如图下所示,会在初始化的时候,直接进行 += 1的操作,从而使得结果为空。 

 【注2】对于c和d进行去重操作时,一定要进行“slow < fast”判断,否则可能会超出列表范围。

  • ACM模式
class Solution:
    def fourSum(self, nums, target):
        nums = sorted(nums)  # 排序
        result = []

        for a in range(len(nums)):
            if target > 0 and nums[a] > target:  # 剪枝
                break

            if a > 0 and nums[a] == nums[a - 1]:  # 去重,注意一定要加a > 0判断
                continue

            for b in range(a + 1, len(nums)):
                if target > 0 and nums[a] + nums[b] > target:  # 剪枝
                    break

                if b > a + 1 and nums[b] == nums[b - 1]:  # 去重,注意一定要加b > a + 1判断
                    continue

                slow, fast = b + 1, len(nums) - 1  # 双指针
                while slow < fast:
                    summation = nums[a] + nums[b] + nums[slow] + nums[fast]  # 求和

                    if summation < target:
                        slow += 1
                    elif summation > target:
                        fast -= 1
                    else:
                        result.append([nums[a], nums[b], nums[slow], nums[fast]])  # 保存结果
                        slow += 1
                        fast -= 1
                        while slow < fast and nums[slow] == nums[slow - 1]:  # 去重,一定要加slow < fast判断
                            slow += 1
                        while slow < fast and nums[fast] == nums[fast + 1]:  # 去重,一定要加slow < fast判断
                            fast -= 1

        return result

# 输入整数列表nums和目标和target
nums = list(map(int, input("nums:").split(",")))
target = int(input("target:"))

# 四数之和
solution = Solution()
result = solution.fourSum(nums, target)
print(result)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值