代码随想录算法训练营第七天 | 454. 四数相加 II、383. 赎金信、15. 三数之和 、18. 四数之和、哈希表总结

112 阅读3分钟

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和数组