[代码随想录算法训练营 Day07 哈希表 Part 2]

Day 07

哈希表

5. 四数相加2(力扣454)

  1. 题目描述:四数相加2
  2. 思路:四层遍历肯定超时,试了一下三层遍历也超时,所以最多只能遍历两次。那就把nums1 nums2可能产生的和记录在一个字典里,注意因为nums1,nums2可能有重复的数字,所以产生一个和的可能性有多种,因此要用字典的键记录可能得和,用字典的值记录和出现的次数。再遍历nums3,nums4,如果这连个数的和的相反数出现在字典里,就把count加上字典的值那么多个可能。
  3. python语法细节:
  • 字典的get用法dic.get(key,deafault_value)get方法可以实现,在字典中寻找某个键,如果存在返回这个键对应的值,如果不存在返回默认值。
  1. python实现:
class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        ## 三层暴力循环超时
        # count = 0
        # dic4 = {}
        # for i in range(len(nums4)):
        #     if nums4[i] not in dic4:
        #         dic4[nums4[i]] = 1
        #     else:
        #         dic4[nums4[i]] +=1
        # print(dic4)
        # for i in nums1:
        #     for j in nums2:
        #         for k in nums3:
        #             if (-i-j-k) in dic4:
        #                 count = count + dic4[-i-j-k]
        # return count

        ### 两层循环  我的写法
        # dic1 = {}
        # for i in range(len(nums1)):
        #     for j in range(len(nums2)):
        #         if nums1[i]+nums2[j] not in dic1:
        #             dic1[nums1[i]+nums2[j]] = 1
        #         else:
        #             dic1[nums1[i]+nums2[j]] += 1
        # dic2 = {}
        # for i in range(len(nums3)):
        #     for j in range(len(nums4)):
        #         if nums3[i]+nums4[j] not in dic2:
        #             dic2[nums3[i]+nums4[j]] = 1
        #         else:
        #             dic2[nums3[i]+nums4[j]] += 1
        # print(dic1,dic2)
        # count = 0
        # for key in dic1:
            
        #     if -key in dic2:
                
        #         count += dic1[key] *dic2[-key]
        # return count

        ## 两层循环,用get,只建立一个dic
        dic = {}
        for num1 in nums1:
            for num2 in nums2:
                dic[num1+num2] = dic.get(num1+num2,0)+1
        count = 0
        for num3 in nums3:
            for num4 in nums4:
                if -(num3+num4) in dic:
                    count = count + dic[-(num3+num4)]
        return count
                    
  1. C++语法细节
  • C++中unordered_map用法
  • 创建std::unordered_map<KeyType, ValueType> my_map;
    在这里插入图片描述
  • 插入元素my_map[key]=value;
  • my_map[key]++这种插入方式可以实现当key不存在时,添加一个元素,值设置为0.
  • 查找元素std::string value = my_map[key];
  • 删除元素my_map.erase(key)
  • 判断元素是否存在if (my_map.find(key) != my_map.end()
  • 获取集合大小:int size = my_map.size();
  1. C++实现
#include <unordered_map>
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        std::unordered_map<int,int> umap;
        for (int num1:nums1){
            for(int num2:nums2){
                if (umap.find(num1+num2)==umap.end()) {
                    umap[num1+num2]=1;
                }else{
                    umap[num1+num2] ++;
                }
            }
        }
        int count = 0;
        for (int num3:nums3){
            for(int num4:nums4){
                int sum_ = -(num3+num4);
                if (umap.find(sum_)!=umap.end()) {
                    count += umap[sum_];
                }
            }
        }
        return count;


    }
};

6. 赎金信(力扣383)

  1. 题目描述:赎金信
  2. 思路:把杂志中出现的字母和每个字母出现的字数用字典统计下,再遍历赎金信,用过的字母要减1.
  3. python语法细节:
  4. python实现:
