hash表
基本概念
哈希表是根据关键码的值而直接进行访问的数据结构,比如数组就是一张哈希表(表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素)。

解决的问题
一般哈希表都是用来快速判断一个元素是否出现集合里。
hash碰撞

如图所示,小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞。
哈希碰撞的两种解决方法
- 拉链法
- 将发生冲突的元素存储在链表中, 这样就可以通过索引找到小李和小王了。
- 拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。
- 线性探测法
- 依靠哈希表中的空位来解决碰撞问题。
- 例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。
- 要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放冲突的数据了。
常见三种hash结构
- 数组
- set(集合)
- map(映射)
总结
- 当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
- 哈希法是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
示例
242.有效的字母异位词

解答:
var isAnagram = function(s, t) {
if(s.length !== t.length) return false;
// 数组也是一种hash结构
const arr = new Array(26).fill(0);
const base = 'a'.charCodeAt();
for (const x of s){
arr[x.charCodeAt() - base]++;
}
for (const x of t){
if(!arr[x.charCodeAt() - base]) return false;
arr[x.charCodeAt() - base]--;
}
return true;
};
349. 两个数组的交集

解答:
var intersection = function(nums1, nums2) {
// 根据数组大小进行交换数组,将大一点的数组作为基准,一直将nums1作为大一点的数组
if (nums1.length < nums2.length) {
[nums1, nums2] = [nums2, nums1];
}
let set1 = new Set(nums1);
let set2 = new Set();
for (const x of nums2){
set1.has(x) && set2.add(x);
}
return Array.from(set2);
};
第202题. 快乐数

解答:
var isHappy = function(n) {
let m = new Map();
// 求n各个位上的数的平方和
const getSum = (n) => {
let sum = 0;
while (n){
sum += ((n % 10) * (n % 10));
n = Math.floor(n / 10);
}
return sum;
}
while (true){
// 如果出现了之前已经出现过的数,说明出现了死循环,就是false
if(m.has(n)) return false;
if(n === 1) return true;
m.set(n, 1);
n = getSum(n);
}
};
1. 两数之和

解答:
var twoSum = function(nums, target) {
let map = new Map();
for (let i = 0; i < nums.length; i++){
if(map.has(target - nums[i])) {
return [i, map.get(target - nums[i])];
} else {
map.set(nums[i], i);
}
}
};
第454题.四数相加II

解答:
var fourSumCount = function(nums1, nums2, nums3, nums4) {
let count = 0;
let twoMap = new Map();
for (let x of nums1) {
for (let y of nums2) {
// 可能不存在,所以隐式做一个判断
twoMap.set(x + y, (twoMap.get(x + y) || 0) + 1);
}
}
for (let x of nums3) {
for (let y of nums4) {
// 可能不存在,所以隐式做一个判断
count += (twoMap.get(-(x + y)) || 0);
}
}
return count;
};
383. 赎金信

解答:
var canConstruct = function(ransomNote, magazine) {
let arr = new Array(26).fill(0);
const base = 'a'.charCodeAt();
for (const x of magazine){
arr[x.charCodeAt() - base]++;
}
for (const x of ransomNote){
if(!arr[x.charCodeAt() - base]) return false;
arr[x.charCodeAt() - base]--;
}
return true;
};
第15题. 三数之和

解答:
var threeSum = function(nums) {
if(nums.length < 3) return [];
let res = [];
nums.sort((a, b) => a - b);
for(let i = 0; i < nums.length - 2; i++){
if(nums[i] > 0) break;
// a 去重
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1, right = nums.length - 1;
while (left < right){
let sum = nums[i] + nums[left] + nums[right];
if(sum > 0){
right--;
continue;
} else if(sum < 0){
left++;
continue;
} else {
res.push([nums[i], nums[left], nums[right]]);
}
// b c 去重
while (left < right && nums[left] === nums[++left]);
while (left < right && nums[right] === nums[--right]);
}
}
return res;
};
第18题. 四数之和

解答:
var fourSum = function(nums, target) {
const len = nums.length;
if(len < 4) return [];
nums.sort((a, b) => a - b);
const res = [];
for(let i = 0; i < len - 3; i++) {
// 去重i
if(i > 0 && nums[i] === nums[i - 1]) continue;
for(let j = i + 1; j < len - 2; j++) {
// 去重j
if(j > i + 1 && nums[j] === nums[j - 1]) continue;
let l = j + 1, r = len - 1;
while(l < r) {
const sum = nums[i] + nums[j] + nums[l] + nums[r];
if(sum < target) { l++; continue}
if(sum > target) { r--; continue}
res.push([nums[i], nums[j], nums[l], nums[r]]);
while(l < r && nums[l] === nums[++l]);
while(l < r && nums[r] === nums[--r]);
}
}
}
return res;
};
本文介绍了哈希表的基本概念,作为直接访问数据结构,它常用于快速判断元素是否存在。文章讨论了哈希碰撞及其拉链法和线性探测法的解决策略,并列举了常见的三种哈希结构:数组、集合和映射。哈希法牺牲空间换取时间效率,适用于快速查找。文中还给出了多个算法题目的应用场景。

1440

被折叠的 条评论
为什么被折叠?



