hash
的实质是一种算法,可以将其看成函数 f()
,即有这样的关系: A=f(a)
。
map
就是一个数据结构, key
放值, value
放值。当放入 value
的内容是基础类型数据时,里面放的是值的本身,当放入的引用类型的时候,其中是地址,然后通过地址调用值。
参考这篇
HASH 与MAP 的区别
leetcode2154. 将找到的值乘以 2
①排序
var findFinalValue = function(nums, original) {
// if(nums.length==1) return nums[0]*2;
nums.sort((a,b)=>a-b);
for(const num of nums){
console.log(original==num);
while(original==num){
original=2*original;
console.log(original==num);
}
}
return original;
};
时间复杂度:O(nlogn)
,其中n为nums
的长度。排序的时间复杂度为O(nlogn)
,遍历更新original
的时间复杂度最多为O(n)
。
空间复杂度:O(logn)
,即为排序的栈空间开销。
②哈希表
用空间换时间
var findFinalValue = function(nums, original) {
const set=new Set();
for(let i=0;i<nums.length;i++){`在这里插入代码片`
set.add(nums[i]);
}
while(set.has(original)){
original*=2;
}
return original;
};
Map 和 Set 使用的区别和联系
js中的map和set
leetcode819. 最常见的单词
题目: 给定一个段落 (paragraph) 和一个禁用单词列表 (banned)。返回出现次数最多,同时不在禁用列表中的单词。
题目保证至少有一个词不在禁用列表中,而且答案唯一。
禁用列表中的单词用小写字母表示,不含标点符号。段落中的单词不区分大小写。答案都是小写字母。
①哈希表+计数
为了高效检查单词是否被禁止,使用了集合Set对象
var mostCommonWord = function(paragraph, banned) {
const len=paragraph.length;
// 把禁用单词放进Set里
const bannedSet=new Set();
for(const word of banned){
bannedSet.add(word);
}
// 记录某个单词出现的最大次数
let maxCount=0;
const maps=new Map();
let sp='';
for(let i=0;i<=len;i++){
if(i<len&&isLetter(paragraph[i])){
sp=sp+paragraph[i].toLowerCase();
}else if(sp.length>0){
if(!bannedSet.has(sp)){
const map=(maps.get(sp)||0)+1;
maps.set(sp,map);
maxCount=Math.max(maxCount,map);
}
sp='';
}
}
let ans="";
for(const [word,map] of maps.entries()){
if(map==maxCount){
ans=word;
break;
}
}
return ans;
};
const isLetter=(word)=>{
return (word>='a'&&word<='z')||(word>='A'&&word<='Z');
}
好复杂这个方法…
时间复杂度:O(n+m)
,其中n
是段落paragraph
的长度,m
是禁用单词列表 banned
的长度。遍历禁用单词列表一次将禁用单词存入哈希集合中需要O(m)
的时间,遍历段落得到每个非禁用单词的计数需要O(n)
的时间,遍历哈希表得到最常见的单词需要O(n)
的时间。
空间复杂度:O(n+m)
,其中n
是段落paragraph
的长度,m
是禁用单词列表 banned
的长度。存储禁用单词的哈希集合需要O(m)
的空间,记录每个单词的计数的哈希表需要O(n)
的空间。
②Set+Map+split()
感觉这种方法就清晰明了很多:
var mostCommonWord = function(paragraph, banned) {
const set=new Set();
banned.forEach((ban)=>{
set.add(ban);
});
let ans="";
const map=new Map();
// 切割单词时,用了js的split方法,
// 该方法支持正则表达式,将连续的空格或标点符号作为分隔符,即可正确分割单词
paragraph.split(/[!\?',;\.' ]+/).forEach((v)=>{ //根据标点符号或空格分割成多个单词,转小写,计数
if(v=="") return;
v=v.toLocaleLowerCase();
if(set.has(v)) return;
map.set(v,map.has(v)?map.get(v)+1:1);
});
let maxCount=0;
// 找次数最多的单词
[...map.entries()].forEach(([k,v])=>{
if(maxCount<v){
maxCount=v;
ans=k;
}
})
return ans;
};
toLowerCase和toLocaleLowerCase()的区别
217. 存在重复元素
题目: 给你一个整数数组 nums
。如果任一值在数组中出现 至少两次 ,返回 true
;如果数组中每个元素互不相同,返回 false
。
①排序
对数组排序,然后判断相邻元素是否相等。
var containsDuplicate = function(nums) {
nums.sort((a,b)=>a-b);
for(let i=0;i<nums.length;i++){
if(nums[i]==nums[i+1]){
return true;
}
}
return false;
};
时间复杂度:O(NlogN)
,其中N
为数组的长度。需要对数组进行排序。
空间复杂度:O(logN)
,其中N
为数组的长度。注意:这里应当考虑递归调用栈的深度。
②哈希表
不需要记录每个数字出现的次数,只要哈希表中已经存在该数字,返回true
即可。
var containsDuplicate = function(nums) {
const set=new Set();
for(const num of nums){
if(set.has(num)){
return true;
}
set.add(num);
}
return false;
};
或者
var containsDuplicate = function(nums) {
const map=new Map();
for(const num of nums){
if(map.has(num)){
return true;
}
map.set(num,1);
}
return false;
};
时间复杂度:O(N)
,其中N
为数组的长度。
空间复杂度:O(N)
,其中N
为数组的长度。
219. 存在重复元素 II
题目: 给你一个整数数组 nums
和一个整数 k
,判断数组中是否存在两个 不同的索引 i
和 j
,满足 nums[i] == nums[j]
且 abs(i - j) <= k
。如果存在,返回 true
;否则,返回 false
。
①哈希表
判断当前元素是否已在哈希表中,不在就添加到哈希表中;如果在,判断当前元素索引和哈希表中的索引差值,小于k
时直接返回true
,否则就用当前元素的索引覆盖上一个相同元素的索引值。
然后看了一下官方解法,发现跟官方解法的代码是一样的,思路也差不多。
var containsNearbyDuplicate = function(nums, k) {
const map=new Map();
for(let i=0;i<nums.length;i++){
if(map.has(nums[i])){
if(Math.abs(map.get(nums[i])-i)<=k){
return true;
}
// map.set(nums[i],i);
}
map.set(nums[i],i);
}
return false;
};
时间复杂度:O(n)
,其中n
是数组nums
的长度。需要遍历数组一次,对于每个元素,哈希表的操作时间都是O(1)
。
空间复杂度:O(n)
,其中n
是数组nums
的长度。需要使用哈希表记录每个元素的最大下标,哈希表中的元素个数不会超过n
。
②滑动窗口+Set
var containsNearbyDuplicate = function(nums, k) {
const set=new Set();
for(let i=0;i<nums.length;i++){
if(i>k){
set.delete(nums[i-k-1]);
}
if(set.has(nums[i])){
return true;
}
set.add(nums[i]);
}
return false;
};
时间复杂度:O(n)
,其中n
是数组nums
的长度。需要遍历数组一次,对于每个元素,哈希集合的操作时间都是O(1)
。
空间复杂度:O(k)
,其中k
是判断重复元素时允许的下标差的绝对值的最大值。需要使用哈希集合存储滑动窗口中的元素,任意时刻滑动窗口中的元素个数最多为k+1
个。
205. 同构字符串
题目: 给定两个字符串 s
和 t
,判断它们是否是同构的。
如果 s
中的字符可以按某种映射关系替换得到 t
,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。
①Set+Map
map
中放s
的元素及其映射到的t
中的元素,set
中放t
的元素,防止出现不同元素映射到同一个元素的情况。
var isIsomorphic = function(s, t) {
const map=new Map();
const set=new Set();
for(let i=0;i<s.length;i++){
if(!map.has(s[i])){
// 如果map中不存在s的当前字符且set中也不存在t的当前字符
if(!set.has(t[i])){
set.add(t[i]);
map.set(s[i],t[i]);
}else{
// 如果map中不存在s的当前字符但set中存在t的当前字符,说明不同字符映射到了同个字符 直接返回false
return false;
}
}
if(map.has(s[i])&&map.get(s[i])!==t[i]){
return false;
}
}
return true;
};