0.通用图模板
// 图Graph:节点表、边集
class Graph{
constructor(){
this.nodes = new Map()
this.edges = new Set()
}
}
// 节点Node:值、出度、入度、边集、邻接节点集
class Node{
constructor(value){
this.value = value
this.in = 0
this.out = 0
this.nexts = new Set()
this.edges = new Set()
}
}
// 边Edge:始节点、末节点、权值
class Edge{
constructor(from,to,weight){
this.from = from
this.to = to
this.weight = weight
}
}
1.模板适配例子
// 二维矩阵表示的图
let matrix = [
[1,2,5], // from,to,weight
[1,3,2],
[2,3,4]
]
// 适配
function matrix2Graph(matrix) {
let graph = new Graph()
for(let i = 0;i<matrix.length;i++){
let from = matrix[i][0]
let to = matrix[i][1]
let weight = matrix[i][2]
if(!graph.nodes.has(from)) graph.nodes.set(from,new Node(from))
if(!graph.nodes.has(to)) graph.nodes.set(to,new Node(to))
from = graph.nodes.get(from)
to = graph.nodes.get(to)
let edge = new Edge(from,to,weight)
graph.edges.add(edge)
from.edges.add(edge)
from.nexts.add(to)
from.out++
to.in++
}
return graph
}
2.BFS:需要队列+集合实现,出队列时处理
function bfs(node) {
let queue = []
let set = new Set()
queue.push(node)
set.add(node)
let cur = null
while (queue.length) {
cur = queue.shift()
console.log(cur) // 出队列时处理
for(let next of cur.nexts){
if(!set.has(next)){
set.add(next)
queue.push(next)
}
}
}
}
3.DFS:需要栈+集合实现,进栈时处理
function dfs(node) {
let stack = []
let set = new Set()
stack.push(node)
set.add(node)
let cur = node
console.log(cur) // 进栈时处理
while (stack.length) {
cur = stack.pop()
for(let next of cur.nexts){
if(!set.has(next)){
stack.push(cur) // 回溯
stack.push(next)
set.add(next)
console.log(next)
break;
}
}
}
}
4.拓扑排序(处理依赖)
function sortedTopology(graph) {
let inMap = new Map() // 节点剩余入度
let zeroInQueue = [] // 入度为0的点才能进入队列
for(let node of graph.nodes.values()){
inMap.set(node,node.in) // 所有节点原始in
if(node.in==0){
zeroInQueue.push(node)
}
}
let res = []
let cur = null
while (zeroInQueue.length) {
cur = zeroInQueue.shift()
res.push(cur)
for(let next of cur.nexts){
inMap.set(next,inMap.get(next)-1)
if(inMap.get(next)==0){
zeroInQueue.push(next)
}
}
}
return res
}
5.K算法:无向图最小生成树
// 从最小边出发,借助并查集来阻断形成环
function kruskalMST(graph) {
let unionSet = new UnionSets(graph.nodes)
let priorityQueue = [...graph.edges].sort((a, b) => a.weight - b - weight)
let res = []
let edge = null
while (priorityQueue.length) {
edge = priorityQueue.shift()
if(!unionSet.isSameSet(edge.from,edge.to)){
unionSet.union(edge.from,edge.to)
res.push(edge)
}
}
return res
}
class UnionSets {
constructor(nodes) {
this.setMap = new Map()
nodes.forEach(node => this.setMap.set(node, new Set([node])))
}
isSameSet(from, to) {
return this.setMap.get(from) === this.setMap.get(to)
}
union(from, to) {
let fromSet = this.setMap.get(from)
let toSet = this.setMap.get(to)
toSet.forEach(node => {
fromSet.add(node)
this.setMap.set(node, fromSet)
})
}
}
6.P算法:有向图最小生成树
// 从任意点出发取最小边,借助Set阻断形成环,有向
function primMST(graph) {
let res = []
let set = new Set()
let priorityQueue = []
let edge = null
for (let node of graph.nodes.values()) {
if (!set.has(node)) {
priorityQueue = [...node.edges].sort((a, b) => a.weight - b.weight)
while (priorityQueue.length) {
edge = priorityQueue.shift()
let toNode = edge.to
if (!set.has(toNode)) {
set.add(toNode)
res.push(edge)
priorityQueue = [
...toNode.edges.filter(edge=>!set.has(edge.to)),
...priorityQueue
].sort((a, b) => a.weight - b.weight)
}
}
}
}
return res
}
7.☆Dijkstra算法:求指定节点到任意节点的最小路径
适用范围:没有权值为负数的边
function dijkstral(head) {
let distanceMap = new Map() // head到所有节点的距离
distanceMap.set(head, 0)
let selectedNodes = new Set()
let minNode = getMinDistanceInUnselectedNode(distanceMap,selectedNodes) // 一开始为head
while (minNode) {
let distance = distanceMap.get(minNode)
for (let edge of minNode.edges.values()) {
let toNode = edge.to
if (!distanceMap.has(toNode)) {
distanceMap.set(toNode, distance + edge.weight)
} else {
distanceMap.set(toNode, Math.min(distanceMap.get(toNode),distance + edge.weight))
}
selectedNodes.add(minNode)
minNode = getMinDistanceInUnselectedNode(distanceMap,selectedNodes)
}
}
return distanceMap
}
function getMinDistanceInUnselectedNode(distanceMap,selectedNodes) {
let minNode = null // 从distanceMap中查找未选择过且距离最小的节点
let minDistance = Infinity
for (let [node, distance] of distanceMap.entries()){
if (!selectedNodes.has(node) && distance < minDistance) {
minNode = node
minDistance = distance
}
}
return minNode
}
// 改进
function dijkstral(head) {
let distanceMap = new Map()
distanceMap.set(head, 0)
let selectedNodes = new Set()
let curNode = head
let curDistance = Infinity
let toNode = null
let toDistance = 0
// ++
let nextNode = nextNodeCopy = {}
distanceMap.set(nextNode,Infinity)
while (curNode) {
curDistance = distanceMap.get(curNode)
for (let edge of curNode.edges.values()) {
toNode = edge.to
toDistance = edge.weight
if (!selectedNodes.has(toNode)) {
distanceMap.set(toNode,curDistance+toDistance)
} else {
distanceMap.set(toNode,Math.min(distanceMap.get(toNode),curDistance+toDistance))
}
// ++
if(distanceMap.get(toNode) < distanceMap.get(nextNode)) nextNode = toNode
}
selectedNodes.add(curNode)
// ++
curNode = selectedNodes.has(nextNode) ? null : nextNode
}
// ++
distanceMap.delete(nextNodeCopy)
return distanceMap
}
用小顶堆改写
function dijkstra2(head,size) {
let nodeHeap = new NodeHeap(size)
nodeHeap.addOrUpdateOrIgnore(head, 0)
let res = new Map()
while (!nodeHead.isEmpty()) {
let { node, distance } = nodeHeap.pop()
for (let edge of node.edges) {
nodeHeap.addOrUpdateOrIgnore(edge.to,edge.weight+distance)
}
res.set(node, distance)
}
return res
}
class NodeHeap{
static nodes
static heapIndexMap
static distanceMap
static size
constructor(size) {
nodes = new Node[size]
heapIndexMap = new Map()
distanceMap = new Map()
size = 0
}
isEmpty() {
return size == 0
}
addOrUpdateOrIgnore(node, distance) {
if (this.inHeap(node)) {
distanceMap.set(node, Math.min(distanceMap.get(node), distance))
this.insertHeapify(node, heapIndexMap.get(node))
}
if (!this.isEntered(node)) {
nodes[size] = node
heapIndexMap.set(node, size)
distanceMap.set(node, distance)
this.insertHeapify(node, size++)
}
}
pop() {
let head = {
node: nodes[0],
distance: distanceMap.get(nodes[0])
}
this.swap(0, size - 1)
heapIndexMap.set(nodes[size - 1], -1)
distanceMap.delete(nodes[size - 1])
nodes[size - 1] = null
this.heapify(0, --size)
return head
}
isEntered(node) {
return heapIndexMap.has(node)
}
inHeap(node) {
return this.isEntered(node) && heapIndexMap.get(node) != -1
}
swap(idx1, idx2) {
heapIndexMap.set(nodes[idx1], idx2)
heapIndexMap.set(nodes[idx2], idx1)
[nodes[idx1], nodes[idx2]] = [nodes[idx2], nodes[idx1]]
}
}