算法-图的强连通分量,图的最小生成树

1.图的强连通分量
(1). 定义
图的强连通分量是图论中的一个重要概念,主要在有向图中进行讨论。具体来说,如果在一个有向图G中,任意两个顶点vivj(其中vi大于vj)之间都存在一条从vivj的有向路径,同时也存在一条从vjvi的有向路径,那么这两个顶点就被称为强连通。如果有向图G的每两个顶点都强连通,那么G就被称为一个强连通图。

进一步地,有向图G的极大强连通子图被称为强连通分量。这里,“极大”意味着如果添加任何额外的顶点,子图将不再保持强连通的属性。因此,强连通分量实际上是原图中满足强连通条件的最大的子图。

强连通分量在实际应用中有着重要的价值。例如,在复杂网络分析中,强连通分量可以帮助我们识别出网络中紧密连接、相互依赖的节点群体,这对于理解网络的结构和功能至关重要。此外,强连通分量还可以用于简化图的复杂度,将原图分解为多个更小的强连通分量,从而方便后续的分析和处理。

在算法实现上,有多种方法可以用来寻找有向图中的强连通分量,如Kosaraju算法、Tarjan算法和Gabow算法等。这些算法通常基于深度优先搜索(DFS)的思想,通过遍历图中的节点和边来识别强连通分量。

(2). Kosaraju算法求解图的强连通分量
Kosaraju算法是一种求解有向图强连通分量(Strongly Connected Components, SCC)的算法。这个算法的基本思想是分两步进行深度优先搜索(DFS)。首先,对原图进行DFS,并记录每个节点的完成时间(即退出DFS的时间)。然后,根据完成时间的逆序(从大到小),对节点进行第二次DFS,每次DFS找到的就是一个强连通分量。
(3). 实例
在这里插入图片描述
上述是一个图结构.利用Kosaraju算法可以求解其中每个强连通分量.

