切换主题
有向图
本篇来手动实现JavaScript中没有的数据结构:有向图。
定义节点
首先定义有向图节点结构:
JavaScript
class Node {
constructor(val) {
this.val = val
this.in = 0 // 入度
this.out = 0 // 出度
this.nexts = [] // 由当前节点出发,直接到达的节点(直接邻居)
this.edges = [] // 与直接邻居形成的边
}
}
class Edge {
constructor(weight, fNode, tNode) {
this.weight = weight
this.from = fNode // 出发节点
this.to = tNode // 到达节点
}
}
class Graph {
constructor() {
this.nodes = new Map() // { fNode.val/tNode.val : Node }
this.edges = new Set()
}
}
实现有向图
假设现有一矩阵matrix
,矩阵每项有[weight, fNode.val, tNode.val]
,在此基础上实现有向图。
JavaScript
class Digraph {
constructor(matrix) {
this.digraph = new Graph()
// 初始化
for (const ele of matrix) {
const [weight, fVal, tVal] = ele // 解构
if (!this.digraph.nodes.has(fVal)) {
this.digraph.nodes.set(fVal, new Node(fVal))
}
if (!this.digraph.nodes.has(tVal)) {
this.digraph.nodes.set(tVal, new Node(tVal))
}
// 获取节点与创建对应的边
const fNode = this.digraph.get(fVal)
const tNode = this.digraph.get(tVal)
const edge = new Edge(weight, fNode, tNode)
// 构建有向图
fNode.nexts.push(tNode) // 添加直接邻居
fNode.out++ // 更新出度
tNode.in++ // 更新出度
fNode.edges.push(edge) // 更新边
this.digraph.edges.add(edge) // 更新边集
}
}
}
拓扑排序
该排序算法使用范围为无环且有入度为0的节点的有向图,同时也会用到
hashMap
与队列
两个数据结构:
hashMap
:用于存放节点与节点的入度值,即{ node : node.in }
;队列
:用于存放入度为0的节点。
算法过程描述:
- 首先遍历无环有向图:
1. 根据图节点初始化map
数据;
2. 找出第一个入度为0
的节点,将其入队。 - 当队列不为空时,出队
入度为0
的节点,记为curr
:
1. 记录当前出队节点curr
;
2. 遍历直接邻居,削弱出队节点curr
的影响(这里的影响是指:从curr
出发,到达的直接邻居所构成的边,即curr
为直接邻居贡献的入度);
3. 在2.2
遍历中找出下一个入度为0
的节点并入队。 - 当队列为空时,拓扑排序完成。
JavaScript
const topoSort = (digraph, ret = []) => {
const [map, queue] = [new Map(), []] // { node : node.in }
// 获取nodes
const nodes = digraph.nodes.values()
for (const node of nodes) {
map.set(node, node.in)
// 第一个入度为 0 的节点
if (node.in === 0) queue.push(node)
}
// 排序
while (queue.length) {
const curr = queue.shift()
ret.push(curr)
for (const next of curr.nexts) {
// 削弱其影响,即直接邻居的入度
map.set(next, map.get(next) - 1)
// 下一个入度为 0 的节点
if (map.get(next) === 0) queue.push(next)
}
}
}