图的最小生成树——Kruskal算法

简介: Kruskal算法的思想如下 假设有n个顶点的连通图。首先先构造有顶点构成的集合0,每个顶点都是一个集合,不含有任何边。 在边找一个最小权值的边 判断这个边的俩个顶点是否来自于两个不同的集合,若是就将它俩归并为一个集合,然后将这个边添加到要构成的图的集合中。

Kruskal算法的思想如下

  1. 假设有n个顶点的连通图。首先先构造有顶点构成的集合0,每个顶点都是一个集合,不含有任何边。
  2. 在边找一个最小权值的边
  3. 判断这个边的俩个顶点是否来自于两个不同的集合,若是就将它俩归并为一个集合,然后将这个边添加到要构成的图的集合中。
  4. 直到上述的边的个数为顶点个数-1;否则,重复2-3;
    算法构成树的过程如下:

如a图所示的图,下面是最小生成树的构造过程
image

(a)

image

(b)

image

(c)

image

(d)

image

(e)

image

(f)

image

在这里判断一个边的两个顶点是不是属于同一个集合,采用的是并查集的方式来实现

并查集的基本思想如下:

并查集可以用数组来和树来实现,在这里先讲树来实现。
1:树实现并查集:
属于同一个集合的结点拥有同一个根,图示如下:
image
上面的树中这些元素都属于一个集合,他们的根元素是1;
image
上面树中元素属于都属于一个集合,他们的根元素都是11;
两个集合合并就是将一个集合连在另一个集合的根元素的下面就好了。如下图所示:
image
那在合并集合的时候怎么合并集合呢?有没有想过,如下图所示的集合
image
一个树的高度很大,一个树的高度很小,这俩个集合在合并的时候要把高度小的集合合并在高度大的集合中。因为在合并集合中的元素的时候,首先要查找这个结点属于那个结点,若是将高度高的集合合并在高度小的集合中,就会增加查找的时间,增加程序的时间复杂度。
数组实现并查集
基本思想就是,在属于一个集合的数据都用一个数据来表示就好,在合并集合的时候,就将两个集合用一个数据来表示就好了,
如下图:

image
有0——7个数据;刚开始的时候他们都是一个集合,单独的个体,每个的根都是自己本身,-1表示他们都是单独个个体,是个根,在查找根结点的时候就是一个判断依据,只要对应的数据项是负数,那就说明这个是根;现在将1和2合并为一个集合如下图:
image
将1对应数据项和2对应的数据项合并在一起,放在1对应的数据项中,在这里面就是-1+-1;将2对应的数据项改为1,表示它的根现在是1;这就是一个集合。
现在将4,5归为一个集合如下图:

image
原理和上面的一样
现在讲3添加到2所集合中如下图:
image
首相查找2的根,发现是1,然后在查找3的根,发现是3,然后在1对应的数据项中加上3对应的数据项。也就是-2+-1;然后将3对应的数据项改为-1;
现在将5所在的集合和3所在的集合合并在一块如下图:
image
先查找5所在的根结点,发现是是4;现在在查找3所在的结点,发现是1;然后还是是和之前一样相加。将4对应的数据项和1对应的数据项相加,合并两个根就好,然后将4对应的数据项改为1;
现在6加到5所在的集合中如下图:
image
查找5所在的集合,5对应的数据项是4,4对应的数据项是1,1对应的数据项是负数,说明这个是根,然后还是相加,还是合并;就好了

好了,并查集讲完了接下来就是树了
算法的流程图如下:
_
程序如下:

#include <iostream>
using namespace std;
typedef struct node  {
    int start;//边的起点
    int end;//边的终点
    int wieght;//边的权重
}node;
class Graph{
private:
    node *edge; //边的数组
    int edgeNum;//边的个数
    int vertexNum;//顶点的个数
    int* unionset;//并查集,顶点的集合
#pragma 下面的这几个private函数都是并查集的函数
    int findRoot(int node);//找根
    void unionSet(int node1,int node2);//合并俩结点成为一个集合
    bool isaSet(int node1,int node2);//判断俩结点在没在一个集合中
public:
    Graph(int size,int vertexNum);
    void create();
    void sort();//冒泡法按照权值从大到小的顺序排序;
    void kuscal();//kruskal算法;
    void print();//输出边的数组和 顶点的集合,
};
void Graph::print(){
    cout<<"unionSet-----------------------------------------------------\n";
    for (int i = 0; i<vertexNum+1; i++) {
        cout<<i<<":"<<unionset[i]<<endl;
    }
    cout<<"unionset______________________________________________________\n";
    
    for (int i =0; i<2; i++) {
        cout<<endl;
    }
    cout<<"edge----------------------------------------------------------\n";
    for (int i = 0; i<edgeNum; i++) {
        cout<<"("<<edge[i].start<<","<<edge[i].end<<","<<edge[i].wieght<<")";
    }
    cout<<endl;
    cout<<"edge__________________________________________________________\n";
}
//看是否在同一个集合,如果在一个集合,返回True,否则返回false;
bool  Graph::isaSet(int node1, int node2){
    return findRoot(node1)==findRoot(node2) ? true : false;
}


