哈希表:JavaScript🧨Set/Map基本操作、242.有效的字母异位词、349. 两个数组的交集、202.快乐数

99 阅读5分钟

Set集合操作

ES6新加Set集合:无序且唯一

// 数组去重:先放入Set集合中去重,再解构回数组中
let arr = [1, 1, 2, 3, 3]
let set1 = new Set(arr) // 无序且唯一
let arr1 = [...set1] // 再将Set展开放回数组中
console.log(arr1)

// 判断是否在集合中:Set.has()
let set2 = new Set(arr)
let has = set2.has(5)
console.log(has)

// 求交集:利用数组的filter()遍历一个,判断另外一个里有没有它
let arr3 = [3, 4]
let set3 = new Set(arr3); // 求set1与set3的交集
let set4 = new Set([...set1].filter(item => set3.has(item)))
console.log(set4)
// 添加add 删除delete 判断是否包含has
let mySet = new Set()

mySet.add(1) // add向Set中添加元素
mySet.add(5)
mySet.add(5)
// 添加重复元素会自动去掉,map元素:1, 5
mySet.add('lsy')
// 可以添加字符串
let obj = { a: 1 }
mySet.add(obj)
mySet.add({ a: 1 }) // 地址不同,成功添加
// 对于对象是判断对象在内存的地址来判断是否是同一个

let has = mySet.has(obj)
// has()快速判断是否包含元素

mySet.delete(5)
// delete()删除元素

// 遍历Set:for..of..直接迭代Set、Set.keys()、Set.values()结果都一样:Set的key和value一样
for (let item of mySet) console.log(item) // 直接迭代

for (let item of mySet.keys()) console.log(item) // 遍历keys

for (let item of mySet.values()) console.log(item) // 遍历values

for (let [key, value] of mySet.entries()) console.log([key, value]) // entries()遍历[keys, values]

// Set 和 数组互转
let myArr = [...mySet] // Set解构到数组
let myArr1 = Array.from(mySet) // Array.from转为数组
let mySet1 = new Set([1, 2]) // 数组转为Set

// 求交集 同时在两个集合中
let jiaoji = new Set([...mySet].filter(item => mySet1.has(item)))
// 求差集 一个集合中有,另一个没有
let chaji = new Set([...mySet].filter(item => !mySet1.has(item)))

