代码随想录第五天|哈希表(2)

目录

LeetCode 454. 四数相加 II

LeetCode 383. 赎金信

LeetCode 15. 三数之和

LeetCode 18. 四数之和

总结


昨天休息了一天,今天继续

LeetCode 454. 四数相加 II

题目链接:LeetCode 454. 四数相加 II

思想:本题需要遍历四个数组中的元素之和,最优化时间复杂度的遍历方式就是先遍历两个数组,然后利用map这一数据结构,其key值为前两个数组元素的相加之和,value值为相加之和出现的次数,遍历完前两个数组之后便遍历后两个数组。因为a+b+c+d=0,通过恒等变换,可以得到a+b=0 -(c+d),所以在遍历后两个数组的时候,可以取对应的数组元素,对map里面下标为[0 - (c+d)]的key值进行查找,如果找到了,便让计数增加对应的value值,如果没找到就遍历下两个元素。

代码如下:

    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> result;
        for (int a : nums1){
            for (int b : nums2){
                result[a + b]++;
            }
        }
        int count = 0;
        for (int c : nums3){
            for (int d : nums4){
                if (result.find( 0 - (c + d)) != result.end()){
                    count += result[0 - (c + d)];
                }
            }
        }
        return count;
    }

时间复杂度:O(n^2),空间复杂度:O(n^2)。

LeetCode 383. 赎金信

题目链接:LeetCode 383. 赎金信

思想:本题和哈希表(1)里面的LeetCode 242. 有效的字母异位词是一个思路,不过上题需要的是两个字符串的字符出现次数要一模一样,而本题允许两个字符串的某些字符不用对应上,只需要一个字符串里面拥有所有能构成另一个字符串的字符即可。所以上题的思路是需要对照的数组每个元素都等于0即可,而本题要求变宽,每个元素都大于等于0即可。(这也是今日唯一不看题解做出来的题)

代码如下:

    bool canConstruct(string ransomNote, string magazine) {
        int count[26] = {0};
        for (int i = 0; i < magazine.size(); i++){
            count[magazine[i] - 'a']++;
        }
        for (int i = 0; i < ransomNote.size(); i++){
            count[ransomNote[i] - 'a']--;
            if (count[ransomNote[i] - 'a'] < 0){
                return false;
            }
        }
        return true;
    }

时间复杂度:O(n+m),空间复杂度:O(1)。

LeetCode 15. 三数之和

题目链接:LeetCode 15. 三数之和

思想:本题说上来要考虑的东西非常多,首先是数组中的三个元素需要满足总和为0,其次是三个元素不能具有相同的下标,最后三元组不能重复。首先看到本题,我脑海里只有一种想法,三重循环暴力解决本题,不能具有相同的下标这个问题好解决,三个for循环的初始和结束判断都不一样就行了,例如最外层循环为for (int i = 0; i < nums.size() - 2; i++),里层循环就为for (int j = i + 1; j = nums.size() - 1; j++)。这就防止了下标重复。而不能具有相同的三元组的话,就可以在set里面套vector就能解决。很明显这题不能这样做,会超出时间限制。

那有没有什么加快的办法呢。暴力解法中一层循环动用一个指针,那可不可以在一个循环里动用两个指针呢?那么答案很明显,可以用我们的双指针法。但双指针不可能在循环里面漫无目的的随便乱动,这时候就可以将数组经过排序之后,再采用双指针法。一个指针在外层设定双指针移动的范围(即这个指针的右边就是移动范围),双指针一个在移动范围左边界,一个在移动范围右边界。如果三数之和小于零的话,便是指向左边界这个指针不够大,将它往后移动一位;如果三数之和大于零的话,便是指向右边界这个指针太大了,将它向前移动一位;直到左右两个指针相遇。至于怎么保证三元组不重复的操作,还是看代码吧,感觉不太好讲清楚,大概地来说就是排序之后,把相等元素跳过。

代码如下:

    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        int left = 0;
        sort(nums.begin(), nums.end());
        for (left = 0; left < nums.size(); left++){
            if (nums[left] > 0){
                return result;
            }
            // 至于这里为什么是与left - 1比较,就好比是[-1,-1,2]这种情况,
            // 如果直接与left + 1比较的话,会直接跳过这一种情况,而不会进入结果数组里面
            if (left >  0 && nums[left] == nums[left - 1]){
                continue;
            }
            int cur = left + 1;
            int right = nums.size() - 1;
            while (cur < right){
                if (nums[left] + nums[cur] + nums[right] < 0){
                    cur++;
                }else if (nums[left] + nums[cur] + nums[right] > 0){
                    right--;
                }else{
                    result.push_back(vector<int>{nums[left], nums[cur], nums[right]});
                    while (right > cur && nums[right] == nums[right - 1]){
                        right--;
                    }
                    while (right > cur && nums[cur] == nums[cur + 1]){
                        cur++;
                    }
                    right--;
                    cur++;
                }
            }
        }
        return result;
    }

时间复杂度:O(n^2),空间复杂度:O(1)。

LeetCode 18. 四数之和

题目链接:LeetCode 18. 四数之和

思想:本题是上题的拓展,无非就是再加一个指针,再加一个for循环,这里就不再赘述。

代码如下:

    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        int length = nums.size();
        if (length < 4){
            return result;
        }
        for (int i = 0; i < nums.size() - 3; i++){
            if (i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
            if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target){
                break;
            }
            if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target){
                continue;
            }
            for (int j = i + 1; j < nums.size() - 2; j++){
                if (j > i + 1 && nums[j] == nums[j - 1]){
                    continue;
                }
                if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target){
                    break;
                }
                if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target){
                    continue;
                }
                int k = j + 1;
                int l = length - 1;
                while (k < l){
                    if ((long) nums[i] + nums[j] + nums[k] + nums[l] > target){
                        l--;
                    }else if ((long) nums[i] + nums[j] + nums[k] + nums[l] < target){
                        k++;
                    }else{
                        result.push_back(vector<int>{nums[i], nums[j], nums[k], nums[l]});
                        while (k < l && nums[l] == nums[l - 1]){
                            l--;
                        }
                        while (k < l && nums[k] == nums[k + 1]){
                            k++;
                        }
                        l--;
                        k++;
                    }
                }
            }
        }
        return result;
    }

时间复杂度:O(n^3),空间复杂度:O(1)。

总结

哈希表两天的练习下来,感觉特别灵活也比较固定。灵活是需要的逻辑比较多,固定就是哈希表要做的事情比较固定,一般就是用来查找的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值