算法第六天|四数相加ⅡLeetCode454、赎金信LeetCode383、三数之和LeetCode15、四数之和LeetCode18

今日总结:

1、在使用map的时候,可以使用迭代器:

//方式2:使用迭代器
unordered_map<int,int>::iterator it
auto it

 也可以使用[ ]进行对应的key查找对应的value,可以使用自加自减。

//方式1:使用[]
map_num[x+i]++;

2、在使用map、set的时候可以使用num.erase(iter/x) ,iter是迭代器,删除这个集合中的迭代器位置的元素,x是元素 ,会删除这个集合中所有x的元素。

3、四数之和的减枝处理需要着重复习

四数相加 

题目链接:LeetCode454、四数相加Ⅱ

整体思路:

因为判断有多少个A+B+C+D=0,所以是一个检测存在性的问题,可以使用哈希表实现

检测的结果是存在多少个A+B+C+D=0的值,四个变量太多-->需要将问题简化:将A+B、C+D的和分别遍历,判断有多少次A+B+C+D=0-->

可以使用无序映射unordered_map,key记录A+B的值,value记录A+B的值出现的次数

通过判断map中存在多少次-(C+D)计算出有多少次A+B+C+D=0

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        //这是一个检测存在性的问题:是不是有数据满足:A+B+C+D=target,所以可以使用map
        //想到了整体使用map进行判断是不是存在target-(A+B)=(C+D),
        //所以需要将一部分数据存入到map中与外边的值进行判断-->将A+B的结果存入到map中使用target-(C+D)的值在map中是不是存在key,有多少次value?

        //定义无序映射unordered_map,key存储遍历的A+B的值,value存储次数
        unordered_map <int,int>map_num;
        int res=0;
        for(auto x:nums1)
        {
            for(auto i:nums2)
            {
                //方式1:使用[]
                map_num[x+i]++;

                //方式2:使用迭代器
                // unordered_map <int,int>::iterator iter = map_num.find(x+i);
                // if(iter==map_num.end())map_num.insert(pair<int,int>(x+i,1));
                // else iter->second++;
            }
        }

        //遍历C+D判断是不是等于0
        for(auto i :nums3)
        {
            for(auto x :nums4)
            {
                unordered_map <int,int> ::iterator iter =map_num.find(0-(i+x));
                if(iter!=map_num.end()) res +=iter->second;
            }
        }
        return res;

        
    }
};

赎金信

题目链接:LeetCode383、 赎金信

整体思路:

        判断能不能由另一个字符串中的字符组成,存在性的问题,可以使用哈希表

方式1:数组模拟哈希表

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        //使用数组模拟哈希表
        int A[26];
        for(auto i :magazine)
        {
            A[i-'a']++;
        }
        for(auto i:ransomNote)
        {
            A[i-'a']--;
            if(A[i-'a']<0)return false;
        }
        return true;
    }
};

方式2:这个题需要记录使用的次数,可以使用unordered_map

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        //key记录出现的字符,value记录出现的次数
        unordered_map <char,int>map_num;
        for(auto i:magazine)
        {
            map_num[i] ++;
        }
        for(auto i :ransomNote)
        {
            map_num[i]--;
            if(map_num[i]<0)return false;
        }
        return true;
        
    }
};

思路三:可以使用multiset记录出现多次的值,需要使用erase

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        multiset <int> set_num;
        for(auto i :magazine)
        {
            set_num.insert(i);
        }
        for(auto i :ransomNote)
        {
            multiset <int>::iterator iter =set_num.find(i);
            if(iter!=set_num.end())set_num.erase(iter);//使用erase的时候,传入迭代器才能删除准确的一个元素,传入元素就会将所有的该元素删除。
            else
            {
                return false;
            }
        }
        return true;
    }
};

 三数之和

题目链接:

LeetCode15、三数之和

整体思路:

        因为是在同一个数组中寻找三个元素,满足A+B+C=0:

不建议使用set、map:

        1、数组中可能存在相同的值,使用set、map很难去控制重复的组合,

        2、当前判断的元素与set、map中的值很难找到独立的关系

可以将一个元素固定,使用双指针遍历其他的元素,寻找A+B+C=0

