Typescript实现集合数据结构

73 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 12 月更文挑战」的第 5 天,点击查看活动详情

最近在学习 Javascript 数据结构与算法相关知识,数据结构与算法对程序员来说就像是内功心法,只有不断修炼自己的内功,才能从容不迫的应对市场上各种让人应接不暇的框架,达到以不变应万变。学习数据结构与算法的过程中不仅要能看懂更要多写多练,今天就来手写下集合数据结构。

集合是一种不允许值重复的顺序数据结构,你也可以把集合想象成一个既没有重复元素,也没有顺序概念的数组。
尽管 ECMAScript 2015 已经新增了 Set 类,今天我们还是想自己实现下,并完善下集合运算,并集 交集 差集 子集。

手写集合

集合的基本操作方法:

  • add(element):向集合添加一个新元素。
  • delete(element):从集合移除一个元素。
  • has(element):如果元素在集合中,返回 true,否则返回 false。
  • clear():移除集合中的所有元素。
  • size():返回集合所包含元素的数量。它与数组的 length 属性类似。
  • values():返回一个包含集合中所有值(元素)的数组。
  • union():对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。
  • intersection() :对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。
  • difference():对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。
  • isSubsetOf():验证一个给定集合是否是另一集合的子集。
class Set<T> {
  private items: any;
  constructor() {
    this.items = {}; // 这里用对象来实现
  }
  has(element: T) {
    return Object.prototype.hasOwnProperty.call(this.items, element);
  }
  add(element: T) {
    if (!this.has(element)) {
      this.items[element] = element; // 同时作为键和值保存,因为这样有利于查找该元素。
      return true;
    }
    return false;
  }
  delete(element: T) {
    if (this.has(element)) {
      delete this.items[element];
      return true;
    }
    return false;
  }
  clear() {
    this.items = {};
  }
  size() {
    return Object.keys(this.items).length;
  }
  values() {
    return Object.values(this.items);
  }
  // 并集
  union(otherSet: Set<T>) {
    const unionSet = new Set();
    this.values().forEach((value) => unionSet.add(value));
    otherSet.values().forEach((value) => unionSet.add(value));
    return unionSet;
  }
  // 交集
  intersection(otherSet: Set<T>) {
    const intersectionSet = new Set<T>();
    const values = this.values();
    const otherValues = otherSet.values();

    let biggerSet = values;
    let smallerSet = otherValues;

    if (otherValues.length - values.length > 0) {
      biggerSet = otherValues;
      smallerSet = values;
    }

    // 只需要遍历最少元素的集合
    smallerSet.forEach((value) => {
      if (biggerSet.includes(value)) {
        intersectionSet.add(value);
      }
    });

    return intersectionSet;
  }
  // 差集
  difference(otherSet: Set<T>) {
    const differenceSet = new Set<T>();
    this.values().forEach((value) => {
      if (!otherSet.has(value)) {
        differenceSet.add(value);
      }
    });
    return differenceSet;
  }
  // 子集
  isSubsetOf(otherSet: Set<T>) {
    if (this.size() > otherSet.size()) {
      return false;
    }
    return this.values().every((value) => otherSet.has(value));
  }
}

使用场景

leetcode 练习题:349

两个数组的交集 给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function (nums1, nums2) {
  const set = new Set();
  let bigArr = nums1;
  let smallArr = nums2;
  if (nums2.length - nums1.length > 0) {
    bigArr = nums2;
    smallArr = nums1;
  }
  smallArr.forEach((item) => {
    if (bigArr.includes(item)) {
      set.add(item);
    }
  });
  return [...set];
};

解题思路
计算两个数组的交集,直观的方法是遍历数组 nums1,对于其中的每个元素,遍历数组 nums2 判断该元素是否在数组 nums2 中,如果存在,则将该元素添加到返回值。
先找出长度最小的数组,遍历最小数组 只要里面的元素在大数组中都能找到就添加到结果集合中,最后利用 set 结构不重复的特点 转为数组后输出