JS 数据结构 – 集合

集合

集合由一组无序且唯一(不能重复)的项组成
使用了与 有限集合 相同的数学概念。

/**
 * @namespace abstract_Set
 * @description 抽象集合命名空间
 */
export namespace abstract_Set {
  /**
   * @class Set
   * @classdesc 数据结构 集合
   *
   * 集合由一组无序且唯一(不能重复)的项组成
   * 使用了与 有限集合 相同的数学概念
   *
   *
   * 集合之间的运算包括 并集、交集、差集\
   * set1 = [1, 2, 3, 4]\
   * set2 = [2, 3, 5, 6]
   *
   * - 并集 即 将两个集合相加,去除重复项
   *        [...set1, ...set2] === [1, 2, 3, 4, 5, 6]
   * - 交集 即 将两个集合相加,去除不重复项
   *        [...set1, ...set2] === [2, 3]
   * - 差集 即 将两个集合对比,去除 集合1相对于集合2 的重复项
   *        set1 - set2 === [1, 4]
   *    - 超集 即 set1 完全拥有 set2 的元素 但 set2 不包含 set1 源
   *    - 子集 即 set2 被 set1 完全拥有 所有元素 但 set2 不包含 set1 源
   *  - 对称差集 即 两个集合相加后,所有重复的项
   *        set1 + set2 = [2, 3]
   *
   * 一个集合中,应有以下方法
   * - add(element) 添加元素
   * - delete(element) 从 集合 删除元素
   * - has(element) 元素是否在集合中
   * - clear() 清空集合
   * - size() 集合大小
   * - values() 返回一个包含集合中所有元素的数组
   * - unions(otherSet) 返回与其它集合的并集
   * - intersection(otherSet) 返回与其它集合的交集
   * - difference(otherSet) 返回与其它集合的差集
   * - isSubsetOf(otherSet) 返回是否为其它集合的子集
   *
   * 在 ES2015 之后 已存在原生的 Set 数据结构
   * 但没有上述方法
   */
  export abstract class CustomSet<T> {
    /** 当前集合项 */
    protected items?: Set<T>
    /**
     * 添加元素到集合
     * @param element
     */
    abstract add (element: T): void
    /**
     * 移除集合元素
     * @param element
     */
    abstract delete (element: T): boolean
    /**
     * 元素是否存在于集合
     * @param element
     */
    abstract has (element: T): boolean
    /**
     * 清空集合
     */
    abstract clear (): void
    /**
     * 返回集合大小
     */
    abstract size (): number
    /**
     * 返回包含集合元素的数组
     */
    abstract values (): T[]
    /**
     * 并集
     * @param otherSet
     */
    abstract unions (otherSet: CustomSet<T>): CustomSet<T>
    /**
     * 交集
     * @param otherSet
     */
    abstract intersection (otherSet: CustomSet<T>): CustomSet<T>
    /**
     * 差集
     * @param otherSet
     */
    abstract difference (otherSet: CustomSet<T>): CustomSet<T>
    /**
     * 子集
     * @param otherSet
     */
    abstract isSubsetOf (otherSet: CustomSet<T>): boolean
  }
}

/**
 * @namespace example_Set
 * @description 实例集合命名空间
 */
export namespace example_Set {
  export class example_Set<T> extends abstract_Set.CustomSet<T> {
    constructor (array?: T[]) {
      super()
      this.items = new Set(array)
    }

    add (element: T): void {
      if (this.has(element)) return
      else this.items?.add(element)
    }

    delete (element: T): boolean {
      if (this.has(element)) {
        this.items?.delete(element)
        return true
      }
      else return false
    }

    has (element: T): boolean {
      return this.items!.has(element)
    }

    values (): T[] {
      const arr: Array<T> = []
      this.items?.forEach(value => arr.push(value))
      return arr
    }

    unions (otherSet: abstract_Set.CustomSet<T>): abstract_Set.CustomSet<T> {
      const unionSet = new example_Set<T>()
      // 先将自身集合的数据添加到创建的 unionSet 中
      this.values().forEach((value => unionSet.add(value)))
      // 在将传入的集合的元素添加到 unionSet 中
      otherSet.values().forEach(value => unionSet.add(value))
      // 直接返回,因为并集取两个集合的不同元素
      return unionSet
    }