步骤流程:

        1、使用双指针,需要对数组进行排序,使用sort或者自己写一个简单的归并、快排

        2、使用i将数组进行遍历

        3、当nums[i]>0时,表示A+B+C必定大于0,可以直接退出循环了

        4、将第一个指针从i+1开始循环,将第二个指针从nums.size()-1开始循环,向中间靠拢

        5、A+B+C>0时,移动第二个指针j--;A+B+C<0时,移动第一个指针m++;

        5、当遇到A+B+C=0的值时,记录当前的三个值 ,并且将第一个指针m向右移动m++,第二个指针j向左移动j--,但是要去掉重复元素:

                1、在移动的时候,需要确保移动到的下一个值与当前的值不同

                2、同时要保证左指针<右指针。

        6、对i遍历,也要将重复的元素跳过(在移动的时候,需要确保移动到的下一个值与当前的值不同,)

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //如果使用set、map会比较困难,因为需要确保三个元素各不相同。
        //因为在同一个数组中,可以选择使用双指针
        //定住一个位置,使用双指针将其他的位置遍历(双指针需要将进行一个排序)
        vector<vector<int>>res;

        sort(nums.begin(),nums.end());
        //从头到尾进行遍历
        for(int i=0;i<nums.size();i++)
        {
            //对其他位置进行双指针遍历

            if(nums[i]>0) return res;//如果最小值都>=0,表示不存在三元组满足A+B+C=0;

            //双指针开始遍历,需要确保m与i不相等
            int  m= i+1;
            int j=nums.size()-1;
            while(m<j)
            {
                if(nums[i]+nums[m]+nums[j]<0)m++;
                else if(nums[i]+nums[m]+nums[j]>0)j--;
                else if(nums[i]+nums[m]+nums[j]==0)
                {
                    res.push_back({nums[i],nums[m],nums[j]});
                    while(m<j&&nums[m]==nums[m+1])m++;//删除相同项,必须添加m<j的限制项,不添加会导致num[m+]超出数组大小,访问越界,
                    while(m<j&&nums[j]==nums[j-1])j--;//删除相同项
                    m++,j--;//进行下一个
                    //同时i向下一个
                    while(nums[i]==nums[i+1])i++;//寻找到下一个不等于i的前一个值 
                }
            }
            
        }

        return res;
        
    }
};


// class Solution {
// public:
//     vector<vector<int>> threeSum(vector<int>& nums) {
//         sort(nums.begin(),nums.end());
//         vector<vector<int>>res;
//         for(int i=0;i<nums.size();i++)
//         {
//             int j = i+1;
//             int q = nums.size()-1;
//             if(nums[i]>0) return res;
//             if(i>0&&nums[i]==nums[i-1])continue;
//             while(j<q)
//             {
//                 if(nums[i]+nums[j]+nums[q]<0)j++;
//                 else if(nums[i]+nums[j]+nums[q]>0) q--;
//                 else if(nums[i]+nums[j]+nums[q]==0)
//                 {
//                      res.push_back({nums[i],nums[j],nums[q]});//添加元素
//                      //去重
//                      if(j<q&&nums[j]==nums[j+1])j++;
//                      if(j<q&&nums[q]==nums[q-1])q--;
//                      j++;


//                 }
//             }
//         }
//         return res;
//     }
// };

四数之和

题目链接:LeetCode18、四数之和

整体思路:

        与三数之和类似,通过确定两个值,其余值使用双指针进行遍历。

        因为要确定两个值, 所以使用两层for嵌套循环,

        在每层的循环中,需要进行减枝处理判断当前两个确定的元素与target的关系

                注意:target的值可能是复数,所以需要在满足两元素和>target的基础上再加一个限制条件:两元素和>=0,才能确保减枝的成功这是导致出错的地方

                注意:在减枝成功后,要使用break,不能直接return res,因为内存循环break后,还会继续遍历外层的循环,只有外层的循环减枝成功才能使用return res。

                注意:在使用四元素相加时,可能元素过大,强制转换成long型

                注意:每层循环的最后,需要判断下一层循环开始时,确定的元素的位置(去掉重复元素)

        

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        sort(nums.begin(),nums.end());
        //三数之和是定一位,四数之和是定两位,嵌套循环
        //使用i在最左,j在i的右边,双指针确定另外的两位
        for(int i =0;i<nums.size();i++)
        {
            if(nums[i]>target&&nums[i]>=0)return res;//前两位>target,表示四位一定大于target
            for(int j = i+1;j<nums.size()-1;j++)
            {
                if(nums[i]+nums[j]>target&&nums[i]+nums[j]>=0)break;
                int m= j+1,n=nums.size()-1;
                while(m<n)
                {
                    if((long)nums[i]+nums[j]+nums[m]+nums[n]<target) m++;
                    else if((long)nums[i]+nums[j]+nums[m]+nums[n]>target) n--;
                    else if((long)nums[i]+nums[j]+nums[m]+nums[n]==target)
                    {
                        //记录合适的值、去掉m,n的重复、m<n、m,n向中间移动
                        res.push_back({nums[i],nums[j],nums[m],nums[n]});
                        while(m<n&&nums[m]==nums[m+1])m++;
                        while(m<n&&nums[n]==nums[n-1])n--;
                        m++,n--;
                    }
                    
                }
                //最后需要确定:j下一次是不是重复的元素
                while(j+1<nums.size()&&nums[j]==nums[j+1])j++;

            }
            //最后需要确定:i下一次是不是重复的元素
                while(i+1<nums.size()&&nums[i]==nums[i+1])i++;
        }
        return res;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值