js- 数据结构 - 集合

134 阅读3分钟

集合

在数据结构中,集合(Set)是一种无序且不重复的数据结构。它是由一组唯一的元素组成,没有顺序概念。集合常用的操作包括添加元素、删除元素、检查元素是否存在以及获取集合的大小等。

在编程语言中,集合通常具有以下特点:

  1. 元素的唯一性:集合中不能包含重复的元素,每个元素只会出现一次。
  2. 无序性:集合中的元素没有固定的顺序,元素之间没有序号或索引。
  3. 添加和删除操作:可以向集合中添加元素,也可以从集合中删除元素。
  4. 成员关系判断:可以检查一个元素是否属于集合。
  5. 集合的大小:可以获取集合中元素的个数。

集合的一些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判断。这个在做缓存的时候,也会用到,先判断有没有,再决定要不要更新