文章目录
- 哈希表
- 242. 有效的字母异位词
- 思路
- 补充:JavaScript String charCodeAt() 方法
- 代码详细分析
- 349. 两个数组的交集
- 代码分析
- 补充:JavaScript Set 对象
- 思考一下哈希是什么?什么时候使用?
- 补充:js 数组 map() 基本用法
- new Map()详细介绍
- 1. 两数之和
- 思路
- 代码
- 第454题.四数相加II
- 思路
- 代码以及分析
- 总结
- 什么是哈希表?
- 什么时候使用哈希表?
- 使用哈希法中的数据结构
- js里面Set的使用方法
- js里面Map的使用方法
哈希表
242. 有效的字母异位词
思路
为了方便举例,判断一下字符串s= “aee”, t = “eae”。
字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。
再遍历 字符串s的时候,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。 这样就将字符串s中字符出现的次数,统计出来了。
那看一下如何检查字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。
record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。
补充:JavaScript String charCodeAt() 方法
代码详细分析
判断 s 的元素是否 t 都有
使用哈希:将 s 的每个元素存到索引里面,对应的值就是当前元素的个数
遍历 t 的元素,如果 s 里面有,个数减一,减到 < 0 时返回 false
如果 s 里面没有,返回false
如果 s 的元素刚好都减到 0 遍历 t结束 返回true
这三种情况其实可以合并,也就是遍历t还没遍历完时s里面已经没有了
具体见代码分析
/*
* @lc app=leetcode.cn id=242 lang=javascript
*
* [242] 有效的字母异位词
*/
// @lc code=start
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
/*
使用哈希:把字符串每个字符作为键名,对应个数作为键值
判断 t 有的字符 是否 s都包括
思路:记录 s 每个字符个数,遍历 t, s存在一次,就减少一个数
*/
if (s.length !== t.length) return false
// 创建一个初始值都为 0 的数组
// 因为要给每个数组键名对应的键值计数,所以需要把每个值初始值设为 0
const resSet = new Array(26).fill(0)
// 把每个字符传承对应的ASCII
const base = "a".charCodeAt()
// 遍历字符串
for(const i of s) {
// 记录每个字符的个数
resSet[i.charCodeAt() - base] ++
}
for(const i of t) {
// 如果当前键名不存在
if(!resSet[i.charCodeAt() - base]) return false
// 存在,便将个数减 1
resSet[i.charCodeAt() - base]--
}
return true
};
// @lc code=end
349. 两个数组的交集
代码分析
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function(nums1, nums2) {
// 这里我们让nums1为较长数组
if(nums1.length < nums2.length) {
const _ = nums1
nums1 = nums2
nums2 = _
}
// nums1去重
const nums1Set = new Set(nums1)
// 存储交集的元素
const resSet = new Set()
// 遍历nums2,nums1里面存在,就把当前元素添加到resSet里面
for (const n of nums2) nums1Set.has(n) && resSet.add(n)
// 返回数组的交集
return Array.from(resSet)
};
补充:JavaScript Set 对象
此处不是完整的用法,只是上面代码涉及到了的,具体使用见总结
数组去重
创建新的 Set 对象
思考一下哈希是什么?什么时候使用?
第202题. 快乐数
思路
- 求和 sum重复出现
- 遇到快速判断一个元素是否出现集合里的时候,考虑哈希算法
- sum重复出现,return false 否则一直找到sum = 1 为止
代码
下面方法推荐使用方法一Map()和方法三Set()
// 方法一:使用Map方法
var isHappy = function (n) {
let m = new Map()
const getSum = (num) => {
let sum = 0
while (n) {
sum += (n % 10) ** 2
n = Math.floor(n / 10)
}
return sum
}
while (true) {
// n出现过,证明已陷入无限循环
if (m.has(n)) return false
if (n === 1) return true
m.set(n, 1)
n = getSum(n)
}
}
// 方法二:使用环形链表的思想 说明出现闭环 退出循环
var isHappy = function(n) {
if (getN(n) == 1) return true;
let a = getN(n), b = getN(getN(n));
// 如果 a === b
while (b !== 1 && getN(b) !== 1 && a !== b) {
a = getN(a);
b = getN(getN(b));
}
return b === 1 || getN(b) === 1 ;
};
// 方法三:使用Set()更简洁
/**
* @param {number} n
* @return {boolean}
*/
var getSum = function (n) {
let sum = 0;
while (n) {
sum += (n % 10) ** 2;
n = Math.floor(n/10);
}
return sum;
}
var isHappy = function(n) {
let set = new Set(); // Set() 里的数是惟一的
// 如果在循环中某个值重复出现,说明此时陷入死循环,也就说明这个值不是快乐数
while (n !== 1 && !set.has(n)) {
set.add(n);
n = getSum(n);
}
return n === 1;
};
// 方法四:使用Set(),求和用reduce
var isHappy = function(n) {
let set = new Set();
let totalCount;
while(totalCount !== 1) {
let arr = (''+(totalCount || n)).split('');
totalCount = arr.reduce((total, num) => {
return total + num * num
}, 0)
if (set.has(totalCount)) {
return false;
}
set.add(totalCount);
}
return true;
};
补充:js 数组 map() 基本用法
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
var data = [3, 4, 5, 6];
var Squares = data.map(function (item) {
return item * item;
});
console.log(Squares);
// [9, 16, 25, 36]
new Map()详细介绍
map.set(key,value)
map.size
map.get(key)
map.has(key)
map.delete(key)
map.clear()
(1)get() 方法用来获取一个 Map 对象中指定的元素。
(2)set() 方法为Map对象添加一个指定键(key)和值(value)的新元素。map的set属性就是添加东西的
(3)has() 返回一个bool值,用来表明map 中是否存在指定元素。
1. 两数之和
思路
- 创建一个空的Map
- 遍历数组,从第一个,满足当前Array.value + Map(key, value) = 9 则找到了
- 要是不满足,≠9,就把当前值添加到Map,接着遍历,直到找到满足条件的为止
代码
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let m = new Map()
for (let i = 0; i < nums.length; i++) {
// Map 里面的值 = target - nums[i] 不为空时,这时找到了匹配值
if(m[target - nums[i]] !== undefined) {
// 返回当前nums下标 和 m的下标
return [i, m[target - nums[i]]]
}
// 如果没有匹配到,就把当前访问的元素和下表加到Map中
m[nums[i]] = i
}
return m
};
第454题.四数相加II
思路
本题是使用哈希法的经典题目
这道题目是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况
本题解题步骤:
- 首先定义 Map,key放a和b两数之和,value 放a和b两数之和出现的次数。
- 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
- 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
- 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
- 最后返回统计值 count 就可以了
代码以及分析
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @param {number[]} nums3
* @param {number[]} nums4
* @return {number}
*/
var fourSumCount = function(nums1, nums2, nums3, nums4) {
// 1 new Map
const twoSumMap = new Map()
let count = 0
// 2 统计nums1 + nums2数组元素之和key 和出现的次数value 放到Map
// 遍历 nums1,nums2 然后判断Map里面是否有这个元素,有次数+1,没有就增加这个元素
for(const n1 of nums1) {
for(const n2 of nums2) {
const sum = n1 + n2
if (twoSumMap.get(sum)) {
twoSumMap.set(sum, twoSumMap.get(sum) + 1)
} else {
twoSumMap.set(sum, 1)
}
}
}
// 3 统计 nums3 + nums4 数组和, 0 - c - d 在Map出现过次数统计出来
for(const n3 of nums3) {
for(const n4 of nums4) {
const sum = n3 + n4
if(twoSumMap.get(0-sum)) {
count += twoSumMap.get(0-sum)
}
}
}
return count
};
总结
什么是哈希表?
哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素
什么时候使用哈希表?
哈希表能解决什么问题呢,一般哈希表都是用来快速判断一个元素是否出现集合里。
哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
例如要查询一个名字是否在这所学校里。
要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
我们只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。
将学生姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数。
把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。
stu[小明] = 0
使用哈希法中的数据结构
想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组
- set (集合)
- map(映射)
js里面Set的使用方法
Set 是 ES6 中新增的一种数据类型,**用于存储一组不重复、无序的数据。**下面是 Set 的使用方法:
1.创建 Set
可以通过以下两种方式创建 Set:
使用 new
关键字创建:
let mySet = new Set();
使用数组来初始化 Set:
let mySet = new Set([1, 2, 3]);
2.添加元素
可以使用add()
方法向 Set 中添加元素,如果元素已经存在,Set 不会进行任何操作。
mySet.add(4);
3.删除元素
可以使用delete()
方法删除 Set 中的元素。
mySet.delete(4);
4.判断元素是否存在
可以使用 has()
方法判断 Set 中是否存在某个元素。
mySet.has(3); // true
mySet.has(4); // false
5.获取 Set 中元素的个数
可以使用size
属性获取 Set 中元素的个数。
mySet.size;
6.遍历 Set
可以使用 for...of
循环或者 forEach()
方法来遍历 Set。
for (let item of mySet) {
console.log(item);
}
mySet.forEach(function(item) {
console.log(item);
});
除此之外,Set 还有一些其他的方法,例如 clear()
、keys()
、values()
等,具体使用方法可以参考 MDN 文档
js里面Map的使用方法
Map 是 ES6 中新增的一种数据类型,用于存储键值对。下面是 Map 的使用方法:
1.创建 Map
可以通过以下两种方式创建 Map:
使用 new 关键字创建:
let myMap = new Map();
使用数组来初始化 Map:
let myMap = new Map([
["key1", "value1"],
["key2", "value2"]
]);
2.添加键值对
可以使用 set()
方法向 Map 中添加键值对,如果键已经存在,Map 会使用新值覆盖旧值。
myMap.set("key3", "value3");
3.删除键值对
可以使用 delete() 方法删除 Map 中的键值对。
myMap.delete(“key3”);
4.判断键是否存在
可以使用 has()
方法判断 Map 中是否存在某个键。
myMap.has("key2"); // true
myMap.has("key3"); // false
5.获取 Map 中键值对的个数
可以使用 size
属性获取 Map 中键值对的个数。
myMap.size;
6.遍历 Map
可以使用 for...of
循环或者 forEach()
方法来遍历 Map。
for (let [key, value] of myMap) {
console.log(key + " = " + value);
}
myMap.forEach(function(value, key) {
console.log(key + " = " + value);
});
除此之外,Map 还有一些其他的方法,例如 clear()、keys()、values() 等,具体使用方法可以参考 MDN 文档。