代码随想录刷题笔记Day6–哈希表简介及有效的字母异位词、两个数组的交集、快乐数 、两数之和
一、哈希表基础知识简介
哈希表(英文名字为Hash table,国内也有一些算法书籍翻译为散列表,大家看到这两个名称知道都是指hash table就可以了)。
哈希表是根据关键码的值而直接进行访问的数据结构。
解决的问题:快速判断一个元素是否出现在集合里面
常见的三种哈希结构
-
数组
-
set(集合)
-
map(映射)
JS中的set和map数据类型
在ES6之前,我们存储数据的结构主要是两种:数组和对象
在ES6之后,新增了另外两种数据结构:Set、Map,以及它们的另外两种形式WeakSet、WeakMap
一、Set
1、基本用法
ES6提供了新的数据结构Set。 它类似于数组,但是**成员的值都是唯一的,没有重复的值**。Set本身是一个构造函数, 用来生成Set数 据结构。
//利用add方法向set结构添加成员,结果表明set结构是不会添加重复的值。
var s=new Set();
[2,3,4,5,3,2,4,3].map(x=>s.add(x))
for(i of s) console.log(i);//2,3,4,5
//Set可以接受一个数组作为参数,用于初始化
var set=new Set([1,2,3,4,4,5]);
[...set]//[1,2,3,4,5]
//向Set加入值时,不会发生类型转换,5和'5'是不同的两个值
2、四种操作方法
add(value)
:添加某个值,返回Set结构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。has(value)
:返回一个布尔值,表示参数是否为Set的成员。clear()
:清除所有成员,没有返回值。
3、4种遍历方法
Set结构的实例有4个遍历方法,可用于遍历成员。
keys()
:返回一个键名的遍历器。values()
:返回一个键值的遍历器。entries()
:返回一个键值对的遍历器。forEach()
: 使用回调函数遍历每个成员。
【注:】由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
4、Set的应用
1)Set转化为数组
由于扩展运算符( … )内部使用for…of循环,所以也可以用于Set结构。代码如下。
let set = new Set(['red', 'green', 'blue']);
let arr = [...set];
// ['red', 'green', 'blue']
2)去除数组的重复元素
方法一: 如下代码。
let arr=[3,5,2,2,5,5];
let unique = [...new Set(arr)];
//[3,5,2];
3)实现并集(Union)、交集(Intersect)、差集(Difference)
let a=new Set([1,2,3]);
let b=new Set([4,3,2]);
//并集
let union=new Set([...a,...b]);
//[1,2,3,4]
//交集
let intersect=new Set([...a].filter(x=>b.has(x)));
//[2,3]
//差集
let difference=new Set([...a].filter(x=>!b.has(x)));
//[1]
二、Map
1、基本用法
JavaScript的对象(Object)本质上是键值对的集合(Hash结构),但是只能用字符串作为键。
var data = {};
var element = document.getElementById("myDiv");
data[element] = metadata;
data["[object HTMLDivElement]"] // metadata
为了解决这个问题,ES6 提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值( 包括对象)都可以当作键。也就是说,Object 结构提供了“字符串一值”的对应,Map结构提供了“值一值"的对应,是-种更完善的Hash结构实现。
如果你需要“键值对”的数据结构,Map比Object更合适。
var m= new Map();
var o ={p: "Hello World"};
m.set(o, "content")
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
2、五种操作方法
- size属性
- set(key, value)
- get(key)
- has(key)
- delete(key)
- clear()
//size属性
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size //2
//set(key,value)
var m = new Map();
m.set("edition", 6) //键是字符串
m.set(262,"standard") //键是数值
m.set(undefined, "nah") //键是undefined
//get(key)
var m = new Map();
var hello = function() {console.log("hello");}
m.set(hello, "Hello ES6!") //键是函数
m.get(hello) // Hello ES6!
//has(key)
var m = new Map();
m.set("edition", 6);
m.set(262, "standard");
m.set(undefined, "nah' );
m.has("edition")// true
m.has("years")// false
m.has(262)// true
m.has(undefined)// true
//delete(key)
var m = new Map();
m.set(undefined, "nah");
m.has(undefined)// true
m.delete(undefined)
m.has(undefined)// false
//clear()
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0
3、四种遍历方法
Map原生提供3个遍历器生成函数和1个遍历方法。
keys()
:返回键名的遍历器。values()
:返回键值的遍历器。entries()
:返回所有成员的遍历器。forEach
():遍历Map的所有成员。
二、leetcode题目
LeetCode 242. 有效的字母异位词
题目描述:
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
**注意:**若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
解题思路
- 本题数据量不多,可以使用数据来解决问题
- 在统计数组s的每一个字符出现的次数时,在计数数组这个字符对应的索引值处,对该数++,统计数组t的每一个字符出现的次数时,对计数数组这个字符对应的索引值处,对该数–
解法:
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
let arr=Array.from(Array(26).fill(0));
for(i of s)
{
let temp=s[i].charCodeAt()-'a'.charCodeAt();
++arr[temp];
}
for(i of t)
{
let temp=t[i].charCodeAt()-'a'.charCodeAt();
--arr[temp];
}
for(let i=0;i<arr.length;i++)
{
if(arr[i]!=0)
return false;
}
return true
};
反思:
- 生成26项为0的数组的代码老是忘记
let arr=Array.from(Array(26).fill(0));
- 每个字符对应的ascii码的代码 charCodeAt()
- 统计s++,统计t–这种思路未考虑到,自己写的时候选择复杂的map,想前面是字符串为键,后面值则为统计数,比较麻烦。
LeetCode 349. 两个数组的交集
题目描述:给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
解题思路
- 本题的解题思路,使用set,因为在set中,值不能重复,故过滤到在一个数组中重复的值
- 利用[…set1]将set转换为数组,即可使用数组的filter方法,遍历set1中的值,set2也有的值
解法:
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function(nums1, nums2) {
let set1=new Set(nums1);
let set2=new Set(nums2);
let intersect=new Set([...set1].filter(x=>set2.has(x)));
return intersect = [...intersect]
};
反思:
1、数组和set的转化
数组---->set let set1=new Set(nums1); set----->数组 […set1]
2、数组有filter方法
LeetCode 202. 快乐数
题目描述:编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
示例 1:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例 2:
输入:n = 2
输出:false
解题思路
- 一开始看到无限循环比较懵,没想到如何使用set解决,后看了卡哥解析,**求和的过程中,sum会重复出现,这对解题很重要!**才明白如何解决此题。
本题,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
- 取出数值各个位上的数字
解法:
本人解法(在处理数值各个位上数字时,仍然过于繁琐)
/**
* @param {number} n
* @return {boolean}
*/
var getsumn=function(n){
let arr=[],sum=0;let temp=n;
while(temp)
{
temp=Math.floor(temp/10);
arr.push(n-temp*10);
n=temp;
}
for(i of arr)
{
sum=i*i+sum;
}
return sum;
}
var isHappy = function(n) {
let set_1=new Set();
while(true)
{ n=getsumn(n);
if(set_1.has(n))
return false;
if (n === 1) return true
set_1.add(n);
}
};
卡哥写法
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)
}
}
LeetCode 1. 两数之和
题目描述:给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
解题思路:
遍历数组,看map(存放已经遍历过的数据)中是否存在target-当前值的数
解法:
本人解法
/**
* @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++)
{
let right=target-nums[i];
if(m.has(right))
return [i,m.get(right)];
m.set(nums[i],i)
}
};
反思:
- 注意!!m.has(target) target是键而不是值
前面哈希表简介中的js的map和set数据结构有参考CSDN博主「征途黯然.」的原创文章,原文链接:https://blog.csdn.net/qq_43592352/article/details/103993479