leetcode——哈希表:快乐数

136 阅读4分钟

202. 快乐数

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

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

输入: n = 19
输出: true
解释: 12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了。
解题思路:给你一个n,最后就两种结果一个是返回的是1,一个是陷入循环(出现了已经出现的结果)。

IMG_5372.HEIC

/**
 * @param {number} n
 * @return {boolean}
 */
var isHappy = function(n) {
    let res = new Map()
    const sum = (n) => {
        let sum = 0
        while(n){
            sum += (n%10) ** 2 // 从最后一位数往前算
            n = Math.floor(n / 10)
        }
        return sum
    }
    while(true){
        if(res.has(n)) return false
        if (n === 1) return true
        res.set(n, 1)
        n = sum(n)
    }
};

1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,
请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

解题思路1:暴力for循环
解题思路2: 我们遍历到数字a时,用target减去a就会得到b,若b存在于哈希表中,我们就可以直接返回结果了。若 b不存在,那么我们需要将a存入哈希表,好让后续遍历的数字使用。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
// var twoSum = function(nums, target) {
//     let map = new Map();
//     for(let i = 0;i < nums.length;i++){
//         for(let j = i+1;j<nums.length;j++){
//             let sum = nums[i] + nums[j]
//             map.set(sum,[i,j])
//         }
//     }
//     return map.get(target)
// };
var twoSum = function(nums, target) {
    let map = new Map();
    for(let i = 0, len = nums.length; i < len; i++){
        if(map.has(target - nums[i])){
            return [map.get(target - nums[i]), i];
        }else{
            map.set(nums[i], i);
        }
    }
    return [];
};

454. 四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

我一开始想法是先放第一个数组a的一个数字,值作为key下标作为vlaue,然后找后面三个数组的三个成员加起来等于sum,sum作为key,各自index作为value。然后看-sum是否在map中,在的话就返回。
上面想法是有瑕疵的,不如先考虑a,b两个组,求其中两个数字之后(sum为key,出现次数为value)。再考虑c,d两个其中两个元素的和sum2,如果-sum2存在map中,就说明这就是其中一个结果。

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @param {number[]} nums3
 * @param {number[]} nums4
 * @return {number}
 */
var fourSumCount = function(nums1, nums2, nums3, nums4) {
    let map = new Map();
    let count = 0;
    for(const n1 of nums1){
        for(const n2 of nums2){
            const sum = n1 + n2;
            map.set(sum, (map.get(sum) || 0) + 1)
        }     
    }
    for(const n3 of nums3){
        for(const n4 of nums4){
            const sum2 = n3 + n4;
            count += (map.get(0 - sum2) || 0)
        }
    }
    return count
};

383. 赎金信

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
输入: ransomNote = "aa", magazine = "aab"
输出: true
/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function(ransomNote, magazine) {
    // 先设置26个字母的数组,字母出现一次就+1
    // 返回的字典没有负号
    let map = new Array(26).fill(0);
    let base = 'a'.charCodeAt(); // a的ascall码
    for(let i of magazine){
        map[i.charCodeAt() - base]++
    }
    for(let j of ransomNote){
        map[j.charCodeAt() - base]--
        if(map[j.charCodeAt() - base] < 0){//如果数组出现符号
            return false
        }
    }
    return true
};

15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 ab,c ,
使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
输入: nums = [-1,0,1,2,-1,-4]
输出: [[-1,-1,2],[-1,0,1]]

哈希解法:两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组,比如:[1,0,-1,-1]。

双指针解法:

image.png image.png 主要这题目有去重问题: a去重:if(i>0 && nums[i]=nums[i-1]) continue b,c去重:
while (L<R && nums[L] == nums[L+1]) L++; while (L<R && nums[R] == nums[R-1]) R--;

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    let len = nums.length;
    let res = [];
    // 如果数组元素连三个都没有直接返回[]
    if(len < 3) return [];
    nums.sort((a, b) => a - b);
    for(let i = 0;i < len-2; i++){
         if(nums[i] > 0) break;//最小的数字都大于0了,直接跳出整个循环
         // a去重 [0,0,-1,-1,-1,1],[-1,0,0,0,1]
         // [-1,-1,2]只找了一次
         if(i > 0 && nums[i] == nums[i-1]) continue; 
         let L = i+1;
         let R = len-1;
         while(L < R){
            //虽然里面还有两个循环,但是整体的L和R移动的时间内复杂度还是o(n)
            const sum = nums[i] + nums[L] + nums[R];
            if(sum == 0){
                res.push([nums[i],nums[L],nums[R]]);
                // b,c 去重 [-4,2,2,2,2]
                while (L<R && nums[L] == nums[L+1]) L++; // 去重
                while (L<R && nums[R] == nums[R-1]) R--; // 去重
                L++;
                R--;
            }
            else if (sum < 0) L++;
            else if (sum > 0) R--;
        }
    }
    return res
};

18. 四数之和

输入: nums = [1,0,-1,0,-2,2], target = 0
输出: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

这题目target不固定不一定是0,解题思路还是和上面三数求和一样但是多了一个j循环,感觉像是四个循环(两个for两个while)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    let res = [];
    let len = nums.length
    if(len < 4) return [];
    nums.sort((a, b) => a - b);
    for(let i = 0;i < len-3;i++){
        // 去重i
        if(i > 0 && nums[i] === nums[i-1]) continue;
        for(let j = i + 1; j < len - 2; j++) {
             // 去重j
             if(j > i + 1 && nums[j] === nums[j - 1]) continue;
             let L = j+1;
             let R = len - 1;
             while(L < R){
                 let sum = nums[i] + nums[j] + nums[L] + nums[R];
                 if(sum === target){
                     res.push([nums[i],nums[j],nums[L],nums[R]]);
                     while(L < R && nums[L] === nums[L + 1]) L++;
                     while(L < R && nums[R] === nums[R - 1]) R--;
                     L++;
                     R--;
                 }
                 else if (sum < target) L++;
                 else if (sum > target) R--;
             }
        }
    }
    return res;
};