哈希表(散列表):
根据关键码的值而直接进行访问的数据结构。
数组其实就是一张哈希表
一般哈希表都是用来快速判断一个元素是否出现在集合里。
哈希函数
把学生的姓名直接映射为哈希表上的索引
通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
index = hashFunction(name)
hashFunction = hashcode(name)%tableSize
如果hashCode得到的数值大于 哈希表的大小了,也就是大于tableSize了,怎么办呢?
取模
如果学生的数量大于哈希表的大小怎么办,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表 同一个索引下标的位置。
哈希碰撞
小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞。
一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
#拉链法
刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了
其实拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。
线性探测法
数据规模是dataSize, 哈希表的大小为tableSize
使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。
常见的三种哈希结构
当我们想使用哈希表来解决问题的时时候
我们一般会选择如下三种结构
数组
set(集合)
map(映射)
总结
当我们遇到了要快速判断一个元素是否出现在集合里时,就要考虑哈希法。
但是哈希表牺牲了空间来换取时间
因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
有效的字母异位词
// /**
// * @param {string} s
// * @param {string} t
// * @return {boolean}
// */
// var isAnagram = function(s, t) {
// // 用哈希表怎么解决 数组其实就是一个简单的哈希表
// // 其实我之前用过,只是当时不知道它是哈希表
// // 用一个数组记录s中每个字符出现的次数
// // 需要把字符映射到数组也就是哈希表的索引下标下
// // 因为字符a到z的ASCII是26个连续的数值,所以把a字符映射为0 ,z 25 ‘b/c/d’-'a'
// // 对于 s +1 t -1 最后只有0 符合
// // 怎么定义
// const record = new Array(26).fill(0)
// for(let i=0;i<s.length;i++){
// // 在js中 为NAN
// record[s[i]-'a']++
// }
// for(let j=0;j<t.length;j++){
// record[t[j]-'a']--
// }
// for(let k=0;k<26;k++){
// if(record[k]!=0)
// return false
// }
// return true
// };
var isAnagram = function(s, t) {
const record = new Array(26).fill(0)
const base = "a".charCodeAt()
for(const i of s){
record[i.charCodeAt()-base]++
}
for(const i of t){
record[i.charCodeAt()-base]--
}
for(let i=0;i<26;i++)
{
if(record[i]!==0)
return false
}
return true
}
‘b’-'a' NaN
const i of s 遍历字符串中的每一位
两个数组的交集
用集合来做
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function(nums1, nums2) {
// 用集合怎么做
const set1 = new Set(nums1)
const set2 = new Set(nums2)
// 不能用const
let array = Array.from(set2)
let array1 = []
let k=0
for(let i=0; i<array.length;i++){
if(set1.has(array[i])){
array1[k++]=array[i]
}
}
return array1
};
思路
JS创建一个集合
const set = new Set(num)
集合转换成数组
Array.from(set)
使用扩展运算符
[...set]
快乐数
/**
* @param {number} n
* @return {boolean}
*/
var getSum = function(n){
let sum = 0
while(n){
sum+=(n%10) * (n%10)
n=Math.floor(n/10)
}
return sum
};
var isHappy = function(n){
let set = new Set()
// 如果循环中某个值循环出现或==1 则出循环
while(n!==1&&!set.has(n)){
set.add(n)
n=getSum(n)
}
return n===1
}
// var isHappy = function(n) {
// 无限循环 在求和的过程中 sum 会重复出现
// 快速判断一个元素是否出现 在集合里 哈希法
// 求每个位置上的数字的平方和
// let sum=0
// let set = new Set()
// while(1){
// sum=getSum(n)
// // = 是赋值 == 才是判断是否相等
// // if(sum==1)
// // return true
// // set.end() 不是函数
// }
// };
思路
求每个数的各个位置上的平方和
无限循环 在求和的过程中 sum会重新出现
两数之和
用哈希法做 map
map是一种key-value的存储结构
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
// 用map怎么做
// map 是一种key-value的结构
// 这里key 存储元素的值 value 存储下标
let hash = {}
for(let i=0;i<nums.length;i++){
if(hash[target-nums[i]]!==undefined){
return [i,hash[target-nums[i]]]
}
hash[nums[i]]=i//如果没找到匹配对 就把访问过的元素和下标加入到map中
}
return []
};
思路
key存元素的值 value 存储元素的下标
遍历数组 在map中寻找匹配对
如果没找到匹配对 就把访问过的元素和下标加入到map中