数据结构与算法学习day11-哈希表-赎金信、三数之和、四数之和

一、赎金信

1.题目

383. 赎金信 - 力扣(LeetCode)

2.思路

这道题和242. 有效的字母异位词 - 力扣(LeetCode)很像。都用了数组做哈希表用来记录每个字母出现的次数。

先用哈希表记录magazine每个字母出现的次数,后面遍历ransomNote的字母时,可以一起判断数值是否小于0(小于0说明ransomNote有的字母magazine没有,可以直接退出)

3.代码

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int mark[26];
        if(ransomNote.length() > magazine.length()){
            return false;
        }
        for(int i=0;i < magazine.length();i++){
            mark[magazine[i]-'a']++;
        }

        for(int j=0;j < ransomNote.length();j++){
            mark[ransomNote[j]-'a']--;
            if(mark[ransomNote[j]-'a'] < 0){
            return false;
        }
        }
        return true;
    }
};

二、三数之和

1.题目

15. 三数之和 - 力扣(LeetCode)

2.思路

这道题用双指针法容易理解,后续可以补上哈希表法。

题目重点是不遗漏且不重复地找到满足条件的三个数。

2.1 不遗漏

需要对数组进行重新从小到大排列,这点很重要,方便后续进行遍历。

有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。依然还是在数组中找到nums[i]+ nums[left]+ nums[right] = 0。

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止

2.2 不重复

需要对三个数遍历的过程中进行去重操作。因为对数组进行了排序,因此进行去重时,可以判断当前元素和周围的元素是否相同,相同则移动对应指针。

2.2.1 nums[i]去重操作

判断nums[i]与nums[i+1]还是与nums[i-1]相同去重呢?

答案是nums[i-1]

当判断与nums[i+1]相同时,会导致{-2,-2,4}这种结构被直接忽略。即当前元素要和遍历过的元素进行比较才能去重,与未遍历的元素去重会导致结果遗漏。当两者重复时,i 需要进入下一个循环。

2.2.2 nums[left]、nums[right]去重操作

与上面同理,nums[left]与nums[left+1]比较,nums[right]与nums[right-1]比较。

需要注意的是,left和right的去重操作需要在找到结果时才能进行操作,不然会导致{0,0,0,0,0}这种数组无法找到对应结果。

3.代码

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());//对数组进行重新排列
        for(int i=0;i<nums.size();i++){
            if(nums[i]>0) return result;//如果重新排序后的首个数字大于0,说明不可能组成=0了,直接返回结果
            if(i>0 && nums[i] == nums[i-1]) continue;//对首个数字进行去重

            //定义双指针
            int left  = i+1;
            int right = nums.size()-1;

            //开始寻找
            while(left < right){//这里不能有=
                if(nums[i]+nums[left]+nums[right] == 0){
                    result.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    while(right > left && nums[right] == nums[right-1]) right--;//用while可以一直去重
                    while(right > left && nums[left]  == nums[left+1] ) left++;

                    //找到答案,双指针一起移动(因为只移动一个肯定会导致重复的)
                    right--;
                    left++;

                }
                else if(nums[i]+nums[left]+nums[right] > 0){//组合偏大,右边的数减小
                    right--;
                }
                else{//组合偏小,左边的数增大
                    left++;
                }              
            }
        }
        return result;
        
    }
};

三、四数之和

1.题目

18. 四数之和 - 力扣(LeetCode)

2.思路

这道题和三数之和类似,需要注意的点是:

nums[i] > target && (nums[i] >=0 || target >= 0)这个条件可以判断后面的值是否还能组成target。

后面数相加后判断要加long字符,否则可能会溢出

3.代码

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());//对数组进行重新排列
        for(int i=0;i<nums.size();i++){
            if(nums[i] > target && (nums[i] >=0 || target >= 0)) break;//如果重新排序后的首个数字大于0,说明不可能组成=0了,直接返回结果
            if(i>0 && nums[i] == nums[i-1]) continue;//对首个数字进行去重
            for(int j = i+1;j<nums.size();j++){
                 if(nums[i] + nums[j] > target && nums[i] + nums[j] >=0) break;

                 if(j>i+1 && nums[j] == nums[j-1]) continue;
                //定义双指针
                int left  = j+1;
                int right = nums.size()-1;

            //开始寻找
            while(left < right){//这里不能有=
                if((long)nums[i]+nums[j]+nums[left]+nums[right] == target){
                    result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
                    while(right > left && nums[right] == nums[right-1]) right--;//用while可以一直去重
                    while(right > left && nums[left]  == nums[left+1] ) left++;

                    //找到答案,双指针一起移动(因为只移动一个肯定会导致重复的)
                    right--;
                    left++;

                }
                else if((long)nums[i]+nums[j]+nums[left]+nums[right] > target){//组合偏大,右边的数减小
                    right--;
                }
                else{//组合偏小,左边的数增大
                    left++;
                }              
            }
            }
            
        }
        return result; 
    }
   
};

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值