// 强连通分量
class NodeInfo{
public:char m_nName;bool m_bVisit = false;int32_t m_nTag = -1;int32_t m_nFirstTick;int32_t m_nLastTick;
};
template<class T>
class Node{
public:T m_nEle;
};
template<class EdgeInfo>
class Edge{
public:bool m_bValid = false;
};
Node<NodeInfo> stNodes[9];
Edge<int> stEdges[9][9];
Edge<int> stEdges2[9][9];
int nVisitTick = 0;
void Visit(int nCurIndex, int32_t nTag = -1);// 对节点按最后访问时间逆序排列
struct VisitInfo{int32_t nVisitTick;int32_t nIndex;
};
void Sort(VisitInfo* lpBegin, VisitInfo *lpEnd);
int main(){stNodes[0].m_nEle.m_nName = 'A';stNodes[1].m_nEle.m_nName = 'B';stNodes[2].m_nEle.m_nName = 'C';stNodes[3].m_nEle.m_nName = 'D';stNodes[4].m_nEle.m_nName = 'E';stNodes[5].m_nEle.m_nName = 'F';stNodes[6].m_nEle.m_nName = 'G';stNodes[7].m_nEle.m_nName = 'H';stNodes[8].m_nEle.m_nName = 'I';stEdges[0][1].m_bValid = true;stEdges[1][0].m_bValid = true;stEdges[1][2].m_bValid = true;stEdges[2][1].m_bValid = true;stEdges[4][5].m_bValid = true;stEdges[5][4].m_bValid = true;stEdges[4][3].m_bValid = true;stEdges[3][4].m_bValid = true;stEdges[4][6].m_bValid = true;stEdges[6][4].m_bValid = true;stEdges[6][8].m_bValid = true;stEdges[8][6].m_bValid = true;stEdges2[1][0].m_bValid = true;stEdges2[0][1].m_bValid = true;stEdges2[2][1].m_bValid = true;stEdges2[1][2].m_bValid = true;stEdges2[5][4].m_bValid = true;stEdges2[4][5].m_bValid = true;stEdges2[3][4].m_bValid = true;stEdges2[4][3].m_bValid = true;stEdges2[6][4].m_bValid = true;stEdges2[4][6].m_bValid = true;stEdges2[8][6].m_bValid = true;stEdges2[6][8].m_bValid = true;for(int i = 0; i < 9; i++){if(stNodes[i].m_nEle.m_bVisit == false){Visit(i);}}VisitInfo stArr[9];for(int32_t i = 0; i < 9; i++){stArr[i].nIndex = i;stArr[i].nVisitTick = stNodes[i].m_nEle.m_nLastTick;}// 按访问时间逆序排列数组元素Sort(stArr, stArr + 9);// 构造一个转置图,已经提前构建好.复用了原图的节点结构.深度优先前复位节点状态.for(int32_t i = 0; i < 9; i++){for(int32_t j = 0; j < 9; j++){stEdges[i][j] = stEdges2[i][j];}}for(int32_t i = 0; i < 9; i++){stNodes[i].m_nEle.m_bVisit = false;stNodes[i].m_nEle.m_nFirstTick = -1;stNodes[i].m_nEle.m_nLastTick = -1;stNodes[i].m_nEle.m_nTag = -1;}// 对转置图按排序后节点顺序再次执行深度搜索int32_t nTag = 0;for(int32_t i = 0; i < 9; i++){int32_t nNodeIndex = stArr[i].nIndex;if(stNodes[nNodeIndex].m_nEle.m_bVisit == false){Visit(nNodeIndex, nTag);nTag++;}}// 此时一次性求出了图的所有强连通分量.tag相同的节点位于同一个强连通分量.printf("strong connect num:%d\n", nTag);for(int32_t i = 0; i < nTag; i++){printf("%d:\n", i);for(int32_t k = 0; k < 9;  k++){if(stNodes[k].m_nEle.m_nTag == i){printf("%c ", stNodes[k].m_nEle.m_nName);}}printf("\n");}return 0;
}void Sort(VisitInfo* lpBegin, VisitInfo *lpEnd){int32_t nNum = lpEnd - lpBegin;if(nNum <= 1){return;}// 归并排序int32_t nMid = nNum / 2;Sort(lpBegin, lpBegin+nMid);Sort(lpBegin+nMid,lpEnd);// 归并VisitInfo* lpArrTmp = (VisitInfo*)malloc(sizeof(VisitInfo) * nNum);int32_t nArrIndex = 0;int32_t nLeftIndex = 0;int32_t nRightIndex = 0;while(nLeftIndex < nMid && nRightIndex < (nNum - nMid)){if(lpBegin[nLeftIndex].nVisitTick >= lpBegin[nRightIndex+nMid].nVisitTick){lpArrTmp[nArrIndex] = lpBegin[nLeftIndex];nArrIndex++;nLeftIndex++;}else{lpArrTmp[nArrIndex] = lpBegin[nRightIndex+nMid];nArrIndex++;nRightIndex++;}}if(nLeftIndex < nMid){for(int32_t i = nLeftIndex; i < nMid; i++){lpArrTmp[nArrIndex] = lpBegin[i];nArrIndex++;}}else{for(int32_t i = nRightIndex; i < (nNum - nMid); i++){lpArrTmp[nArrIndex] = lpBegin[i+nMid];nArrIndex++;}}// 设置回去for(int32_t i = 0; i < nNum; i++){lpBegin[i] = lpArrTmp[i];}
}
// 基础的深度优先重点在于节点遍历.而不是,走完所有可能路径.
void Visit(int nCurIndex, int32_t nTag){nVisitTick++;stNodes[nCurIndex].m_nEle.m_bVisit = true;stNodes[nCurIndex].m_nEle.m_nFirstTick = nVisitTick;stNodes[nCurIndex].m_nEle.m_nTag = nTag;for(int i = 0; i < 9; i++){if(stEdges[nCurIndex][i].m_bValid && stNodes[i].m_nEle.m_bVisit == false){Visit(i, nTag);}}stNodes[nCurIndex].m_nEle.m_nLastTick = nVisitTick;nVisitTick++;// 用来保证LastTick不出现重复
}

特意说明下,这里的深度优先访问是最基础的深度优先版本.此版本主要侧重于对每个可达节点执行一次访问.

2.图的最小生成树
(1). 定义
图的最小生成树(Minimum Spanning Tree,MST)是一个在图论中常见的概念。给定一个加权连通图(即图的每条边都有一个权重,连通图是图中任意两点均可相互到达的意思),最小生成树是一个子图,它包含了原图中的所有顶点,且所有的边都属于原图的边,同时边的权值和在所有这样的子图中是最小的。换句话说,最小生成树是一个连通所有顶点的树,且所有边的权值和最小。(也可说成是代价最小的连通所有顶点的子图)

最小生成树问题是一个优化问题,它在网络设计、电路设计等领域有广泛的应用。例如,在构建通信网络时,我们可能希望以最小的成本连接所有的城市,这就可以通过求解最小生成树问题来实现。

