最小生成树kruskal算法实现python,kotlin

以下是 Kruskal 算法的 Python 实现,包含详细的注释:

python 复制代码
class UnionFind:
    """并查集类,用于检测环路"""
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n
    
    def find(self, x):
        """查找根节点(路径压缩)"""
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x, y):
        """合并两个集合(按秩合并)"""
        root_x = self.find(x)
        root_y = self.find(y)
        
        if root_x == root_y:
            return False  # 已经在同一集合
        
        # 按秩合并
        if self.rank[root_x] < self.rank[root_y]:
            self.parent[root_x] = root_y
        elif self.rank[root_x] > self.rank[root_y]:
            self.parent[root_y] = root_x
        else:
            self.parent[root_y] = root_x
            self.rank[root_x] += 1
        return True

def kruskal(n, edges):
    """
    Kruskal算法求最小生成树
    
    参数:
        n: 节点数
        edges: 边列表,格式 [(u, v, w), ...] 
               u,v为节点编号(0到n-1),w为权重
    
    返回:
        mst_edges: 最小生成树的边列表
        total_weight: 最小生成树的总权重
    """
    # 1. 按权重从小到大排序边
    edges.sort(key=lambda x: x[2])
    
    # 2. 初始化并查集
    uf = UnionFind(n)
    
    # 3. 初始化结果
    mst_edges = []
    total_weight = 0
    edges_used = 0
    
    # 4. 遍历所有边
    for u, v, w in edges:
        # 如果加入这条边不会形成环路
        if uf.union(u, v):
            mst_edges.append((u, v, w))
            total_weight += w
            edges_used += 1
            
            # 当已经找到n-1条边时,最小生成树已构建完成
            if edges_used == n - 1:
                break
    
    # 如果无法形成包含所有节点的生成树
    if edges_used != n - 1:
        return [], float('inf')
    
    return mst_edges, total_weight

# 测试示例
if __name__ == "__main__":
    # 示例图(邻接表表示)
    # 节点数
    n = 5
    
    # 边列表: (u, v, weight)
    edges = [
        (0, 1, 2),
        (0, 3, 6),
        (1, 2, 3),
        (1, 3, 8),
        (1, 4, 5),
        (2, 4, 7),
        (3, 4, 9)
    ]
    
    # 运行Kruskal算法
    mst_edges, total_weight = kruskal(n, edges)
    
    # 输出结果
    print("最小生成树的边:")
    for u, v, w in mst_edges:
        print(f"{u} -- {v} (权重: {w})")
    print(f"最小生成树总权重: {total_weight}")
    
    # 可视化表示
    print("\n最小生成树结构:")
    # 创建邻接表表示
    adj_list = [[] for _ in range(n)]
    for u, v, w in mst_edges:
        adj_list[u].append((v, w))
        adj_list[v].append((u, w))
    
    for i in range(n):
        connections = ", ".join([f"{neighbor}({weight})" for neighbor, weight in adj_list[i]])
        print(f"节点 {i}: {connections if connections else '无连接'}")

算法步骤说明:

  1. 初始化

    • 创建并查集用于检测环路
    • 按权重对边进行排序
  2. 处理每条边

    • 从小到大遍历排序后的边
    • 使用并查集检查当前边是否会形成环路
    • 如果不形成环路,则加入最小生成树
  3. 终止条件

    • 当找到 n-1 条边时停止(n为节点数)
    • 或者遍历完所有边

时间复杂度分析:

  • 排序边:O(E log E)
  • 并查集操作:近似 O(α(V)),其中α是阿克曼函数的反函数
  • 总体:O(E log E)

使用示例:

python 复制代码
# 自定义图的示例
n = 4
edges = [
    (0, 1, 10),
    (0, 2, 6),
    (0, 3, 5),
    (1, 3, 15),
    (2, 3, 4)
]

mst_edges, total_weight = kruskal(n, edges)

print("最小生成树:")
for edge in mst_edges:
    print(f"边 {edge[0]}-{edge[1]},权重: {edge[2]}")
print(f"总权重: {total_weight}")

这个实现包含了并查集的优化(路径压缩和按秩合并),确保算法的高效性。

以下是 Kruskal 算法的 Kotlin 实现:

kotlin 复制代码
import java.util.*

class UnionFind(private val n: Int) {
    private val parent = IntArray(n) { it }
    private val rank = IntArray(n) { 0 }
    
