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

news2024/9/22 7:30:15

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.coloradmin.cn/o/1536268.html

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

相关文章

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

Android的自动化测试有很多框架&#xff0c;其中ui automator是google官方提供的黑盒UI相关的自动化测试工具&#xff0c;&#xff08;GitHub主页&#xff1a;case使用java写&#xff0c;今天实践了一下官方文档中样例程序&#xff0c;其中还是有一些小问题需要总结一下的。 环…

为什么签名apk,需要公钥证书和私钥证书,不是私钥就能签名吗?对应的公钥通常包含在APK文件中,这样用户和系统可以验证签名的有效性

在Android开发中&#xff0c;对APK进行签名确实需要使用到公钥证书和私钥证书&#xff0c;而不仅仅是私钥。以下是详细解释&#xff1a; 身份验证&#xff1a;公钥证书作为应用程序的身份证明&#xff0c;可以帮助用户或系统验证安装的APK的真实性。当用户下载并安装APK时&…

第十四届蓝桥杯(C/C++ 大学B组)

试题 A&#xff1a;日期统计 #include <bits/stdc.h> using namespace std;const int numbers[100] {5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7, 5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5,8, 6, 1, 8, 3, 0, 3, 7, 9, 2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, …

2016年认证杯SPSSPRO杯数学建模A题(第二阶段)洗衣机全过程文档及程序

2016年认证杯SPSSPRO杯数学建模 A题 洗衣机 原题再现&#xff1a; 洗衣机是普及率极高的家用电器&#xff0c;它给人们的生活带来了很大的方便。家用洗衣机从工作方式来看&#xff0c;有波轮式、滚筒式、搅拌式等若干种类。在此基础上&#xff0c;各厂商也推出了多种具体方案…

思科无线控制器配置学习

文章目录 简单拓扑WLC配置 简单拓扑 WLC配置 WLC#show running-config Building configuration...Current configuration : 11943 bytes ! ! Last configuration change at 16:22:44 UTC Thu Mar 14 2024 by admin ! version 17.9 service timestamps debug datetime msec se…

OrangeDAO联合创始人Don Ho确认出席Hack.Summit() 2024区块链开发者大会

随着Web3技术的快速发展&#xff0c;区块链领域备受关注的盛会——Hack.Summit() 2024 区块链开发者大会即将于 2024 年 4 月 9 日至 10 日在香港数码港隆重启幕。本次大会不仅是 Hack.Summit() 系列在亚洲的首次亮相&#xff0c;更象征着全球区块链行业对亚洲&#xff0c;尤其…

SAP-MM-设置字段默认值

当我们创建订单时&#xff0c;有些字段总是重复输入&#xff0c;每次值也是固定的&#xff0c;例如生产订单 如上图“生产工厂都是1000”如何设置成默认每次进入都是1000呢&#xff1f; 点击字段&#xff0c;F1 查看参数ID“WRK” 输入tcode&#xff1a;SU3 按上图维护数据100…

gimp教程

一、gimp下载安装 二、基本概念和术语 &#xff08;一&#xff09;图像 图像是GIMP要处理的对象。 一个图像对应一个文件&#xff0c;例如一个TIFF或JPEG文件。 一个图像对应一个显示窗口。 可以同时打开多个图像。 &#xff08;二&#xff09;图层 一个图像就像一堆纸叠在…

TypeScript在学习(0)

1.什么是TypeScript? 答:TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集&#xff0c;而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。 个人浅见&#xff0c;我一直把ts简单理解成&#xff0c;其实就是javascript上多了…

美易官方:美股维持涨势,三大股指再创新高

在今日的早盘交易中&#xff0c;美股市场继续维持其涨势&#xff0c;三大股指再次刷新历史纪录。市场信心受到一系列积极经济数据的支撑&#xff0c;投资者对未来的经济增长和企业盈利保持乐观态度。 首先&#xff0c;让我们来看一下道琼斯工业平均指数的表现。该指数在早盘交易…

