454. 四数相加 II
用dict记录下一个a+b的和出现的次数(组合数)
- 和两数之和很像,只不过把4个数两两组合了
# Time complexity: O(N^2)
# Space complexity: O(N^2)
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
sum_count = {}
for num_1 in nums1:
for num_2 in nums2:
sum_count[num_1+num_2] = sum_count.get(num_1+num_2, 0) + 1
res = 0
for num_3 in nums3:
for num_4 in nums4:
remain = 0 - (num_3 + num_4)
if remain in sum_count:
res += sum_count[remain]
return res
383. 赎金信
用dict存储每个char出现的数量
# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
count_dict = Counter(ransomNote)
for s in magazine:
if s in count_dict:
count_dict[s] -= 1
return all(x <= 0 for x in count_dict.values())
使用数组存储每个char出现的数量
- 因为只含有小写英文字母
# Time complexity: O(N)
# Space complexity: O(|Z|) Z为小写英文字母集
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
count = [0] * 26
for s in magazine:
count[ord(s)-ord('a')] += 1
for s in ransomNote:
count[ord(s)-ord('a')] -= 1
if count[ord(s)-ord('a')] < 0:
return False
return True
使用count_dicti相减的性质
# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
c1 = collections.Counter(ransomNote)
c2 = collections.Counter(magazine)
x = c1 - c2
#x只保留值大于0的符号,当c1里面的符号个数小于c2时,不会被保留
#所以x只保留下了,magazine不能表达的
if(len(x)==0):
return True
else:
return False
15. 三数之和
排序+去重+二数之和
- 先排序,再从头遍历数组
- 如果数字大于0,则剩下的数无法完成sum=0,直接break。
- 如果数字在之前出现过,我们跳过(因为不能重复组合)。
- 剩下的情况,我们对i+1以及之后的数组做twoSum的操作,找到所有的组合
# Time complexity: O(N^2)
# Space complexity: O(N)
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
sort_nums = sorted(nums)
res = []
for i in range(len(nums)):
if sort_nums[i] > 0:
break
elif i == 0 or sort_nums[i] != sort_nums[i-1]:
self.twoSum(sort_nums, res, i)
return res
def twoSum(self, sort_nums, res, i):
seen = set()
j = i + 1
while j < len(sort_nums):
remain = 0 - sort_nums[i] - sort_nums[j]
if remain in seen:
res.append([sort_nums[i], sort_nums[j], remain])
while j + 1 < len(sort_nums) and sort_nums[j] == sort_nums[j + 1]:
j += 1
seen.add(sort_nums[j])
j += 1
双指针
- 一样的思路,只是用双指针来遍历i+1之后的数组
# Time complexity: O(N^2)
# Space complexity: O(N)/ O(logN) for sorting
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
sort_nums = sorted(nums)
res = []
for i in range(len(nums)):
if sort_nums[i] > 0:
break
elif i == 0 or sort_nums[i] != sort_nums[i-1]:
left = i+1
right = len(sort_nums)-1
while left < right:
if sort_nums[left] + sort_nums[right] + sort_nums[i] > 0:
right -= 1
elif sort_nums[left] + sort_nums[right] + sort_nums[i] < 0:
left += 1
else:
res.append(
[sort_nums[left], sort_nums[right], sort_nums[i]])
left += 1
right -= 1
while left < right and sort_nums[left] == sort_nums[left-1]:
left += 1
return res
18. 四数之和
两层循环确定两个数+剩下的数用twoSum确定(哈希表)
# Time complexity: O(N^3)
# Space complexity: O(N)
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums = sorted(nums)
res = []
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1]:
continue
for j in range(i+1, len(nums)):
if j > i+1 and nums[j] == nums[j-1]:
continue
self.twoSum(nums, i, j, target, res)
return res
def twoSum(self, sort_nums, i, j, target, res):
seen = set()
k = j + 1
while k < len(sort_nums):
remain = target - sort_nums[i] - sort_nums[j] - sort_nums[k]
if remain in seen:
res.append([sort_nums[i], sort_nums[j], sort_nums[k], remain])
while k + 1 < len(sort_nums) and sort_nums[k] == sort_nums[k + 1]:
k += 1
seen.add(sort_nums[k])
k += 1
两层循环确定两个数+剩下的数用twoSum确定(双指针)
# Time complexity: O(N^3)
# Space complexity: O(N)/ O(logN) for sorting
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums = sorted(nums)
res = []
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1]:
continue
for j in range(i+1,len(nums)):
if j > i+1 and nums[j] == nums[j-1]:
continue
self.twoSum(nums, i, j, target, res)
return res
def twoSum(self, sort_nums, i, j , target, res):
left = j+1
right = len(sort_nums)-1
while left < right:
if sort_nums[left] + sort_nums[right] + sort_nums[i] + sort_nums[j] > target:
right -= 1
elif sort_nums[left] + sort_nums[right] + sort_nums[i] + sort_nums[j] < target:
left += 1
else:
res.append([sort_nums[left], sort_nums[right], sort_nums[i],sort_nums[j]])
left += 1
right -= 1
while left < right and sort_nums[left] == sort_nums[left-1]:
left += 1
哈希表总结
数组作为哈希表
-
当出现的字符是固定大小的集合,如小写英文字母集
-
我们可以用数组的下标来记录出现的字符,数组的值来记录我们需要记录的对应字符需要的值。
arr[ord(chr) - ord('a')] = val
set作为哈希表
-
使用数组的局限
- 数组的大小是有限的,受到系统栈空间(不是数据结构的栈)的限制。
- 如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
-
对于需要去重的数据,我们也会使用set
map作为哈希表
-
使用数组和set来做哈希法的局限。
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
-
虽然map是万能的,但因为占用空间大小的缘故,我们有时会使用set和数组