//把node2添加到node1集合中
void Graph::unionSet(int node1, int node2){
    int root1 = findRoot(node1);
    int root2 = findRoot(node2);
    unionset[root1]+=unionset[root2];
    unionset[root2] = root1;
}


void Graph::sort(){
    for (int i = 0; i<edgeNum -1; i++) {
        for (int j = 0; j<edgeNum-1-i; j++) {
            if(edge[j].wieght < edge[j+1].wieght){
                int temp = edge[j].wieght;
                int start = edge[j].start;
                int end = edge[j].end;
                edge[j].wieght = edge[j+1].wieght;
                edge[j+1].wieght = temp;
                
                edge[j].start = edge[j+1].start;
                edge[j+1].start = start;
                
                edge[j].end = edge[j+1].end;
                edge[j+1].end = end;
            }
        }
    }
    print();
}

int Graph::findRoot(int node){
    int root = node;
    while (unionset[root] >= 0) {
        root = unionset[root];
    }
    return  root;
}

Graph::Graph(int size,int vertexNum){
    edge = new node[size];
    edgeNum = size;
    this->vertexNum = vertexNum;
    /*
     1:一般来说,多申请一个,这样有利于数据的统一
     */
    unionset = new int[vertexNum+1];
    for (int i = 0; i<vertexNum+1; i++) {
        unionset[i]= -1;
    }
}

void Graph::create(){
    cout<<"input edge start and end and widght\n";
    for (int i = 0; i<edgeNum; i++) {
        cin>>edge[i].start>>edge[i].end>>edge[i].wieght;
    }

}
void Graph::kuscal(){
    sort();
    int edgeNum1 = 0;//找到的满足要求的边的个数
    int sum = 0;//找的满足要求的边的权重和
    int i = edgeNum-1;//控制边数组的下标,从后往前取,最小值在后面
    while (edgeNum1 != this->vertexNum-1 ) {
        int start = edge[i].start;
        int end = edge[i].end;
        
        if( isaSet(start, end) == false){
            unionSet(start, end);
            edgeNum1++;
            sum+=edge[i].wieght;
        }
        i--;
    }
    cout<<"sum"<<sum<<endl;
}
int main(){
    Graph a(8, 6);
    a.create();
    a.print();
    a.kuscal();
    a.print();
}

测试的时候用的图就是这个图image

输入如下:
image

输出如下:
image

相关文章
|
5月前
|
存储 算法 Java
Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。
【6月更文挑战第21天】Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。二叉树遍历通过访问根、左、右子节点实现。DFS采用递归遍历图的节点,而BFS利用队列按层次访问。以下是简化的代码片段:[Java代码略]
45 4
|
28天前
|
算法 决策智能
基于prim算法求出网络最小生成树实现网络社团划分和规划
该程序使用MATLAB 2022a版实现路线规划,通过排序节点权值并运用Prim算法生成最小生成树完成网络规划。程序基于TSP问题,采用遗传算法与粒子群优化算法进行路径优化。遗传算法通过编码、选择、交叉及变异操作迭代寻优;粒子群优化算法则通过模拟鸟群觅食行为,更新粒子速度和位置以寻找最优解。
|
4月前
|
存储 算法 Python
“解锁Python高级数据结构新姿势:图的表示与遍历,让你的算法思维跃升新高度
【7月更文挑战第13天】Python中的图数据结构用于表示复杂关系,通过节点和边连接。常见的表示方法是邻接矩阵(适合稠密图)和邻接表(适合稀疏图)。图遍历包括DFS(深度优先搜索)和BFS(广度优先搜索):DFS深入探索分支,BFS逐层访问邻居。掌握这些技巧对优化算法和解决实际问题至关重要。**
39 1
|
4月前
|
存储 传感器 算法
|
4月前
|
数据采集 存储 算法
「AIGC算法」图搜索算法详解
本文探讨了图搜索算法,包括遍历和最短路径搜索。DFS和BFS是遍历算法,前者使用栈深入搜索,后者用队列逐层遍历。Dijkstra、Bellman-Ford、A*、Floyd-Warshall和Johnson算法则解决最短路径问题。文中还给出了DFS的Python实现示例。这些算法在路径规划、网络分析等领域有重要应用。
76 0
|
5月前
|
算法 Java
Java数据结构与算法:贪心算法之最小生成树
Java数据结构与算法:贪心算法之最小生成树
|
6月前
|
存储 算法 C++
c++算法学习笔记 (8) 树与图部分
c++算法学习笔记 (8) 树与图部分
|
5月前
|
算法 计算机视觉
图像处理之基于图的广度优先搜索组件标记算法
图像处理之基于图的广度优先搜索组件标记算法
29 0
|
5月前
|
算法 C语言
数据结构与算法——最小生成树问题(什么是最小生成树、Prim算法、Kruskal算法)
数据结构与算法——最小生成树问题(什么是最小生成树、Prim算法、Kruskal算法)
31 0
|
5月前
|
存储 算法
数据结构学习记录——图应用实例-六度空间(题目描述、算法思路、伪代码及解读、图解)
数据结构学习记录——图应用实例-六度空间(题目描述、算法思路、伪代码及解读、图解)
52 0