跟着代码随想录练算法——哈希表(js)

242. 有效的字母异位词

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isAnagram = function(s, t) {
    if(s.length !== t.length) return false
    let arr = new Array(26)
    arr.fill(0)
    for(let i = 0; i < s.length; i++){
        arr[s[i].charCodeAt() - 97] ++
    }
    for(let i = 0; i < t.length; i++){
        arr[t[i].charCodeAt() - 97] --
    }
    for(let i = 0; i < 26; i++){
        if(arr[i] !== 0) return false
    }
    return true

};

383. 赎金信

/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function(ransomNote, magazine) {
    if(ransomNote.length > magazine.length) return false
    let arr = new Array(26)
    arr.fill(0)
    for(let i = 0; i < magazine.length; i++){
        arr[magazine[i].charCodeAt() - 97] ++
    }
    for(let i = 0; i < ransomNote.length; i++){
        arr[ransomNote[i].charCodeAt() - 97] --
    }
    for(let i = 0; i < 26; i++){
        if(arr[i] < 0) return false
    }
    return true

};

438. 找到字符串中所有字母异位词

暴力求解:

以字符串 s的每个下标为起始位置判断长度为 p.length 的子串是否符合条件

可以过,但是时间复杂度为 O(mn)mn分别为 s p 的长度

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    if(s.length < p.length) return []
    let arr = new Array(26)
    arr.fill(0)
    let len = p.length
    for(let i = 0; i < len; i++){
        arr[p[i].charCodeAt() - 97] ++
    }
    let ans = []
    for(let i = 0; i <= s.length - p.length; i++){
        let each = [...arr]
        let sum = len
        for(let j = 0; j < p.length; j++){
            if(each[s[i+j].charCodeAt() - 97] <= 0)
                break
            else{
                each[s[i+j].charCodeAt() - 97] --
                sum --
            }
        }
        if(sum === 0) ans.push(i)
    }
    return ans
    
};

滑动窗口

  • arrP存放p中字母出现次数,arrS存放当前窗口s子串中字符出现的次数
  • 当两者相等时,满足条件,将开始位置push进ans数组
/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    if(s.length < p.length) return []
    let arrP = new Array(26)
    arrP.fill(0)
    let arrS = new Array(26)
    arrS.fill(0)
    for(let i = 0; i < p.length; i++){
        arrP[p[i].charCodeAt() - 97] ++
        arrS[s[i].charCodeAt() - 97] ++
    }
    let ans = []
    if(arrP.toString() === arrS.toString()) ans.push(0)
    for(let i = 0; i < s.length - p.length; i++){
        arrS[s[i].charCodeAt() - 97] --
        arrS[s[i + p.length].charCodeAt() - 97] ++
        if(arrP.toString() === arrS.toString()){
            ans.push(i + 1)
        }
    }
    return ans
    
};

49. 字母异位词分组

  • 满足异位词的字符串中每个字母出现的次数相同,我们可以将出现的次数作为map的key
  • 那么出现的次数如何用什么数据结构存储呢,数组?可是在js中数组本质是对象,就算是元素都相等的数组,只要不是同时指向同一个内存都是不相等的,所以最好用字符串,将数组join()一下就行了
  • 遍历strs,计算每个strs[i]的arr,判断map中是否有,没有就set,有的话就在对应的value中push进去当前的字符串 strs[i]
  • 最终将使用forEach遍历map,将value放在ans中返回
/**
 * @param {string[]} strs
 * @return {string[][]}
 */
var groupAnagrams = function(strs) {
    let map = new Map()
    let arr = new Array(26)
    for(let i = 0; i < strs.length; i++){
        arr.fill(0)
        for(let j = 0; j < strs[i].length; j++){
            arr[strs[i][j].charCodeAt() - 97]++
        }
        if(!map.has(arr.join())) map.set(arr.join(),[strs[i]])
        else map.get(arr.join()).push(strs[i])
    }
    let ans = []
    map.forEach((value) => {
        ans.push(value)
    })
    return ans
};

349. 两个数组的交集

  • 遍历 nums1 放入 set中(对nums1中的元素去重了)
  • 遍历 nums2 ,如果set 中有,则为交集元素,将该元素放入 ans,并且删掉set中的这个元素,达到对nums2 中的元素去重
  • 最后返回 ans
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    let set = new Set()
    let ans = []
    for(let i = 0; i < nums1.length; i++){
        if(!set.has(nums1[i])) set.add(nums1[i])
    }
    for(let i = 0; i < nums2.length; i++){
        if(set.has(nums2[i])){
            ans.push(nums2[i])
            set.delete(nums2[i])
        } 
    }
    return ans
};

350. 两个数组的交集 II

  • 遍历 nums1,使用数组 arr 存储 nums1 中元素出现的次数
  • 遍历 nums2,如果 arr 数组中该元素出现的次数 > 0 ,则添加进 ans,并且出现次数需要 -1
  • 返回 ans
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersect = function(nums1, nums2) {
    let arr = new Array(1005).fill(0)
    for(let i = 0; i < nums1.length; i++){
        arr[nums1[i]] ++
    }
    let ans = []
    for(let i = 0; i < nums2.length; i++){
        if(arr[nums2[i]] !== 0){
            ans.push(nums2[i])
            arr[nums2[i]] --
        }
    }
    return ans
};