    intersection (otherSet: abstract_Set.CustomSet<T>): abstract_Set.CustomSet<T> {
      const intersectionSet = new example_Set<T>()

      // 当前集合过滤 otherSet 中 不存在元素
      // 先将自身元素以数组形式取出
      this.values()
        // 在过滤 掉 传入的 otherSet 中 存在 当前遍历的元素的 项
        .filter(v => otherSet.has(v))
        // 再遍历已经过滤的数组 添加数据到 intersectionSet 中
        .forEach(v => intersectionSet.add(v))
      // 返回
      return intersectionSet
    }

    difference (otherSet: abstract_Set.CustomSet<T>): abstract_Set.CustomSet<T> {
      const differenceSet = new example_Set<T>()

      // 在 当前集合中过滤 otherSet 中存在元素
      this.values()
        .filter(v => !otherSet.has(v))
        .forEach(v => differenceSet.add(v))

      return differenceSet
    }
    isSubsetOf (otherSet: abstract_Set.CustomSet<T>): boolean {
      // 如果 当前集合的大小 大于 传入的集合大小 则 不是 传入集合的子集
      if (this.size() > otherSet.size()) return false
      // 当前集合的元素在 otherSet 中 都有
      this.values().forEach(v => {
        // 如果 传入集合中不存在 当前遍历的 元素 则 不是
        if (!otherSet.has(v)) return false
      })
      // 通过 是子集
      return true
    }

    isEmpty (): boolean {
      return this.size() === 0
    }

    size (): number {
      return this.items!.size
    }

    clear (): void {
      this.items = new Set()
    }

    toString (): string {
      return this.values().toString()
    }
  }
}

多重集合

/**
 * @namespace abstract_multiSet
 * @description 抽象 多重集 命名空间
 */
export namespace abstract_multiSet {

    /**
     * @class MultiSet
     * @description 数据结构 多重集
     *
     * 集合结构不允许存在重复的元素,但是在数学中存在 多重集 的概念
     * 允许向集合插入重复元素
     * 在计算集合中元素出现次数很有用
     *
     * 多重集和普通集合的区别
     * - count(element) 返回集合中指定元素的数量
     * - set(element, count) 将 集合中 指定 element 设置为 count 个
     * - add(element, count) 向集合添加 count 个 element
     * - remove(element, count) 在 集合 删除 count 个 element
     * - delete(element) 移除所有 element
     * - dimension() 返回集合所包含元素的种类数,与数组 length 类似
     */
    export abstract class MultiSet<T> {
        protected items: Map<T, number> = new Map()

        /**
         * 当前集合是否包含 element
         * @param element
         */
        abstract has(element: T): boolean
        /**
         * 当前集合包含多少 element
         * @param element
         */
        abstract count(element: T): number
        /**
         * 设置当前集合中 element 为 count 个
         * @param element
         * @param count
         */
        abstract set(element: T, count: number): boolean
        /**
         * 添加 count 个 element 到当前集合中
         * @param element
         * @param count
         */
        abstract add(element: T, count: number): boolean
        /**
         * 在当前集合中 删除 count 个 element
         * @param element
         * @param count
         */
        abstract remove(element: T, count: number): boolean
        /**
         * 删除所有当前集合中的 element
         * @param element
         */
        abstract delete(element: T): boolean
        /**
         * 返回当前集合所包含的元素种类数
         */
        abstract dimension(): number
        /**
         * 返回 当前集合与 otherSet 的并集\
         * this = [1,2,3]\
         * otherSet = [2,3,4]\
         * 返回 [1,2,3,4]
         * @param otherSet
         */
        abstract union(otherSet: MultiSet<T>): MultiSet<T>
        /**
         * 返回 当前集合与 otherSet 的交集\
         * this = [1,2,3]\
         * otherSet = [2,3,4]\
         * 返回 [2,3]
         * @param otherSet
         */
        abstract intersection(otherSet: MultiSet<T>): MultiSet<T>
        /**
         * 返回当前集合与 otherSet 的差集\
         * this = [1,2,3]\
         * otherSet = [2,3,4]\
         * 返回 [1]
         * @param otherSet
         */
        abstract difference(otherSet: MultiSet<T>): MultiSet<T>
        /**
         * 返回当前集合是否是 otherSet 的子集\
         * this = [1,2,3]\
         * otherSet1 = [0,1,2,3,4,5]\
         * otherSet2 = [1,2,4,5,6]\
         * otherSet1 是 this 的超集, this 是 otherSet1 的子集\
         * otherSet2 不是 this 的超集, this 不是 otherSet2 的子集
         * @param otherSet
         */
        abstract isSubsetOf(otherSet: MultiSet<T>): boolean
        /**
         * 返回一个包含了当前集合所有元素的数组
         */
        abstract values():Array<T>
        /**
         * 返回集合大小
         */
        abstract size(): number
    }
}

