当前位置: 首页 > article >正文

代码随想录刷题题Day6

刷题的第六天,希望自己能够不断坚持下去,迎来蜕变。😀😀😀
刷题语言:C++ / Python
哈希表理论基础见代码随想录刷题题Day5
Day6 任务
● 454.四数相加II
● 383. 赎金信
● 15. 三数之和
● 18. 四数之和
● 总结

1 四数相加II

在这里插入图片描述

本题使用map解决问题,体会使用哈希法如何提高程序执行效率,降低时间复杂度,当然使用哈希法会提高空间复杂度,但一般来说就是舍空间换时间,工业开发也是这样

当我们遇到要快速判断一个元素是否出现在集合里,就要考虑哈希法
伪代码:

(1)定义一个unordered_map,key存放nums1和nums2里面的元素之和,value存放nums1和nums2里面的元素之和出现的次数
(2)遍历nums1和nums2数组,统计key和value
(3)定义变量count,统计 n u m s 1 [ i ] + n u m s 2 [ j ] + n u m s 3 [ k ] + n u m s 4 [ l ] = 0 nums1[i]+nums2[j]+nums3[k]+nums4[l] = 0 nums1[i]+nums2[j]+nums3[k]+nums4[l]=0出现的次数
(4)遍历nums3和nums4数组,如果 0 − ( n u m s 3 [ k ] + n u m s 4 [ l ] ) 0-(nums3[k] + nums4[l]) 0(nums3[k]+nums4[l])在map出现过,就用count把map中key对应的value统计出来
(5)返回统计值count

C++:

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
		unordered_map<int, int> map;
		for (auto a : nums1)
			for (auto b : nums2)
			{
				map[a+b]++;
			}
		int count = 0;// 统计a+b+c+d = 0 出现的次数
		for (auto c : nums3)
			for (auto d : nums4)
			{
				int target = 0 - (c + d);
				if (map.find(target) != map.end())
				{
					count += map[target];
				}
			}
		return count;
    }
};

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( n 2 ) O(n^2) O(n2)
Python:

class Solution(object):
    def fourSumCount(self, nums1, nums2, nums3, nums4):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :type nums3: List[int]
        :type nums4: List[int]
        :rtype: int
        """
        # 使用字典存储nums1和nums2中的元素及其和
        hashmap = dict()
        for a in nums1:
            for b in nums2:
                if a + b in hashmap:
                    hashmap[a+b] += 1
                else:
                    hashmap[a+b] = 1
        # 如果 -(a+b) 存在于nums3和nums4, 存入结果
        count = 0
        for c in nums3:
            for d in nums4:
                target = 0 - (c + d)
                if target in hashmap:
                    count += hashmap[target]
        return count

2 赎金信

在这里插入图片描述
本题和有效的字母异位词是一个思路

有效的字母异位词:求字符串a和字符串b是否可以相互组成,这道题目求字符串a能否组成字符串b,而不用管字符串b能否组成a
本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成

(1)杂志字符串中的每个字符只能在赎金信字符串中使用一次
(2)两个字符串均只含有小写字母:可以用数组做哈希法
暴力法
C++:

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        for (int i = 0; i < magazine.size(); i++)
        {
            for (int j = 0; j < ransomNote.size(); j++)
            {
                // 在ransomNote中找到和magazine相同的字符
                if (magazine[i] == ransomNote[j])
                {
                    ransomNote.erase(ransomNote.begin() + j); // ransomNote删除这个字符
                    break;
                }
            }
        }
        // 如果ransomNote为空,则说明magazine的字符可以组成ransomNote
        if (ransomNote.length() == 0) return true;
        return false;
    }
};

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)
哈希解法
本题使用map的空间消耗要比数组大一些,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效
伪代码:

(1)定义hash[26],记录每个字符出现的次数
(2)如果ransomNote的字符数大于magazine的字符数,return false
(3)记录 magazine里各个字符出现次数
(4)遍历ransomNote,在hash里对应的字符个数做–操作,如果小于零说明ransomNote里出现的字符,magazine没有,return false
(5)遍历完,返回true

C++:

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int hash[26] = {0};
        if (ransomNote.size() > magazine.size()) return false;
        // 记录 magazine里各个字符出现次数
        for (auto a : magazine)
        {
        	hash[a - 'a']++;
        }
        // 遍历ransomNote,在hash里对应的字符个数做--操作
        for (auto b : ransomNote)
        {
        	hash[b - 'a']--;
        	// 如果小于零说明ransomNote里出现的字符,magazine没有
        	if (hash[b - 'a'] < 0) return false;
        }
        return true;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
Python:

class Solution(object):
    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        count = [0] * 26
        for c in magazine:
            count[ord(c) - ord('a')] += 1
        for c in ransomNote:
            count[ord(c) - ord('a')] -= 1
            if count[ord(c) - ord('a')] < 0:
                return False
        return True

3 三数之和

在这里插入图片描述
本题虽然和两数之和很像,也能用哈希法,但是用哈希法会很麻烦,双指针法才是正解

双指针
本道题目用哈希法不合适,因为去重有很多细节需要注意,面试中很难做到bug free
在这里插入图片描述
伪代码:

(1)先将数组nums排序
(2)for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]
(3)如果 n u m s [ i ] > 0 nums[i]>0 nums[i]>0,返回。对a做去重处理,如果i>0&&a等于a的前一个数,continue
(4)移动left和right,把left < right作为循环结束条件,不能等于是因为不能将两个指针指向同一个数
(5)如果三数之和>0,right下标向左移动
(6)如果三数之和<0,left下标向右移动
(7)如果三数之和=0,把结果存到result,对left和right做去重操作,如果right下标等于right下标的前一个数,right–;如果left下标等于left下标的后一个数,left++
(8)找到答案时,双指针同时收缩

处理left和right的去重细节
这里也类似前面的i,要和之前已经处理过的数据比较
这里同时要保证left是小于right的,不然容易越界,也没必要做任何操作了
违反这个会直接退出大的while(left<right)循环
这里要用while处理,因为可能有连续的好几个

C++:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
		vector<vector<int>> result;
		sort(nums.begin(), nums.end());
		// 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
		for (int i = 0; i < nums.size(); i++)
		{
			// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果
			if (nums[i] > 0) return result;
			// 正确去重a方法
			if (i > 0 && nums[i] == nums[i - 1]) continue;
			int left = i + 1;
			int right = nums.size() - 1;
			while (left < right)
			{
				if (nums[i] + nums[left] + nums[right] > 0) right--;
				else if (nums[i] + nums[left] + nums[right] < 0) left++;
				else 
				{
					result.push_back(vector<int>{nums[i], nums[left], nums[right]});
					// 去重逻辑应该放在找到一个三元组之后,对b和c去重
					while (left < right && nums[right] == nums[right - 1]) right--;
					while (left < right && nums[left] == nums[left + 1]) left++;
					// 找到答案时,双指针同时收缩
					left++;
					right--;
				}
			}
		}
		return result;
    }
};

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)
Python:

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()
        for i in range(len(nums)):
            # 如果第一个元素已经大于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 left < right:
                if nums[i] + nums[left] + nums[right] > 0:
                    right -= 1
                elif nums[i] + nums[left] + nums[right] < 0:
                    left += 1
                else:
                    result.append([nums[i], nums[left], nums[right]])
                    # 跳过相同的元素以避免重复
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1
                    while left < right and nums[left] == nums[left + 1]:
                        left += 1
                    right -= 1
                    left += 1
        return result

哈希法

三数之和去重是怎么保证a的去重,而没有把合适的b也去掉了呢?
b在a后面,当a的数字确定,后面b+c的值也确定,比如序列-2,-2,4,8,16,第一次遇到某一个a=-2的时候已经把后面所有b+c=2的情况跑完了,第二次再次遇到a=-2的时候已经没有跑的意义了,更遑论有没有去掉合适的b,而且有可能第一次遇到a=-2的时候,可能正好有b=-2,c=4这种情况,但你第二次遇到a=-2,再去跑,就会发现甚至这种情况还会被漏掉。nums[i] == nums[i-1]基本就是应对这两种情况
对于哈希法,b和c去重的逻辑
对于 b 的去重,一般可以和 a 一样检查当前的 b 是否和前一个 b 相同,如果相同,则跳过当前的 b。这样可以保证每个 b 只被使用一次。但是这种方法有一个问题,就是如果数组中有连续三个或以上相同的元素,那么第一个和第二个元素都会被跳过,导致漏掉一些可能的解。例如,如果数组中有三个0,那么[0,0,0]就是一个有效的解,但是用这种方法就会被忽略。
改进一下条件,只有当当前的b和前两个b都相同时才跳过当前的 b。这样可以保证至少有一个 b 被使用,并且不会出现重复对于 c 的去重,利用哈希集合的特性,在找到一个 c 后将其从哈希集合中删除。这样可以保证每个 c 只被使用一次且不会出现重复。

C++:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[j], c = -(a + b)
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
                continue;
            }
            unordered_set<int> set;
            for (int j = i + 1; j < nums.size(); j++) {
                if (j > i + 2
                        && nums[j] == nums[j-1]
                        && nums[j-1] == nums[j-2]) { // 三元组元素b去重
                    continue;
                }
                int c = 0 - (nums[i] + nums[j]);
                if (set.find(c) != set.end()) {
                    result.push_back({nums[i], nums[j], c});
                    set.erase(c);// 三元组元素c去重
                } else {
                    set.insert(nums[j]);
                }
            }
        }
        return result;
    }
};

4 四数之和

在这里插入图片描述
思路:和前面的三数之和思路很像,都是用双指针的方法来解决。

三数之和的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是 O ( n 2 ) O(n^2) O(n2),四数之和的时间复杂度是 O ( n 3 ) O(n^3) O(n3)
五数之和、六数之和等等都采用这种解法

四数相加II 相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复,而四数相加II是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况

C++:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for (int k = 0; k < nums.size(); k++)
        {
        	// 剪枝
        	if (nums[k] > target && nums[k] > 0 && target > 0) break;
        	// 对nums[k]去重
        	if (k > 0 && nums[k] == nums[k - 1]) continue;
        	for (int i = k + 1; i < nums.size(); i++)
        	{
        	    // 2级剪枝
        		if (nums[k] + nums[i] > target && nums[k] + nums[i] > 0 && target > 0) break;
        		// 对nums[i]去重
        		if (i > k + 1 && nums[i] == nums[i - 1])continue;
        		int left = i + 1;
        		int right = nums.size() - 1;
        		while (left < right)
        		{
        		    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
        			if ((long)nums[k] + nums[i] + nums[left] + nums[right] > target) right--;
        			else if ((long)nums[k] + nums[i] + nums[left] + nums[right] < target) left++;
        			else
        			{
        				result.push_back({nums[k], nums[i], nums[left], nums[right]});
        				// 对nums[left]和nums[right]去重
        				while (left < right && nums[left] == nums[left + 1]) left++;
        				while (left < right && nums[right] == nums[right - 1]) right--;
        				// 找到答案时,双指针同时收缩
        				left++;
        				right--;
        			}
        		}
        	}
        }
        return result;
    }
};

时间复杂度: O ( n 3 ) O(n^3) O(n3)
空间复杂度: O ( 1 ) O(1) O(1)
Python:

class Solution(object):
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        nums.sort()
        n = len(nums)
        result = []
        for i in range(n):
            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):
                if nums[i] + nums[j] > target and nums[i] + nums[j] > 0 and target > 0: #剪枝
                    break
                if j > i + 1 and nums[j] == nums[j-1]: # 去重
                    continue
                left = j + 1
                right = n - 1
                while left < right:
                    s = nums[i] + nums[j] + nums[left] + nums[right]
                    if s < target:
                        left += 1
                    elif s > target:
                        right -= 1
                    else:
                        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
        return result

鼓励坚持七天的自己😀😀😀


http://www.kler.cn/a/158348.html

相关文章:

  • 01_阿里云_Xshell连接服务器
  • Python之元祖(tuple)基础知识点
  • 使用DGL实现GAT(并在6个节点的2分类图中进行简单应用)
  • 基于vue+node.js智慧校园学生办证系统
  • uniapp快速入门大纲,带你入门并具备开发基本应用的能力
  • 博客访问量到达2万了!
  • 【Trino权威指南(第二版)】Trino介绍:trino解决大数带来的问题
  • 文心一言 VS 讯飞星火 VS chatgpt (150)-- 算法导论12.2 6题
  • struct queue_limits结构体参数学习
  • 华为无线配置模板 一
  • Chrome清除特定网站的Cookie,从而让网址能正常运行(例如GPT)
  • Node包管理工具 - nvm、npm、yarn、cnpm、pnpm
  • centos 7.9 二进制部署 kubernetes v1.27.7
  • 第二节JavaScript 语法、语句、注释、变量、数据类型等
  • new Promise用法
  • ES-环境安装(elasticsearch:7.17.9,kibana,elasticsearch-head)
  • Python小案例:打印三角形
  • 一篇吃透大厂面试题,2024找工作一帆风顺。
  • 训练lora小模型
  • 什么是多域名SSL证书?