求解最小生成树问题的常见算法有两种:普里姆算法(Prim's algorithm)和克鲁斯卡尔算法(Kruskal's algorithm)。

普里姆算法:
(1). 将源节点加入当前最小生成树.
(2). 对所有边按权重排序.
(3). 循环迭代:
a. 从所有边中选出跨越当前最小生成树,图剩余部分的权重最小的边.跨越意味着此边一个节点在当前最小生成树中,另一节点不在.
b. 将此边的另一节点也加入当前最小生成树.
(4). 树中所有节点均加入最小生成树后,迭代结束.获得最小生成树.

克鲁斯卡尔算法:
(1).对图中的所有边按照权值大小进行排序.
(2). 按照权值从小到大的顺序依次选择边。
在选择每一条边时,需要判断这条边的两个顶点是否已经在同一个连通分量中,如果是,则跳过这条边;如果不是,则将这条边加入最小生成树中,并更新连通分量的信息。

(2). 实例
在这里插入图片描述
可利用上述算法求解上图中的一颗最小生成树.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/763440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享基于PDF.js的pdf阅读器代码

一、前言 有时候开发PC端web页面的时候会遇到PDF预览、下载等功能&#xff0c;为了兼容浏览器&#xff0c;需要找一款前端插件进行开发。比较好的PDF插件&#xff0c;就是mozilla的pdf.js&#xff08;注意是mozilla&#xff0c;如果你百度遇到需要收费的&#xff0c;那应该是下…

python矢量算法-三角形变化寻找对应点

1.算法需求描述 现有随机生成的两个三角形A与B&#xff0c;在三角形A中存在Pa&#xff0c;使用算法计算出三角形B中对应的点Pb 2.python代码 import numpy as np # 计算三角形A的面积 def area_triangle(vertices): return 0.5 * np.abs(np.dot(vertices[0] - vertices[…

品时尚精酿啤酒,探秘时尚背后的故事

在琳琅满目的啤酒市场中&#xff0c;Fendi Club啤酒以其时尚的风格和品质&#xff0c;成为了引人注目的焦点。这款啤酒不仅是一种味觉的享受&#xff0c;更是一种时尚与品味的象征。接下来&#xff0c;让我们一起探索Fendi Club啤酒背后的故事。 一、Fendi Club啤酒的特色 Fen…

师徒互电,眼冒金星,采集系统变电刺激系统!

原文来自微信公众号&#xff1a;工程师看海&#xff0c;很高兴分享我的原创文章&#xff0c;喜欢和支持我的工程师&#xff0c;一定记得给我点赞、收藏、分享哟。 加微信[chunhou0820]与作者进群沟通交流 电的我眼冒金星&#xff0c;以为自己被三体召唤&#xff0c;整个世界为我…

什么是浏览器指纹识别?Maskfog指纹浏览器有用吗?

浏览器指纹识别是好是坏&#xff1f;这现在确实是一个有争议的话题。83%的消费者经常或偶尔会根据浏览历史记录看到广告。其实这就是利用了浏览器指纹技术。 如果您想了解浏览器指纹识别是什么&#xff0c;那就看下去&#xff01; 一、什么是浏览器指纹识别 浏览器指纹是指无…

鸿蒙一次开发,多端部署(四)工程管理

DevEco Studio的基本使用&#xff0c;请参考DevEco Studio使用指南。本章主要介绍如何使用DevEco Studio进行多设备应用开发。 说明&#xff1a; 本章的内容基于DevEco Studio 3.1.1 Release版本进行介绍&#xff0c;如您使用DevEco Studio其它版本&#xff0c;可能存在文档与产…

《妈妈是什么》笔记(一)孩子都有被关注的需求

简介 作者渡渡鸟&#xff0c; 其本名韩谨&#xff0c;微博原创育儿理念、故事、分享妈妈经。毕业于中国社科院哲学系&#xff0c;有过八年记者生涯。育有一儿两女&#xff0c;在中美两国生活&#xff0c;熟悉跨文化养育&#xff0c;有坚定的根文化意识。 渡渡鸟妈妈能够基于孩…

docker基础超详细教程,一篇文章帮助你从零开始学习docker,从入门到实战

docker 概述 docker 官网&#xff1a;http://www.docker.com 官网文档&#xff1a; https://docs.docker.com/get-docker/ Docker Hub官网&#xff1a;https://hub.docker.com &#xff08;仓库&#xff09; 什么是 docker docker 是一个开源的容器化平台&#xff0c;可以…

