Day07

Day07 代码随想录刷题

知识点:哈希表、指针

一、LeetCode454. 四数相加II

1.解题思路

考点为哈希表字典的用法

根据题意,提供四个有序列表,求从各列表中取出一个数的和为0,问有几种元组取法。因为元组tuple类似于只读列表list,所以不会出现重复,不需要去重。

2.代码实现

2.1 方法一:普通字典

class Solution:
    #解法一:普通字典
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        #新建字典
        hashDict = {}
        #遍历前2个列表各元素求和(key)与各种和的取法(value)存入字典
        for x1 in nums1:
            for x2 in nums2:
                hashDict[x1 + x2] = hashDict.get(x1 + x2, 0) + 1
        #遍历后2个列表个元素求和后的互补temp是否在字典key里,是的话,对其取法value进行计数
        count = 0
        for x3 in nums3:
            for x4 in nums4:
                temp = 0 - (x3 + x4)
                if temp in hashDict:
                    count += hashDict[temp]
        return count

2.2 方法二:defaultdict字典

利用hashDict.get(-(i + j), 0) 直接取出字典的value,一步到位,因为如果不存在value,就取到0,不影响计数结果

class Solution:
    #解法二:default字典
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        #新建字典
        hashDict = defaultdict(int)
        for i in nums1:
            for j in nums2:
                hashDict[i + j] += 1
        count = 0
        for i in nums3:
            for j in nums4:
                count += hashDict.get(-(i + j), 0) 
        return count

两种解法逻辑一样,都执行了两次两层嵌套的循环,所以时间复杂度O(n^2);额外使用了一个哈希字典来保存前两个列表的元素求和以及对应的取法次数。字典的大小取决于列表1和列表2中元素求和的唯一值的数量,最坏情况下可以达到O(n^2)。所以,解法的空间复杂度O(n^2)

二、LeetCode383.赎金信

1.解题思路

本题很类似之前学的LeetCode242.有效的字母异位词,用一个列表listmagazine的元素,再遍历ransomNote去消除容器中的元素,如果出现没得消则返回False

2.代码实现

2.1 方法一:哈希列表

class Solution:
    #解法一:列表
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        hashList = [0]*26
        for x in magazine:
            hashList[ord(x) - ord('a')] += 1
        for y in ransomNote:
            hashList[ord(y) - ord('a')] -= 1
        for i in range(26):
            if hashList[i] < 0:return False
        return True

2.2 方法二:defaultdict哈希字典

利用字典先装magazine的全部元素和个数,再遍历ransomNote,将其中出现的元素在字典中消去,若字典里没有该元素可消则返回False

class Solution:
    #解法二:defaultdict
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        hashDict = defaultdict(int)
        for x in magazine:
            hashDict[x] += 1
        for x in ransomNote:
            temp = hashDict.get(x)
            if not temp:
                return False
            else:
                hashDict[x] -= 1
        return True

方法一和方法二一样,两个字符串分别都遍历一次,且创建了一个字典容器,所以时间复杂度空间复杂度都是O(n)

2.3 方法三:Counter

再次加强记忆,Counter是将可哈希的容器元素变成一个字典,返回值是字典。
字典的相减

1.减数和被减数存在同一个key,则对应的value相减,如果差值为正值(> 0),则结果里保留该key,且对应value为差值;如果差值 <= 0,则结果不保留该key

2.如果减数内没有被减数中的某个key,则结果中被减数的这个key和对应的value依旧保留。

3.如果减数内存在被减数内不存在的key,即被减数没有key去减减数的key,结果依旧不会出现这个key和对应的value

所以说,针对本题我们只关注ransomNote里的全部元素是否都存在于magazine,当字典ransomNote减字典magazine时,只会出现:

1.ransomNote中有,magazine中也有,但是magazine中的多,则差值为负数,取not后为True

2.ransomNote中有,magazine中也有,但是magazine中的少,则差值为正数,取not后为False

3.ransomNote中有,magazine中也有,两者一样多,则差值为0,取not后为True

4.ransomNote中有,magazine中没有,对应的key的差值为存在,取not后为False

class Solution:
    #解法三:Counter
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        from collections import Counter
        return not Counter(ransomNote) - Counter(magazine) 

Counter构建字典本身就需要时间复杂度O(n),最坏情况下空间复杂度也是O(n)

2.4 方法四:count计数法

对于ransomNote中的每个元素依次在两个列表中计数,如果ransomNote中的数量 <= magazine中的数量,则为True

class Solution:
    #解法四:count计数法
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        return all(ransomNote.count(c) <= magazine.count(c) for c in set(ransomNote))

遍历一个set并且每一步都通过count来遍历列表,所以该解答时间复杂度O(n^2);因为创建了一个set存储ransomNote空间复杂度O(n)

三、LeetCode15.三数之和

1.解题思路

提供一个列表,求三数之和为0的元素不重复取法结果。当列表元素升序后,我们只需要先确定好一个值num[i],那么另外两个值就可以通过leftright两个指针在剩余列表部分里向中间逼近找到答案。列表升序,且第一个值若重复则跳过,这一操作可实现避免重复

2.代码实现

2.1 方法:字典+双指针

class Solution:
    #解法:字典+双指针
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        #升序操作,无返回值,确保了最后的元组不会因为顺序不同而重复
        nums.sort()
        #创建字典
        resDict = {}
        #开始遍历列表
        for i in range(len(nums)):
            #最小元素已经超过结果
            if nums[i] > 0:
                break
            #元素重复直接跳过
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            #双指针
            left = i + 1
            right = len(nums) - 1
            while left < right:
                temp = nums[i] + nums[left] + nums[right]
                if temp < 0: left += 1
                elif temp > 0: right -= 1
                else:
                    #符合要求的元素值拼成元组后存入字典
                    key = (nums[i], nums[left], nums[right])
                    resDict[key] = resDict.get(key,0) + 1
                    #指针向中间逼近
                    left += 1
                    right -= 1
        #将字典key全部取出组装成lsit返回
        return list(resDict.keys())

列表排序时间复杂度O(nlogn),然后开始循环遍历,外层循环需要遍历 n 次,内层循环使用双指针,左指针从 i+1 开始,右指针从末尾开始,需要遍历 n 次。
综上所述,该算法的时间复杂度O(nlogn + n^2) = O(n^2)

结果字典 resDict 最多刚好全部储存,所以算法的空间复杂度O(n)

四、LeetCode18.四数之和

1.解题思路

和上一题类似,但是我们需要双重循环来确定两个值,最后再用双指针找到解。不同之处在于本题的target不再是0,对于target为负数的情况,需要特殊处理。

2.代码实现

2.1方法:字典+双指针

class Solution:
    #解法:双指针+字典
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        #升序操作,创建字典
        nums.sort()
        resDict = {}
        #双层遍历,注意去重操作
        for i in range(len(nums)):
            #索引i去重
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i + 1, len(nums)):
                if nums[i] + nums[j] > target and target > 0:
                    break
                #索引j去重
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                
                left = j + 1
                right = len(nums) - 1
                while left < right:
                    temp = nums[i] + nums[j] + nums[left] + nums[right]
                    if temp < target:
                        left += 1
                    elif temp > target:
                        right -= 1
                    else:
                        #升序操作使得相同的解其元素摆放位置一定相同,利用字典避免了list.append的相同元素重复添加
                        resDict[(nums[i], nums[j], nums[left], nums[right])] = True
                        left += 1
                        right -= 1
        return list(resDict.keys())

时间复杂度O(n^3)空间复杂度 O(n)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值