图———最小生成树——普利姆算法

本文介绍了普利姆算法,用于找到图的最小生成树。该算法从一个点开始,逐步选择最近未加入集合的点,直到所有点都包含在内,同时记录下最小权值的边。文章提供了代码示例来展示算法的实现。

普利姆算法:
GV表示图的点集, GW 表示图的边集。
V表示最小生成树中的点集合, W表示最小生成树的边集合。

①从某个u点开始, 将u纳入V集合中。
②选取最近没在V集合的点y, 将其纳入V集合中, 加上对应边的权值(对应边 e(x,y), x∈V, y∈ 加入到 集合W中)。
③然后重复②, 直到所有的点都被纳入V集合中。

代码一:
#include<bits/stdc++.h>
#define maxn 103
#define inf 0x3f3f3f
using namespace std;
int mp[maxn][maxn];
int n, m; // 点 边

void init() // 初始化图
{
    memset(mp, inf, sizeof(mp));
}

void create() // 创建图
{
    int i, u, v, w;
    for(i = 0; i < m; i++)
    {
        scanf("%d %d %d", &u, &v, &w);
        if(mp[u][v] > w)
        {
            mp[u][v] = w;
            mp[v][u] = w;
        }
    }
}
struct Node
{
    int w; // lowcost 对应边的花费
    int v;//距离该点最近的点
} close[maxn]; // closeEdge数组, 该数组用于寻找最近的没在V集合的点

void Prim(int u)
{
    int sum = 0;
    int k = -1, low = inf;// k 最近的那个点 ,  low 对应的全职

    close[u].w = -1 ; //表示该点已经被纳入 V集合中
    for(int i = 1; i <= n; i++) // 第一遍 更新一下close数组
    {
        if(i!= u)
        {
            close[i].v = u;
            close[i].w = mp[u][i];
            if(close[i].w < low)
            {
                low = close[i].w;
                k = i;
            }
        }
    }

    if(k != -1)sum+=low;
    close[k].w = -1; // 将其纳入 V数组中

    int s = k;
    for(int j = 1; j < n-1; j++) // 将其他点纳入 V集合中
    {
        low = inf;
        k = -1;
        for(int i = 1; i <= n; i++)
        {
        // 每次维护数组
            if(close[i].w!= -1)
            {
                if(mp[s][i] != inf&& close[i].w > mp[s][i])// 如果相邻
                {
                    close[i].w = mp[s][i];
                    close[i].v = s;
                }
                if(close[i].w < low)
                {
                    low = close[i].w;
                    k = i;
                }
            }
        }
        sum+=low;

        close[k].w = -1;
        s = k;

    }
    printf("%d\n", sum);

}

int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        init();
        create();
        Prim(1);

    }


    return 0;
}

代码二:
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#define maxn 1005
#define inf 0x3f3f3f
int mp[maxn][maxn];// 邻接矩阵存图
int n, m;
void init()// 初始化函数
{
    memset(mp, inf, sizeof(mp));
}
void build()// 建图函数
{
    int i;
    for(i = 0;i < m;i++)
    {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        if(mp[u][v] > w)
        {
            mp[u][v] = w;
            mp[v][u] = w;
        }
    }
}
struct node
{
    int flag;// 标记该点是否已经被纳入V集合中
    int d;// V集合中的点到达该点的最小距离
    int u;// V集合中到达该点的点
}low[maxn];

int Prim(int S)
{
    // 初始化
    int sum = 0;
    int i;
    for(i = 1;i <= n;i++)
    {
        low[i].flag = 0;
        low[i].d = mp[S][i];
        low[i].u = S;
    }
    low[S].flag = 1;

    // 将其他点纳入V集合中
    int j;
    for(i = 0;i < n-1;i++)
    {
        int u = -1;// u表示要纳入的点
        int min = inf;// min 表示 距离
        
        for(j = 1;j <= n;j++)// 找一个最近的点
        {
            if(low[j].flag == 0&& low[j].d < min)
            {
                u = j;
                min = low[j].d;
            }

        }
        if(u == -1)break;
        
        low[u].flag = 1;// 纳入
        sum+=min;// 加上数值

        for(j = 1;j <= n;j++)// 更新一下数组
        {
            if(low[j].flag== 0 && low[j].d > mp[u][j])
            {
                low[j].d = mp[u][j];
            }
        }

    }

    for(i = 1;i <= n;i++)// 判断是否连通
    {
        if(low[i].flag == 0)return -1;
    }

    return sum;
}