class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        dic={}
        for m in magazine:
            dic[m] = dic.get(m,0)+1
        print(dic)
        for r in ransomNote:
            if dic.get(r,0)>=1:
                dic[r] -=1
            else:
                return False
        return True
  1. C++语法细节
  • 对字符串进行for each遍历for(char ch:my_string)
  • 构造一个键位字符 值为整数的字典unordered_map<char,int> umap;
  1. C++实现
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        unordered_map<char,int> umap;
        for(char ch:magazine){
            umap[ch]++;
        }
        for(char ch:ransomNote){
            if(umap[ch]>=1){
                umap[ch]--;
            }
            else{
                return false;
            }
        }
        return true;

    }
};

7. 三数之和(力扣15)

  1. 题目描述:三数之和
  2. 思路:这题很难。第一个想法是,遍历两轮,然后剩下的用哈希寻找,但是由于数组有重复,只能用键记录位置,值记录数值。但是这样寻找的会后需要再字典里找值而不是找键,还是要遍历字典。所以这个题需要用双指针。先对数组进行排序,外层依旧是遍历,内层用两个指针分别指向开头和结尾。由于需要对结果去重,所以想到把结果用set去一下重,但是超时了,所以还是要考虑剪枝去重。
  • 这题怎么剪枝!
    最好自己设计一个重复比较高的测试用例,不然就是一遍一遍的跑,一遍一遍的剪枝,痛苦面具
  • 外层剪枝:当外层指针遇到一个刚刚已经遇到一遍的元素,那就continue跳过!但是这里需要有i-1,所以要把其实元素排除
  • left剪枝:和外层剪枝类似
  • right剪枝:当right指针遇到一个已经遇到过一次的元素,那就continue跳过。但是这里需要有i+1,所以要把末尾元素排除
  1. python语法细节:
  • 在while中移动指针后不做操作,要continue让它去while处判断条件,不然就往下继续执行了
  1. python实现:
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ### 一次痛苦的尝试
        # dic={}
        # for num in nums:
        #     dic[num]=dic.get(num,0)+1
        # for key in dic:
        #     if dic[key]>3:
        #         dic[key]=3
        # numss = []
        
        # for key in dic:
        #     count = dic[key]
        #     for i in range(count):
        #         numss.append(key)
        # print(numss)
        # numss.sort()
        # l = len(numss)
        # res = []
        # for i in range(l):
            
        #     if numss[i]>0:
        #         return res
        #     if i!=0 and numss[i] == numss[i-1]:
        #         # print(f'外循环重复{i}')
        #         continue
        #     left = i+1
        #     right = l-1
        #     while left < right:
        #         # print(f'遍历到了什么{i,left,right}')
        #         if(left!= i+1 and numss[left]==numss[left-1]):
        #             left +=1
        #             continue
        #         if (right!=l-1 and numss[right] == numss[right+1]):
        #             right -=1
        #             continue
        #         if numss[i]+numss[left]+numss[right]==0:
        #             # print(i,left,right)
        #             res.append([numss[i],numss[left],numss[right]])
        #             left += 1
        #         elif numss[i]+numss[left]+numss[right]<0:
        #             left += 1
        #         else:
        #             right -=1
        # return res
        ### 删减以下前面去重的部分
        nums.sort()
        l = len(nums)
        res = []
        for i in range(l):
            
            if nums[i]>0:
                return res
            if i!=0 and nums[i] == nums[i-1]:
                # print(f'外循环重复{i}')
                continue
            left = i+1
            right = l-1
            while left < right:
                # print(f'遍历到了什么{i,left,right}')
                if(left!= i+1 and nums[left]==nums[left-1]):
                    left +=1
                    continue
                if (right!=l-1 and nums[right] == nums[right+1]):
                    right -=1
                    continue
                if nums[i]+nums[left]+nums[right]==0:
                    # print(i,left,right)
                    res.append([nums[i],nums[left],nums[right]])
                    left += 1
                elif nums[i]+nums[left]+nums[right]<0:
                    left += 1
                else:
                    right -=1
        return res
  1. C++语法细节
  • C++中没有elif 要写if …else if …
  • C++中嵌套列表额实现vector<vector<int>> res;
  • 向嵌套列表中增加元素 res.push_back({...}
  • C++中对列表排序sort(nums.begin(),nums.end())
  1. C++实现
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        for(int i=0;i<nums.size();i++){
            if (nums[i]>0){
                return res;
            }
            if(i!=0 and nums[i]==nums[i-1]){
                continue;
            }
            int left = i+1;
            int right = nums.size()-1;
            while (left<right){
                if (left!=i+1 and nums[left]==nums[left-1]){
                    left++;
                    continue;
                }
                if (right!=nums.size()-1 and nums[right]==nums[right+1]){
                    right--;
                    continue;
                }
                int sum_ = nums[i]+nums[left]+nums[right];
                if (sum_==0){
                    res.push_back({nums[i],nums[left],nums[right]});
                    left++;
                }
                else if(sum_<0){
                    left++;
                }
                else{
                    right--;
                }
            }

        }
        return res;
        
    }
};

