第一题四数之和
一开始还是只能想到说是四重循环 但是我估计肯定不行
另外这个题的主要思想就是用两个循环去解决4个循环的暴力解法
我一上来想的是去用四个multimap去存储ABCD 但那样是不行的 因为:
使用四个multimap存储A、B、C和D的元素,然后进行四个循环,其实就是一种暴力解法。对于每一个a、b、c和d的组合,你都需要检查它们的和是否为0。这种方法的时间复杂度是O(n⁴),因为你需要遍历A、B、C和D中的所有元素四元组。
虽然multimap可以按照键(key)进行排序并快速查找特定的键,但这并不能改变你需要遍历所有四元组的事实。只有当你需要查找或删除特定键的元素时,multimap的特性才能发挥作用。
也就是说 根本没用 那个只是你去查找特定元素的时间开销会小 但是你还是需要去遍历的话 那么时间复杂度就没变 还是O(n4)
继续说这个题 其关键就是 用两个unordered map 然后key去存值 然后value去存数量 也就是有几个相同的和 这样就能把时间复杂度减小到O(n2)
还有一个就是比较神奇的点 虽然有重复 但是还是可以用unordered_map去处理 因为你可以让value 去存储重复的次数
另外你还要注意一个问题 就是你在外层循环和内层循环不能同时int i=0 应该区分变量
其他没什么 还是很简单的小题:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map <int,int> map1;
int sum1=0,sum2=0;
for(int i=0;i<nums1.size();i++){
for(int j=0;j<nums2.size();j++){
sum1=nums1[i]+nums2[j];
if(map1.find(sum1)!=map1.end()){
auto iter=map1.find(sum1);
iter->second++;
}
else{
map1.insert({sum1,1});
}
}
}
int count=0;
for(int i=0;i<nums3.size();i++){
for(int j=0;j<nums4.size();j++){
sum2=nums3[i]+nums4[j];
if(map1.find(-sum2)!=map1.end()){
auto iter=map1.find(-sum2);
count=count+iter->second;
}
}
}
return count;
}
};
另外还有一个就是你用map 可以直接用 map[a+b]++; 这种操作 它的意思是先去查a+b在不在 如果在 就value加1 如果不在就创建一个 value为0的 为什么是value加1呢 因为在std::unordered_map
中,键(key)是唯一的,并且在键值对创建后,键是不可变的。你不能直接修改键,但是你可以修改与键关联的值。所以当你使用umap[a + b]++
时,你修改的是与键a + b
关联的值,而不是键本身。
总而言之就是 key是不能改的 所以肯定不能加1
vector <int> num[26]={0}; 注意不能这么写 因为容器不是数组 你能先vector <int> num[26];这么写 然后再去循环赋值
另外数组不能用xxx.size() 因为不是容器
赎金信 非常简单:一遍过 没什么好说的
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int num[26]={0};
for(int i=0;i<magazine.size();i++){
num[magazine[i]-'a']++;
}
for(int i=0;i<ransomNote.size();i++){
num[ransomNote[i]-'a']--;
}
for(int i=0;i<26;i++){
if(num[i]<0)
return false;
}
return true;
}
};
主要注意一点:其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
能用数组就不用别的!
第三题 三数之和
还是要注意 你用花括号可以用容器的初始化 但是你以后还想添加的时候 必须得用push_back()方法
而外你也不能用insert() 因为你需要给一个迭代器 比如说insert(it,10) 这样 这和set或者map不同 因为他们都是排了序的 或者是之前一直用的unordered_set 这种 你直接输数值就行 因为它是之前按插入的值去给你索引的
另外像这种二维数组 你直接push_back后面的括号里还得有一个对象 你可以直接用花括号 不然你就得std::vector<int> temp; temp.push_back(nums[i]); temp.push_back(nums[left]); temp.push_back(nums[right]); result.push_back(temp); 这么做 就很没效率 就是先创建一个temp 然后push_back temp 最后再push_back 这个result
另外这个题还有一个最重要的一点 就是你去重的时候 是
if (nums[i] == nums[i + 1]) {
continue;
}
还是
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
上面那种就不行 因为这样就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。
还有一个你就是理解错了 i是不能移动的 而要移动的是left和right 因为你要保证i是不变的 你才能在这个基础上去遍历left和right数组
那么你在找到后 肯定是要同时移动两个指针的 因为数组已经被排好序了 你要是只移动left或者right的话 那么还是不行 肯定不是0
而且每次你内循环的时候都要把left和right归位
另外在c++中:
如果在循环体中声明并初始化一个变量,那么在每次循环开始时,这个变量都会被重新声明和初始化。这就意味着这个变量的生命周期仅限于当前循环迭代。
这就是说,每次循环迭代结束后,该变量就会被销毁,然后在下次循环迭代开始时,再次被创建并初始化。
另外还有一个问题是:因为一开始是0 你如果不写i>0的话 就会访问到非法的下标 因此你还有写上i>0
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
另外就是找到答案的时候也要去去重right和left 没找到答案不用去重 因为反正也没找到 有什么好去重的 不过我觉得也可以去重
另外你在去重后 还是要left++和right-- 不得不说这道题我只能用恶心来形容
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int i=0,left,right=nums.size()-1,count=0;
sort(nums.begin(),nums.end());
for(;i<nums.size();i++){
left=i+1;
right=nums.size()-1;
if(i>0&&nums[i]==nums[i-1])
continue;
while(left<right){
if(nums[left]+nums[i]+nums[right]==0){
result.push_back({nums[i],nums[left],nums[right]});
while(left<right&&nums[left]==nums[left+1])
left++;
while(right>left&&nums[right]==nums[right-1])
right--;
left++;
right--;
}
else if(nums[left]+nums[i]+nums[right]>0)
{
right--;
}
else if(nums[left]+nums[i]+nums[right]<0)
{
left++;
}
}
}
return result;
}
};
非常恶心 有很多很多很多的小细节
总结如下
1 i的去重
2 left和right的去重
另外在元素不相等的时候这两个去重完全没必要加 因为编译器还是会一点点的执行下去。
另外这个四数之和也是 其实还是双指针 而不是哈希表 这个思路要对 相当于在三数之和上又加上了 一个指针就完事了
注意啊注意啊 这个四数之和的双指针法并不是最优时间复杂度 因为这样的时间复杂度是O(n的立方) 而哈希表的时间复杂度是O(n的平方) 但是来说空间复杂度要低很多 因为这样的空间复杂度是O(1) 而那种的复杂度是O(n的平方)因为空间复杂度是指除了输入和输出所需的空间之外的额外空间。输入和输出的空间需求通常不计入空间复杂度。
其实这个题非常简单 就是在三数之和外面再套一层for循环 做一个剪枝处理 以后再多数之和也是一个道理。
跟那个一开始的四数之和不一样 那个是四个独立的数组 这个是一个数组 因此要保留双指针不变 相当于是两个数 然后外面套循环 多一个就多套一层 做剪枝处理、
!!!!!!注意注意先排序!!!!!!!
!!!!!!另外left要初始化为i+1而不是i!!!!!!!
!!!!!!注意注意!!!!! 这个
if(nums[i]==nums[i-1])
continue;
并不是说i和numsi-1相等都要去重 因为在i等于k+1的时候 每一个i都是对应的不同的k 所以i相同有可能k不同
比如说 k -1 i 0 和k 1 i 2 就不需要去重 但是k-1 i0 i0 第二个i就需要去重了 所以:总结起来 第一个元素都不需要去重 即使是k 因为第一个元素根本就不会重复!!!
!!!!注意溢出问题 四个数以上相加就会溢出问题!!!!