    /**
     * 查找根节点(带路径压缩)
     */
    fun find(x: Int): Int {
        if (parent[x] != x) {
            parent[x] = find(parent[x])
        }
        return parent[x]
    }
    
    /**
     * 合并两个集合(按秩合并)
     * @return 如果x和y原本不在同一集合中则返回true,否则返回false
     */
    fun union(x: Int, y: Int): Boolean {
        val rootX = find(x)
        val rootY = find(y)
        
        if (rootX == rootY) {
            return false  // 已经在同一集合中
        }
        
        // 按秩合并
        when {
            rank[rootX] < rank[rootY] -> parent[rootX] = rootY
            rank[rootX] > rank[rootY] -> parent[rootY] = rootX
            else -> {
                parent[rootY] = rootX
                rank[rootX]++
            }
        }
        return true
    }
}

data class Edge(
    val u: Int,    // 起始节点
    val v: Int,    // 终止节点
    val weight: Int // 权重
)

/**
 * Kruskal算法求最小生成树
 * @param n 节点数量
 * @param edges 边列表
 * @return Pair(最小生成树的边列表, 总权重),如果无法形成生成树则返回null
 */
fun kruskal(n: Int, edges: List<Edge>): Pair<List<Edge>, Int>? {
    // 1. 按权重从小到大排序边
    val sortedEdges = edges.sortedBy { it.weight }
    
    // 2. 初始化并查集
    val uf = UnionFind(n)
    
    // 3. 初始化结果
    val mstEdges = mutableListOf<Edge>()
    var totalWeight = 0
    var edgesUsed = 0
    
    // 4. 遍历所有边
    for (edge in sortedEdges) {
        // 如果加入这条边不会形成环路
        if (uf.union(edge.u, edge.v)) {
            mstEdges.add(edge)
            totalWeight += edge.weight
            edgesUsed++
            
            // 当已经找到n-1条边时,最小生成树已构建完成
            if (edgesUsed == n - 1) {
                break
            }
        }
    }
    
    // 如果无法形成包含所有节点的生成树
    return if (edgesUsed == n - 1) {
        Pair(mstEdges, totalWeight)
    } else {
        null
    }
}

/**
 * 打印图结构
 */
fun printGraph(n: Int, edges: List<Edge>, title: String) {
    println("\n=== $title ===")
    // 创建邻接表
    val adjList = Array(n) { mutableListOf<Pair<Int, Int>>() }
    
    for (edge in edges) {
        adjList[edge.u].add(Pair(edge.v, edge.weight))
        adjList[edge.v].add(Pair(edge.u, edge.weight))
    }
    
    for (i in 0 until n) {
        val connections = adjList[i].joinToString(", ") { 
            "${it.first}(${it.second})" 
        }
        println("节点 $i: ${if (connections.isNotEmpty()) connections else "无连接"}")
    }
}

fun main() {
    // 示例1:与Python版本相同的图
    println("示例1:")
    val n1 = 5
    val edges1 = listOf(
        Edge(0, 1, 2),
        Edge(0, 3, 6),
        Edge(1, 2, 3),
        Edge(1, 3, 8),
        Edge(1, 4, 5),
        Edge(2, 4, 7),
        Edge(3, 4, 9)
    )
    
    printGraph(n1, edges1, "原始图")
    
    val result1 = kruskal(n1, edges1)
    
    if (result1 != null) {
        val (mstEdges1, totalWeight1) = result1
        println("\n最小生成树的边:")
        mstEdges1.forEach { edge ->
            println("${edge.u} -- ${edge.v} (权重: ${edge.weight})")
        }
        println("最小生成树总权重: $totalWeight1")
        
        printGraph(n1, mstEdges1, "最小生成树")
    } else {
        println("无法形成最小生成树")
    }
    
    // 示例2:另一个图
    println("\n\n示例2:")
    val n2 = 4
    val edges2 = listOf(
        Edge(0, 1, 10),
        Edge(0, 2, 6),
        Edge(0, 3, 5),
        Edge(1, 3, 15),
        Edge(2, 3, 4)
    )
    
    printGraph(n2, edges2, "原始图")
    
    val result2 = kruskal(n2, edges2)
    
    if (result2 != null) {
        val (mstEdges2, totalWeight2) = result2
        println("\n最小生成树的边:")
        mstEdges2.forEach { edge ->
            println("边 ${edge.u}-${edge.v},权重: ${edge.weight}")
        }
        println("总权重: $totalWeight2")
        
        printGraph(n2, mstEdges2, "最小生成树")
    } else {
        println("无法形成最小生成树")
    }
    
    // 示例3:无法形成生成树的情况
    println("\n\n示例3: 测试不连通图")
    val n3 = 5
    val edges3 = listOf(
        Edge(0, 1, 1),
        Edge(2, 3, 2),
        Edge(2, 4, 3)
        // 节点0,1与其他节点不连通
    )
    
    val result3 = kruskal(n3, edges3)
    
    if (result3 == null) {
        println("图不连通,无法形成包含所有节点的生成树")
    }
}

