【算法-哈希表3】四数相加2 和 赎金信

今天,带来哈希表相关算法的讲解。文中不足错漏之处望请斧正!

理论基础点这里


1. 四数相加2

分析题意

求符合条件的四元组的出现次数,条件:

  • nums1
  • nums2
  • nums3
  • nums4
    从四个数组中的每一个数组取一个数 num1, num2, num3, num4,满足 num1 + num2 + num3 + num4 == 0,则这是一个满足条件的四元组,可以记上它的出现次数。

题意转化

可以简单转化为 直接遍历取得4个数, 判断是否满足条件.但太慢,时间复杂度O(n^4)。

其实可以动动脑筋,将题意转化为 是否存在 两个两数之和 sum1 和 sum2 相加为0。

解决思路

四个数组该怎样去遍历,建立映射?

我们可以先遍历前两个数组,将数组中的元素两两求和得到sum1,把sum1和其出现次数建立映射得到哈希表sums1。接着遍历后两个数组,也两两求和得到sum2,在sums1中O(1)查找是否有一个和,和当前sum相加为0。

但为什么要这样遍历,我先遍历一个建立映射,再遍历三个不行吗?

这样我们在最终搜索比对的时候需要3层for来玩儿,那就是O(n^3)。而我们两两遍历只需要O(2 * n^2),这才是更好的。

编程实现

class Solution {
public:
    // 四数之和的判断 拆分为 两数之和的判断
    // 先遍历两个数组并求得所有两数之和sums1, 再遍历两个数组求剩下的两数之和, 查找是否有sum1 = -sum2
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> sums1; //  -- 题目不要求返回下标, 只用返回次数
        int sum1 = 0;
        int sum2 = 0;
        
        // 先遍历两个数组并求得所有两数之和sums1
        for (int &num1 : nums1) {
            for (int &num2 : nums2) {
                sum1 = num1 + num2;
                ++sums1[sum1];
            }
        }

        // 再遍历两个数组求剩下的两数之和, 查找是否有sum1 = -sum2
        int cnt = 0;
        for (int &num3 : nums3) {
            for (int &num4 : nums4) {
                sum2 = num3 + num4;
                auto iter = sums1.find(-sum2);
                if (iter != sums1.end()) cnt += iter->second;
            }
        }

        return cnt;
    }
};

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

2. 赎金信

分析题意

“给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。”

题意转化

判断ransomNote的组成字符是否全部都在magazine中有足够的字符与之对应。

解决思路

查找,上哈希。遍历magazine,用哈希表描述magazine中的字符出现过多少次。

编程实现

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int appeared[26] = {0};

        // 用哈希表(数组)描述magazine中的哪些字符出现过.
        for (char &ch : magazine) ++appeared[ch - 'a'];
        // 在magazine中查找ransomNote的所有字符, 所有都能找到才是赎金信.
        for (char &ch : ransomNote) {
            --appeared[ch - 'a'];
            if (appeared[ch - 'a'] < 0) return false; // magazine中没有足够字符构成ransomNote
        }
        
        return true;
    }
};

时间复杂度:O(n)

空间复杂度:O(1)


今天的分享就到这里了,感谢您能看到这里。

这里是培根的blog,期待与你共同进步!

你可能感兴趣的:(算法,算法,散列表,数据结构)