第三天——哈希表

第三天——哈希表

两个数组的交集

力扣原题

这道题其实思路很清楚,我们需要记录一个数组的不重复的元素然后再和另一个数组比较。但因为我们无法保证内容的顺序性,因此采用哈希表来存储是最合适的。

代码如下:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> res;
        unordered_set<int> num1_set(nums1.begin(), nums1.end()); // 构建v1数组的哈希表
        for (int num : nums2) {
            if (num1_set.find(num) != num1_set.end()) { // 检查v2中的元素是否在v1的哈希表中
                res.insert(num);
            }
        }
        return vector<int>(res.begin(), res.end());
    }
};

快乐数

力扣原题

题目是快乐数,但是一开始做这个题真的不快乐。如果你单纯按照题目思路去考虑,难免会陷入硬算的陷阱。这时我们再回头看看题目,这里给了两个退出计算的条件:

如果循环某一个结果,则不是快乐数

如果某次结果是1,则是快乐数

如果只考虑结果为1的退出条件,我们其实会在非快乐数进入无限循环,因此需要一个哈希表记录每次的计算结果,如果重复直接返回false。

代码如下:

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> sum_set;
        while (1) {
            int sum = 0;
            while (n) { // 求每位数的平方和
                sum += (n % 10) * (n % 10);
                n /= 10;
            }
            if (sum == 1) { // 快乐数退出条件
                return true;
            }
            if (sum_set.find(sum) != sum_set.end()) { // 如果结果已经出现过说明进入循环,失败退出
                return false;
            }
            else {
                sum_set.insert(sum); // 添加结果到哈希表
            }
            n = sum;
        }
    }
};

当然,这个是非常直观的解法,看过题解之后我们还可以用一种更加巧妙的解法去做:快慢指针

想想我们之前在循环链表那题里使用的快慢指针,不难发现如果快指针每次算两步,慢指针每次算一步,如果有循环就会相遇。

代码如下:

class Solution {
public:
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
         	n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow = n, fast = n;
        do{
            slow = getSum(slow);
            fast = getSum(fast);
            fast = getSum(fast);
        }while(slow != fast);
        return slow == 1;
    }
};

这样做的好处主要是,如果采用哈希表存储,会占据过多的内存,如果是一个较大的数字,有可能算很多步还没有出现循环,这时反复的存入可能导致内存爆掉。说白就是用时间换空间。

两数之和

力扣原题

这道题与之前的题最大的差别就在于我们需要记录两个值:数组元素与目标数的差值和数组元素位置,因此这里要求我们存入的不再是一个单一的值,而是具有对应关系的一组值。这时单纯的使用hash_set是不够的,我们要引出另一种数据结构:hash_map
C++中的Map有三种类型:

映射底层实现是否有序数组是否可以重复能否更改数值查询复杂度增删复杂度
std::map红黑树有序不可重复不可修改O(logN)O(logN)
std::multimap红黑树有序可重复不可修改O(logN)O(logN)
std::unordered_map哈希表无序不可重复不可修改O(1)O(1)

在这道题里我们使用unordered_map效率最高。
代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            auto iter = map.find(target - nums[i]);
            if (iter != map.end()) {
                return {iter->second, i}; // 如果找到对应存储的值就返回
            }
            map.insert(pair<int, int>(nums[i], i)); // 添加一对值
        }
        return {};
    }
};

三数之和

力扣原题
接下来的这题是有点难度的。因为题目中要求了三元组不能有重复,所以如果使用之前我们的哈希表来做,则每次计算结果都要考虑是否有重复,去重的话,自己提交试试就知道了,直接超时没得说的。
这时哈希表搞不定,我们还有什么别的办法搞定呢,其实使用我们之前用过的双指针就能解决。先将数组排序,再将a+b+c=0化成寻找A+b+c=0,固定a用双指针寻找b和c就行。
代码如下:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end()); // 排序
        for (int i = 0; i < nums.size(); i++) {
            // 如果排序后第一个元素就大于零,则再求和不可能为零
            if (nums[i] > 0) {
                return res;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int l = i + 1;
            int r = nums.size() - 1;
            while (l < r) {
                if (nums[i] + nums[l] + nums[r] > 0) {
                    r--;
                }
                else if (nums[i] + nums[l] + nums[r] < 0) {
                    l++;
                }
                else {
                    // 存入找到的三元组
                    res.push_back(vector<int>{nums[i], nums[l], nums[r]});
                    // 去重逻辑应该放在找到一个三元组之后
                    while (r > l && nums[r] == nums[r - 1]) r--;
                    while (r > l && nums[l] == nums[l + 1]) l++;

                    // 找到答案时,双指针同时收缩
                    r--;
                    l++;
                }

            }
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值