一键批量查询快递单号,一键批量查询,共享备份物流,快递物流尽在掌控

随着网购的普及&#xff0c;快递物流信息的管理变得尤为重要。每天都有大量的快递单号需要查询&#xff0c;如果一个个手动查询&#xff0c;不仅费时费力&#xff0c;还容易出错。为了解决这个问题&#xff0c;我们教您如何批量查询快递单号&#xff0c;并将快递物流信息进行备…

Linux docker4--本地jar包生成镜像和docker部署运行

一、通过springboot创建一个java项目&#xff0c;打成出jar包。 二、将jar包生成docker镜像 &#xff08;1&#xff09;、创建Dockerfile文件 创建Dockerfile文件&#xff0c;将如下的代码内容粘贴进去即可。 注意&#xff1a;本例中我打出的jar包是boot.jar。如果你打出的jar…

开源项目ChatGPT-Next-Web的容器化部署(二)-- jenkins CI构建并推送镜像

一、背景 接着上文已制作好了Dockerfile&#xff0c;接下来就是docker build/tag/push等一系列操作了。 不过在这之前&#xff0c;你还必须在jenkins等CI工具中&#xff0c;拉取源码&#xff0c;然后build构建应用。 因为本文的重点不是讲述jenkins ci工具&#xff0c;所以只…

罗德与施瓦茨CMA180电信无线电测试仪

181/2461/8938产品概述&#xff1a; R&S CMA180 是适用于在 100 kHz 至 3 GHz 范围内操作的无线电系统的无线电通信测试仪。其技术完全基于数字信号处理及先进计算。 简介&#xff1a;R&S CMA180 无线电通信测试仪 R&SCMA180 是适用于在 100 kHz 至 3 GHz 范围内…

MQTT 简介

MQTT 简介 MQTT 是非常简单的协议&#xff0c;最初由 IBM 的两位工程师 Andy Stanford-Clark 以及 Arlen Nipper 在 1999 年为监控输油管道设计的。它被设计的场景就是有限的带宽、轻量级以及很小的耗电量&#xff0c;在那个时候&#xff0c;卫星宽带就是那么小&#xff0c;且…

蓝桥杯算法心得——游戏(优先队列)

大家好&#xff0c;我是晴天学长&#xff0c;优先队列的题&#xff0c;式子化简非常重要&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .游戏 2) .算法思路 附近最小 1.接收数据 2.找出最小的&#…

C#探索之路基础篇(2):接口Interface的概念、实现、应用范围

文章目录 1 概念2 示例代码&#xff1a;2.1 简单接口的实现2.2 简单的使用接口2.3 使用接口呈现多态性2.4 通过接口实现一个数组迭代器2.5 通过接口来实现松耦合的关系2.6 使用接口实现可扩展、便利性 3 使用范围与时机4 注意事项 不知道大家在学习的过程中&#xff0c;有没有反…

鸿蒙Harmony应用开发—ArkTS-全局UI方法(警告弹窗)

通过CustomDialogController类显示自定义弹窗。使用弹窗组件时&#xff0c;可优先考虑自定义弹窗&#xff0c;便于自定义弹窗的样式与内容。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 接口 Custom…

【实例】React 状态管理库 MobX Redux 入门及对比

上一篇&#xff1a;【实例】React 组件传值方法: Props、回调函数、Context、路由传参 MobX MobX 是一个状态管理库&#xff0c;它提供了一种响应式的数据流方案&#xff0c;使得状态的变化能够自动地反映到相关的组件中。 MobX 的核心理念是可观察的状态&#xff08;Observa…

一种基于约化因子上三角矩阵求逆方法与MATLAB仿真

一种基于约化因子上三角矩阵求逆的方法与MATLAB仿真 目录 前言 一、上三角矩阵单位化 二、C对角矩阵求逆 三、A 矩阵求逆 四、A矩阵求逆 五、计算量分析 六、MATLAB仿真 七、参考资料 总结 前言 矩阵运算广泛应用于实时性要求的各类电路中&#xff0c;其中矩阵求逆运算…