8. 四数之和(力扣18)

  1. 题目描述:四数之和
  2. 思路:有了上一道三数之和,这道题就好想一些了。就外圈多加一次循环遍历连个数,内圈使用两个指针实现一次遍历两个数字。也就是说,使用双指针可以减少一次遍历,三次变两次,四次变三次,懂伐~
  • 这里的剪枝有一丢丢变化,不再是第一个数大于0就跳过,而是第一个数大于target的1/4就跳过,因为数组是顺序的,第一个数应该最小,最多占目标值的1/4。
  1. python语法细节:
  2. python实现:
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        result = []
        l = len(nums)
        nums.sort()
        for i in range(l):
            if nums[i] >target/4:
                return result
            if i!=0 and nums[i]==nums[i-1]:
                continue
            for j in range(i+1,l):
                if j!=i+1 and nums[j]==nums[j-1]:
                    continue
                left = j+1
                right = l-1
                while left<right:
                    # print(f'遍历到{i,j,left,right}')
                    if left!= j+1 and nums[left]==nums[left-1]:
                        left+=1
                        continue
                    if right!= l-1 and nums[right]==nums[right+1]:
                        right-=1
                        continue
                    sum = nums[i]+nums[j]+nums[left]+nums[right]
                    if sum==target:
                        result.append([nums[i],nums[j],nums[left],nums[right]])
                        left +=1
                    elif sum<target:
                        left +=1
                    else:
                        right -= 1
        return result
  1. C++语法细节
  • 这里有一个测试用例[0,0,0,1000000000,1000000000,1000000000,1000000000]
  • C风格的数据类型转换(type) expression
  • C++中数据类型转换int num = 123; long long_num = num;
  1. C++实现
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        int l = nums.size();
        for(int i = 0;i<l-1;i++){
            if(nums[i]>target/4){
                return res;
            }
            if(i!=0 and nums[i]==nums[i-1]){
                continue;
            }
            for(int j=i+1;j<l-1;j++){
                if(j!=i+1 and nums[j]==nums[j-1]){
                    continue;
                }
                int left = j+1;
                int right = l-1;
                while (left<right){
                    // cout << i << ", " << j << ", " << left << ", " << right << endl;
                    long sum_ = (long)nums[i]+nums[j]+nums[left]+nums[right];
                    if (left!=j+1 and nums[left]==nums[left-1]){
                        left++;
                        continue;
                    }
                    if (right!=l-1 and nums[right]==nums[right+1]){
                        right--;
                        continue;
                    }
                    if (sum_ == target){
                        res.push_back({nums[i],nums[j],nums[left],nums[right]});
                        left++;
                        right--;
                    }
                    else if(sum_<target){
                        left++;
                    }
                    else{
                        right--;
                    }
                }

            }
        }
        return res;

    }
};
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值