• 并查集——最小生成树算法Kruskal


    连通图: 在无向图中,若任意两个顶点 V i V_i Vi V j V_j Vj都有路径相通,则称该无向图为连通图

    强连通图: 在有向图中,若任意两个顶点 V i V_i Vi V j V_j Vj都有路径相通,则称该有向图为强连通图

    连通网: 在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权值,权值代表着连接各个顶点的代价,称这种连通图叫做连通网

    生成树: 一个连通子图,它包含连通图中全部n个顶点,有n-1条边。如果生成树中再添加一条边,则必定成环

    最小生成树: 在连通网的所有生成树中,所有边的代价之和最小的生成树,称为最小生成树

    在这里插入图片描述

    Kruskal算法是处理边的,所以在稀疏的边比较少的连通网中,用Kruskal克鲁斯卡尔算法效率就比较高;在边比较多,点比较少的连通网中,用加点法Prim算法效率比较高

    Kruskal算法:此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里,步骤如下:

    1. 把图中的所有边按权值从小到大排序,把图中的n个顶点看成独立的n颗树组成的森林
    2. 按权值从小到大选择边,所选的边连接的两个顶点 V i V_i Vi V j V_j Vj应属于两颗不同的树,则称为最小生成树中的一条边,并合并成一棵树
    3. 重复步骤2,直到所有顶点都在一棵树内或者有n-1条边为止

    在这里插入图片描述

    我们接下来求该联通网的最小生成树,选择权值最小的A-C边,由于A、C不在一个集合(并查集),没形成环路

    在这里插入图片描述
    然后选出权值为2的D-F边,此时D、F分别属于两个集合,D-F连接起来也不产生回路
    在这里插入图片描述
    此时B-E边的权值最小,我们选择B-E,B、E分别属于两个集合,连接起来也没有回路
    在这里插入图片描述

    此时C-F的边的权值最小,我们选择C-F,C、F分别属于两个集合,连接起来没有回路

    在这里插入图片描述
    现在B-C,C-D,A-D的权值都是5,都是当前权值最小的边。但是C-D或者A-D都属于同一个集合(并查集),一旦连接就形成回路了
    所以我们选择B-C,因为B-C属于不同的集合,连接起来不会形成回路

    在这里插入图片描述

    我们接下来使用并查集的方式来选择边,当一条边的两个节点属于同一个集合时,我们不能选择这条边,否则会形成回路;这条边的两个节点不属于同一个集合时,我们选择这条边,此时不会形成回路

    注意了,并查集方法中有一个parent数组,这个parent数组最后展现出来的并查集结构,并不是实际的最小生成树结构,只是利用并查集判断某条边的两个顶点是否在同一个集合,进而确定是否选择这条边

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    int parent[1000] = { 0 };
    
    struct Edge {
    	Edge(char start, char end, int w)
    		: start_(start)
    		, end_(end)
    		, w_(w)
    	{}
    
    	int start_;
    	int end_;
    	int w_;
    };
    
    // 找编号为x的节点的根节点
    int find(int x) {
    	if (x == parent[x]) {
    		return x;
    	}
    	else {
    		// find优化,查找根节点的过程中,把遍历过的节点的父节点都改成根节点
    		parent[x] = find(parent[x]);
    		return parent[x];
    	}
    }
    
    int main(){
    	int num_edge = 0;
    	cin >> num_edge;
    	vector<Edge> edges;
    
    	char start, end;
    	int w;
    	// 输入数据,保存边
    	for (int i = 0; i < num_edge; i++) {
    		cin >> start >> end >> w;
    		edges.emplace_back(start, end, w);
    		// 初始化parent数组
    		parent[start] = start;
    		parent[end] = end;
    	}
    	// 边进行升序排列
    	sort(edges.begin(), edges.end(), 
    		[](const Edge& e1, const Edge& e2) ->bool{
    		return e1.w_ < e2.w_;
    		});
    	// 开始合并集合,如果根节点不在同一集合,则可以合并
    	for (int i = 0; i < num_edge; i++) {
    		int root1 = find(edges[i].start_);
    		int root2 = find(edges[i].end_);
    		if (root1 != root2) {
    			// 根节点不同,可以连接
    			parent[root1] = root2;
    			printf("%c - %c , w = %d\n", edges[i].start_, edges[i].end_, edges[i].w_);
    		}
    	}
    	return 0;
    }
    
    /*
    10
    A B 6
    A C 1
    A D 5
    B C 5
    C D 5
    B E 3
    C E 6
    C F 4
    E F 6
    D F 2
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    在这里插入图片描述

  • 相关阅读:
    Linux C++ 海康摄像头 Alarm Demo
    Python+高光谱数据预处理-机器学习-深度学习-图像分类-参数回归
    Unity之Hololens如何升级MRTK内置shader支持URP
    CMake:构建时运行自定义命令add_custom_command
    【机器人算法】机器人运动学参数辨识/DH参数校准/DH参数辨识
    MySQL约束
    java栈和自定义栈
    YOLOv8独家改进:逐元素乘法(star operation)二次创新 | 微软新作StarNet:超强轻量级Backbone CVPR 2024
    Seata——基础(笔记)
    基于STC15单片机温度光照蓝牙传输-proteus仿真-源程序
  • 原文地址:https://blog.csdn.net/qq_42500831/article/details/125609243