图论

/**
 * 邻接矩阵
 */
export class DenseGraph {
  // 节点数
  n: number
  // 边数
  m: number
  // 是否为有向图
  directed: boolean
  // 图得具体数据
  g: boolean[][]
  constructor(n: number, directed: boolean) {
    console.assert(n >= 0, 'n必须大于0')
    this.n = n
    this.m = 0
    this.directed = directed
    // g初始化为n*n得布尔矩阵,每个g[i][j]均为false,表示没有任何边
    this.g = this.twoArr(n)
  }
  // 返回节点个数
  V() {
    return this.n
  }
  // 返回边得个数
  E() {
    return this.m
  }
  //向图中添加一个边
  add(v: number, w: number): void {
    try {
      console.assert(v >= 0 && v < this.n, 'v必须大于等于0,小于n')
      console.assert(w >= 0 && w < this.n, 'w必须大于等于0,小于n')
      if (this.hasEdge(v, w)) {
        return
      }
      this.g[v][w] = true
      //如果为无向图就职位true
      if (!this.directed) {
        this.g[w][v] = true
      }
      this.m++
    } catch (error) {}
  }
  //验证图中是否有从v到w得边
  hasEdge(v: number, w: number): boolean {
    try {
      console.assert(v >= 0 && v < this.n, 'v必须大于等于0,小于n')
      console.assert(w >= 0 && w < this.n, 'w必须大于等于0,小于n')
      return this.g[v][w]
    } catch (error) {}
    return false
  }
  //构造一个二位数组
  twoArr(n: number) {
    let aa = []
    for (let i = 0; i < n; i++) {
      aa[i] = new Array()
      for (let j = 0; j < n; j++) {
        aa[i][j] = false
      }
    }
    return aa as boolean[][]
  }
}
let graph1 = new DenseGraph(4, true)
graph1.add(3, 3)
console.log(graph1.g)

/**
 * 邻接表
 */
export class SparseGraph {
  // 节点数
  n: number
  // 边数
  m: number
  // 是否为有向图
  directed: boolean
  // 图得具体数据
  g: number[][]
  constructor(n: number, directed: boolean) {
    console.assert(n >= 0, 'n必须大于0')
    this.n = n
    this.m = 0
    this.directed = directed
    // g初始化为n*n得布尔矩阵,每个g[i][j]均为false,表示没有任何边
    this.g = this.twoArr(n)
  }
  // 返回节点个数
  V() {
    return this.n
  }
  // 返回边得个数
  E() {
    return this.m
  }
  //向图中添加一个边
  add(v: number, w: number): void {
    try {
      console.assert(v >= 0 && v < this.n, 'v必须大于等于0,小于n')
      console.assert(w >= 0 && w < this.n, 'w必须大于等于0,小于n')
      this.g[v].push(w)
      if (v != w && !this.directed) {
        this.g[w].push(v)
      }
      this.m++
    } catch (error) {}
  }
  //验证图中是否有从v到w得边
  hasEdge(v: number, w: number): boolean {
    try {
      console.assert(v >= 0 && v < this.n, 'v必须大于等于0,小于n')
      console.assert(w >= 0 && w < this.n, 'w必须大于等于0,小于n')
      for (let i = 0; i < this.g[v].length; i++) {
        if (this.g[v][i] === w) {
          return true
        }
        return false
      }
    } catch (error) {}
    return false
  }
  //构造一个二位数组
  twoArr(n: number) {
    let aa = []
    for (let i = 0; i < n; i++) {
      aa[i] = new Array()
    }
    return aa as number[][]
  }
}

深度优先搜索求出图的联调分量

//求出无权图的联调分量
  const components = () => {
    let graph2 = new DenseGraph(7, false)
    graph2.add(0, 1)
    graph2.add(0, 2)
    graph2.add(0, 5)
    graph2.add(0, 6)
    graph2.add(3, 4)
    graph2.add(3, 5)
    graph2.add(4, 5)
    graph2.add(4, 6)
    console.log(graph2.g)
    console.log(graph2.V())
    // 记录节点是否被访问
    let visited: Boolean[] = new Array(graph2.V()).fill(false)
    // 代表每个节点所对应的联调分量标记
    let id: number[] = new Array(graph2.V()).fill(-1)
    // 记录联调分量个数
    let count = 0
    // 记录深度优先搜索过程
    let dfsArr: number[] = []
    //图得深度优先搜索
    const dfs = (v: number) => {
      dfsArr.push(v)
      visited[v] = true
      id[v] = count
      for (let i of graph2.adj(v)) {
        if (!visited[i]) {
          dfs(i)
        }
      }
    }
    for (let i = 0; i < graph2.V(); i++) {
      if (!visited[i]) {
        console.log(`${i}0.0`, visited[i])
        dfs(i)
        count++
      }
    }
    console.log('count', count)
    console.log(dfsArr)
    console.log(graph2.adj(0))
    // console.log('id', id)
    // console.log('visited', visited)
  }

 //寻路算法,寻找图graph2从s点到其他点的路径
  const shortestPath = (s: number, w: number) => {
    let graph2 = new DenseGraph(7, false)
    graph2.add(0, 1)
    graph2.add(0, 2)
    graph2.add(0, 5)
    graph2.add(0, 6)
    graph2.add(3, 4)
    graph2.add(3, 5)
    graph2.add(4, 5)
    graph2.add(4, 6)
    // 记录节点是否被访问
    let visited: Boolean[] = new Array(graph2.V()).fill(false)
    // from[i]表示查找的路径上i的上一个节点
    let from: number[] = new Array(graph2.V()).fill(-1)
    // ord[i]表示i节点在路径中的次序
    let ord: number[] = new Array(graph2.V()).fill(-1)
    let q: number[] = []
    let bfs: number[] = []
    q.push(s)

    visited[s] = true
    ord[s] = 0
    while (q.length > 0) {
      let v = q.shift()
      bfs.push(v)
      for (let i of graph2.adj(v)) {
        if (!visited[i]) {
          q.push(i)
          visited[i] = true
          from[i] = v
          ord[i] = ord[v] + 1
        }
      }
    }
    // 查询s点到w点是否有路径
    const hasPath = (w: number) => {
      return visited[w]
    }
    //查看s点到w点的最短路径长度
    const length = (w: number) => {
      return ord[w]
    }
    //查询s点到w点的路径,存放在vec中
    const path = (w: number) => {
      let s = []
      let p = w
      while (p !== -1) {
        s.push(p)
        p = from[p]
      }
      let res = []
      while (s.length > 0) {
        res.push(s.pop())
      }
      return res
    }
    //打印出s点到w点的路径
    const showPath = (w: number) => {
      let vec = path(w)
      for (let i = 0; i < vec.length; i++) {
        console.log(vec[i])
        if (i === vec.length - 1) {
          console.log('结束了')
        } else {
          console.log('->')
        }
      }
    }
    console.log('bfs', bfs)
    console.log('form', from)
    console.log('ord', ord)
    console.log('haspath', hasPath(w))
    console.log('haspath', length(w))
    showPath(w)
  }
posted @ 2022-07-29 14:40  zeal666  阅读(23)  评论(0)    收藏  举报