哈希表
哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做哈希函数,存放记录的数组称做哈希表。
经典例题
-
遍历数组中每个元素,统计比它小的元素的个数
示例: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,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 }
-
两句话中的不常见单词
示例: A [‘we’, ‘are’, ‘good’], B [‘you’, ‘are’, ‘good’], 返回 [‘we’, ‘you’]
解题思路
- 遍历两个数组,用哈希表记录,每个单词出现的次数;
- 遍历哈希表,找出次数为1的所有key;时间复杂度O(n)
// 哈希表 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 }
-
给你一个整数数组 nums ,如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组好数对 。
返回好数对的数目。
解题思路:
- 首先想到暴力法,双层遍历,找到所有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 }
-
给你一个 严格升序排列 的正整数数组
arr
和一个整数k
;请你找到这个数组里第k
个缺失的正整数。示例: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] }
-
设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对
示例:
输入: 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)