代码改变世界

完整教程:kotlin图算法

2025-10-24 11:27  tlnshuju  阅读(5)  评论(0)    收藏  举报

欢迎访问我的主页: https://heeheeaii.github.io/

import java.util.*
import kotlin.math.*
// 边的数据类
data class Edge(val to: Int, val weight: Double = 1.0)
data class WeightedEdge(val from: Int, val to: Int, val weight: Double) : Comparable<WeightedEdge> {
  override fun compareTo(other: WeightedEdge) = weight.compareTo(other.weight)
  }
  /**
  * 集合: {0} {1} {2} {3} {4}
  * parent: [0,1,2,3,4]
  * rank:   [0,0,0,0,0]
  */
  // 并查集, 快速确定有限节点连通性
  class UnionFind(n: Int) {
  private val parent = IntArray(n) { it }
  private val rank = IntArray(n)
  fun find(x: Int): Int = if (parent[x] != x) find(parent[x]).also { parent[x] = it } else x
  fun union(x: Int, y: Int): Boolean {
  val px = find(x);
  val py = find(y)
  if (px == py) return false
  if (rank[px] < rank[py]) parent[px] = py
  else if (rank[px] > rank[py]) parent[py] = px
  else {
  parent[py] = px; rank[px]++
  }
  return true
  }
  }
  class GraphAlgorithms {
  // 1. 深度优先搜索 (DFS)
  fun dfs(graph: Array<MutableList<Edge>>, start: Int, visited: BooleanArray = BooleanArray(graph.size)): List<Int> {
    val result = mutableListOf<Int>()
      fun dfsRec(v: Int) {
      visited[v] = true
      result.add(v)
      graph[v].forEach { if (!visited[it.to]) dfsRec(it.to) }
      }
      dfsRec(start)
      return result
      }
      // 2. 广度优先搜索 (BFS)
      fun bfs(graph: Array<MutableList<Edge>>, start: Int): List<Int> {
        val visited = BooleanArray(graph.size)
        val queue = LinkedList<Int>()
          val result = mutableListOf<Int>()
            queue.offer(start)
            visited[start] = true
            while (queue.isNotEmpty()) {
            val v = queue.poll()
            result.add(v)
            graph[v].forEach {
            if (!visited[it.to]) {
            visited[it.to] = true
            queue.offer(it.to)
            }
            }
            }
            return result
            }
            // 3. Dijkstra算法
            fun dijkstra(graph: Array<MutableList<Edge>>, startIdx: Int): DoubleArray {
              val dist = DoubleArray(graph.size) { Double.POSITIVE_INFINITY }
              val protq = PriorityQueue<Pair<Double, Int>>(compareBy { it.first })
                dist[startIdx] = 0.0
                protq.offer(0.0 to startIdx)
                while (protq.isNotEmpty()) {
                val (disVlu, idx) = protq.poll()
                if (disVlu > dist[idx]) continue
                graph[idx].forEach { edge ->
                val newDist = dist[idx] + edge.weight
                if (newDist < dist[edge.to]) {
                dist[edge.to] = newDist
                protq.offer(newDist to edge.to)
                }
                }
                }
                return dist
                }
                // 4. Floyd-Warshall算法
                /**
                * @param adj matrix
                * @return min distance matrix
                */
                fun floydWarshall(adj: Array<DoubleArray>): Array<DoubleArray> {
                  val inSize = adj.size
                  val dist = Array(inSize) { adj[it].clone() }
                  for (idx in 0 until inSize) // mid station
                  for (jdx in 0 until inSize) // from station
                  for (kdx in 0 until inSize) // to station
                  if (dist[jdx][idx] + dist[idx][kdx] < dist[jdx][kdx]) dist[jdx][kdx] =
                  dist[jdx][idx] + dist[idx][kdx]
                  return dist
                  }
                  fun primForest(graph: Array<List<Edge>>): List<List<WeightedEdge>> {
                    val inSize = graph.size
                    val visited = BooleanArray(inSize)
                    val forest = mutableListOf<List<WeightedEdge>>()
                      for (start in 0 until inSize) {
                      if (!visited[start]) {
                      // 对每个连通分量运行Prim
                      val tree = prim(graph, start, visited)
                      if (tree.isNotEmpty()) {
                      forest.add(tree)
                      }
                      }
                      }
                      return forest
                      }
                      // 5. 单Prim算法
                      /**
                      * 生成最小生成树 mst
                      * 包含所有顶点:生成树必须包含图中的每一个顶点。
                      * 无环:生成树是一棵树,因此它不能包含环。
                      * 连通:生成树必须是连通的,即从任意一个顶点都可以到达其他所有顶点
                      */
                      private fun prim(
                      graph: Array<List<Edge>>,
                        start: Int,
                        globalVisited: BooleanArray,
                        ): List<WeightedEdge> {
                          val inMST = BooleanArray(graph.size)
                          val protQ = PriorityQueue<WeightedEdge>() // weight queue
                            val mst = mutableListOf<WeightedEdge>()
                              // 从指定起点开始
                              inMST[start] = true
                              globalVisited[start] = true
                              graph[start].forEach { protQ.offer(WeightedEdge(start, it.to, it.weight)) }
                              while (protQ.isNotEmpty()) {
                              val edge = protQ.poll()
                              if (inMST[edge.to]) continue
                              inMST[edge.to] = true
                              globalVisited[edge.to] = true
                              mst.add(edge)
                              graph[edge.to].forEach {
                              if (!inMST[it.to]) protQ.offer(WeightedEdge(edge.to, it.to, it.weight))
                              }
                              }
                              return mst
                              }
                              /**
                              *  从边的角度出发,构建最小生成树森林
                              */
                              fun kruskalForest(num: Int, edges: List<WeightedEdge>): List<WeightedEdge> {
                                if (num <= 0) return emptyList()
                                val uf = UnionFind(num)
                                val forest = mutableListOf<WeightedEdge>()
                                  edges.sorted().forEach { edge ->
                                  if (uf.union(edge.from, edge.to)) {
                                  forest.add(edge)
                                  // 最多有 num-1 条边(全连通的情况)
                                  if (forest.size == num - 1) return forest
                                  }
                                  }
                                  return forest
                                  }
                                  // 7. 拓扑排序
                                  fun topologicalSortForest(graph: Array<List<Edge>>): List<List<Int>> {
                                    val inSize = graph.size
                                    val inDegree = IntArray(inSize)
                                    val visited = BooleanArray(inSize)
                                    val forest = mutableListOf<List<Int>>()
                                      // 计算初始入度
                                      graph.forEach { it.forEach { edge -> inDegree[edge.to]++ } }
                                      while (visited.count { !it } > 0) {  // 还有未访问的节点
                                      val queue = LinkedList<Int>()
                                        val curTree = mutableListOf<Int>()
                                          // 找到当前未访问节点中入度为0的节点
                                          for (idx in 0 until inSize) {
                                          if (!visited[idx] && inDegree[idx] == 0) {
                                          queue.offer(idx)
                                          }
                                          }
                                          // 如果没有入度为0的节点,说明剩余节点形成环,跳过这些节点
                                          if (queue.isEmpty()) {
                                          // 标记所有剩余节点为已访问(因为它们形成环,无法拓扑排序)
                                          for (i in 0 until inSize) {
                                          if (!visited[i]) {
                                          visited[i] = true
                                          }
                                          }
                                          break
                                          }
                                          // 对当前连通分量进行拓扑排序
                                          while (queue.isNotEmpty()) {
                                          val current = queue.poll()
                                          if (visited[current]) continue  // 避免重复处理
                                          visited[current] = true
                                          curTree.add(current)
                                          // 处理当前节点的邻接节点
                                          graph[current].forEach { edge ->
                                          if (!visited[edge.to]) {
                                          inDegree[edge.to]--
                                          if (inDegree[edge.to] == 0) {
                                          queue.offer(edge.to)
                                          }
                                          }
                                          }
                                          }
                                          // 将当前连通分量的结果添加到森林中
                                          if (curTree.isNotEmpty()) {
                                          forest.add(curTree)
                                          }
                                          }
                                          return forest
                                          }
                                          /**
                                          * 寻找全部强连通图
                                          *
                                          * 0 → 1
                                          * 1 → 2
                                          * 2 → 0  // 形成环 {0,1,2}
                                          * 1 → 3  // 3是独立节点
                                          *
                                          * 1. 访问0: findTime[0]=0, minFindTime[0]=0, 栈=[0]
                                          * 2. 访问1: findTime[1]=1, minFindTime[1]=1, 栈=[0,1]
                                          * 3. 访问2: findTime[2]=2, minFindTime[2]=2, 栈=[0,1,2]
                                          * 4. 2→0: 0在栈中(回边)
                                          *    minFindTime[2] = min(2, findTime[0]) = 0
                                          * 5. 回溯到1:
                                          *    minFindTime[1] = min(1, minFindTime[2]) = min(1, 0) = 0
                                          * 6. 访问3: findTime[3]=3, minFindTime[3]=3, 栈=[0,1,3]
                                          * 7. 回溯到1:
                                          *    minFindTime[1] = min(0, minFindTime[3]) = min(0, 3) = 0
                                          */
                                          // 8. Tarjan算法 (strongly connected components, 从一个点出发通过某个路径能达到任意一个点)
                                          fun tarjanSCC(graph: Array<List<Edge>>): List<List<Int>> {
                                            val inSize = graph.size
                                            var time = 0
                                            val firstFindTime = IntArray(inSize)
                                            val perFindTime = IntArray(inSize)
                                            val onStack = BooleanArray(inSize)
                                            val stack = Stack<Int>()
                                              val sccs = mutableListOf<List<Int>>()
                                                fun findSccByFirstFindTimeDFS(fromIdx: Int) {
                                                perFindTime[fromIdx] = time; firstFindTime[fromIdx] = time++
                                                stack.push(fromIdx); onStack[fromIdx] = true
                                                graph[fromIdx].forEach { edge ->
                                                val toIdx = edge.to
                                                if (perFindTime[toIdx] == -1) {
                                                findSccByFirstFindTimeDFS(toIdx)
                                                firstFindTime[fromIdx] = min(
                                                firstFindTime[fromIdx], firstFindTime[toIdx]
                                                ) // firstFindTime may transport from other place
                                                } else if (onStack[toIdx]) {
                                                firstFindTime[fromIdx] = min(firstFindTime[fromIdx], perFindTime[toIdx]) // a explore stop place
                                                }
                                                }
                                                if (firstFindTime[fromIdx] == perFindTime[fromIdx]) { // a scc input port
                                                val scc = mutableListOf<Int>()
                                                  do {
                                                  val midIdx = stack.pop()
                                                  onStack[midIdx] = false
                                                  scc.add(midIdx)
                                                  } while (midIdx != fromIdx)
                                                  sccs.add(scc)
                                                  }
                                                  }
                                                  perFindTime.fill(-1)
                                                  for (idx in 0 until inSize) if (perFindTime[idx] == -1) findSccByFirstFindTimeDFS(idx)
                                                  return sccs
                                                  }
                                                  /**
                                                  * Kosaraju算法 - 寻找强连通分量
                                                  *
                                                  * 算法原理:
                                                  * 图: 0→1→2→0 (强连通分量{0,1,2}),3→4 (强连通分量{3},{4})
                                                  *
                                                  * 第一步:正向DFS,记录完成顺序
                                                  * 访问顺序可能是: 0→1→2,然后3→4
                                                  * 完成顺序(后序): [2,1,0,4,3]
                                                  *
                                                  * 第二步:构建转置图
                                                  * 原图: 0→1→2→0,3→4
                                                  * 转置: 0←1←2←0,3←4
                                                  *
                                                  * 第三步:按完成顺序逆序DFS转置图
                                                  * 从3开始:只能访问3 → SCC{3}
                                                  * 从4开始:只能访问4 → SCC{4}
                                                  * 从0开始:能访问0←1←2 → SCC{0,1,2}
                                                  */
                                                  fun kosarajuStronglyConnectedComponents(graph: Array<List<Edge>>): List<List<Int>> {
                                                    val nodeCount = graph.size
                                                    val isNodeVisited = BooleanArray(nodeCount)
                                                    val findTime = mutableListOf<Int>() // 节点完成时间顺序
                                                      val reversedGraph = Array(nodeCount) { mutableListOf<Edge>() } // 转置图
                                                        // 第一步:正向DFS,记录节点完成顺序
                                                        fun recordFinishOrderByDFS(currentNode: Int) {
                                                        isNodeVisited[currentNode] = true
                                                        // 遍历当前节点的所有邻居
                                                        graph[currentNode].forEach { edge ->
                                                        if (!isNodeVisited[edge.to]) {
                                                        recordFinishOrderByDFS(edge.to)
                                                        }
                                                        }
                                                        // 当前节点处理完毕,加入完成顺序列表
                                                        findTime.add(currentNode)
                                                        }
                                                        // 第二步:在转置图中进行DFS,收集强连通分量
                                                        fun collectStronglyConnectedComponentByDFS(currentNode: Int, currentSCC: MutableList<Int>) {
                                                          isNodeVisited[currentNode] = true
                                                          currentSCC.add(currentNode)
                                                          // 在转置图中遍历邻居
                                                          reversedGraph[currentNode].forEach { edge ->
                                                          if (!isNodeVisited[edge.to]) {
                                                          collectStronglyConnectedComponentByDFS(edge.to, currentSCC)
                                                          }
                                                          }
                                                          }
                                                          // 执行第一步:记录完成顺序
                                                          for (nodeIndex in 0 until nodeCount) {
                                                          if (!isNodeVisited[nodeIndex]) {
                                                          recordFinishOrderByDFS(nodeIndex)
                                                          }
                                                          }
                                                          // 构建转置图:将所有边反向
                                                          graph.forEachIndexed { fromNode, edges ->
                                                          edges.forEach { edge ->
                                                          reversedGraph[edge.to].add(Edge(fromNode))
                                                          }
                                                          }
                                                          // 重置访问标记,准备第二步
                                                          isNodeVisited.fill(false)
                                                          val sccs = mutableListOf<List<Int>>()
                                                            // 执行第二步:按完成顺序逆序遍历转置图
                                                            for (nodeIndex in findTime.reversed()) {
                                                            if (!isNodeVisited[nodeIndex]) {
                                                            val currentSCC = mutableListOf<Int>()
                                                              collectStronglyConnectedComponentByDFS(nodeIndex, currentSCC)
                                                              sccs.add(currentSCC)
                                                              }
                                                              }
                                                              return sccs
                                                              }
                                                              /**
                                                              * Ford-Fulkerson最大流算法 (使用BFS寻找增广路径)
                                                              *
                                                              * 算法原理:
                                                              * 容量图: S→A(100), S→B(100), A→C(1), A→T(100), B→C(100), C→T(100)
                                                              *
                                                              * 第1次增广:S→A→C→T,流量=1,剩余容量:S→A(99), A→C(0), C→T(99), 反向边:A→S(1), C→A(1), T→C(1)
                                                              * 第2次增广:S→A→T,流量=99,剩余容量:S→A(0), A→T(1), 反向边:A→S(100), T→A(99)
                                                              * 第3次增广:S→B→C→T,流量=99,剩余容量:S→B(1), B→C(1), C→T(0), 反向边:B→S(99), C→B(99), T→C(100)
                                                              * 第4次增广:S→B→C→A→T,流量=1,使用反向边C→A,剩余容量:S→B(0), B→C(0), C→A(0), A→T(0)
                                                              * 最大流 = 1 + 99 + 99 + 1 = 200 (如果没有第4次反向边增广,最大流只能是199)
                                                              *
                                                              * Ford-Fulkerson算法数学原理详解
                                                              *
                                                              * 一、基础概念与定义
                                                              *
                                                              * 1.1 流网络基本结构
                                                              *
                                                              * 流网络定义:
                                                              *
                                                              * - 网络结构:有向图 G = (V, E),包含源点 s 和汇点 t
                                                              * - 边容量:c(u,v) ≥ 0,表示边 (u,v) 的最大流量容量
                                                              * - 流量函数
                                                              *   :f(u,v) 表示边 (u,v) 上的实际流量,满足容量约束:
                                                              *       0 ≤ f(u,v) ≤ c(u,v)
                                                              *
                                                              * 1.2 流量守恒定律
                                                              *
                                                              * 中间节点的流量守恒 对于任意中间节点 u(u ≠ s 且 u ≠ t):
                                                              *
                                                              *     ∑[v∈V] f(v,u) = ∑[v∈V] f(u,v)
                                                              *
                                                              * 含义:流入节点 u 的总流量 = 流出节点 u 的总流量
                                                              *
                                                              * 源点和汇点的流量性质
                                                              *
                                                              * - 源点 s:∑[v∈V] f(s,v) - ∑[v∈V] f(v,s) = |f| (净输出等于总流量)
                                                              * - 汇点 t:∑[v∈V] f(v,t) - ∑[v∈V] f(t,v) = |f| (净输入等于总流量)
                                                              *
                                                              * 1.3 残存网络
                                                              *
                                                              * 给定当前流 f,残存网络中的残存容量定义为:
                                                              *
                                                              *     残存容量(u,v) = {
                                                              *       c(u,v) - f(u,v)    如果原图存在边 u→v(正向边剩余容量)
                                                              *       f(v,u)             如果原图存在边 v→u(反向边可撤销容量)
                                                              *       0                  其他情况
                                                              *     }
                                                              *
                                                              * 二、核心理论基础
                                                              *
                                                              * 2.1 最大流-最小割定理
                                                              *
                                                              * 定理陈述:在任何流网络中,最大流的值等于最小割的容量。
                                                              *
                                                              * 割的定义: 割 (X,Y) 将节点集 V 分为两个不相交的子集,使得 s ∈ X,t ∈ Y。
                                                              *
                                                              *     割的容量 = ∑[u∈X,v∈Y] c(u,v)
                                                              *
                                                              * 证明过程
                                                              *
                                                              * 方向1:最大流 ≤ 最小割
                                                              *
                                                              * 对于任意流 f 和任意割 (X,Y):
                                                              *
                                                              *     |f| = 从 X 到 Y 的净流量
                                                              *         = ∑[u∈X,v∈Y] f(u,v) - ∑[v∈Y,u∈X] f(v,u)
                                                              *         ≤ ∑[u∈X,v∈Y] c(u,v) - 0    (容量约束 + 流量非负)
                                                              *         = 割(X,Y)的容量
                                                              *
                                                              * 方向2:最大流 ≥ 最小割
                                                              *
                                                              * 当 Ford-Fulkerson 算法终止时的流 f*:
                                                              *
                                                              * 1. 构造关键割:
                                                              *    - X = {从 s 在残存网络中能到达的所有节点}
                                                              *    - Y = V - X(注意:t ∈ Y,否则算法未终止)
                                                              * 2. 关键性质:
                                                              *    - 若 u ∈ X, v ∈ Y 且原图有边 (u,v),则 f*(u,v) = c(u,v)(正向边饱和)
                                                              *    - 若 u ∈ X, v ∈ Y 且原图有边 (v,u),则 f*(v,u) = 0(反向边无流)
                                                              * 3. 等式成立:
                                                              *        |f*| = ∑[u∈X,v∈Y] f*(u,v) - ∑[v∈Y,u∈X] f*(v,u)
                                                              *             = ∑[u∈X,v∈Y] c(u,v) - 0
                                                              *             = 割(X,Y)的容量
                                                              *
                                                              * 结论:最大流 = 最小割
                                                              *
                                                              * 2.2 增广路径定理
                                                              *
                                                              * 定理:流 f 是最大流 ⟺ 残存网络中不存在从 s 到 t 的路径
                                                              *
                                                              * 证明要点:
                                                              *
                                                              * - 充分性:若残存网络中存在 s 到 t 的路径,则可继续增加流量,f 非最大
                                                              * - 必要性:若 f 是最大流,则残存网络中必无 s 到 t 的路径
                                                              *
                                                              * 三、算法运作机制
                                                              *
                                                              * 3.1 反向边的数学含义
                                                              *
                                                              * 作用机制:允许算法"撤销"先前的流量分配决策
                                                              *
                                                              * 数学表示:
                                                              *
                                                              * - 如果边 (u,v) 当前流量为 f(u,v)
                                                              * - 残存网络中反向边 (v,u) 的容量 = f(u,v)
                                                              * - 表示最多可撤销 f(u,v) 单位的流量重新分配
                                                              *
                                                              * 3.2 流量增广操作
                                                              *
                                                              * 增广步骤:
                                                              *
                                                              * 1. 寻找增广路径:在残存网络中找到 s 到 t 的路径 P
                                                              * 2. 计算增广量:δ = min{残存容量(u,v) | (u,v) ∈ P}
                                                              * 3. 更新流量
                                                              *    :沿路径 P 的每条边 (u,v):
                                                              *    - 若为正向边:f(u,v) ← f(u,v) + δ
                                                              *    - 若为反向边:f(v,u) ← f(v,u) - δ
                                                              *
                                                              * 四、算法正确性分析
                                                              *
                                                              * 4.1 终止性保证
                                                              *
                                                              * 整数容量情况:
                                                              *
                                                              * - 每次增广至少增加 1 单位流量
                                                              * - 流量存在上界(等于最小割容量)
                                                              * - 因此算法必然在有限步内终止
                                                              *
                                                              * 4.2 最优性证明
                                                              *
                                                              * 算法终止时的流 f* 满足:
                                                              *
                                                              * 1. 终止条件:残存网络中 s 无法到达 t
                                                              * 2. 构造最小割
                                                              *    :
                                                              *    - X = {从 s 在残存网络中可达的节点}
                                                              *    - Y = V - X
                                                              * 3. 流量等于割容量:|f*| = 割(X,Y)的容量
                                                              * 4. 最优性结论:根据最大流-最小割定理,f* 是最大流
                                                              *
                                                              * 五、反向边必要性分析
                                                              *
                                                              * 5.1 经典反例
                                                              *
                                                              * 网络结构:
                                                              *
                                                              *     S → A (容量100)    A → T (容量100)
                                                              *     S → B (容量100)    B → C (容量100)
                                                              *     A → C (容量1)      C → T (容量100)
                                                              *
                                                              * 5.2 贪心策略的失败
                                                              *
                                                              * 错误的增广序列:
                                                              *
                                                              *     第1次:S→A→C→T,流量 = 1
                                                              *     第2次:S→A→T,  流量 = 99
                                                              *     第3次:S→B→C→T,流量 = 99
                                                              *     总流量 = 199 (非最优)
                                                              *
                                                              * 5.3 反向边的拯救
                                                              *
                                                              * 修正增广:
                                                              *
                                                              *     第4次:S→B→C→A→T,流量 = 1
                                                              *
                                                              * - 利用反向边 C→A 撤销第1次的次优分配
                                                              * - 最终达到最优解:总流量 = 200
                                                              *
                                                              * @param capacityMatrix 如果两个节点之间有容量, (row, col) 表示 row 到 col的容量, 否则是0.
                                                              */
                                                              fun fordFulkersonMaxFlow(capacityMatrix: Array<IntArray>, sourceNode: Int, sinkNode: Int): Int {
                                                                val nodeCount = capacityMatrix.size
                                                                val capacityMatrix = Array(nodeCount) { capacityMatrix[it].clone() } // 剩余容量图
                                                                var maxFlow = 0
                                                                // 使用BFS寻找从源点到汇点的增广路径
                                                                fun findAugmentingPathByBFS(): IntArray? {
                                                                val isNodeVisited = BooleanArray(nodeCount)
                                                                val parentNode = IntArray(nodeCount) { -1 } // 记录路径中每个节点的父节点
                                                                val nodeQueue = LinkedList<Int>()
                                                                  nodeQueue.offer(sourceNode)
                                                                  isNodeVisited[sourceNode] = true
                                                                  while (nodeQueue.isNotEmpty()) {
                                                                  val currentNode = nodeQueue.poll()
                                                                  // 检查当前节点的所有邻居
                                                                  for (neighborNode in 0 until nodeCount) {
                                                                  // 如果邻居未访问且有剩余容量
                                                                  if (!isNodeVisited[neighborNode] && capacityMatrix[currentNode][neighborNode] > 0) {
                                                                  nodeQueue.offer(neighborNode)
                                                                  isNodeVisited[neighborNode] = true
                                                                  parentNode[neighborNode] = currentNode
                                                                  // 如果到达汇点,返回路径
                                                                  if (neighborNode == sinkNode) {
                                                                  return parentNode
                                                                  }
                                                                  }
                                                                  }
                                                                  }
                                                                  return null // 没有找到增广路径
                                                                  }
                                                                  // 主循环:不断寻找增广路径直到无法找到
                                                                  while (true) {
                                                                  val findPath = findAugmentingPathByBFS() ?: break
                                                                  // 计算这条路径上的最小流
                                                                  var minFlow = Int.MAX_VALUE
                                                                  var currentNode = sinkNode
                                                                  while (currentNode != sourceNode) {
                                                                  val parentNode = findPath[currentNode]
                                                                  minFlow = min(minFlow, capacityMatrix[parentNode][currentNode])
                                                                  currentNode = parentNode
                                                                  }
                                                                  maxFlow += minFlow
                                                                  var toNode = sinkNode
                                                                  while (toNode != sourceNode) {
                                                                  val fromNode = findPath[toNode]
                                                                  capacityMatrix[fromNode][toNode] -= minFlow // 减少正向容量
                                                                  capacityMatrix[toNode][fromNode] += minFlow // 增加反向容量
                                                                  toNode = fromNode
                                                                  }
                                                                  }
                                                                  return maxFlow
                                                                  }
                                                                  /**
                                                                  * Bellman-Ford单源最短路径算法 (支持负权边)
                                                                  *
                                                                  * 算法原理:
                                                                  * 图: A→B(1), B→C(-3), A→C(4), C→B(2)
                                                                  *
                                                                  * 初始化:dist[A]=0, dist[B]=∞, dist[C]=∞
                                                                  * 第1轮松弛:
                                                                  *   A→B: dist[B] = min(∞, 0+1) = 1
                                                                  *   A→C: dist[C] = min(∞, 0+4) = 4
                                                                  *   B→C: dist[C] = min(4, 1+(-3)) = -2
                                                                  * 第2轮松弛:
                                                                  *   C→B: dist[B] = min(1, -2+2) = 0
                                                                  * 第3轮检测负环:无更新 → 无负环
                                                                  */
                                                                  fun bellmanFordShortestPath(nodeCount: Int, edgeList: List<WeightedEdge>, startNode: Int): DoubleArray? {
                                                                    val distanceFromStart = DoubleArray(nodeCount) { Double.POSITIVE_INFINITY }
                                                                    distanceFromStart[startNode] = 0.0
                                                                    // 进行(nodeCount-1)轮松弛操作
                                                                    // 最短路径最多包含(nodeCount-1)条边
                                                                    repeat(nodeCount - 1) { _ ->
                                                                    var hasDistanceUpdate = false
                                                                    edgeList.forEach { edge ->
                                                                    val fromNode = edge.from
                                                                    if (distanceFromStart[fromNode] == Double.POSITIVE_INFINITY) {
                                                                    return@forEach
                                                                    }
                                                                    val toNode = edge.to
                                                                    val edgeWeight = edge.weight
                                                                    val newDistanceToTarget = distanceFromStart[fromNode] + edgeWeight
                                                                    if (newDistanceToTarget < distanceFromStart[toNode]) {
                                                                    distanceFromStart[toNode] = newDistanceToTarget
                                                                    hasDistanceUpdate = true
                                                                    }
                                                                    }
                                                                    // 如果本轮没有任何更新,可以提前结束
                                                                    if (!hasDistanceUpdate) return@repeat
                                                                    }
                                                                    // 检测负环:再进行一轮松弛,如果还有更新说明存在负环
                                                                    edgeList.forEach { edge ->
                                                                    val fromNode = edge.from
                                                                    val toNode = edge.to
                                                                    val edgeWeight = edge.weight
                                                                    if (distanceFromStart[fromNode] != Double.POSITIVE_INFINITY) {
                                                                    val newDistanceToTarget = distanceFromStart[fromNode] + edgeWeight
                                                                    if (newDistanceToTarget < distanceFromStart[toNode]) {
                                                                    return null // 检测到负环
                                                                    }
                                                                    }
                                                                    }
                                                                    return distanceFromStart
                                                                    }
                                                                    /**
                                                                    * Dijkstra最短路径算法 (用于Bellman-Ford和其他算法)
                                                                    */
                                                                    fun dijkstraShortestPath(graph: Array<MutableList<Edge>>, startNode: Int): DoubleArray {
                                                                      val nodeCount = graph.size
                                                                      val distanceFromStart = DoubleArray(nodeCount) { Double.POSITIVE_INFINITY }
                                                                      val isNodeProcessed = BooleanArray(nodeCount)
                                                                      val priorityQueue = PriorityQueue<Pair<Double, Int>>(compareBy { it.first })
                                                                        distanceFromStart[startNode] = 0.0
                                                                        priorityQueue.offer(0.0 to startNode)
                                                                        while (priorityQueue.isNotEmpty()) {
                                                                        val (currentDistance, currentNode) = priorityQueue.poll()
                                                                        if (isNodeProcessed[currentNode]) continue
                                                                        isNodeProcessed[currentNode] = true
                                                                        graph[currentNode].forEach { edge ->
                                                                        val neighborNode = edge.to
                                                                        val edgeWeight = edge.weight
                                                                        val newDistance = currentDistance + edgeWeight
                                                                        if (newDistance < distanceFromStart[neighborNode]) {
                                                                        distanceFromStart[neighborNode] = newDistance
                                                                        priorityQueue.offer(newDistance to neighborNode)
                                                                        }
                                                                        }
                                                                        }
                                                                        return distanceFromStart
                                                                        }
                                                                        /**
                                                                        * A*启发式搜索算法
                                                                        *
                                                                        * 算法原理:
                                                                        * f(n) = g(n) + h(n)
                                                                        * g(n) = 从起点到当前节点的实际代价
                                                                        * h(n) = 从当前节点到目标的启发式估计代价
                                                                        *
                                                                        * 示例:在网格中从(0,0)到(2,2)
                                                                        * 启发式函数:曼哈顿距离 h(x,y) = |x-2| + |y-2|
                                                                        *
                                                                        * 过程:
                                                                        * 1. 将起点(0,0)加入开放列表,g=0, h=4, f=4
                                                                        * 2. 扩展(0,0)的邻居,选择f值最小的节点
                                                                        * 3. 重复直到找到目标或开放列表为空
                                                                        */
                                                                        fun aStarPathFinding(
                                                                        graph: Array<List<Edge>>,
                                                                          startNode: Int,
                                                                          targetNode: Int,
                                                                          heuristicFunction: (Int) -> Double,
                                                                          ): List<Int>? {
                                                                            val nodeCount = graph.size
                                                                            val prrtQ = PriorityQueue<Pair<Double, Int>>(compareBy { it.first })
                                                                              val toFromPair = mutableMapOf<Int, Int>()
                                                                                val knownCost = DoubleArray(nodeCount) { Double.POSITIVE_INFINITY }
                                                                                val predictAllCost = DoubleArray(nodeCount) { Double.POSITIVE_INFINITY }
                                                                                knownCost[startNode] = 0.0
                                                                                predictAllCost[startNode] = heuristicFunction(startNode)
                                                                                prrtQ.offer(predictAllCost[startNode] to startNode)
                                                                                while (prrtQ.isNotEmpty()) {
                                                                                val curNode = prrtQ.poll().second
                                                                                if (curNode == targetNode) {
                                                                                val findPath = mutableListOf<Int>()
                                                                                  var pathNode = curNode
                                                                                  while (pathNode in toFromPair) {
                                                                                  findPath.add(0, pathNode)
                                                                                  pathNode = toFromPair[pathNode]!!
                                                                                  }
                                                                                  findPath.add(0, startNode)
                                                                                  return findPath
                                                                                  }
                                                                                  // 扩展当前节点的所有邻居
                                                                                  graph[curNode].forEach { edge ->
                                                                                  val toNode = edge.to
                                                                                  val wight = edge.weight
                                                                                  // 计算通过当前节点到达邻居的代价
                                                                                  val newCost = knownCost[curNode] + wight
                                                                                  // 如果找到更好的路径
                                                                                  if (newCost < knownCost[toNode]) {
                                                                                  toFromPair[toNode] = curNode
                                                                                  knownCost[toNode] = newCost
                                                                                  predictAllCost[toNode] = knownCost[toNode] + heuristicFunction(toNode)
                                                                                  // 将邻居加入开放列表
                                                                                  prrtQ.offer(predictAllCost[toNode] to toNode)
                                                                                  }
                                                                                  }
                                                                                  }
                                                                                  return null // 无法找到路径
                                                                                  }
                                                                                  /**
                                                                                  * Johnson全对最短路径算法
                                                                                  *
                                                                                  * 定理1:对于任意从节点u到节点v的路径P,设P = u → w₁ → w₂ → ... → wₖ → v
                                                                                  * 原始路径权重:
                                                                                  * W_original(P) = weight(u,w₁) + weight(w₁,w₂) + ... + weight(wₖ,v)
                                                                                  * 重新赋权后的路径权重:
                                                                                  * W_new(P) = [weight(u,w₁) + h[u] - h[w₁]] +
                                                                                  *            [weight(w₁,w₂) + h[w₁] - h[w₂]] +
                                                                                  *            ... +
                                                                                  *            [weight(wₖ,v) + h[wₖ] - h[v]]
                                                                                  * 展开并约简:
                                                                                  * W_new(P) = weight(u,w₁) + weight(w₁,w₂) + ... + weight(wₖ,v) +
                                                                                  *            h[u] - h[w₁] + h[w₁] - h[w₂] + ... + h[wₖ] - h[v]
                                                                                  *          = W_original(P) + h[u] - h[v]
                                                                                  * 关键结论:任何从u到v的路径,重新赋权后都增加了相同的常数 h[u] - h[v]
                                                                                  * 推论:最短路径保持不变
                                                                                  * 如果路径P₁是原图中u到v的最短路径,路径P₂是任意其他路径:
                                                                                  *
                                                                                  * 原图:W_original(P₁) ≤ W_original(P₂)
                                                                                  * 新图:W_original(P₁) + h[u] - h[v] ≤ W_original(P₂) + h[u] - h[v]
                                                                                  *
                                                                                  * 因此P₁在新图中仍然是最短路径!
                                                                                  * 性质2:负权边的消除
                                                                                  * 定理2:通过Bellman-Ford算法计算的h函数满足三角不等式,保证所有重新赋权后的边都是非负的。
                                                                                  * 三角不等式:对于任意边(i,j)
                                                                                  * h[j] ≤ h[i] + weight(i,j)
                                                                                  * 证明:
                                                                                  *
                                                                                  * h[j] = 从虚拟源点s到j的最短距离
                                                                                  * h[i] = 从虚拟源点s到i的最短距离
                                                                                  * 从s到j的路径可以是:s → i → j,距离为 h[i] + weight(i,j)
                                                                                  * 由于h[j]是最短距离,所以 h[j] ≤ h[i] + weight(i,j)
                                                                                  *
                                                                                  * 推导非负权重:
                                                                                  * 新权重 = weight(i,j) + h[i] - h[j] ≥ weight(i,j) + h[i] - (h[i] + weight(i,j)) = 0
                                                                                  * 性质3:距离恢复的正确性
                                                                                  * 定理3:最终的距离恢复公式能正确计算原图的最短距离。
                                                                                  * 设d_new[u][v]是重新赋权图中u到v的最短距离,那么原图中u到v的最短距离为:
                                                                                  * d_original[u][v] = d_new[u][v] - h[u] + h[v]
                                                                                  * 证明:
                                                                                  *
                                                                                  * 在重新赋权图中,u到v最短路径的权重 = 原图最短路径权重 + h[u] - h[v]
                                                                                  * 因此:原图最短路径权重 = 重新赋权图最短路径权重 - (h[u] - h[v])
                                                                                  * 即:d_original[u][v] = d_new[u][v] - h[u] + h[v]
                                                                                  */
                                                                                  fun johnsonAllPairsShortestPath(inSize: Int, edgeList: List<WeightedEdge>): Array<DoubleArray>? {
                                                                                    val anyVlu = 0.0
                                                                                    val extendedEdgeList = edgeList + (0 until inSize).map {
                                                                                    WeightedEdge(inSize, it, anyVlu)
                                                                                    } // 选择几是无所谓的
                                                                                    val reFunc = bellmanFordShortestPath(inSize + 1, extendedEdgeList, inSize) // 起点到终点最小值
                                                                                    ?: return null // 存在负环
                                                                                    val newWeights = edgeList.map { edge ->
                                                                                    WeightedEdge(edge.from, edge.to, edge.weight + reFunc[edge.from] - reFunc[edge.to])
                                                                                    }
                                                                                    val newGraph = Array(inSize) { mutableListOf<Edge>() }
                                                                                      newWeights.forEach { edge ->
                                                                                      newGraph[edge.from].add(Edge(edge.to, edge.weight))
                                                                                      }
                                                                                      val distanceMatrix = Array(inSize) { DoubleArray(inSize) }
                                                                                      for (idx in 0 until inSize) {
                                                                                      val minDistanceFromIdx = dijkstraShortestPath(newGraph, idx) // must find min distance from idx
                                                                                      for (target in 0 until inSize) {
                                                                                      distanceMatrix[idx][target] = when {
                                                                                      minDistanceFromIdx[target] == Double.POSITIVE_INFINITY -> Double.POSITIVE_INFINITY
                                                                                      else -> minDistanceFromIdx[target] - reFunc[idx] + reFunc[target]
                                                                                      }
                                                                                      }
                                                                                      }
                                                                                      return distanceMatrix
                                                                                      }
                                                                                      /**
                                                                                      * PageRank算法 - 网页排名算法
                                                                                      *
                                                                                      * 算法原理:
                                                                                      * PR(A) = (1-d)/N + d × Σ(PR(T)/C(T))
                                                                                      * 其中:d为阻尼系数(通常0.85), N为总页面数, T为链向A的页面, C(T)为T的出链数
                                                                                      *
                                                                                      * 示例:3个页面 A→B, B→C, C→A
                                                                                      * 初始:PR(A)=PR(B)=PR(C)=1/3
                                                                                      * 迭代:
                                                                                      * PR(A) = 0.15/3 + 0.85×PR(C)/1 = 0.05 + 0.85×(1/3) ≈ 0.33
                                                                                      * PR(B) = 0.05 + 0.85×PR(A)/1 ≈ 0.33
                                                                                      * PR(C) = 0.05 + 0.85×PR(B)/1 ≈ 0.33
                                                                                      */
                                                                                      fun pageRankCalculation(
                                                                                      linkGraph: Array<MutableList<Edge>>,
                                                                                        iteratorTime: Int = 100,
                                                                                        dampingFactor: Double = 0.85,
                                                                                        ): DoubleArray {
                                                                                        val inSize = linkGraph.size
                                                                                        val pageRanks = DoubleArray(inSize) { 1.0 / inSize } // 初始化为均匀分布
                                                                                        val addRanks = DoubleArray(inSize)
                                                                                        repeat(iteratorTime) { _ ->
                                                                                        addRanks.fill((1.0 - dampingFactor) / inSize)
                                                                                        for (jdx in 0 until inSize) {
                                                                                        val outSize = linkGraph[jdx].size
                                                                                        if (outSize > 0) {
                                                                                        val rankContribution = dampingFactor * pageRanks[jdx] / outSize
                                                                                        linkGraph[jdx].forEach { edge ->
                                                                                        addRanks[edge.to] += rankContribution
                                                                                        }
                                                                                        }
                                                                                        }
                                                                                        // 更新PageRank值
                                                                                        pageRanks.indices.forEach { idx ->
                                                                                        pageRanks[idx] = addRanks[idx]
                                                                                        }
                                                                                        }
                                                                                        return pageRanks
                                                                                        }
                                                                                        /**
                                                                                        * HITS算法 - 权威性和中心性计算
                                                                                        *
                                                                                        * 算法原理:
                                                                                        * Authority(p) = Σ Hub(q) (对所有指向p的页面q)
                                                                                        * Hub(p) = Σ Authority(q) (对所有p指向的页面q)
                                                                                        *
                                                                                        * 示例:A→B→C, A→C
                                                                                        * 初始:所有页面的Authority和Hub值都是1
                                                                                        *
                                                                                        * 第1次迭代:
                                                                                        * Auth(A)=0, Auth(B)=Hub(A)=1, Auth(C)=Hub(A)+Hub(B)=2
                                                                                        * Hub(A)=Auth(B)+Auth(C)=3, Hub(B)=Auth(C)=2, Hub(C)=0
                                                                                        *
                                                                                        * 然后归一化,继续迭代直到收敛
                                                                                        */
                                                                                        fun hitsAuthorityHub(
                                                                                        links: Array<List<Edge>>,
                                                                                          maxIterations: Int = 100,
                                                                                          ): Pair<DoubleArray, DoubleArray> {
                                                                                            val inSize = links.size
                                                                                            val authorityScore = DoubleArray(inSize) { 1.0 }
                                                                                            val navScore = DoubleArray(inSize) { 1.0 }
                                                                                            val tmpAtrtScore = DoubleArray(inSize)
                                                                                            val tmpNavScore = DoubleArray(inSize)
                                                                                            repeat(maxIterations) { _ ->
                                                                                            tmpAtrtScore.fill(0.0)
                                                                                            tmpNavScore.fill(0.0)
                                                                                            for (idx in 0 until inSize) {
                                                                                            links[idx].forEach { edge ->
                                                                                            tmpAtrtScore[edge.to] += navScore[idx]
                                                                                            }
                                                                                            }
                                                                                            for (idx in 0 until inSize) {
                                                                                            links[idx].forEach { edge ->
                                                                                            tmpNavScore[idx] += tmpAtrtScore[edge.to]
                                                                                            }
                                                                                            }
                                                                                            val atrtNormal = sqrt(tmpAtrtScore.sumOf { it * it })
                                                                                            if (atrtNormal > 0) {
                                                                                            for (idx in 0 until inSize) {
                                                                                            authorityScore[idx] = tmpAtrtScore[idx] / atrtNormal
                                                                                            }
                                                                                            }
                                                                                            val navNormal = sqrt(tmpNavScore.sumOf { it * it })
                                                                                            if (navNormal > 0) {
                                                                                            for (pageIndex in 0 until inSize) {
                                                                                            navScore[pageIndex] = tmpNavScore[pageIndex] / navNormal
                                                                                            }
                                                                                            }
                                                                                            }
                                                                                            return authorityScore to navScore
                                                                                            }
                                                                                            }
                                                                                            // 使用示例
                                                                                            fun main() {
                                                                                            val algorithms = GraphAlgorithms()
                                                                                            // 创建示例图
                                                                                            val graph = Array(5) { mutableListOf<Edge>() }
                                                                                              graph[0].add(Edge(1, 4.0))
                                                                                              graph[0].add(Edge(2, 2.0))
                                                                                              graph[1].add(Edge(3, 3.0))
                                                                                              graph[2].add(Edge(3, 1.0))
                                                                                              graph[3].add(Edge(4, 5.0))
                                                                                              // 测试算法
                                                                                              println("DFS from 0: ${algorithms.dfs(graph, 0)}")
                                                                                              println("BFS from 0: ${algorithms.bfs(graph, 0)}")
                                                                                              println("Dijkstra from 0: ${algorithms.dijkstra(graph, 0).contentToString()}")
                                                                                              val edges = listOf(
                                                                                              WeightedEdge(0, 1, 4.0),
                                                                                              WeightedEdge(0, 2, 2.0),
                                                                                              WeightedEdge(1, 3, 3.0),
                                                                                              WeightedEdge(2, 3, 1.0),
                                                                                              WeightedEdge(3, 4, 5.0)
                                                                                              )
                                                                                              println("MST (Kruskal): ${algorithms.kruskalForest(5, edges)}")
                                                                                              println("PageRank: ${algorithms.pageRankCalculation(graph).contentToString()}")
                                                                                              }