4.图

145 阅读2分钟
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]]
  }
}