【代码随想录算法训练营——Day7】哈希表——454.四数相加II、383.赎金信、15.三数之和、18.四数之和
LeetCode题目链接
https://leetcode.cn/problems/4sum-ii/description/
https://leetcode.cn/problems/ransom-note/description/
https://leetcode.cn/problems/3sum/description/
https://leetcode.cn/problems/4sum/description/
题解
454.四数相加II
拿到题目,首先想到的就是暴力解法,但那样时间复杂度太高。
看了题解,不看题解这思路想不到。
感觉是纯思路题,不看定义和记住后面的处理过程完全想不到。
代码很简单,就不写了,因为写了也是抄思路。
383.赎金信
稍微动动脑就能写出来。
15.三数之和
拿到题目,又是暴力解法,三层循环依次遍历三个数,这个方法也不保证不包含重复三元组。
看题解,题解也说不重复比较难。
以上是用哈希法实现的题目。
题解说到另一个方法,双指针法,比哈希法的代码好懂些,但看懂思路后自己去实现发现没有去重,再看题解,发现去重的代码是一行if就完事。但是加到自己的代码里并不对,经过与chatGPT的沟通,发现原因是:使用双指针时,一旦找到一个满足条件的三元组,就先记录结果,然后分别移动 left 和 right 指针,跳过重复元素。(来自chatGPT)这点没有做到。最后还是按照卡哥的代码逻辑写了,把left<right的while判断放在外面,并且要注意,对right和left去重时,要把right和左边的比,left和右边的比。
关于i、left、right的去重,chatGPT有话说:
当我们遍历数组时,我们希望 i 遍历到不同的数。对于每个数 nums[i],我们用双指针来找出可能的三元组。如果数组中有多个相同的 nums[i],那么这些相同的 nums[i] 会生成重复的三元组。
在排序后的数组中,nums[i] 是当前正在处理的元素。我们希望确保对于每个 i,只有当 nums[i] 是新的(没有重复)时,我们才去尝试用双指针方法寻找三元组。如果当前 nums[i] 和 nums[i-1] 相同,那么它已经被处理过了,直接跳过。
如果我们比较 right + 1 和 right,这将是一个向右的偏移(增加),而我们是从数组的右端向左收缩指针,所以我们需要从后向前比较。
同理,比较 left - 1 和 left 是不合理的,因为 left 是从左端向右收缩的,所以需要向右比较(即 left + 1)。
18.四数之和
被三数之和吓到了,看题目没有思路,看题解说又是双指针法。写了一通,不知道怎么加i和j的去重,并且判断逻辑也还是三个if不是if-else,如果用三个if的话当数据为{0,0,0,0}和target=1时会由于target没放好(这是一个愚蠢的错误)超出时间限制,并且还要加上防止溢出的(long)。
细节慢慢,总之这题算是改到了!记得先补了前两道题目再来做这道,这道的代码放了。
代码
//454.四数相加II
三刷再补
//383.赎金信
#include <iostream>
#include <vector>
#include <string>
using namespace std;class Solution {
public:bool canConstruct(string ransomNote, string magazine) {vector<int> hash1(26, 0), hash2(26, 0);for (int i = 0;i < ransomNote.size();i++) {hash1[ransomNote[i] - 'a']++;}for (int i = 0;i < magazine.size();i++) {hash2[magazine[i] - 'a']++;}for (int i = 0;i < 26;i++) {if (hash1[i] > 0 && hash2[i] > 0) {if (hash2[i] > hash1[i]) hash1[i] = 0;else hash1[i] -= hash2[i];}}bool flag = true;for (int i = 0;i < 26;i++) {if (hash1[i] != 0) flag = false;}return flag;}
};int main() {Solution s;string ransomNote = "a", magazine = "b";printf("%d", s.canConstruct(ransomNote, magazine));return 0;
}
//15.三数之和
三刷再补
//18.四数之和
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {sort(nums.begin(), nums.end());vector<vector<int>> result;for (int i = 0;i < nums.size();i++) {if (i > 0 && nums[i] == nums[i - 1]) continue;for (int j = i + 1;j < nums.size();j++) {if (j > i + 1 && nums[j] == nums[j - 1]) continue;int left = j + 1, right = nums.size() - 1;while (left < right) {if ((long)nums[i] + nums[j] + nums[left] + nums[right] > target) right--;else if ((long)nums[i] + nums[j] + nums[left] + nums[right] < target) left++;else {result.push_back({ nums[i], nums[j], nums[left], nums[right] });while (left < right && nums[right] == nums[right - 1]) right--;while (left < right && nums[left] == nums[left + 1]) left++;right--;left++;}}}}return result;}
};int main() {vector<int> nums = { 1000000000,1000000000,1000000000,1000000000 };int target = 0;Solution s;vector<vector<int>> result = s.fourSum(nums, target);for (int i = 0;i < result.size();i++) {for (int j = 0;j < result[0].size();j++) {printf("%d ", result[i][j]);}printf("\n");}return 0;
}