/**
 * 实用扩展:从邻接矩阵创建边列表
 */
fun edgesFromAdjacencyMatrix(matrix: Array<IntArray>): List<Edge> {
    val edges = mutableListOf<Edge>()
    val n = matrix.size
    
    for (i in 0 until n) {
        for (j in i + 1 until n) {
            if (matrix[i][j] > 0) {
                edges.add(Edge(i, j, matrix[i][j]))
            }
        }
    }
    
    return edges
}

/**
 * 实用扩展:从邻接表创建边列表
 */
fun edgesFromAdjacencyList(adjList: Array<List<Pair<Int, Int>>>): List<Edge> {
    val edges = mutableListOf<Edge>()
    val visited = mutableSetOf<String>()
    
    for (u in adjList.indices) {
        for ((v, weight) in adjList[u]) {
            // 避免重复添加无向边
            val key = if (u < v) "$u-$v" else "$v-$u"
            if (!visited.contains(key)) {
                edges.add(Edge(u, v, weight))
                visited.add(key)
            }
        }
    }
    
    return edges
}

关键特性:

1. UnionFind 类

  • 实现了带路径压缩和按秩合并的并查集
  • 用于高效检测环路

2. Edge 数据类

  • 使用 Kotlin 的 data class 简洁表示边
  • 自动生成 toString(), equals(), hashCode() 等方法

3. kruskal 函数

  • 返回 Pair<List<Edge>, Int>?,使用可空类型处理无法形成生成树的情况
  • 使用 Kotlin 标准库的 sortedBy 进行排序

4. 辅助函数

  • printGraph():可视化图结构
  • edgesFromAdjacencyMatrix():从邻接矩阵转换
  • edgesFromAdjacencyList():从邻接表转换

5. Kotlin 特性应用

  • 使用 when 表达式替代多重 if-else
  • 使用扩展函数增强实用性
  • 使用 mutableListOfjoinToString 等标准库函数

使用示例:

kotlin 复制代码
// 简单用法
val n = 5
val edges = listOf(
    Edge(0, 1, 2),
    Edge(0, 2, 3),
    Edge(1, 2, 1),
    Edge(1, 3, 4),
    Edge(2, 3, 5),
    Edge(3, 4, 2)
)

val result = kruskal(n, edges)

result?.let { (mstEdges, totalWeight) ->
    println("最小生成树找到 ${mstEdges.size} 条边")
    println("总权重: $totalWeight")
} ?: println("图不连通")

时间复杂度:

  • 排序边:O(E log E)
  • 并查集操作:近似 O(α(V))
  • 总体:O(E log E),适合稀疏图

这个 Kotlin 实现充分利用了 Kotlin 的语言特性,代码更简洁且类型安全。

相关推荐
sin_hielo1 小时前
leetcode 1523
数据结构·算法·leetcode
古城小栈1 小时前
Spring AI Alibaba 重磅更新:Java 的开发新纪元
java·人工智能·spring
ㄣ知冷煖★1 小时前
基于openEuler操作系统的大模型智能医疗诊断问答应用开发与部署实践
python
智算菩萨1 小时前
从试错学习到安全进化:强化学习重塑自动驾驶决策与控制
人工智能·机器学习·自动驾驶
腾飞开源1 小时前
21_Spring AI 干货笔记之 Mistral AI 聊天
人工智能·ocr·多模态·springai·聊天模型·mistral ai·openai兼容
海上飞猪1 小时前
【python】基础数据类型之String-字符串
python
pengzhuofan1 小时前
用AI武装你的学习:高效掌握Java新技术的方法论
java·人工智能·学习
代码游侠1 小时前
复习——线性表
linux·c语言·数据结构·学习·算法
子午1 小时前
【岩石种类识别系统】Python+TensorFlow+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习