### 的遍历算法 的遍历是指按照某种顺序访问中的每一个顶点一次且仅一次的过程。常见的两种遍历方式为广度优先搜索(BFS)和深度优先搜索(DFS)[^1]。 #### 广度优先搜索 (BFS) 广度优先搜索是一种逐层向外扩展的方式,通常借助队列来实现。对于给定的一个起始节点,先访问该节点再依次访问其相邻的所有未被访问过的节点;接着对这些新加入的节点重复上述过程直到所有可达节点都被访问过为止[^2]。 ```python from collections import deque, defaultdict def bfs(graph, start): visited = set() queue = deque([start]) while queue: vertex = queue.popleft() if vertex not in visited: print(vertex, end=' ') visited.add(vertex) for neighbour in graph[vertex]: if neighbour not in visited: queue.append(neighbour) graph = { 'A': ['B', 'C'], 'B': ['D', 'E'], 'C': [], 'D': [], 'E': [] } bfs(graph, 'A') ``` #### 深度优先搜索 (DFS) 深度优先搜索则是尽可能深入地探索每一条路径,在遇到死胡同时回溯至上一节点继续尝试其他分支。此方法一般采用栈结构或递归来完成[^3]。 ```python def dfs(graph, node, visited=None): if visited is None: visited = set() if node not in visited: print(node, end=" ") visited.add(node) for other_node in graph[node]: dfs(graph, other_node, visited) dfs(graph, 'A') ``` ### 最小生成树(MST) 在一个连通无向加权中找到一棵包含全部顶点并且边权重之和最小的子树称为最小生成树。存在多种求解MST的方法,其中最著名的有两种:普利姆算法和克鲁斯卡尔算法[^4]。 #### 普利姆算法 普利姆算法通过不断选取当前已知集合内距离最近的新顶点并将其纳入到正在构建的MST当中直至覆盖整个形。具体来说是从任意一点出发建立初始森林F={V0},之后每次从未选入T的结点集中选出离F最近的一条边(u,v),将v及其关联边加入到F中形成新的部分树,循环往复直到所有的顶点都加入了这棵树里[^5]。 ```python import sys class Graph(): def __init__(self, vertices): self.V = vertices self.graph = [[0 for column in range(vertices)] for row in range(vertices)] # A utility function to find the vertex with minimum distance value, # from the set of vertices not yet included in shortest path tree. def minKey(self, key, mstSet): # Initialize min value min_val = sys.maxsize for v in range(self.V): if key[v] < min_val and mstSet[v] == False: min_val = key[v] min_index = v return min_index # Function to construct and print MST for a graph represented using adjacency matrix representation. def primMST(self): # Key values used to pick minimum weight edge in cut key = [sys.maxsize] * self.V parent = [None] * self.V # Array to store constructed MST key[0] = 0 # Make key 0 so that this vertex is picked as first vertex mstSet = [False] * self.V parent[0] = -1 # First node is always root of MST for cout in range(self.V): u = self.minKey(key, mstSet) mstSet[u] = True for v in range(self.V): if self.graph[u][v] > 0 and \ mstSet[v] == False and \ key[v] > self.graph[u][v]: key[v] = self.graph[u][v] parent[v] = u result = [] for i in range(1, self.V): result.append((parent[i],i,self.graph[parent[i]][i])) return result g = Graph(5) g.graph = [ [0, 2, 0, 6, 0], [2, 0, 3, 8, 5], [0, 3, 0, 0, 7], [6, 8, 0, 0, 9], [0, 5, 7, 9, 0]] print(g.primMST()) ``` #### 克鲁斯卡尔算法 不同于普利姆算法自底向上逐步扩大现有树木范围的做法,克鲁斯卡尔算法采取的是全局视角——它会把所有可能成为候选成员的边按从小到大排序,然后依照这个序列逐一考察各条边能否安全地添加进来而不造成环路现象的发生。每当成功接纳了一条有效连接,则意味着两个原本独立的小树林合二为一形成了更大的一片林区[^6]。 ```python class Edge(object): def __init__(self, src, dest, wt): self.src = src self.dest = dest self.wt = wt class Graph: def __init__(self, V, E): self.V = V self.E = E self.edges = [] def addEdge(self, src, dest, wt): self.edges.append(Edge(src, dest, wt)) @staticmethod def _find(parent, i): if parent[i] != i: parent[i] = Graph._find(parent, parent[i]) # Path compression return parent[i] @staticmethod def _union(parent, rank, x, y): xroot = Graph._find(parent, x) yroot = Graph._find(parent, y) if rank[xroot] < rank[yroot]: parent[xroot] = yroot elif rank[xroot] > rank[yroot]: parent[yroot] = xroot else: parent[yroot] = xroot rank[xroot] += 1 def kruskalMST(self): result = [] e = 0 sorted_edges = sorted( self.edges, key=lambda item: item.wt) parent = list(range(self.V)) rank = [0]*self.V for edge in sorted_edges: u, v, w = edge.src, edge.dest, edge.wt x = Graph._find(parent, u) y = Graph._find(parent, v) if x != y: e += 1 result.append(edge) Graph._union(parent, rank, x, y) return [(edge.src, edge.dest, edge.wt) for edge in result] if __name__ == '__main__': g = Graph(4, 5) g.addEdge(0, 1, 10) g.addEdge(0, 2, 6) g.addEdge(0, 3, 5) g.addEdge(1, 3, 15) g.addEdge(2, 3, 4) print(g.kruskalMST()) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值