Python螺旋折线蓝桥杯(来源lanqiao.cn 题目176) 时间超限

题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点(X, Y)&#xff0c;我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。 例如dis(0, 1)3, dis(-2, -1)9 给出整点坐标(X, Y)&#xff0c;你能计算出dis(X, Y)吗&#xff1f; 输入格式 …

js【详解】typeof 运算符

typeof()表示“获取变量的数据类型”&#xff0c;返回的是小写&#xff0c;语法为&#xff1a;&#xff08;两种写法都可以&#xff09; // 写法1 typeof 变量;// 写法2 typeof(变量); typeof 这个运算符的返回结果就是变量的类型。 返回结果&#xff1a; typeof 的代码写法…

Swift 中的 Sequence 是什么 ?

在 Swift 中&#xff0c;Sequence 是一个协议&#xff0c;它表示一个可以遍历其元素的集合类型。任何遵循 Sequence 协议的类型都必须提供一个迭代器&#xff0c;用于按顺序访问其元素。迭代器是通过 makeIterator() 方法获取的&#xff0c;该方法返回一个遵循 IteratorProtoco…

【数组、字符串】算法例题

每个题的【方法1】是自己的思路&#xff0c;【其他方法】是力扣上更优的解题思路 目录 一、数组、字符串 1. 合并两个有序数组 ① 2. 移除元素 ① 3. 删除有序数组中的重复项 ① 4. 删除有序数组中的重复项 II ② 5. 多数元素 ① 6. 轮转数组 ② 7. 买卖股票的最佳时机…

基于单片机的指纹打卡机设计

摘要 在科学技术飞速发展的今天&#xff0c;社会对身份识别的要求越来越高&#xff0c;尤其是在企业管理的人员签到、工作考勤等活动中对身份识别的高效性和可靠性的需求更为迫切。而传统的个人身份识别手段&#xff0c;如钥匙、密码、IC卡等&#xff0c;由于具有可盗用、可伪…

C语言:volatile关键字讲解

读音&#xff1a;vaoletail C语言中的volatile关键字是一个重要的类型修饰符&#xff0c;它用于声明一个变量具有“易变性”&#xff0c;即可能在编译器无法察觉的情况下被改变其值。Volatile意思是“易变的”&#xff0c;应该解释为“直接存取原始内存地址”比较合适。 “易变…

好看的表情壁纸

不定时更新表情壁纸&#xff0c;后期支持头像&#xff0c;wx背景等&#xff0c;个人开发&#xff0c;觉得不错&#xff0c;可前往小程序或者公众号查看

定制红酒:品质保障,从源头做起

云仓酒庄的洒派定制红酒&#xff0c;以其卓着的品质和与众不同的口感&#xff0c;赢得了众多消费者的喜爱。而这种品质的保障&#xff0c;正是从源头上开始的。 在葡萄种植方面&#xff0c;种植者对土壤、气候等自然条件进行严格的筛选和评估&#xff0c;确保葡萄能够在理想的环…

Python的错误和异常,异常的处理

我们平时经常会碰到一些报错信息&#xff0c;特别是对于入门的敲代码选手来说&#xff0c;碰到报错嘎嘎头大&#xff0c;但我们要知道它们是程序执行过程中的常态而非例外。本篇文章&#xff0c;就让我们一起来了解一下错误和异常&#xff0c;在平时编程过程中正确理解和有效处…

android adb 实时画面 和操作

1. 下载 scrcpy 建议 windows10 用户 点击链接下载 不然可能会提示缺少部分 dll https://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-win32-v2.3.1.ziphttps://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-win32-v2.3.1.zip windo…

Android App开发的自动化测试框架UI Automator使用教程

UI Automator为Android程序的UI开发提供了测试环境,这里我们就来看一下Android App开发的自动化测试框架UI Automator使用教程,需要的朋友可以参考下 Android的自动化测试有很多框架&#xff0c;其中ui automator是google官方提供的黑盒UI相关的自动化测试工具&#xff0c;&am…

【进程和线程】操作系统中的并发执行机制

目录 一、什么是进程(Process)&#xff1f; 进程的管理 进程调度(重点) 二、什么是线程(Thread)&#xff1f; 三、进程和线程的区别与联系 进程(Process) 线程(Thread) 总结比较 一、什么是进程(Process)&#xff1f; 进程和线程是操作系统中一个非常核心的话题&#…