typescript实现拓扑排序(TopSort)

class TopSort {
  // 邻接表
  private readonly map: Set<number>[]
  // 所有节点的入度
  private readonly rd: number[]

  // 节点数量k,边集合arr,是否使用序号0
  constructor(k: number, arr?: number[][], hasZero: boolean = false) {
    if (!arr) return this

    // 初始化map和rd
    this.rd = new Array(k + 1)
    this.rd.fill(0)
    this.map = new Array(k + 1)
    for (let i = 0; i < k + 1; i++)
      this.map[i] = new Set<number>()

    // 构建邻接表及统计入度
    for (const [a, b] of arr) {
      // 连接ab
      this.connect(a, b)
    }

    // 如果不从0开始,将0的入度设为-1
    if (!hasZero) this.rd[0] = -1
  }

  // 连接ab
  connect(a: number, b: number): void {
    // 避免重复录边对入度产生影响
    if (this.map[a].has(b)) return
    this.map[a].add(b)
    // b的入度+1
    this.rd[b]++
  }

  // 计算拓扑排序,仅当res.length === k 时无环(排序有效)
  getTopSort(): number[] {
    let res: number[] = [], queue: number[] = []

    // 将入度为0的点加入queue
    for (let i = 0; i < this.rd.length; i++) {
      if (!this.rd[i]) {
        queue.push(i)
        // 排过的点标记为-1,避免重复访问
        this.rd[i] = -1
      }
    }

    // 拓扑排序
    while (queue.length) {
      let size = queue.length
      for (let i = 0; i < size; i++) {
        let top = queue.shift()
        res.push(top)
        let topNext = this.map[top]
        // 遍历当前节点邻接表,找到当前节点指向的其它节点,将入度-1
        topNext.forEach((v) => {
          this.rd[v]--
          // 当该点入度为0时,加入队列,标记为-1
          if (!this.rd[v]) {
            queue.push(v)
            this.rd[v] = -1
          }
        })
      }
    }
    return res
  }
}

/* 测试样例 */
~function test() {
  let ts = new TopSort(3, [[1, 2], [3, 2]])
  console.log(ts.getTopSort()) // [1, 3, 2]

  ts = new TopSort(3, [[3, 2], [2, 1], [1, 3]])// 环图
  console.log(ts.getTopSort()) // [] 注:结果长度不为k,表明该图有环,无法拓扑排序
}()
posted @ 2022-08-28 16:51  ぃ往事深处少年蓝べ  阅读(78)  评论(0编辑  收藏  举报