代码随心录第七天|Leecode 454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和
Leecode 454.四数相加II
题目描述:https://leetcode.cn/problems/4sum-ii/description/
思路:不需要去重。
1.首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。
2.遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
3.定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
4.再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
5.最后返回统计值 count 就可以了
假设 A = [1, 2, 3] , B = [-1, -2, -3] , C = [4, 5] , D = [-4, -5] 。
统计 A 和 B 数组元素和的出现次数:
- 当 a = 1 , b = -1 时, a + b = 0 , umap[0] = 1 。
- 当 a = 1 , b = -2 时, a + b = -1 , umap[-1] = 1 。
- 当 a = 1 , b = -3 时, a + b = -2 , umap[-2] = 1 。
- 当 a = 2 , b = -1 时, a + b = 1 , umap[1] = 1 。
- 当 a = 2 , b = -2 时, a + b = 0 , umap[0] = 2 。
- 当 a = 2 , b = -3 时, a + b = -1 , umap[-1] = 2 。
- 当 a = 3 , b = -1 时, a + b = 2 , umap[2] = 1 。
- 当 a = 3 , b = -2 时, a + b = 1 , umap[1] = 2 。
- 当 a = 3 , b = -3 时, a + b = 0 , umap[0] = 3 。
统计满足条件的四元组数量:
- 遍历 C 和 D 数组:
- 当 c = 4 , d = -4 时, 0 - (c + d) = 0 , count += umap[0] , count 变为 3 。
- 当 c = 4 , d = -5 时, 0 - (c + d) = 1 , count += umap[1] , count 变为 5 。
- 当 c = 5 , d = -4 时, 0 - (c + d) = -1 , count += umap[-1] , count 变为 7 。
- 当 c = 5 , d = -5 时, 0 - (c + d) = 0 , count += umap[0] , count 变为 10 。
- 最终 count = 10 ,即满足 a + b + c + d = 0 的四元组有 10 个。
Leecode 383. 赎金信
题目描述:https://leetcode.cn/problems/ransom-note/description/
思路:解法一----for循环;解法二---哈希表(字母异位词的变式)。
解答:
解法一:
解法二:
假设 ransomNote = "aab" , magazine = "aabb" 。
初始化
首先,定义了长度为26的数组 record 并初始化为 {0} ,用于记录 magazine 中每个字母出现的次数。
初步判断
ransomNote.size() 为3, magazine.size() 为4,由于 3 < 4 ,不满足 ransomNote.size() > magazine.size() 的条件,所以继续执行后续代码。
统计 magazine 中字符出现次数
执行 for (int i = 0; i < magazine.length(); i++) { record[magazine[i] - 'a']++; } 这一循环:
- 当 i = 0 时, magazine[0] 为 'a' , 'a' - 'a' = 0 ,则 record[0]++ ,此时 record 数组变为 {1, 0, 0, ..., 0} 。
- 当 i = 1 时, magazine[1] 为 'a' , 'a' - 'a' = 0 ,则 record[0]++ ,此时 record 数组变为 {2, 0, 0, ..., 0} 。
- 当 i = 2 时, magazine[2] 为 'b' , 'b' - 'a' = 1 ,则 record[1]++ ,此时 record 数组变为 {2, 1, 0, ..., 0} 。
- 当 i = 3 时, magazine[3] 为 'b' , 'b' - 'a' = 1 ,则 record[1]++ ,此时 record 数组变为 {2, 2, 0, ..., 0} 。
检查能否构建 ransomNote
执行 for (int j = 0; j < ransomNote.length(); j++) { record[ransomNote[j] - 'a']--; if (record[ransomNote[j] - 'a'] < 0) { return false; } } 这一循环:
- 当 j = 0 时, ransomNote[0] 为 'a' , 'a' - 'a' = 0 ,则 record[0]-- ,此时 record 数组变为 {1, 2, 0, ..., 0} 。
- 当 j = 1 时, ransomNote[1] 为 'a' , 'a' - 'a' = 0 ,则 record[0]-- ,此时 record 数组变为 {0, 2, 0, ..., 0} 。
- 当 j = 2 时, ransomNote[2] 为 'b' , 'b' - 'a' = 1 ,则 record[1]-- ,此时 record 数组变为 {0, 1, 0, ..., 0} 。
在整个遍历 ransomNote 的过程中, record 数组的元素值始终没有小于0,所以遍历结束后,执行 return true ,即可以用 magazine = "aabb" 构建出 ransomNote = "aab"
15. 三数之和
题目链接:https://leetcode.cn/problems/3sum/description/
题目描述:
思路:解法一---哈希法(去重);解法二---双指针
解答:
解法一
解法二:
首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
Leecode 18. 四数之和
题目链接:https://leetcode.cn/problems/4sum/description/
题目描述:
思路:
四数之和,和15.三数之和 是一个思路,都是使用双指针法, 基本解法就是在15.三数之和 (opens new window)的基础上再套一层for循环。
但是有一些细节需要注意,例如: 不要判断nums[k] > target 就返回了,三数之和 可以通过 nums[i] > 0 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是[-4, -3, -2, -1],target是-10,不能因为-4 > -10而跳过。但是我们依旧可以去做剪枝,逻辑变成nums[k] > target && (nums[k] >=0 || target >= 0)就可以了。
15.三数之和 (opens new window)的双指针解法是一层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^3) 。
解答: