算法系列--哈希表

哈希表

哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做哈希函数,存放记录的数组称做哈希表。

经典例题

  1. 遍历数组中每个元素,统计比它小的元素的个数

    leetcode 1365

    示例:nums[1,2,3,2,1,4], 每个元素小的数组[0,2,4,5], 所以返回[0,2,4,2,0,5]

    解题思路

    • 第一种思路,是排序后, 然后用哈希表记录每个数字比它小的元素的个数,最后遍历原数组,统计个数即可;其中排序后的数组中,首个不同数字的下标即为比它小的个数;时间复杂度 O(nlogn)
    • 第二种思路,直接遍历数组,用哈希表记录每个数字出现次数;时间复杂度O(n)
    // 快速排序 + 哈希表 O(nlogn)
    function fn (nums) {
      const arr = [...nums]
      // 排序
      nums.sort((a, b) => {
        return a - b
      })
      // 哈希表
      const hash = new Map()
      for (let i = 0, n = nums.length; i < n; i++) {
        if (hash.has(nums[i])) {
          continue
        } else {
          hash.set(nums[i], i)
        }
      }
    
      for (let i = 0, n = arr.length; i < n; i++) {
        arr[i] = hash.get(arr[i])
      }
    
      return arr
    }
    
    const nums = [8, 1, 2, 2, 3]
    const result = fn(nums)
    console.log('result', result)
    
    // 哈希表 o(n)
    function fn2(nums) {
      // 统计次数 (值为0-100)
      const countHash = new Array(101).fill(0)
      for (let i = 0, n = nums.length; i < n; i++) {
        const key = nums[i]
        countHash[key]++
      }
      // 统计累加次数
      const sumArr = [0]
      for (let i = 1, n = countHash.length; i < n; i++) {
        sumArr[i] = countHash[i - 1] + sumArr[i - 1]
      }
      // 返回数组
      const result = []
      for (let i = 0, n = nums.length; i < n; i++) {
        result[i] = sumArr[nums[i]]
      }
      return result
    }
    const nums = [8, 1, 2, 2, 3]
    const result = fn2(nums)
    console.log('result', result)
    
  2. 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

    leetcode 136

    示例:[2,3,3,2,4,5,5], 返回 4

    解题思路

    • 第一种思路:哈希表记录每个数字出现的次数;然后遍历哈希表找到出现次数为1的数字, 时间复杂度O(n);
    • 第二种思路:利用异或性质,遍历数组,两两异或,剩下那个即是出现一次的元素;
    • 异或特点:a ^ a = 0, 0^a=a, aba = a ab = 0^b= b;
    // 哈希表
    function fn1(nums) {
      let mySet = new Set()
      for (let i = 0, n = nums.length; i < n; i++) {
        let key = nums[i]
        if (mySet.has(key)) {
          mySet.delete(key)
        } else {
          mySet.add(key)
        }
      }
      return [...mySet][0]
    }
    
    // 异或
    function fn2(nums) {
      let result = 0
      for (let i = 0, n = nums.length; i < n; i++) {
        result = result ^ nums[i]
      }
      return result
    }
    
  3. 两句话中的不常见单词

    示例: A [‘we’, ‘are’, ‘good’], B [‘you’, ‘are’, ‘good’], 返回 [‘we’, ‘you’]

    解题思路

    • 遍历两个数组,用哈希表记录,每个单词出现的次数;
    • 遍历哈希表,找出次数为1的所有key;时间复杂度O(n)

    leetcode884

    // 哈希表
    function fn1(A, B) {
      let arr = (A + ' ' + B).split(' ')
      let map = new Map()
      let result = []
      // 统计出现次数
      for (let item of arr) {
        if (map.has(item)) {
          map.set(item, map.get(item) + 1)
        } else {
          map.set(item, 1)
        }
      }
      // 找出次数为1的key
      for (let [key, value] of map) {
        if (value === 1) {
          result.push(key)
        }
      }
      return result
    }
    
  4. 给你一个整数数组 nums ,如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组好数对 。

    返回好数对的数目。

    leetcode 1512

    解题思路

    • 首先想到暴力法,双层遍历,找到所有nums[i] == nums[j]. 时间复杂度 O(n^2);
    • 然后尝试降低时间复杂度, 第一次遍历统计每个数字出现的次数,用哈希表记录,key为数字,value为出现次数;第二次遍历哈希表,统计次数大于1的好数对总数; 时间复杂度 O(n)
    • 其中,如果出现次数为n, 好数对组合为 n*(n-1)/2;
    function fn1(nums) {
      let result = 0
      let map = new Map()
      // 第一次遍历,统计每个数字出现次数
      for (let item of nums) {
        let value = 1
        if (map.has(item)) {
          value = map.get(item) + 1
        }
        map.set(item, value)
      }
      // 第二次遍历,统计次数大于1的好数对总数
      for (let [key, value] of map) {
        if (value > 1) {
          let temp = value * (value - 1) / 2
          result += temp
        }
      }
      return result
    }
    
  5. 给你一个 严格升序排列 的正整数数组 arr 和一个整数 k ;请你找到这个数组里第 k 个缺失的正整数。

    leetcode 1539

    示例:arr = [1,4,5], k = 2; 缺失数列[2,3], 应返回3;

    解题思路:

    • 这道题不好想到用哈希表来解决,难住我一会儿;
    • 第一步,遍历数组arr, 用哈希表记录,key为arr[i], value任意值,不妨也设置成arr[i];
    • 第二步,遍历0至arr[arr.length-1], 如果map.get(i) 不存在,就push到缺失数列lostArr;
    • 第三步,返回缺失数列第K个值; 如果缺失数列不够长,则继续填充arr[arr.length-1] + 1;
    function fn(arr, k) {
      let lostArr = []
      let map = new Map()
      let n = arr.length
      for (let item of arr) {
    		map.set(item, item)
      }
      for (let i = 0; i <= arr[n-1]; i++) {
        if (!map.has(i)) {
          lostArr.push(i)
        }
      }
      while (lostArr.length < k) {
        let 
        let value = Math.max(arr[n-1], lostArr[lostArr.length - 1]) + 1
        lostArr.push(value)
      }
      return lostArr[k-1]
    }
    
  6. 设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对

    示例:

    输入: nums = [5,6,5], target = 11
    输出: [[5,6]]
    

    解题思路:

    • 方法1:快速排序+遍历统计,时间复杂度O(nlogn)
    • 方法2:遍历数组,用哈希表记录每个数字出现次数;遍历哈希表,统计配对数;时间复杂度O(n);
    function fn(nums, target) {
      let map = new Map()
      let result = []
      for (let key of nums) {
        let value = 1
        if (map.has(key)) {
          value = map.get(key) + 1
        }
        map.set(key, value)
      }
      for (let [key, value] of map) {
        console.log(key, value)
        let otherKey = target - key
        let otherValue = map.get(otherKey) || 0
        let minValue = Math.min(value, otherValue)
        if (key === otherKey) {
          minValue = value / 2
        }
        while(minValue >= 1) {
          result.push([key, otherKey])
          minValue--
        }
        map.set(otherKey, 0)
      }
      return result
    }
    
    let nums = [6,6,6], target = 12
    let result = fn(nums, target)
    console.log('result', result)
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值