第六天|454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和
第六天|454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和
454.四数相加II
学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili
笔记
通过单独遍历两个数组来提高时间效率
- 首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。
- 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
- 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
- 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
- 最后返回统计值 count 就可以了
实操出现问题
为什么我的代码时间非常长?
代码/比较
我的代码:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
//遍历前两个和后两个数组 1234
int n=nums1.size();
int sum12=0;//数组1 和 2 的元素和,放进map里面
unordered_map<int,int> m_map;//map的key存放和,value存放出现次数
for(int i=0;i<n;i++)//数组1和2,把他们的和放进一个map里面,map的key存放和,value存放出现次数
{
for(int j=0;j<n;j++)
{
sum12=nums1[i]+nums2[j];
if(m_map.find(sum12)!=m_map.end())//如果出现过,则value自增
{
m_map.find(sum12)->second++;
}
//如果没有出现过,则插入,value=1
m_map.insert(pair<int,int> (sum12,1));
}
}
int sum34=0;
int count=0;//和为0的次数
//遍历数组3和4,在map里面寻找和的相反数,找到了则加上value,没找到则下一个
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
sum34=nums3[i]+nums4[j];
if(m_map.find(0-sum34)!=m_map.end())
{
count+=m_map.find(0-sum34)->second;
}
}
}
return count;
}
};
其他:
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
// 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
for (int a : A) {
for (int b : B) {
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : C) {
for (int d : D) {
if (umap.find(0 - (c + d)) != umap.end()) {
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
能通过key直接访问value,不用额外的使用find方法。
383. 赎金信
笔记:
自己的想法:
把magazine里面的字符都统计一下个数存放到一个数据结构里,再遍历ransomnote,去那个数据结构里找,找得到就把个数自减,找不到返回false,并且时刻检测个数是否小于0,如果小于零返回false。
用map实现,为什么?key存放magazine串中的字母,value存放个数。(注意:unorder_map.find()找的是key,map的key可以初始化为char、long等其他形式)
一遍过
代码随想录上的思路:
因为题目说只有小写字母,那可以采用空间换取时间的哈希策略,用一个长度为26的数组来记录magazine里字母出现的次数。
然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。
依然是数组在哈希法中的应用。
一些同学可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
实操出现问题:
unorder_map.find()找的是key,map的key可以初始化为char、long等其他形式
代码:
我自己的:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<char,int> magazine_map;//用于存放magazine
//unordered_map<char,int> ransomNote_map;//ransomNote
for(int i=0;i<magazine.size();i++)//将magazine转化为map
{
magazine_map[magazine[i]]++;
}
//相减
//怎么遍历一个map?不遍历map,而直接遍历string即可
for(int i=0;i<ransomNote.size();i++)
{
char n=ransomNote[i];
if(magazine_map.find(n)!=magazine_map.end())//如果能找到则对应的value自减
{
magazine_map[n]--;
if(magazine_map[n]<0)//如果小于零,则出现magazine内的字母不够的情况
{
return false;
}
}
else//如果找不到,说明不能由magazine里面的字符构筑,返回false
{
return false;
}
}
return true;
}
};
代码随想录上的:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0};
//add
if (ransomNote.size() > magazine.size()) {
return false;
}
for (int i = 0; i < magazine.length(); i++) {
// 通过record数据记录 magazine里各个字符出现次数
record[magazine[i]-'a'] ++;
}
for (int j = 0; j < ransomNote.length(); j++) {
// 遍历ransomNote,在record里对应的字符个数做--操作
record[ransomNote[j]-'a']--;
// 如果小于零说明ransomNote里出现的字符,magazine没有
if(record[ransomNote[j]-'a'] < 0) {
return false;
}
}
return true;
}
};
15.三数之和
梦破碎的地方!| LeetCode:15.三数之和_哔哩哔哩_bilibili
笔记:
用双指针法:首先将数组从小到大排序,一个循环内,左指针指向i+1,右指针指向末尾,如果i+left+right>0,说明大了,这个数要减小只能right--,同理如果小于0,只能left++;
去重细节:
i的去重:和前一个(已经遍历过的那个i)对比,如果相同,不再遍历,直接i++(即continue)
left和right的去重:left如果和left+1相同,则往下遍历,不管当下的left;如果right和right-1相同,则往下遍历,不管当下的``right
去重的逻辑详细看文章第15题. 三数之和 | 代码随想录
实操出现的问题:
1.二维数组的插入:result.push_back(vector<int> {nums[i],nums[left],nums[right]});//把当前的结果放进result
2.什么时候去重left和right:应该再找到一个符合的结果后进行去重left和right,不然会导致漏掉遍历的。
代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;//结果数组
int result_hangshu=0;//存放第几个结果
sort(nums.begin(),nums.end());//从小到大排序
if(nums[0]>0)
return {};
for(int i=0;i<nums.size();i++)//遍历整个数组
{
if(i>0&&nums[i]==nums[i-1])//对i去重
continue;
int left=i+1;
int right=nums.size()-1;//指向末尾
while(right>left)//left和right遍历
{
if((nums[right]+nums[left]+nums[i])>0)//结果大了,需要让这个和变小只能是right--
right--;
else if((nums[right]+nums[left]+nums[i])<0)//结果小了,需要让这个和变小只能是left++
left++;
else if((nums[right]+nums[left]+nums[i])==0)//找到目标值
{
result.push_back(vector<int> {nums[i],nums[left],nums[right]});//把当前的结果放进result
//去重left和right
while(right>left&&nums[left]==nums[left+1])
left++;
while(right>left&&nums[right]==nums[right-1])
right--;
left++;
right--;
}
}
}
return result;
}
};
没有操作减枝!
9.四数之和
笔记:
和上面的题目类似,多加一层循环即可
实操出现的问题:
1.第二次循环没有注意nums[i]+nums[k]是一个整体
2.注意如果是较大的整型相加会溢出,用long来接收运算结果
代码:
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) {
break; // 这里使用break,统一通过最后的return返回
}
// 对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) {
break;
}
// 对nums[i]去重
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return result;
}
};
1

浙公网安备 33010602011771号