new Set()创建一个 Set 对象

  • add()添加元素
  • delete()删除元素
  • has()判断 Set 中是否包含
  • 遍历 Set(for..of..
    • 直接遍历 Set
    • 遍历Set.keys()
    • 遍历Set.values()
    • Set.entries(),获取[key, value]k-v值相同
  • Set 和 数组互转:
    • [...Set]解构到数组、Array.from(Set)转为数组
    • new Set([1, 2])数组转为Set
  • 求交集:let jiaoji = new Set([...Set1].filter(item => Set2.has(item)))
  • 求并集:let chaji = new Set([...Set1].filter(item => !Set2.has(item)))

Map字典操作

// 键值对的一一映射
const m = new Map()

// 添加键值对:set
m.set('a', 'aa')
m.set('b', 'bb')

// 查:get
let v = m.get('b')

// 改
m.set('a', 'new aa') // 重新赋值

// 删除键值对:delete
m.delete('b') // 键值
m.clear() //删除所有元素
  • set()添加,重新set()赋值:修改
  • get()查:获取
  • delete(k)删除、clear()删除全部

基础知识

  • 用来快速判断元素是否出现集合内
  • 哈希函数,把值映射为哈希表的索引
  • 哈希碰撞,映射到同一个索引时
    • 拉链法

image.png

  • 线性探测:表的大小 > 数据总大小,多的放到冲突的下一个

image.png

  • 常见的三种哈希结构
    • 数组
    • set(集合)
    • map(映射)

哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法 面试题目中,需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!

242.有效的字母异位词

242. 有效的字母异位词 两个字符串,字母出现的次数相同√不同就× image.png

数组哈希表映射

242.有效的字母异位词.gif 技巧,先遍历s数组,记录到函数中++,再遍历t数组,函数值--,如果函数值还全为0,就说明全相等

用数组映射
  • 字母的ASCII码映射到数组下标
  • '字符串'.chatCodeAt()获取ASCII码映
  • for of循环遍历获取元素
  • 技巧:遍历第一个时数组对应位置+1,第二个时-1,如果最后全为0说明相同
返回字符串给定index索引的 UTF-16 码 默认index为0 超出长度返回NaN
"ABC".charCodeAt(0) // returns 65:"A"
"ABC".charCodeAt(1) // returns 66:"B"
"ABC".charCodeAt(2) // returns 67:"C"
"ABC".charCodeAt(3) // returns NaN
/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isAnagram = function(s, t) { // 判断两个字符串出现的字母次数相同
    let hash = new Array(26).fill(0) // 数组=>哈希表:映射为26个字母数组
    let base = 'a'.charCodeAt() // 得到a的ASCII码,charCodeAt()获取ASCII码
    for (let i of s) { // for of遍历字符串,直接得到元素
        hash[i.charCodeAt() - base]++
    }
    for (let i of t) { // let有块级作用域所以可以重复用i
        hash[i.charCodeAt() - base]--
    }
    for (let i of hash) { // 遍历最后得到的数组
        if (i !== 0) return false // 有不为0的直接false
    }
    return true
};

349. 两个数组的交集

349. 两个数组的交集 image.png image.png

Set

  1. nums1指向大的那个,放到Set里可以少遍历元素
  2. 遍历nums2Set.has()快速判断元素是也在Set里,如果在就加入新Set
  3. 将最后新Set再转为数组
  • 技巧:数组.filter()直接会遍历每个元素,执行回调函数,将过滤后(return返回值为true)的元素放到一个新数组中返回
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    if (nums1.length < nums2.length) {
        let temp = nums1
        nums1 = nums2
        nums2 = temp
    } // 把nums1指向大的那个,放到Set里可以少遍历元素
    let set = new Set(nums1)
    let arr = new Array(0) // 数组存结果
    for (let i = 0; i < nums2.length; i++) {
        if (set.has(nums2[i])) { // 如果在set集合中就加入result集合
            arr.push(nums2[i])
        }
    }
    let res = new Set(arr)
    return Array.from(res) // 把集合转为数组Array.from
};
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    if (nums1.length > nums2.length) {
        let temp = nums1
        nums1 = nums2
        nums2 = temp
    }
    let set = new Set(nums1);
    return [...new Set(nums2.filter(item => set.has(item)))]
};

202.快乐数

202. 快乐数 用每个位置上数字的平方和替换这个数,如果最后为1,则为快乐数,如果无限循环就不是

  • **无限循环:**计算出的sum值和之前已经算过的一样-->会进入循环
  • 把每次计算的sum值都存入set集合
    • 如果集合中已经有这个数,说明会死循环!false
    • 如果没有这个数
      • **sum==1**:为快乐数,true
      • 不为1:加入**set集合**
  • 想到用哈希表:可以快速找集合里面是不是已经有这个sum值

image.png

Set

/**
 * @param {number} n
 * @return {boolean}
 */
function getSum (n) {
    let sum = 0
    while (n) {
        let x = n % 10 // 取数
        sum += x * x
        n = Math.floor(n / 10) // 去掉已经取的
    }
    return sum
}

var isHappy = function(n) { // 用Set求,如果sum值出现过就说明会无限循环
    let sum = getSum(n)
    let set = new Set()
    while (sum !== 1) { // 不是快乐数,就重复操作
        if (set.has(sum)) {
            return false // 如果sum值出现过就说明会无限循环=不是快乐数
        } else {
            set.add(sum) // 加入集合,再更新sum
            sum = getSum(sum)
        }
    } // 出循环=1是快乐数
    return true
};