目录:
1.有向图、无向图:邻接链表存储结构
2.标记无向图中的各个连通分量
3.求有向图的中的所有路径
标记无向图中的各个连通分量
连通分量的标记一般在无向图中进行:
1.会图的存储结构之邻接链表
2.有向图和无向图的邻接链表存储结构(会将有向图的邻接链表存储转成无向图的邻接链表存储:补充一下双向存储即可!)
问题:会求无向图的各个连通分量,即将G3的各个顶点属于哪一个连通分量给标记出来:
V1、V2、V3属于第一个连通分量;V4、V5、V6、V7属于第二个连通分量
利用图的深度优先遍历DFS(或者是广度优先遍历BFS);
对于连通图,从图中的任一顶点出发就可以遍历所有的顶点;
对于非连通同,则需要从多个顶点出发才能遍历图中的所有顶点;而每次从一个新的起点出发进行搜索的过程中得到的顶点访问序列就是该连通分量中的顶点集合。
对于G3需要调用两次DFS,顶层的递归DFS函数会返回两次,第一次得到V1、V2、V3集合;第二次得到V4、V5、V6、V7集合。
下面是图节点结构和邻接表图的定义:
component:标记该节点属于哪一个连通分量;
typedef struct NodeS
{
int num;
int visited; //0:该顶点未被访问;1该顶点被访问过了!
int component;//标记该顶点属于那一个连通分量,初始化时为0
}Node;
//有向图的邻接表表示,每个顶点对应一个列表
typedef struct ALGraphS{
vector vecNodes;
vector*> vexLists;//vector里存list的对象的指针
int vexNum;
vector vexIn;//顶点的入度向量
}ALGraph;
下面是图的深度优先遍历算法:
void DFS(ALGraph& al, int v,int label)
{//从图al的顶点v出发,递归地深度优先遍历图G
al.vecNodes[v]->visited = 1;
al.vecNodes[v]->component = label;
cout << v + 1 << " ";
for (int i = 0; i < (al.vexLists[v]->size()); i++)
{
int nodeNum = al.vexLists[v]->at(i)->num;
if (al.vecNodes[nodeNum]->visited == 0)
{
DFS(al, nodeNum, label);
}
}
}
int DFSTraverse(ALGraph& al,vector& nodeComponent)
{
int label = 0;
for (int i = 0; i < al.vexNum; i++)
{
if (al.vecNodes[i]->visited == 0)
{
label++;
DFS(al, i, label);
cout << endl;
}
}
/*
algraph是在堆中分配的结点,每次范围后,其每一个结点的标志位都设置为1了,退出时,
下次再遍历前要清一下标志位。
*/
for (int i = 0; i < al.vexNum; i++)
{
al.vecNodes[i]->visited = 0;
nodeComponent.push_back(al.vecNodes[i]->component);
al.vecNodes[i]->component = 0;
}
return label;
}
下面是测试代码:得到G2无向图的各个顶点连通分量的标记:
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
int main()
{
ALGraph algraph;
cin >> algraph.vexNum;
for (int i = 0; i < algraph.vexNum; i++)
{
Node* vexptr = new Node;
vexptr->num = i;
vexptr->visited = 0;
vexptr->component = 0;
algraph.vecNodes.push_back(vexptr);
vector* vexVec = new vector < Node* >;
//vexVec->push_back(vexptr);//直接存表节点,不存表头节点了!
algraph.vexLists.push_back(vexVec);
//顶点的入度向量
algraph.vexIn.push_back(0);
}
for (int i = 0; i < algraph.vexNum; i++)
{
vector friends;
while (1)
{
char temp;
cin >> temp;
if (temp == '0')
break;
else if (temp == ' ')
continue;
else
{
friends.push_back(temp - '0'-1);//注意这里V1顶点的下标是0,即存储下标从0计起
}
}
for (int j = 0; j < friends.size(); j++)
{
algraph.vexLists[i]->push_back(algraph.vecNodes[friends[j]]);
//algraph.vexIn[friends[j]]++;//顶点的入度向量加1
algraph.vexLists[friends[j]]->push_back(algraph.vecNodes[i]);//存成无向图,双向的
}
friends.clear();
}
vector NodeComponent;
cout << DFSTraverse(algraph, NodeComponent) << endl;
for (auto ele : NodeComponent)
{
cout << ele << " ";
}
//下面注释掉的代码只是去判断那些孤岛结点分量,即出入度都为0的结点,会错题意!
//int group = 0;
//for (int i = 0; i < algraph.vexNum; i++)
//{
// if ((algraph.vexLists[i]->size() == 0) &&( algraph.vexIn[i] == 0))
// {
// group++;
// }
//}
//cout << group+1;
for (int i = 0; i < algraph.vecNodes.size(); i++)
{
delete algraph.vecNodes[i];
algraph.vecNodes[i] = nullptr;
}
/*真正new出来的对象实体是Node结点,邻接表存从只是Node对象的地址而已!*/
for (int i = 0; i < algraph.vexLists.size(); i++)
{
algraph.vexLists[i]->clear();//将指针向量清空。
delete algraph.vexLists[i];
}
system("pause");
return 0;
}
输入输出:
10
0
5 3 0
8 4 0
9 0
9 0
3 0
0
7 9 0
0
9 7 0
1
2 5 9 4 3 8 7 10 6
2
1 2 2 2 2 2 2 2 2 2 请按任意键继续. . .
结果:V1属于第一个连通分量;其他节点属于第2个连通分量;
3.求有向图的中的所有路径
再来一个有向图:
V1
V2->V5;V2->V3
V3->V4;V3->V8
V4->V9
V5->V9;
V6->V3
V7
V8->V7;V8->V9
V9
V10->V7;V10->V9
//10个顶点,11条边,每个顶点的数据域存1
10 11
1
1
1
1
1
1
1
1
1
1
2 5
2 3
3 8
3 4
4 9
5 9
6 3
8 7
8 9
10 9
10 7
全局遍历来存各个路径:
vector > paths;//存储所有路径的二维向量
vector path;//存储单条路径
下面是节点和图定义:
/*邻接表:
Adjacency List
*/
//用邻接表来存有向图
//先定义图的顶点数据结构,及存一个调用耗时
typedef struct NodeS
{
int num;
int time;
int visited;
}Node;
//有向图的邻接表表示,每个顶点对应一个列表
typedef struct ALGraphS{
vector vecNodes;//图的结点地址向量,依次存每一个结点的地址
vector*> vexLists;//存储图中有向边的信息:每个顶点有一个邻接表,该邻接表上依次挂有其邻接顶点的地址
int vexNum, arcNum;
vector vexIn;//顶点的入度向量
}ALGraph;
/*像ALGraphS这样的只包含指针,不对指针所只资源进行管理的类(结构)的数据结构是很糟糕的数据结构!
应该将图定义成一个类,这个类包含构造函数(包括复制)构造函数,赋值=构造函数、析构函数等。
*/
下面是基于深度优先遍历的getpath()函数:
void dfs(ALGraph& al, int v)
{//注意:顶点编号从0计起,所以说第一个顶点V1的下标是0
/*这个函数的注意两点就是对顶点编号的压栈、退栈、以及对访问节点的设置visited标志位和撤销visited标志位*/
/*关键就是以栈的形式对递归调用的过程加以完美描述!*/
path.push_back(v);
if (al.vexLists[v]->size() == 1)//这里等同于判断v顶点的出度为0,
{
paths.push_back(path);
}
else
{
for (int i = 1; i < (al.vexLists[v]->size()); i++)
{
int nodeNum = al.vexLists[v]->at(i)->num;
if (al.vecNodes[nodeNum]->visited == 0 )
{
al.vecNodes[nodeNum]->visited = 1;
dfs(al, nodeNum);
al.vecNodes[nodeNum]->visited = 0;
}
}
}
path.pop_back();
return;
}
void getPaths(ALGraph& al)
{
for (int i = 0; i < al.vexNum; i++)
{
if (al.vexIn[i] == 0)
{//对入度为0的顶点进行深度优先遍历
dfs(al, i);
}
}
}
下面是测试例子:
#include "stdafx.h"
#include
#include
#include
#include
#include
using namespace std;
vector > paths;//存储所有路径的二维向量
vector path;//存储单条路径
int _tmain(int argc, _TCHAR* argv[])
{
ALGraph algraph;
cin >> algraph.vexNum;
cin >> algraph.arcNum;
//algraph.visited = new vector(algraph.vexNum,0);
//fill(algraph.visited->begin(), algraph.visited->end(), 0);
for (int i = 0; i < algraph.vexNum; i++)
{
int time;
cin >> time;
Node* vexptr = new Node;
vexptr->time = time;
vexptr->num = i;
vexptr->visited = 0;
algraph.vecNodes.push_back(vexptr);
vector* vexVec = new vector < Node* > ;
vexVec->push_back(vexptr);//这条语句可以取消掉,就只存邻接表的表结点而不存头节点,以后遍历单个顶点的邻接表时下标可从0开始
algraph.vexLists.push_back(vexVec);
//顶点的入度向量
algraph.vexIn.push_back(0);
}
int Vex1, Vex2;
for (int i = 0; i < algraph.arcNum; i++)
{
cin >> Vex1 >> Vex2;
algraph.vexLists[Vex1-1]->push_back(algraph.vecNodes[Vex2-1]);
//顶点的入度统计
algraph.vexIn[Vex2-1]++;
}
//cout << "深度优先遍历:" << endl;
//DFSTraverse(algraph);
//cout << endl;
///*
//algraph是在堆中分配的结点,每次范围后,其每一个结点的标志位都设置为1了,在遍历前要清一下标志位。
//*/
//
//cout << "广度优先遍历:" << endl;
//BFSTraverse(algraph);
getPaths(algraph);
int maxTime=0;
int time=0;
for (int i = 0; i < paths.size(); i++)
{
for (int j = 0; j < paths[i].size(); j++)
{
cout << paths[i][j] +1<< " ";
time += (algraph.vecNodes[paths[i][j]]->time);
}
cout << endl;
if (time > maxTime)
{
maxTime = time;
}
time = 0;
}
//cout << paths.size() << " " << maxTime;
//释放图所占用的内存空间:将那些new出来的,在堆区分配的空间删除一下,养成良好的编程习惯!
//最好的办法是使用智能指针,对这些对象进行引用计数,让系统帮我们回收
for (int i = 0; i < algraph.vecNodes.size(); i++)
{
delete algraph.vecNodes[i];
algraph.vecNodes[i] = nullptr;
}
/*真正new出来的对象实体是Node结点,邻接表存从只是Node对象的地址而已!*/
for (int i = 0; i < algraph.vexLists.size(); i++)
{
algraph.vexLists[i]->clear();//将指针向量清空。
delete algraph.vexLists[i];
}
system("pause");
return 0;
}
输入输出:
10 11
1
1
1
1
1
1
1
1
1
1
2 5
2 3
3 8
3 4
4 9
5 9
6 3
8 7
8 9
10 9
10 7
1
2 5 9
2 3 8 7
2 3 8 9
2 3 4 9
6 3 8 7
6 3 8 9
6 3 4 9
10 9
10 7
请按任意键继续. . .
找出的路径如下:
节点V1
结点V2->V5->V9
1
2 5 9
2 3 8 7
2 3 8 9
2 3 4 9
6 3 8 7
6 3 8 9
6 3 4 9
10 9
10 7
图的遍历的DFS_BFS:求有向图的中的所有路径;标记无向图中的各个连通分量