(一)图的基本术语
(1)无向完全图:图中任意两个顶点都有边存在则该图为无向完全图,且图的边树数为:n*(n-1)/2
(2)有向完全图:图中任意两点都有方向相反的边则该图为有向完全图,且图的边树数为:n*(n-1)
(3)连通图:若力图中任意两个顶点都有路径(即可到达)则成该图为连通的。
(4)连通分量:无向图中的极大连通子图称为该图的连通分量,显然无向连通图的连通分量为其自身。
(5)强连通图:无向图中任意两点都有路径则称该有向图为强连通图。
(6)强连通分量:有向图中的极大连通子图称为强连通分量。
例如:
(二)图的存储结构
(1)邻接矩阵
无权值:定义一个数组,图中两个顶点在数组中有一个对应的位置,其他为零
有权值:定义一个数组,图中两个顶点在图中有一个对应的位置放权值,其他为无穷大.
例如:
代码实现:
#define MAXSIZE 20
//图的存储 (邻接矩阵)
typedef struct Node
{
int e;//边的个数
int n;//顶点的个数
int vertex[MAXSIZE];
int edge[MAXSIZE][MAXSIZE];
}Graph;
void CreateGraph(Graph * g)
{
cout << "请输入顶点个数:";
cin >> g->n;
cout << "请输入边的个数:";
cin >> g->e;
for (size_t i = 0; i < g->n; i++)
{
g->vertex[i] = i;
}
//初始化邻接矩阵
for (size_t i = 0; i < g->n; i++)
{
for (size_t j = 0; j < g->n; j++)
{
g->edge[i][j] = 0;
}
}
for (size_t i = 0; i < g->n; i++)
{
int x, y;
cout << "请输入边信息:";
cin >> x >> y;
g->edge[x][y] = 1;
g->edge[y][x] = 1;
}
}
(1)邻接表:定义一个含有全部顶的点结构体数组,数组中存放节点值与一个链表的头指针,该头指针指向的链表存放与该节点相邻的顶点。假如再存放有权值则添加一个属性就可以。
例如:
代码实现:
//图的存储(邻接表)
typedef struct node
{
int adjvex;//邻接点
struct node *next;//指向下一个邻接点
}EdgeNode;
typedef struct arr
{
int vertex;//顶点域
EdgeNode *first;//邻接表第一个指针域
}VertexNode;
void CreateListGraph(VertexNode *g,int n,int e)
{
//初始化邻接矩阵
for (size_t i = 0; i < n; i++)
{
g[i].vertex = i;
g[i].first = NULL;
}
for (size_t i = 0; i < e; i++)
{
cout << "请输入顶点的信息:";
int x, y;
cin >> x >> y;
EdgeNode *pNew = new EdgeNode;
pNew->adjvex = y;
pNew->next = g[x].first;
g[x].first = pNew;
pNew = new EdgeNode;
pNew->adjvex = x;
pNew->next = g[y].first;
g[y].first = pNew;
}
}
(三)图的遍历
(1)深度优先遍历
算法思想:
1)访问顶点v;
2)从v的未被访问的邻接点出发,继续对图进行深度优先遍历,若从某点出发所有邻接点都已访问过,退回前一个点继续上述过程,若退回开始点,结束。
例如:
按照上面图进行遍历顺序如下:
**所以遍历的一种序列: V1 , V2 , V4 , V8 , V5 ,
V3 , V6 , V7 , V9 , V10 , V12 , V11 **
注意:便利序列是和你的存储方式有关的,如果使用邻接矩阵 则:
代码如下:
int visit[MAXSIZE];//标记顶点是否访问
void DFSGraph(Graph* g, int n)
{
cout << g->vertex[n] << " ";
visit[n] = 1;//若访问过就标记为1
for (size_t i = 0; i < g->n; i++)
{
if ( !visit[i]&& g->edge[n][i] == 1 )
{
DFS(g, i);
}
}
}
void DFSTraveseGraph(Graph *g)
{
for (size_t i = 0; i < g->n; i++)
{
visit[i] = 0;
}
for (size_t i = 0; i < g->n; i++)
{
if (!visit[i])
{
DFS(g, i);//防止连通图
}
}
}
如果使用邻接表 则: 代码如下:
//图的深度优先搜索遍历(邻接表)
int visit[MAXSIZE];//标记顶点是否访问
void DFS(VertexNode* g,int i)
{
cout << g[i].vertex << " ";
visit[i] = 1;//若访问过就标记为1
EdgeNode *p = g[i].first;//找到其下一个连接点
while (p!=NULL)
{
if (!visit[p->adjvex])//没有被访问则继续深度搜索
{
DFS(g, p->adjvex);
}
p = p->next;
}
}
void DFSTravese(VertexNode *g,int n)
{
for (size_t i = 0; i < n; i++)
{
visit[i] = 0;
}
for (size_t i = 0; i < n; i++)
{
if(!visit[i])
{
DFS(g, i);//防止连通图
}
}
}
(2)广度优先遍历
算法思想:
(1)首先访问顶点v
(2)访问顶点v的相邻节点
(3)然后依次从邻接点出发再访问其相邻节点
其实就是相当于树的层次遍历
在此只写出基于邻接表存储的图的广度优先遍历代码如下::
//广度优先搜索
#include <queue>
void BFS(VertexNode *g,int n)
{
cout << g[n].vertex<<" ";//输出第一个邻接点
visit[n] = 1;
queue<int> q;//定义一个队列用来存放顶点
q.push(n);//首先把顶点n入队
while (!q.empty())//判断队列是否为空
{
int i = q.back();//取队顶点
q.pop();
EdgeNode *p = g[i].first;
while ( p )
{
if (!visit[p->adjvex])
{
cout << p->adjvex << " ";
visit[p->adjvex] = 1;
q.push(p->adjvex);
}
p = p->next;
}
}
}
好了终于写完了,累死我了,基于树的存储方式还有很多种,但是比较麻烦,也是比较少用,也就没有介绍。有意向可以自己研究。