202. 快乐数

  • set 记录出现过得数
  • 不断使用每一位平方和替换当前数,如果是 1,就返回 true
  • 如果不是 1,就放入 set 中,当下次再出现这个数证明进入无限循环,返回 false
/**
 * @param {number} n
 * @return {boolean}
 */
var isHappy = function(n) {
    if(n === 1) return true
    let set = new Set()
    while(1){
        let sum = 0
        while(n){
            sum += (n%10)*(n%10)
            n = Math.floor(n/10)
        }
        n = sum
        if(sum === 1) return true
        if(set.has(sum)) return false
        set.add(sum)
    }
    
};

1. 两数之和

/**
 * @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++){
        if(map.has(target - nums[i])){
            return [map.get(target-nums[i]),i]
        }else{
            map.set(nums[i],i)
        }
    }
    return []
};

454. 四数相加 II

  • 两层 for 循环遍历 nums1 和 nums2,将和作为key存到 map中,对应的值是该和出现的次数(要记录符合情况的所有数量)
  • cnt 用来计数,表示符合情况的数量
  • 再来两层 for 循环遍历 nums3 和 nums4,在map中找对应的-(nums3+nums4),找到就将其value累加到cnt
var fourSumCount = function(nums1, nums2, nums3, nums4) {
    let cnt = 0
    let map = new Map()
    let n = nums1.length
    for(let i=0; i < n; i++ ){
        for(let j = 0; j < n; j++){
            if(!map.has(nums1[i]+nums2[j])){
                map.set(nums1[i]+nums2[j],1)
            }else{
                map.set(nums1[i]+nums2[j],map.get(nums1[i]+nums2[j])+1)
                
            }
        }
    }
    for(let i=0; i < n; i++ ){
        for(let j = 0; j < n; j++){
            
            if(map.has(-nums3[i]-nums4[j])){
                cnt += map.get(-nums3[i]-nums4[j])
            }
         
        }
    }
   
    return cnt
};

15. 三数之和

  • 先将数组中的数字排序

  • 使用一层for循环遍历数组,如果nums[i]与上一个元素相等就跳过

  • 设置左右两个指针,left,right

  • 判断当前三个指针i,left,right 的和与0的关系

  • 如果 == 0,则将三者对应元素作为一种符合情况的答案放入ans,同时需要移动left,right,注意需要去重

  • 如果 > 0 ,则 right –

  • 如果 < 0 , 则 left ++

  • 剪枝:看了题解之后,给for循环入口加剪枝:

    • 如果当前元素已经大于目标值(0),直接break

    • 当前元素加上最大的两个元素比目标值(0)还小,直接 continue;

    • 当前元素加上与它连续的两个元素(当前情况的最小两个元素)比目标值(0)还大,直接break

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    if(nums.length <= 2) return []
    let ans = []
    nums.sort((a,b) => a-b)
    for(let i = 0; i < nums.length - 2; i++){
        if(nums[i] > 0) break
        if(nums[i] + nums[nums.length - 1] + nums[nums.length - 2] < 0) continue
        if(nums[i] + nums[i+1] + nums[i+2] > 0) break
        if(i>0 && nums[i] === nums[i-1]) continue
        let left = i + 1
        let right = nums.length - 1
        while(left<right){
            if(nums[i] + nums[left] + nums[right] === 0){
                ans.push([nums[i] , nums[left] , nums[right]])
                left ++
                right --
                while(nums[left]==nums[left-1]){
                    left ++
                }
                while(nums[right]==nums[right+1]){
                    right --
                }
            }else if(nums[i] + nums[left] + nums[right] < 0){
                left ++
            }else{
                right -- 
            }
        }
    }
    return ans
};

18. 四数之和

  • 与三数之和一样,只是多一层 for 循环
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    if(nums.length <= 3) return []
    let ans = []
    nums.sort((a,b) => a - b)
    for(let i = 0; i < nums.length - 3; i++){
        if(nums[i] + nums[nums.length -1] + nums[nums.length -2] + nums[nums.length -3] < target) continue
        if(nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break
        if(i > 0 && nums[i] === nums[i-1]) continue
        for(let j = i + 1; j < nums.length - 2; j++){
            if(nums[i] + nums[j] + nums[nums.length -1] + nums[nums.length -2] < target) continue
            if(nums[i] + nums[j] + nums[j+1] + nums[j+2] > target) break
            if( j > i + 1 && nums[j] === nums[j-1]) continue
            let l = j + 1
            let r = nums.length - 1
            while(l<r){
                if(nums[i]+nums[j]+nums[l]+nums[r] === target){
                    ans.push([nums[i],nums[j],nums[l],nums[r]])
                    l ++
                    r --
                    while(nums[l]==nums[l-1]){
                        l ++
                    }
                    while(nums[r]==nums[r+1]){
                        r --
                    }
                }else if(nums[i]+nums[j]+nums[l]+nums[r] > target){
                    r --
                }else{
                    l ++
                }
            }
        }
    }
    return ans
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值