集合
在数据结构中,集合(Set)是一种无序且不重复的数据结构。它是由一组唯一的元素组成,没有顺序概念。集合常用的操作包括添加元素、删除元素、检查元素是否存在以及获取集合的大小等。
在编程语言中,集合通常具有以下特点:
- 元素的唯一性:集合中不能包含重复的元素,每个元素只会出现一次。
- 无序性:集合中的元素没有固定的顺序,元素之间没有序号或索引。
- 添加和删除操作:可以向集合中添加元素,也可以从集合中删除元素。
- 成员关系判断:可以检查一个元素是否属于集合。
- 集合的大小:可以获取集合中元素的个数。
集合的一些api
集合数据结构初始化
class Set {
constructor() {
this.item = {};
}
}
has
has(value) {
// return value in this.items
return Object.hasOwnPrototype.call(this.items, value)
}
add
add(value) {
if (!this.items.has(value)) {
this.items[value] = value
}
}
delete
delete(value){
if (this.items.has(value)) {
delete this.items[value]
}
}
clear
clear(){
this.items = {}
}
size
size(){
return Object.keys(this.items)
}
sizeLegacy
自己实现一个size,每次迭代对象,count++
sizeLegacy() {
let count = 0
for (let key in this.items) {
if (this.items.hasOwnPrototype(key))
count++
}
}
return count
}
values
values(){
return Object.values(this.items)
}
valueLegacy
自己实现一个value,每次迭代对象,push到 values
valueLegacy(){
let values = []
for (let key in this.items) {
if (this.items.hasOwnPrototype(key)) {
values.push([key])
}
}
return values
}
并集
利用集合add的唯一性,对两个集合进行add,完成交集操作
union(otherSet) {
let union = new SetP();
this.values().forEach((val) => union.add(val));
otherSet.values().forEach((val) => union.add(val));
return union;
}
交集
利用has 判断,如果两个集合都有,则将共有的数据,添加到新的集合里。有点像找出两个数组相同的值,我们一般会双重for循环,如果也换成集合的形式,复杂度会减半哦!!!
intersection(otherSet) {
let intersection = new SetP();
let values = this.values();
values.forEach((val) => {
if (otherSet.has(val)) {
intersection.add(val);
}
});
return intersection;
}
差集
交集的取反即可
difference(otherSet) {
let difference = new SetP();
let values = this.values();
values.forEach((val) => {
if (!otherSet.has(val)) {
difference.add(val);
}
});
return difference;
}
子集
A是否是B的子集,用every 判断每一个value
isSubset(otherSet) {
if (this.size() > otherSet.size()) {
return false;
}
return this.values().every((val) => {
if (!otherSet.has(val)) {
return false;
}
return true;
});
}
用原生API实现
没有class的this,所以需要现实的传递两个参数即可,把之前的this.xxx 换成参数即可。别的都一样,这里距一个并集合交集的🌰
并集
union(setA, setB) {
let unionSet = new Set();
setA.forEach((item) => unionSet.add(item));
setB.forEach((item) => unionSet.add(item));
return unionSet;
}
交集
const intersection = (setA, setB) => {
let intersection = new Set();
let values = setA.values();
values.forEach((val) => {
if (setB.has(val)) {
intersection.add(val);
}
});
return intersection;
};
极简实现
上面的实现,大家会发现,比如查找是不是全部满足条件,或者找出满足条件的值,其实都会有现成的api,那我们可以全部使用api来实现,就会方便很多
console.log('并集', new Set([...setA, ...setB]));
console.log('交集', new Set([...setA].filter((val) => setB.has(val))));
console.log('差集', new Set([...setA].filter((val) => !setB.has(val))));
console.log(
'子集',
[...setA].every((val) => setB.has(val))
);
测试
const setA1 = new Set();
setA1.add(1);
setA1.add(2);
setA1.add(3);
setA1.add(7);
setA1.has(1) // true
union(setA1, setB1) // 交集 利用api 调用方式
setA1.union(setB1) // 交集 自己实现的调用方式
setA1.isSubset(setB1) // 子集 A 是不是B的子集 利用api 调用方式
isSubset(setA1, setB1) // 子集 A 是不是B的子集 自己实现的调用方式
注意 ⚠️ 自己实现的结合,使是用一个对象包items着的,而且里面有key,value。而原生 new Set() 是一个集合,在测试的时候,切不可两者混用
总结
- 在集合这里,其实并不难,我柑橘就是对平时的数组结构的一种操作,比如找出两组数组中相同的,不同,包含的值,但是数组操作又太常见了,就换了个集合来考你(并且在求集合的交集的时候,一个add本身就是非重复的,不知道这个,可能还要绕一些弯)
- 让我最深刻的是,怎么做才能保证add的值是不重复的,那就是先进行一个has判断。这个在做缓存的时候,也会用到,先判断有没有,再决定要不要更新