当前位置: 首页 > news >正文

kotlin图算法

欢迎访问我的主页: 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 xfun union(x: Int, y: Int): Boolean {val px = find(x);val py = find(y)if (px == py) return falseif (rank[px] < rank[py]) parent[px] = pyelse if (rank[px] > rank[py]) parent[py] = pxelse {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] = trueresult.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] = truewhile (queue.isNotEmpty()) {val v = queue.poll()result.add(v)graph[v].forEach {if (!visited[it.to]) {visited[it.to] = truequeue.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.0protq.offer(0.0 to startIdx)while (protq.isNotEmpty()) {val (disVlu, idx) = protq.poll()if (disVlu > dist[idx]) continuegraph[idx].forEach { edge ->val newDist = dist[idx] + edge.weightif (newDist < dist[edge.to]) {dist[edge.to] = newDistprotq.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.sizeval dist = Array(inSize) { adj[it].clone() }for (idx in 0 until inSize) // mid stationfor (jdx in 0 until inSize) // from stationfor (kdx in 0 until inSize) // to stationif (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.sizeval visited = BooleanArray(inSize)val forest = mutableListOf<List<WeightedEdge>>()for (start in 0 until inSize) {if (!visited[start]) {// 对每个连通分量运行Primval 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 queueval mst = mutableListOf<WeightedEdge>()// 从指定起点开始inMST[start] = trueglobalVisited[start] = truegraph[start].forEach { protQ.offer(WeightedEdge(start, it.to, it.weight)) }while (protQ.isNotEmpty()) {val edge = protQ.poll()if (inMST[edge.to]) continueinMST[edge.to] = trueglobalVisited[edge.to] = truemst.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.sizeval 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] = truecurTree.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.sizevar time = 0val 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] = truegraph[fromIdx].forEach { edge ->val toIdx = edge.toif (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 portval scc = mutableListOf<Int>()do {val midIdx = stack.pop()onStack[midIdx] = falsescc.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.sizeval 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] = truecurrentSCC.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.sizeval 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] = truewhile (nodeQueue.isNotEmpty()) {val currentNode = nodeQueue.poll()// 检查当前节点的所有邻居for (neighborNode in 0 until nodeCount) {// 如果邻居未访问且有剩余容量if (!isNodeVisited[neighborNode] && capacityMatrix[currentNode][neighborNode] > 0) {nodeQueue.offer(neighborNode)isNodeVisited[neighborNode] = trueparentNode[neighborNode] = currentNode// 如果到达汇点,返回路径if (neighborNode == sinkNode) {return parentNode}}}}return null // 没有找到增广路径}// 主循环:不断寻找增广路径直到无法找到while (true) {val findPath = findAugmentingPathByBFS() ?: break// 计算这条路径上的最小流var minFlow = Int.MAX_VALUEvar currentNode = sinkNodewhile (currentNode != sourceNode) {val parentNode = findPath[currentNode]minFlow = min(minFlow, capacityMatrix[parentNode][currentNode])currentNode = parentNode}maxFlow += minFlowvar toNode = sinkNodewhile (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 = falseedgeList.forEach { edge ->val fromNode = edge.fromif (distanceFromStart[fromNode] == Double.POSITIVE_INFINITY) {return@forEach}val toNode = edge.toval edgeWeight = edge.weightval newDistanceToTarget = distanceFromStart[fromNode] + edgeWeightif (newDistanceToTarget < distanceFromStart[toNode]) {distanceFromStart[toNode] = newDistanceToTargethasDistanceUpdate = true}}// 如果本轮没有任何更新,可以提前结束if (!hasDistanceUpdate) return@repeat}// 检测负环:再进行一轮松弛,如果还有更新说明存在负环edgeList.forEach { edge ->val fromNode = edge.fromval toNode = edge.toval edgeWeight = edge.weightif (distanceFromStart[fromNode] != Double.POSITIVE_INFINITY) {val newDistanceToTarget = distanceFromStart[fromNode] + edgeWeightif (newDistanceToTarget < distanceFromStart[toNode]) {return null // 检测到负环}}}return distanceFromStart}/*** Dijkstra最短路径算法 (用于Bellman-Ford和其他算法)*/fun dijkstraShortestPath(graph: Array<MutableList<Edge>>, startNode: Int): DoubleArray {val nodeCount = graph.sizeval distanceFromStart = DoubleArray(nodeCount) { Double.POSITIVE_INFINITY }val isNodeProcessed = BooleanArray(nodeCount)val priorityQueue = PriorityQueue<Pair<Double, Int>>(compareBy { it.first })distanceFromStart[startNode] = 0.0priorityQueue.offer(0.0 to startNode)while (priorityQueue.isNotEmpty()) {val (currentDistance, currentNode) = priorityQueue.poll()if (isNodeProcessed[currentNode]) continueisNodeProcessed[currentNode] = truegraph[currentNode].forEach { edge ->val neighborNode = edge.toval edgeWeight = edge.weightval newDistance = currentDistance + edgeWeightif (newDistance < distanceFromStart[neighborNode]) {distanceFromStart[neighborNode] = newDistancepriorityQueue.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.sizeval 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.0predictAllCost[startNode] = heuristicFunction(startNode)prrtQ.offer(predictAllCost[startNode] to startNode)while (prrtQ.isNotEmpty()) {val curNode = prrtQ.poll().secondif (curNode == targetNode) {val findPath = mutableListOf<Int>()var pathNode = curNodewhile (pathNode in toFromPair) {findPath.add(0, pathNode)pathNode = toFromPair[pathNode]!!}findPath.add(0, startNode)return findPath}// 扩展当前节点的所有邻居graph[curNode].forEach { edge ->val toNode = edge.toval wight = edge.weight// 计算通过当前节点到达邻居的代价val newCost = knownCost[curNode] + wight// 如果找到更好的路径if (newCost < knownCost[toNode]) {toFromPair[toNode] = curNodeknownCost[toNode] = newCostpredictAllCost[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.0val 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 idxfor (target in 0 until inSize) {distanceMatrix[idx][target] = when {minDistanceFromIdx[target] == Double.POSITIVE_INFINITY -> Double.POSITIVE_INFINITYelse -> 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.sizeval 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].sizeif (outSize > 0) {val rankContribution = dampingFactor * pageRanks[jdx] / outSizelinkGraph[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.sizeval 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()}")
}
http://www.dtcms.com/a/399701.html

相关文章:

  • 汕尾网站建设 生意好吗有专门为个人网站做推广的吗
  • 网站建设企业响应式网站模板宿迁城乡住房建设厅网站
  • 建网站需要学什么网站专业术语中SEO的意思是
  • 2025无人机在低空物流中的应用实践
  • Python实现海鸥优化算法(Seagull Optimization Algorithm, SOA)(附完整代码)
  • PostgreSQL 知识体系
  • 网站建设与维护专业实训室昆山做网站的
  • 插针弹簧镀金耐腐蚀、高导电的电子连接“保护盾”|深圳同远
  • 【MySQL✨】MySQL 入门之旅 · 第十一篇:MySQL 表连接(JOIN)基础
  • 德州有做网站的制作表白网站
  • 公司网站做推广成化区建设局网站
  • Openssl TRNG provider demo guide
  • 建立论坛网站做自己的网站不是免费的
  • 赵艳红网站建设规划卢松松博客源码 wordpress博客模板
  • IoT水利监控系统:从需求到实现的完整技术方案
  • 小杰机器学习(seven)——贝叶斯分类
  • 名作之壁吧网站建设参与网站网站建设可判几年
  • mobaxterm里面勾选了Follow terminal folder,但是不生效
  • 培训餐饮网站建设中企动力如何
  • vector的使用和模拟
  • 织梦网站栏目无法生成网站关键字多少个
  • n8n中的postgres节点中插入数据怎么自动插入,不设置id?
  • 全钢PVC防静电地板优势与不足全解析
  • 上海魔力网站建设公司免费制作链接的软件
  • 巅云建站企业购物平台
  • UMI企业智脑:数字资产与知识管理的行业新标杆
  • 北京 旅游攻略 颐和园 圆明园(第一天下午逛) 长城最后一天早上逛 如果到北京早 也可以第一天长城
  • 孝感做网站如何做网站视频模板
  • 网站开发成本有哪些idea 做网站登录
  • 网站推广在哪好外贸网站备案不成功的原因有哪些