// 强连通分支(邻接表存储).cpp : Defines the entry point for the console application.
//通过二次利用深度优先搜索访问节点,每次的深度优先搜索操作不同
#include "stdafx.h"
#include<iostream>
#define MAX 100
using namespace std;
//深度搜索访问节点层次标志枚举变量
enum Color{white,gray,black};
//边端点结构体
struct edgeNode
{
int no; //边尾端的序号
char info; //边端的名称
struct edgeNode * next; //下一个
};
//节点结构体
struct vexNode
{
char info; //节点名称
struct edgeNode *link; //与之相连的端点
};
//强连通子图的链表头结构体
struct StronConHead
{
int num; // 强连通子图序号
struct StronConNode *link; //此强连通子图的第一个节点
};
//
struct StronConNode
{
int num; //一个强连通子图节点的序号
char info; //一个强连通子图节点的名称
struct StronConNode *next; //下一个
};
//深度搜索访问节点的开始/完成时间结构体
struct Time
{
int num; //节点序号
int time; //时间
};
//存储节点信息,adjlist1存储图,adjlist2存储图的转置
vexNode adjlist1[MAX],adjlist2[MAX];
//访问层次
//还没访问为white,访问了但是还没完成它的所有后裔的搜索为gray
//完成它的所有后裔的搜索为black
Color color[MAX];
//访问的开始时间
Time d[MAX];
//访问的完成时间
Time f[MAX];
//深度搜索时,访问过的节点标志
bool visited[MAX];
//con[j]是序号为j的强连通子图的链表头数组,
StronConHead con[MAX];
//建立邻接表存储与其转置
//adjlist1为节点集,adjlist2为图转置节点集,n为节点个数,e为边数目
void createGraph(vexNode *adjlist1,vexNode *adjlist2,int n,int e)
{
int i;
for(i=1;i<=n;i++)
{
cout<<"请输入节点"<<i<<"的名称:";
cin>>adjlist1[i].info;
adjlist2[i].info = adjlist1[i].info;
adjlist1[i].link = NULL;
adjlist2[i].link = NULL;
}
edgeNode *p1,*p2;
int v1,v2;
for(i=1;i<=e;i++)
{
cout<<"请输入边"<<i<<"的起始节点序号:";
cin>>v1;
cout<<"请输入边"<<i<<"的尾节点序号:";
cin>>v2;
//建立原图邻接表
p1 = (edgeNode*)malloc(sizeof(edgeNode));
p1->no = v2;
p1->info = adjlist1[v2].info;
p1->next = adjlist1[v1].link;
adjlist1[v1].link = p1;
//建立转置图邻接表
p2 = (edgeNode*)malloc(sizeof(edgeNode));
p2->no = v1;
p2->info = adjlist2[v1].info;
p2->next = adjlist2[v2].link;
adjlist2[v2].link = p2;
}
}
//深度优先搜索有向无权图
//adjlist1为原图的邻接表存储,time为一个全局时间戳,v是第几个节点
void DFS1(vexNode *adjlist1,int &time,int v)
{
color[v] = gray;
time += 1;
d[v].num = v;
d[v].time = time;
edgeNode *p;
p = adjlist1[v].link;
while(p != NULL)
{
if(color[p->no]==white)
{
DFS1(adjlist1,time,p->no);
}
p = p->next;
}
color[v] = black;
time += 1;
f[v].num = v;
f[v].time = time;
}
////降序排列第一次深度搜索各节点的完成时间
void fast_sort_f(Time *f,int begin,int end)
{
if(begin<end)
{
int i = begin-1, j = begin;
f[0] = f[end];
while(j<end)
{
if(f[j].time>f[0].time)
{
i++;
Time temp = f[i];
f[i] = f[j];
f[j] = temp;
}
j++;
}
Time temp1 = f[end];
f[end] = f[i+1];
f[i+1] = temp1;
fast_sort_f(f,begin,i);
fast_sort_f(f,i+2,end);
}
}
//深度优先搜索有向无权图的转置图
//adjlist2为转置图的邻接表存储,con是序号为no的强连通子图的链表头数组,
//time为一个全局时间戳,v是第几个节点
void DFS2(vexNode *adjlist2,StronConHead *con,int &no,int v)
{
visited[v] = true;
StronConNode *p1 = (StronConNode*)malloc(sizeof(StronConHead));
p1->info = adjlist2[v].info;
p1->num = v;
p1->next = con[no].link;
con[no].link = p1;
edgeNode *q1;
q1 = adjlist2[v].link;
while(q1 != NULL)
{
if(!visited[q1->no])
{
DFS2(adjlist2,con,no,q1->no);
}
q1 = q1->next;
}
}
////打印各个强连通分支节点
//con是序号为no的强连通子图的链表头数组,no是强连通全局分支序号
void print_StronConCom(StronConHead *con,int &no)
{
int i;
StronConNode * p;
for(i=1;i<no;i++)
{
cout<<"强连通分支"<<i<<":";
p = con[i].link;
while(p !=NULL)
{
cout<<"("<<p->num<<":"<<p->info<<") ";
p = p->next;
}
cout<<endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int cases;
cout<<"请输入案例的个数:";
cin>>cases;
while(cases--)
{
int n,e;
cout<<"请输入节点数:";
cin>>n;
cout<<"请输入边数:";
cin>>e;
//访问节点的时间戳,全局变量
int time = 0;
//no是强连通全局分支序号,全局变量
int no = 1;
//访问标志清空与前驱节点都初始化为0
int i;
for(i=1;i<=n;i++)
{
color[i] = white;
visited[i] = false;
con[i].link = NULL;
}
//创建邻接表
createGraph(adjlist1,adjlist2,n,e);
//第一次深度搜索,搜索原图
for(i=1;i<=n;i++)
{
if(color[i]==white)
DFS1(adjlist1,time,i);
}
//降序排列第一次深度搜索各节点的完成时间
fast_sort_f(f,1,n);
//第二次深度搜索,搜索原图的转置图
for(i=1;i<=n;i++)
{
if(!visited[i])
{
DFS2(adjlist2,con,no,f[i].num);
no += 1;
}
}
//打印各个强连通分支节点
print_StronConCom(con,no);
}
system("pause");
return 0;
}
--------------------------------------------------程序测试-----------------------------------------------------
请输入案例的个数:1
请输入节点数:8
请输入边数:14
请输入节点1的名称:q
请输入节点2的名称:b
请输入节点3的名称:c
请输入节点4的名称:d
请输入节点5的名称:e
请输入节点6的名称:f
请输入节点7的名称:g
请输入节点8的名称:h
请输入边1的起始节点序号:1
请输入边1的尾节点序号:2
请输入边2的起始节点序号:2
请输入边2的尾节点序号:3
请输入边3的起始节点序号:2
请输入边3的尾节点序号:6
请输入边4的起始节点序号:2
请输入边4的尾节点序号:5
请输入边5的起始节点序号:3
请输入边5的尾节点序号:4
请输入边6的起始节点序号:3
请输入边6的尾节点序号:7
请输入边7的起始节点序号:4
请输入边7的尾节点序号:3
请输入边8的起始节点序号:4
请输入边8的尾节点序号:8
请输入边9的起始节点序号:5
请输入边9的尾节点序号:1
请输入边10的起始节点序号:5
请输入边10的尾节点序号:6
请输入边11的起始节点序号:6
请输入边11的尾节点序号:7
请输入边12的起始节点序号:7
请输入边12的尾节点序号:6
请输入边13的起始节点序号:7
请输入边13的尾节点序号:8
请输入边14的起始节点序号:8
请输入边14的尾节点序号:8
强连通分支1:(2:b) (5:e) (1:q)
强连通分支2:(4:d) (3:c)
强连通分支3:(7:g) (6:f)
强连通分支4:(8:h)
请按任意键继续. . .
分享到:
相关推荐
十字链表是一种特殊的链表结构,它可以同时存储有向图的邻接表和逆邻接表的信息。每个顶点都有一个对应的节点,每条边也有一个对应的节点。这种数据结构使得我们可以方便地实现对有向图的操作,尤其是对于强连通分量...
对于有向图,邻接表可以分为出度表和入度表,分别存储指向其他顶点的边和从其他顶点指向的边。邻接表节省空间,适用于稠密图。 - **邻接矩阵**:邻接矩阵是一个二维数组,数组的索引对应图中的顶点。如果矩阵[i][j]...
而有向图只需一个边节点,因此节点数量为e。建立邻接表的时间复杂度通常为O(n*e),但如果顶点信息可以直接用顶点的下标表示,时间复杂度可以降低到O(n+e)。 **深度优先算法**的思想是:从图中的一个未访问顶点开始...
综上所述,用C语言实现无向图的算法时,需要掌握图的逻辑结构、存储结构(邻接矩阵和邻接表)、遍历算法(DFS和BFS)、最小生成树算法、最短路径算法以及拓扑排序等核心知识。在实际编程中,根据图的特性选择合适的...
4. 强连通分量:Tarjan算法或Kosaraju算法用于找到有向图的强连通分量。 五、实践应用 在C++编程中,图论广泛应用于网络路由、任务调度、社交网络分析、编译器优化、物流配送等领域。通过学习图论并结合C++,可以...
在有向图中,每个节点(或顶点)可以连接到其他节点,形成一种单向的链接关系。深度优先搜索在寻找两个特定顶点之间是否存在路径时非常有用。在描述的问题中,我们需要找出是否有从顶点Vi到Vj的路径,且i≠j。这通常...
总的来说,理解和掌握图的邻接表和邻接矩阵存储,以及DFS和BFS的实现,对于提升C++编程能力,特别是解决涉及图的问题至关重要。在实际项目中,这些技能可以应用于网络路由、社交网络分析、推荐系统等多种场景。
8. **强/弱连通分量**:在有向图中,强连通分量是图中任何顶点都能到达其他任何顶点的子图。弱连通分量则是将所有强连通分量连接起来的分量。 9. **最小生成树**:在加权无向图中,寻找连接所有顶点且总权重最小的...
7. **拓扑排序**:对于有向无环图(DAG),拓扑排序是将所有节点排成线性序列,且满足图中任意边(u, v)的起点u都出现在终点v之前。C++中,可以使用队列配合拓扑标志进行实现。 8. **Karger's算法**:这是求解最小割...
对于有向图,矩阵可能不对称。邻接矩阵适用于表示稠密图,即顶点之间有很多边的情况,因为其空间效率较高。 2. **邻接表**:邻接表是链表的集合,每个顶点对应一个链表,链表中的元素表示与该顶点相连的所有其他...
根据边的方向,图可以分为无向图(边没有方向)和有向图(边有方向)。 图的ADT定义包括了基本操作,如结构的建立和销毁、顶点的访问、顶点和边的插入与删除,以及对邻接点的操作。例如,CreatGraph()函数用于根据...
在有向图中,强连通图指的是图中的每个节点都可以到达其他所有节点。 **图的存储** - **邻接矩阵**:使用一个二维数组来表示图,其中adj[u][v]为1表示存在从u到v的边,为0表示不存在。适合于存储无重边的图,查询...
在无向图中,邻接表的构建需要在两个顶点的链表中互相插入对方,而在有向图中,仅在起点的链表中插入终点。 四、课程设计目标 课程设计的目标是让学生深入理解数据结构中的图概念,掌握邻接矩阵和邻接表的使用,...
Koenig's Theorem指出,一个有向图有欧拉回路当且仅当所有的节点的入度等于出度。 8. **色数问题**:四色定理是图论中的经典问题,表明任何平面图都可以用不超过四种颜色进行染色,使得相邻的顶点颜色不同。这在...
1.4 C++程序的编写和实现 1.5 关于C++上机实践 习题 第2章 数据类型与表达式 2.1 C++的数据类型 2.2 常量 2.2.1 什么是常量 2.2.2 数值常量 2.2.3 字符常量 2.2.4 符号常量 2.3 变量 2.3.1 什么是变量 2.3.2 ...
通常,我们可以使用邻接矩阵或邻接表来表示无向图。邻接矩阵是一个二维数组,其中的元素表示图中节点之间的连接关系;邻接表则是用链表或数组来存储每个节点的所有邻居节点。在C++中,可以使用`std::vector`来实现这...
对于有向图,边具有方向性,而无向图则没有方向。 深度优先遍历的策略是从一个顶点出发,尽可能深地搜索图的分支,直到访问到叶子节点,然后回溯到上一步,继续探索其他未被访问过的邻接顶点。在C++中,实现DFS通常...
图形算法涉及到图的表示(如邻接矩阵和邻接表)、遍历(深度优先搜索DFS和广度优先搜索BFS)以及图的基本操作,如查找路径、最小生成树(Kruskal's或Prim's算法)和最短路径问题(Dijkstra算法和Floyd-Warshall算法...
4. **C++实现**:在C++中,可以使用STL中的容器(如vector、list)来实现图的存储和遍历。例如,邻接表可以通过vector嵌套vector或vector嵌套list实现,DFS可以借助递归或栈,BFS则通常用队列配合。 5. **代码实践*...