Prim & Kruskal Algorithm

                      **Prim算法** 

Prim算法是图论中的一种算法,可在加权连通图里搜索最小生成树。此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。
中文名:
普里姆算法
提出时间: 1930年
外文名: Prim Algorithm
应用学科: 计算机,数据结构,数学(图论)
别称: 最小生成树算法
适用领域范围: 应用图论知识的实际问题
算法:贪心
/**********************************************************/

  • Prim算法的描述

    1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
    2).初始化:Vnew = {x},其中x为集合V中的任一节点(起点),Enew= {},为空;
    3).重复下列操作,直到Vnew = V:
    a.在集合E中选取权值最小的边 (u,v),其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
    b.将v加入集合Vnew中,将(u, v)边加入集合Enew中;
    4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。

  • 时间复杂度:

通过邻接矩阵图表示的简易实现中,找到所有最小权边共需O(V)的运行时间。使用简单的二叉堆与邻接表来表示的话,普里姆算法的运行时间则可缩减为O(ElogV),其中E为连通图的边数,V为顶点数。如果使用较为复杂的斐波那契堆,则可将运行时间进一步缩短为O(E+VlogV),这在连通图足够密集时(当E满足Ω(VlogV)条件时),可较显著地提高运行速度。
http://blog.csdn.net/zenail501129/article/details/23551909
http://blog.csdn.net/u014488381/article/details/41447229

#include <stdio.h>
#include <cstdlib>
#include<memory.h>
const int Max =0x7fffffff;
const int N=50;

int n;
int g[N][N],dis[N],visited[N];

int prim()
{
  int i,j;
  int pos,min;
  int ans=0;
  memset(visited,0,sizeof(visited));
  visited[1]=1;pos=1;
  //assign a value to the dis[N] first
  for(i=2;i<=n;i++)
    dis[i]=g[pos][i];
  for(i=1;i<n;i++)
  {
    min=Max;
    for(j=1;j<=n;j++)
    {
      if(visited[j]==0&&min>dis[j])
      {
        min=dis[j];
        pos=j;
      }
    }
    printf("The node being traversed is :%d\n",pos);
    ans+=min;
    printf("The value of ans is %d\n",ans);
    //mark the node
    visited[pos]=1;
    //update the weight
    for(j=1;j<=n;j++)
      if(visited[j]==0&&dis[j]>g[pos][j])
        dis[j]=g[pos][j];
  }
  return ans;
}

int main()
{
  int i=1,j=1;
  int ans=0;
  int w;
  printf("Please enter the number of the nodes:\n");
  scanf("%d",&n);
  for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
    {
      if(i==j)
        g[i][j]=0;
      else
        g[i][j]=Max;
    }
  printf("Please enter the number of the edges:\n");
  int edgenum;
  scanf("%d",&edgenum);
  int v1,v2;
  printf("Please enter the number and the corresponding weight:\n");
  for(i=1;i<=edgenum;i++)
  {
    scanf("%d%d%d",&v1,&v2,&w);
    g[v1][v2]=g[v2][v1]=w;
  }
  ans=prim();
  printf("The sum of the weight of the edges is:%d\n",ans);
  system("pause");
  return 0;

}

Kruskal Algorithm

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

#define MAXN 11     //顶点个数的最大值
#define MAXM 20     //边的个数的最大值
struct edge         //边
{
    int u, v, w;
}edges[MAXM];       //边的数组
int parent[MAXN];   //parent[i]为顶点i所在集合对应的树中的根结点
int n, m;           //顶点个数、边的个数
int i, j;           //循环变量
void UFset()        //初始化
{
    for(i = 1; i <= n; i++) parent[i] = -1;
}
int Find(int x)     //查找并返回结点x所属集合的根结点
{
    int s;          //查找位置
    for(s = x; parent[s] >=0; s = parent[s]) ;
    while(s != x)   //优化方案——压缩路径,使后续的查找操作加速
    {
        int tmp = parent[x];
        parent[x] = s;
        x = tmp;
    }
    return s;
}
//运用并查集,将两个不同集合的元素进行合并,使两个集合中任意两个元素都连通
void Union(int R1, int R2)
{
    int r1 = Find(R1), r2 = Find(R2);       //r1和r2分别为R1和R2的根结点
    int tmp = parent[r1] + parent[r2];      //两个集合结点数之和(负数)
    //如果R2所在树结点个数 > R1所在树结点个数(注意parent[r1]是负数)
    if(parent[r1] > parent[r2])
    {
        parent[r1] = r2;
        parent[r2] = tmp;
    }
    else
    {
        parent[r2] = r1;
        parent[r1] = tmp;
    }
}
int cmp(const void *a, const void *b)       //实现从小到大的比较函数
{
    edge aa = *(const edge *)a, bb = *(const edge *)b;
    return aa.w-bb.w;
}
void Kruskal()
{
    int sumweight = 0;      //生成树的权值
    int num = 0;            //已选用的边的数目
    UFset();                //初始化parent数组
    for(i = 0; i < m; i++)
    {
        if(Find(edges[i].u) != Find(edges[i].v))
        {
            printf("%d %d %d\n", edges[i].u, edges[i].v, edges[i].w);
            sumweight += edges[i].w; num++;
            Union(edges[i].u, edges[i].v);
        }
        if(num >= n-1) break;
    }
    printf("The weight of MST is : %d\n", sumweight);
}
  main()
{
    scanf("%d%d", &n, &m);  //读入顶点个数和边数
    for(int i = 0; i < m; i++)
        scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].w); //读入边的起点和终点
    printf("The edges chosen are :\n");
    qsort(edges, m, sizeof(edges[0]), cmp); //对边按权值从小到大排序
    Kruskal();
}