/**
 * @namespace example_multiSet
 * @description 实例 多重集 命名空间
 */
export namespace example_multiSet {
    class MultiSet<T> extends abstract_multiSet.MultiSet<T> {

        has(element: T): boolean {
            return this.items.has(element)
        }

        dimension(): number {
            return this.items.size
        }

        count(element: T): number {
            // 双问号 在 前值为 null 或 undefined 时 取后值
            // 否则为前值
            return this.items.get(element) ?? 0
        }

        delete(element: T): boolean {
            return this.items.delete(element)
        }

        set(element: T, count: number): boolean {
            if (count <= 0) return this.delete(element)
            this.items.set(element, count)
            return true
        }

        add(element: T, count: number = 1): boolean {
            let newCount = this.count(element) + count
            return this.set(element, newCount)
        }

        remove(element: T, count: number = 1): boolean {
            return this.add(element, -count)
        }

        isEmpty(): boolean {
            return this.dimension() === 0
        }

        values(): T[] {
            // reduce 方法
            // 接受四个参数
            // Accumulator (acc 累计器)
            // Current Value (cur 当前值)
            // Current Index (idx 当前索引)
            // Source Array (src 源数组)
            return Array.from(this.items.entries())
                .reduce((acc, cur) => {
                    // Array.from 可以将 Map 转换为了数组
                    // map 的 数组形式为 [[k, v], [k, v]]
                    // 解构赋值 分别取出 k 和 v
                    const [key, value] = cur
                    // 循环遍历, 多重集 允许元素重复
                    // 遍历次数为 元素重复次数, 并在 acc 累计器中重复添加元素
                    for(let i = 0; i < value; i++){
                        (acc as T[]).push(key)
                    }
                    // 返回
                    return acc
                // [] 的作用为 在 充当 reduce 第一次调用时的 callback 的 acc 参数
                // 如果没有提供 [], 则 会以 数组第一个元素作为 acc
                }, [])
        }

        size(): number {
            return this.values().length
        }

        clear(): void {
            this.items = new Map()
        }

        union(otherSet: abstract_multiSet.MultiSet<T>): abstract_multiSet.MultiSet<T> {
            const unionSet = new MultiSet<T>()

            this.values().forEach(v => unionSet.add(v))
            otherSet.values().forEach(v => unionSet.add(v))

            return unionSet
        }

        intersection(otherSet: abstract_multiSet.MultiSet<T>): abstract_multiSet.MultiSet<T> {
            const intersectionSet = new MultiSet<T>()

            this.values()
                .filter(v => otherSet.has(v))
                .forEach(v => intersectionSet.add(v))

            return intersectionSet
        }

        difference(otherSet: abstract_multiSet.MultiSet<T>): abstract_multiSet.MultiSet<T> {
            const differenceSet = new MultiSet<T>()

            this.values()
                .filter(v => !otherSet.has(v))
                .forEach(v => differenceSet.add(v))

            return differenceSet
        }

        isSubsetOf(otherSet: abstract_multiSet.MultiSet<T>): boolean {
            if (this.size() > otherSet.size()) return false

            this.values().forEach(v => {
                if (!otherSet.has(v)) return false
            })
            return true
        }

        toString(): string {
            return `${this.values()}`
        }
    }
}

JS 数据结构 – 集合
http://localhost:8080/archives/86eae6c1-da2b-4714-8a81-0bf3dec8717a
作者
inksha
发布于
2024年09月14日
许可协议