哈希表- 快乐数&两数之和&四数相加II

哈希表

快乐数(leetcode.202)

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

哈希表

快乐数,这其实也就是找是否循环的问题,首先我们需要先通过一个函数得到每个位置上的和。

从个位上开始,n % 10这个得到的就是个位上的数字了,然后我们需要将个位截断,也就是得到除了个位上的数,这其实并不难n / 10整除10就行了,这样循环就可以得到sum。

接下来是判断是否为快乐数,那么就是看是否发生循环,也就是某个元素是否重复出现,我们使用unordered_set来实现,定义一个集合,将传入的数放进集合,之后用循环开始遍历获取sum,得到sum后看看集合里有没有,有的话,那么直接返回false,如果等到满足条件了,直接退出返回true。

class Solution {
public:
    int getSum(int n){
        int sum = 0;
        while(n > 0){
            int num = n % 10;
            sum += num * num;
            n /= 10;
        }
        return sum;
    }

    bool isHappy(int n) {
        std::unordered_set<int> set_num;
        set_num.insert(n);
        
        int sum = getSum(n);
        while(sum != 1){
            if(set_num.find(sum) != set_num.end()){
                return false;
            }
            set_num.insert(sum);
            sum = getSum(sum);
        }
        return true;
    }
};

双指针思路

什么的思路可以知道如果不是快乐数,那么就会进入循环,那么我们可以使用双指针的思路,让快指针每次运行两次,慢指针每次运行一次,如果快指针追上了慢指针说明是循环的,就不是快乐数了。这里注意循环条件,是快指针没有到1,说明没找到,继续找,因为如果是快乐数,那么再求和,也依然是1。

bool isHappy(int n) {
        int fast = getSum(getSum(n));
        int slow = n;
        while(fast != 1){
            if(slow == fast){
                return false;
            }
            else if(fast == 1){
                return true;
            }
            slow = getSum(slow);
            fast = getSum(getSum(fast));
        }
        return true;
    }

两数之和 (leetcode.1)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

哈希表使用时机始终是一个比较难以判断的问题,什么时候使用哈希法,当查询一个元素时候出现过,或者一个元素是否在集合里,就要想到哈希法。

我们之前的有效字母异位词,我们想到哈希,我们是用数组实现的,因为字母是26个,所以定义了一个hash[26],遍历一个字符串,可以得到每个字符的出现次数并记录到数组中,之后再遍历另一个字符串,减去之前的各个字符个数,这里是不是比对了元素是否出现?考虑哈希

两个数组的交集,我们是需要知道一个数组中元素的出现的,然后,遍历另一个数组,看看能不能找到元素,也就是元素是否在集合里?哈希

前面的快乐数,依旧是把出现的元素放进集合,判断下一次和是否出现过,出现表示进入循环不是快乐数,一个元素是否在集合,哈希

那么这里的两数之和,这里我们不但得知道这个元素是否出现过,还得知道下标,那么是不是可以通过map映射的方式。

因为是两个数,那么注意到,在遍历数组的时候,可以将元素放入unordered_map,那么得考虑key - value怎么存储了,key = 值,value = 索引,我们可以直接去找键,找到了,就直接返回索引,找不到,就存入map

map目的用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的(也就是相加等于target)

map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map<int,int> index_num;
        for(int i = 0; i < nums.size(); ++i){
            auto it = index_num.find(target-nums[i]);
            if(it != index_num.end()){
                return {i,it->second};
            } else{
                index_num[nums[i]] = i;
            }
        }
        return {};
    }
};

四数相加II (leetcode.454)

给你四个整数数组 nums1nums2nums3nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n

  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

    解题思路

    1. 将四数之和转化为两数之和
    2. 先计算 A+B 的所有和及其频次
    3. 遍历 C+D,查找相反数
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        std::unordered_map<int,int> sumAB;
        for(const auto& n1 : nums1){
            for(const auto& n2 : nums2){
                ++sumAB[n1+n2];
            }
        }
        int count = 0;
        for(const auto& n3 : nums3){
            for(const auto& n4 : nums4){
                int target = -(n3+n4);
                auto it = sumAB.find(target);
                if(it != sumAB.end()){
                    count += it->second;
                }
            }
        }
        return count;
    }
};

这里在总结一下哈希表核心应用场景,以空间换时间,很重要的思想

统计频次

  • 键:元素/和/状态
  • 值:出现次数

快速查找

  • 键:需要查找的值
  • 值:索引/相关信息

缓存中间结果

  • 键:计算过程中的状态
  • 值:对应的结果
posted @ 2026-03-06 18:55  wenyiGamecpp  阅读(20)  评论(0)    收藏  举报