图论(五)--强连通分量

本文介绍了一种求解图的强连通分量的方法,通过两次深度优先搜索(DFS)实现,首先对原始图进行DFS,然后根据节点的访问结束时间逆序对图的转置再次进行DFS。该算法的时间复杂度为O(V+E),适用于寻找有向图中的强连通分量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于算法导论图算法-强连通分量

  • 题目描述
  • 问题分析
  • 源代码
  • 结果截图

题目描述

求图的连通分量

问题分析

先对原图进行DFS,在根据结束时间的倒序对原图的转置进行DFS即可,具体证明可以参考算法导论第22章22.5节-强连通分量。

伪代码及算法复杂度:
求连通分量

源代码

Graph G_Transpose(Graph G);//求图的转置
void strongly_connected_components(Graph G);//求强连通分量
void DFS_G_T(Graph G);//对图的转置按照结束时间递减顺序DFS

int Time;
int finishTime_descreasing[VertexNum + 1];//在深搜时按结束时间存
int count_finishTime_descreasing = VertexNum;//数组下标,从后往前存

void DFS_visit_stack(Graph G, Vertex v) {//深搜用栈实现
    PtrToNode ptr;
    stack<int> S;
    S.push(v);
    G->vertices[v].dist = 0;
    G->vertices[v].color = 1;//灰色
    G->vertices[v].discover_time = ++Time;
    printf("%d", v);
    while (!S.empty()) {
        Vertex u = S.top();
        ptr = G->vertices[u].adjto;
        while (ptr != NULL) {
            if (G->vertices[ptr->adjvex].color == 0) {
                S.push(ptr->adjvex);
                G->vertices[ptr->adjvex].dist = G->vertices[u].dist + 1;//权为1计算
                G->vertices[ptr->adjvex].color = 1;//灰色
                Time++;
                G->vertices[ptr->adjvex].discover_time = Time;
                G->vertices[ptr->adjvex].pred = u;
                printf(" %d", ptr->adjvex);
                break;
            }
            ptr = ptr->next;
        }
        if (S.top() == u) {
            G->vertices[u].color = 2;//黑色
            Time++;
            G->vertices[u].finish_time = Time;
            finishTime_descreasing[--count_finishTime_descreasing] = u;
            S.pop();

        }

    }
    printf("\n");
}

void DFS(Graph G) {

    for (int i = 0; i < G->vexnum; i++) {
        G->vertices[i].color = 0;//白色
        G->vertices[i].pred = -1;
    }
    Time = 0;
    for (int i = 0; i < G->vexnum; i++) {
        if (G->vertices[i].color == 0) {
            DFS_visit_stack(G, i);
        }
    }
}


Graph G_Transpose(Graph G) {//求图的转置
    Graph G_T;
    PtrToNode ptr, node;
    //创建图的初始化
    G_T = (struct GraphRecord*)malloc(sizeof(struct GraphRecord));
    G_T->vexnum = G->vexnum;
    G_T->vertices = (struct VertexRecord*)malloc(sizeof(struct VertexRecord)*G_T->vexnum);
    for (int i = 0; i < G_T->vexnum; i++) {
        G_T->vertices[i].adjto = NULL;
        G_T->vertices[i].in_degree = 0;
        G_T->vertices[i].out_degree = 0;
    }
    //创建图
    for (int i = 0; i < G->vexnum; i++) {
        ptr = G->vertices[i].adjto;
        while (ptr != NULL) {
            node = (struct Node*)malloc(sizeof(struct Node));
            node->adjvex = i;
            node->next = G_T->vertices[ptr->adjvex].adjto;
            G_T->vertices[ptr->adjvex].adjto = node;
            ptr = ptr->next;
        }
    }

    //print_graph(G_T);打印转置图
    return G_T;
}


void DFS_G_T(Graph G) {

    int count = 0;
    for (int i = 0; i < G->vexnum; i++) {
        G->vertices[i].color = 0;//白色
        G->vertices[i].pred = -1;
    }
    Time = 0;
    for (int i = 0; i < G->vexnum; i++) {
        if (G->vertices[finishTime_descreasing[i]].color == 0) {
            printf("第%d个强连通分量:", ++count);
            //DFS_VISIT(G, i);
            DFS_visit_stack(G, finishTime_descreasing[i]);
            //print_dist_dfs(G, finishTime_descreasing[i]);
        }
    }
    printf("共有%d个强连通分量\n", count);
    //print_path_everyPoint(G);
    //print_dist_dfs(G, 0);
}


void strongly_connected_components(Graph G) {

    printf("深搜G:");
    DFS(G);

    Graph G_T = G_Transpose(G);

    /*for (int i = 0; i < VertexNum; i++) {//按递减顺序打印结束时间
    printf("顶点%d的结束时间为:%d\n", finishTime_descreasing[i], G->vertices[finishTime_descreasing[i]].finish_time);
    }*/
    printf("按照递减顺序dfs原图的转置得到强连通分量:\n");
    DFS_G_T(G_T);
}

int main() {
    CreateRandomDirectGraph();
    Graph G = CreateDirectGraph();//参考[图论(一)-图的建立](http://blog.youkuaiyun.com/deep_kang/article/details/70877468),这里以十个顶点做测试
    printf("打印图结构:\n");
    print_graph(G);//打印图

    printf("\n求强连通分量:\n");
    strongly_connected_components(G);
    return 0;
}

结果截图

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值