/**
7 9
1 2 28
1 6 10
6 5 25
5 7 24
5 4 22
7 4 18
4 3 12
3 2 16
7 2 14
**/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kruskal算法Prim算法都是用于求解最小生成树的经典算法。下面我将分别介绍这两个算法的基本原理和C++代码实现。 1. Kruskal算法Kruskal算法基于贪心思想,通过不断选择权重最小的边来构建最小生成树。具体步骤如下: - 将图中的所有边按照权重从小到大进行排序。 - 初始化一个空的森林,每个顶点都是一个独立的树。 - 依次选择权重最小的边,如果这条边连接的两个顶点不在同一棵树中,则将这条边加入最小生成树中,并将这两个顶点所在的树合并为一棵树。 - 重复上述步骤,直到最小生成树中包含了图中的所有顶点。 以下是使用C++实现Kruskal算法的代码示例: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 定义边的结构体 struct Edge { int src, dest, weight; }; // 定义并查集的数据结构 struct DisjointSet { int *parent, *rank; int n; DisjointSet(int n) { this->n = n; parent = new int[n]; rank = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; rank[i] = 0; } } int find(int u) { if (u != parent[u]) { parent[u] = find(parent[u]); } return parent[u]; } void merge(int x, int y) { int xroot = find(x); int yroot = find(y); if (rank[xroot] < rank[yroot]) { parent[xroot] = yroot; } else if (rank[xroot] > rank[yroot]) { parent[yroot] = xroot; } else { parent[yroot] = xroot; rank[xroot]++; } } }; // 定义比较函数,用于边的排序 bool compareEdges(Edge a, Edge b) { return a.weight < b.weight; } // Kruskal算法求解最小生成树 void kruskalMST(vector<Edge> edges, int V) { // 对边按照权重进行排序 sort(edges.begin(), edges.end(), compareEdges); // 创建并查集 DisjointSet ds(V); // 存储最小生成树的边 vector<Edge> mst; for (auto edge : edges) { int srcRoot = ds.find(edge.src); int destRoot = ds.find(edge.dest); // 如果这条边的两个顶点不在同一棵树中,则将这条边加入最小生成树中 if (srcRoot != destRoot) { mst.push_back(edge); ds.merge(srcRoot, destRoot); } } // 输出最小生成树的边 for (auto edge : mst) { cout << edge.src << " - " << edge.dest << " : " << edge.weight << endl; } } int main() { int V, E; cout << "Enter the number of vertices: "; cin >> V; cout << "Enter the number of edges: "; cin >> E; vector<Edge> edges(E); cout << "Enter the source, destination and weight of each edge:" << endl; for (int i = 0; i < E; i++) { cin >> edges[i].src >> edges[i].dest >> edges[i].weight; } kruskalMST(edges, V); return 0; } ``` 2. Prim算法Prim算法是一种以顶点为中心的算法,通过不断选择与当前最小生成树相连的权重最小的边来构建最小生成树。具体步骤如下: - 初始化一个空的最小生成树,选择一个起始顶点。 - 将起始顶点加入最小生成树中,并将与起始顶点相连的边加入优先级队列。 - 从优先级队列中选择权重最小的边,如果这条边连接的顶点不在最小生成树中,则将这条边加入最小生成树中,并将与这个顶点相连的边加入优先级队列。 - 重复上述步骤,直到最小生成树中包含了图中的所有顶点。 以下是使用C++实现Prim算法的代码示例: ```cpp #include <iostream> #include <vector> #include <queue> using namespace std; // 定义边的结构体 struct Edge { int dest, weight; }; // 定义比较函数,用于边的排序 struct Compare { bool operator()(Edge a, Edge b) { return a.weight > b.weight; } }; // Prim算法求解最小生成树 void primMST(vector<vector<Edge>> graph, int V) { // 存储最小生成树的边 vector<Edge> mst; // 创建一个优先级队列,用于动态排序边 priority_queue<Edge, vector<Edge>, Compare> pq; // 创建一个数组,用于标记顶点是否已经加入最小生成树 vector<bool> visited(V, false); // 选择一个起始顶点 int src = 0; visited[src] = true; // 将起始顶点的所有边加入优先级队列 for (auto edge : graph[src]) { pq.push(edge); } while (!pq.empty()) { // 选择权重最小的边 Edge minEdge = pq.top(); pq.pop(); int dest = minEdge.dest; int weight = minEdge.weight; // 如果这条边连接的顶点不在最小生成树中,则将这条边加入最小生成树中 if (!visited[dest]) { mst.push_back(minEdge); visited[dest] = true; // 将与这个顶点相连的边加入优先级队列 for (auto edge : graph[dest]) { pq.push(edge); } } } // 输出最小生成树的边 for (auto edge : mst) { cout << edge.dest << " - " << edge.weight << endl; } } int main() { int V, E; cout << "Enter the number of vertices: "; cin >> V; cout << "Enter the number of edges: "; cin >> E; vector<vector<Edge>> graph(V); cout << "Enter the source, destination and weight of each edge:" << endl; for (int i = 0; i < E; i++) { int src, dest, weight; cin >> src >> dest >> weight; graph[src].push_back({dest, weight}); graph[dest].push_back({src, weight}); } primMST(graph, V); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值