代码随想录算法训练营第六天| 242.有效的字母异位词、 349. 两个数组的交集 、202. 快乐数 、 1. 两数之和 「哈希表」

161 阅读5分钟

242.有效的字母异位词

题目
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

思路
分别设置两个map集合,对两个不同的字符串中的每个字符进行统计,map中key为字符值,value为字符出现的次数。 对两个map集合进行比较:先比较map集合的长度,对键的个数进行比较,如果不同返回false,再对两个集合中的键值进行精确比较,如果有不同,直接返回false,最后返回true。

代码

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isAnagram = function(s, t) {
    const stat = (s) => {
        const obj = new Map()
        for (let i = 0;i<s.length;i++) {
            if (obj[s[i]]) {
                obj[s[i]]++
            }else {
                obj[s[i]]=1
            }
        }
        return obj
    }
    // console.log(stat(s))
    const statS = stat(s)
    const statT = stat(t)
    const compare = (s,t) => {
        if (Object.keys(s).length!==Object.keys(t).length) return false
        for (let k in s) {
            if (s[k]!==t[k]) {
                return false
            }
        }
        return true
    }
    return compare(statS,statT)
};

优化:

看了代码随想录的思路,自己重新写了一遍:设置一个数组,因为字符串中只包含小写字母,所以设置索引值为0-25,对第一个字符串中的每一个字符进行遍历,减去'a'的unicode编码,得到和索引对应的值,将数组中该索引对应值+1,对第二个字符串中的每一个字符进行遍历,按照上面的方法,对数组中该索引对应值-1,最后判断数组中的每个值是否等于0,如果不等于0直接返回false,最后返回true。 注意:在对数组进行遍历的过程中,利用forEach方法不可以用return返回,return只能跳出循环,也不能用every方法遍历途中return返回,因为every中只要执行到return就会直接返回。可以用for of方法,实现判断条件返回。

代码

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

总结:

第一次接触哈希表,对基本概念都能理解,无论是数组还是set、map都是之前学到的东西,但是实际应用的时候会有点想当然,想不到最优的方法,也许会把问题弄的复杂。

349. 两个数组的交集

题目
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

思路
?看到这题感觉好简单是我的错觉吗,对nums1使用filter方法,返回在nums2中存在的元素,最后使用扩展运算符搭配new Set直接输出结果

代码

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    const arr = nums1.filter(el=>nums2.indexOf(el)!==-1)
    console.log(...new Set(arr))
    return [...new Set(arr)]
};

优化:

看了一下代码随想录的思路,是设置一个set集合,放入较长的数组,然后对较短的数组进行遍历,如果较长的数组中存在较短数组中的某个值,就放入一个新的set集合中,最后输入set转换后的数组。

代码

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    if (nums1.length<nums2.length) {
        const tmp = nums2
        nums2 = nums1
        nums1 = tmp
    }
    const numsSet = new Set(nums1)
    const resSet = new Set()
    for (let k of nums2) {
        numsSet.has(k) && resSet.add(k)
    }
    return [...resSet]
};

总结:

耍一哈

const intersection = (nums1,nums2) => [...new Set(nums1.filter(el=>nums2.indexOf(el)!==-1))]

1. 两数之和

题目
编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为:

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

思路
首先得到一个数的每个位置上的数字平方和的结果,然后声明一个set集合,对这个结果进行判断,如果为1就返回true,如果不为1,先判断这个结果是否在set集合中,不在就把这个结果放进一个set集合中,然后对整个函数进行一次递归调用,如果在set集合中就返回false。

代码

/**
 * @param {number} n
 * @return {boolean}
 */
var isHappy = function(n) {
    const resSet = new Set()
    const ishappy = (n) => {
    const arr = []
    while (n/10!==0) {
        arr.push(parseInt(n%10))
        n = parseInt(n/10)
    }
    // arr.push(n)
    console.log(arr)
    let res = arr.reduce((prev,cur)=>{
        return prev+cur*cur
    },0)
    
    if (res===1){
        return true
    }else {
        if (resSet.has(res)){
            return false
        }else {
            resSet.add(res)
            console.log(resSet)
            return ishappy(res)
        }
    }
    }
    return ishappy(n)
};

优化:

自己的代码执行的时候用了很久,我就知道自己的代码肯定是比较笨的那一种,看了代码随想录的思路,感觉整体思路是一样的,但是他是用一个函数获取一个数的每个位置上的平方和,然后设置一个无限循环,内部对return的条件进行判断,如果都不生效,就重新获取当前的结果的每个位置上的平方和再次判断,时间复杂度更低。

代码

var isHappy = function (n) {
  const getRes = (n) => {
    const arr = []
    while (n / 10 !== 0) {
      arr.push(parseInt(n % 10))
      n = parseInt(n / 10)
    }
    // arr.push(n)
    // console.log(arr)
    let res = arr.reduce((prev, cur) => {
      return prev + cur * cur
    }, 0)
    return res
  }
  let res = getRes(n)
  const resSet = new Set()

  while (true) {
    if (res === 1) {
      return true
    }
    if (resSet.has(res)) {
      return false
    }
    resSet.add(res)
    res = getRes(res)
  }
};

总结:

这道题自己其实已经把大概思路想出来,加深了对哈希表的理解。

1. 两数之和

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

思路
只会暴力解法。

代码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
  const arr = []
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] + nums[j] === target) {
        arr.push(i)
        arr.push(j)
      }
    }
  }
  return arr
};

优化:

看了代码随想录的思路,是先声明一个map集合,然后对数组进行一次遍历,取数组中每一个值与目标值的差值,在map集合中寻找这个差值,如果找到,就把当前值和差值的下标放入一个数组中返回,如果找不到,就把当前值和下标以键值对的形式存入map集合中。

代码

var twoSum = function(nums, target) {
    const twoMap = new Map()
    let res = []
    for (let i = 0;i<nums.length;i++) {
        let diff = target - nums[i]
        if (twoMap.has(diff)) {
            res.push(i,twoMap.get(diff))
            break
        }else {
            twoMap.set(nums[i],i)
        }
    }
    // console.log([...twoMap.values()])
    return res
};

总结:

希望自己能在不停做题的过程中更加熟练对哈希表的应用,比较难的其实是思路怎么想出来。

Day6总结

第一次写关于哈希表的题,有些生疏,但是能使用,一些题目大体思路是正确的,可能是逻辑不是最优的,感觉哈希表比链表简单一丢丢,毕竟数组setmap都是es6学过的知识。