454.四数相加II
题目链接:https://leetcode.cn/problems/4sum-ii/
视频链接:学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili
文章链接:代码随想录
python:
(版本一)使用字典
class Solution(object):
def fourSumCount(self, nums1, nums2, nums3, nums4):
# 使用字典存储nums1和nums2中元素的和及其出现次数
hashmap = dict() #创建一个空字典,即哈希表
# 遍历nums1和nums2中的每一个元素组合,计算它们的和
for n1 in nums1:
for n2 in nums2:
# 如果这个和已经在字典中,更新出现次数
if n1 + n2 in hashmap:
hashmap[n1+n2] += 1
else:
# 如果这个和不在字典中,初始化出现次数为1
hashmap[n1+n2] = 1
# 初始化计数器,用于记录四元组的数量
count = 0
# 遍历nums3和nums4中的每一个元素组合,计算它们的和的相反数
for n3 in nums3:
for n4 in nums4:
key = - n3 - n4
# 如果相反数在之前的字典中存在,则累加相应的次数到计数器中
if key in hashmap:
count += hashmap[key]
# 返回计数器的值,即满足条件的四元组的数量
return count
(版本二)使用字典
class Solution(object):
def fourSumCount(self, nums1, nums2, nums3, nums4):
# 使用字典存储nums1和nums2中元素的和及其出现次数
hashmap = dict()
# 遍历nums1和nums2中的每一个元素组合,计算它们的和
for n1 in nums1:
for n2 in nums2:
# 使用字典的get方法,如果和已经存在于字典中,则取出当前值并加1,
# 如果不存在,则使用默认值0,然后加1
hashmap[n1 + n2] = hashmap.get(n1 + n2, 0) + 1
# 初始化计数器,用于记录四元组的数量
count = 0
# 遍历nums3和nums4中的每一个元素组合,计算它们的和的相反数
for n3 in nums3:
for n4 in nums4:
key = -n3 -n4
# 如果相反数在之前的字典中存在,则累加相应的次数到计数器中
if key in hashmap:
count += hashmap[key]
# 返回计数器的值,即满足条件的四元组的数量
return count
不要把循环搞错
(版本三)使用defaultdict
from collections import defaultdict
class Solution:
def fourSumCount(self, nums1: list, nums2: list, nums3: list, nums4: list) -> int:
# 使用 defaultdict 创建一个默认值为0的字典 rec
# rec 用于存储 nums1 和 nums2 的元素组合的和及其出现的次数
rec, cnt = defaultdict(lambda: 0), 0
# 遍历 nums1 和 nums2 中的每一个元素组合,计算它们的和并存储到 rec 中
for i in nums1:
for j in nums2:
rec[i + j] += 1 # 如果和已经存在于字典中,则值加1,否则初始化为1
# 遍历 nums3 和 nums4 中的每一个元素组合,计算它们的和的相反数
for i in nums3:
for j in nums4:
# 如果相反数存在于 rec 中,则累加相应的次数到 cnt 中
cnt += rec.get(-(i + j), 0) # 如果相反数不存在,则返回默认值0
# 返回计数器的值,即满足条件的四元组数量
return cnt
383. 赎金信
题目链接:. - 力扣(LeetCode)
文章链接:代码随想录
python:
(版本一)使用数组
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
# 创建两个大小为26的列表,用于记录每个字母出现的次数
# ransom_count记录赎金信中的字母计数
# magazine_count记录杂志中的字母计数
ransom_count = [0] * 26
magazine_count = [0] * 26
# 遍历赎金信中的每个字符,统计每个字母的出现次数
for c in ransomNote:
# 计算字符c在字母表中的位置,并更新对应位置的计数
ransom_count[ord(c) - ord('a')] += 1
ord 函数用于返回一个字符的 Unicode 码点(整数表示)。这个函数接收一个字符(长度为1的字符串)作为参数,并返回它对应的整数值。
# 遍历杂志中的每个字符,统计每个字母的出现次数
for c in magazine:
# 计算字符c在字母表中的位置,并更新对应位置的计数
magazine_count[ord(c) - ord('a')] += 1
# 检查赎金信中每个字母的数量是否都不超过杂志中对应字母的数量
return all(ransom_count[i] <= magazine_count[i] for i in range(26))
(版本二)使用defaultdic
from collections import defaultdict
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
# 使用 defaultdict 创建一个默认值为 0 的字典 hashmap
# hashmap 用于存储杂志中每个字母出现的次数
hashmap = defaultdict(int)
# 遍历杂志中的每个字符,统计每个字母的出现次数
for x in magazine:
hashmap[x] += 1
# 遍历赎金信中的每个字符,检查是否可以在杂志中找到足够的匹配字符
for x in ransomNote:
value = hashmap.get(x)
# 如果字母 x 不在杂志中或杂志中的该字母数量不足,则返回 False
if not value:
return False
else:
# 如果字母 x 在杂志中,减少该字母的计数
hashmap[x] -= 1
# 如果所有字母都可以在杂志中找到足够的匹配字符,则返回 True
return True
(版本三)使用字典
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
# 创建一个字典 counts,用于存储杂志中每个字母出现的次数
counts = {}
# 遍历杂志中的每个字符,统计每个字母的出现次数
for c in magazine:
# 使用 get 方法获取当前字母 c 的计数值,如果不存在则返回 0,然后加 1
counts[c] = counts.get(c, 0) + 1
# 遍历赎金信中的每个字符,检查是否可以在杂志中找到足够的匹配字符
for c in ransomNote:
# 如果字母 c 不在 counts 字典中或 counts[c] 的值为 0
# 则表示杂志中没有足够的字符 c,返回 False
if c not in counts or counts[c] == 0:
return False
# 如果字母 c 在 counts 字典中,且 counts[c] 的值大于 0
# 则将 counts[c] 的值减 1
counts[c] -= 1
# 如果所有字母都可以在杂志中找到足够的匹配字符,则返回 True
return True
(版本四)使用Counter
from collections import Counter
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
# 使用 Counter 创建两个计数字典,分别统计 ransomNote 和 magazine 中每个字符出现的次数
ransom_counter = Counter(ransomNote)
magazine_counter = Counter(magazine)
# 检查 ransom_counter 是否是 magazine_counter 的子集
# 即检查 magazine 中是否有足够的字符来构建 ransomNote
return not ransom_counter - magazine_counter
(版本五)使用count
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
# 使用 all() 函数和生成器表达式来检查 ransomNote 中的每个字符
# 对于每个字符 c,比较 ransomNote 中字符 c 的计数和 magazine 中字符 c 的计数
# 如果 ransomNote 中的所有字符的计数都小于或等于 magazine 中的相应字符的计数,则返回 True
return all(ransomNote.count(c) <= magazine.count(c) for c in set(ransomNote))
(版本六)使用count(简单易懂)
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
# 遍历 ransomNote 中的每个字符
for char in ransomNote:
# 检查该字符是否在 magazine 中,并且其在 ransomNote 中的计数是否小于等于其在 magazine 中的计数
if char in magazine and ransomNote.count(char) <= magazine.count(char):
continue
else:
# 如果上述条件不满足,则返回 False
return False
# 如果所有字符都满足条件,则返回 True
return True
15. 三数之和
题目链接:. - 力扣(LeetCode)
视频链接:梦破碎的地方!| LeetCode:15.三数之和_哔哩哔哩_bilibili
文章链接:代码随想录
python:
(版本一)双指针
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = [] # 存储结果的列表
nums.sort() # 对数组进行排序
# 遍历数组中的每个元素
for i in range(len(nums)):
#一个遍历列表 nums 的循环结构,它会生成从 0 到 len(nums) - 1 的索引。
# 如果当前元素已经大于0,后续元素也都大于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 # 如果和小于0,移动左指针以增加和
elif sum_ > 0:
right -= 1 # 如果和大于0,移动右指针以减少和
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 # 返回所有和为零的三元组
(版本二)使用字典
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort() # 首先对数组进行排序
# 找出a + b + c = 0的三元组
# a = nums[i], b = nums[j], c = -(a + b)
for i in range(len(nums)):
# 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if nums[i] > 0:
break # 因为数组已经排序,后面的数也会大于零
# 三元组元素a去重
if i > 0 and nums[i] == nums[i - 1]:
continue
# 使用字典存储两数之和
d = {}
for j in range(i + 1, len(nums)):
# 三元组元素b去重
if j > i + 2 and nums[j] == nums[j-1] == nums[j-2]:
continue
c = 0 - (nums[i] + nums[j]) # 计算c使得a + b + c = 0
if c in d:
# 找到一组满足条件的三元组
result.append([nums[i], nums[j], c])
d.pop(c) # 三元组元素c去重
else:
d[nums[j]] = j # 记录nums[j]的位置
return result
18. 四数之和
题目链接:. - 力扣(LeetCode)
视频链接:难在去重和剪枝!| LeetCode:18. 四数之和_哔哩哔哩_bilibili
文章链接:代码随想录
python:
(版本一)双指针
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort() # 首先对数组进行排序
n = len(nums)
result = []
# 遍历数组,选定第一个元素
for i in range(n):
# 剪枝优化:如果当前元素大于目标值,并且目标值大于0,则后续元素无法组成四元组
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):
# 剪枝优化:如果当前两个元素之和大于目标值,并且目标值大于0,则后续元素无法组成四元组
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 # 返回所有符合条件的四元组
(版本二)使用字典
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
# 创建一个字典来存储输入列表中每个数字的频率
freq = {}
for num in nums:
freq[num] = freq.get(num, 0) + 1
# 创建一个集合来存储最终答案,并遍历4个数字的所有唯一组合
ans = set()
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
for k in range(j + 1, len(nums)):
# 计算第四个数的值,使得四数之和等于目标值
val = target - (nums[i] + nums[j] + nums[k])
if val in freq:
# 确保没有重复,计算val在当前组合中的出现次数
count = (nums[i] == val) + (nums[j] == val) + (nums[k] == val)
if freq[val] > count:
# 将满足条件的四元组加入集合,并进行排序以避免重复
ans.add(tuple(sorted([nums[i], nums[j], nums[k], val])))
# 将集合中的四元组转换为列表并返回
return [list(x) for x in ans]