一.图的存储结构
1. 邻接矩阵(顺序存储结构)
typedef struct
{
int no; //顶点编号
char info; //顶点其他信息
}VertexType; //顶点类型
typedef struct
{
int edges[maxSize][maxSize]; //邻接矩阵定义
int n,e; //分别为顶点数和边数
VertexType vex[maxSize]; //存放结点信息
}MGraph; //图的邻接矩阵类型
2. 邻接表(链式存储结构)
typedef struct ArcNode
{ //边结点表
int adjvex; //该边所指向的结点的位置
struct ArcNode *nextarc; //指向下一条边的指针
int info; //该边的相关信息
}ArcNode;
typedef struct
{ //顶点表
char data; //顶点信息
ArcNode *firstarc; //指向第一条边的指针
}VNode;
typedef struct
{
VNode adjlist[maxSize]; //邻接表
int n,e; //顶点数和边数
}AGraph; //图的邻接表类型
二.图的遍历算法
1. DFS深度优先搜索遍历
算法执行过程:任取一个顶点,访问之,然后检查这个顶点的所有邻接顶点,递归访问其中未被访问过的顶点。
int visit[maxSize];
/* v是起点编号,visit[]是一个全局数组,作为顶点的访问标记,初始时所有元素*/
/* 均为0,表示所有顶点都未被访问过。因图中可能存在回路,当前经过的顶点在将*/
/* 来还可能再次经过,所以要对每个顶点进行标记,以免重复访问*/
void DFS(AGraph *G,int v)
{
ArcNode *p;
visit[v] = 1; //置已访问标记
Visit(v);
p = G->adjlist[v].firstarc; //p指向顶点v的第一条边
while(p != NULL)
{
if(visit[p->adjvex] == 0) //若顶点未访问,则递归访问它
DFS(G,p->adjvex);
p = p->nextarc; //p指向顶点v的下一条边的终点
}
}
2. BFS广度优先搜索遍历
算法执行过程:
1). 任取图中一个顶点访问,入队,并将这个顶点标记为已访问
2). 当队列不空时循环执行:出队,依次检查出队结点的所有邻接顶点,访问没有被访问过的邻接顶点并将其入队
3). 当队列为空时跳出循环,广度优先搜索即完成
void BFS(AGraph *G,int v,int visit[maxSize])
{ //visit[]数组被初始化为全0
ArcNode *p;
int que[maxSize];
int front = 0,rear = 0;
int j;
Visit(v);
visit[v] = 1;
rear = (rear + 1) % maxSize;
que[rear] = v;
while(front != rear) //队空的时候说明遍历完成
{
front = (front + 1) % maxSize; //顶点出队
j = que[front];
p = G->adjlist[j].firstarc; //p指向出队顶点j的第一条边
while(p != NULL)
{
if(visit[p->adjvex] == 0) //当前邻接顶点未被访问,则入队
{
Visit(p->adjvex);
visit[p->adjvex] = 1;
rear = (rear + 1) % maxSize; //该顶点入队
que[rear] = p->adjvex;
}
p = p->nextarc; //p指向j的下一条边
}
}
}
3.
int BFS(AGraph *G,int v)
{
/*本题用邻接表作为图的存储结构*/
ArcNode *p;
int que[maxSize],front = 0,rear = 0;
int visit[maxSize];
int i,j;
for(i=0;i<G->n;++i)
visit[i] = 0; //将visit数组全部初始化为0
rear = (rear + 1) % maxSize;
que[rear] = v;
visit[v] = 1;
while(front != rear)
{
front = (front + 1) % maxSize;
j = que[front];
p = G->adjlist[j].firstarc;
while(p != NULL)
{
if(visit[p->adjvex] == 0)
{
visit[p->adjvex] = 1;
rear = (rear + 1) % maxSize;
que[rear] = p->adjvex;
}
p = p->nextarc;
}
}
return j; //队空时,j保存了遍历过程中的最后一个顶点
}
4.
void DFS(AGraph *G,int v,int &vn,int &en)
{
ArcNode *p;
visit[v] = 1;
++vn; //本题中对当前顶点的访问即为vn计数器自增1
p = G->adjlist[v].firstarc;
while(p != NULL)
{
++en; //边数自增1
if(visit[p->adjvex] == 0)
DFS(G,p->adjvex,vn,en);
p = p->nextarc;
}
}
int GisTree(AGraph *G)
{
int vn = 0,en = 0;
for(i=0;i<G->n;++i)
visit[i] = 0;
DFS(G,1,vn,en);
if(vn == G->n && (G->n-1) == en/2) //在每次来到一个新顶点,en累加了当前访问顶点的所有边,也就是说每条边访问了两次
/*如果遍历过程中访问过的顶点数和图中的顶点数相等,且边数等于顶点数减1,则证明是树*/
return 1;
else
return 0;
}
5.
int DFSTrave(AGraph *G,int i,int j)
{
int k;
for(k=0;k<G->n;++k)
visit[k] = 0;
DFS(G,i);
if(visit[j] == 1) //visit[j]等于1则证明访问过程中遇到了j
return 1;
else
return 0;
}
三.最小生成树
1.普里姆算法
void Prim(MGraph g,int v0,int &sum)
{
int lowcost[maxSize],vset[maxSize],v;
int i,j,k,min;
v = v0;
for(i=0;i<g.n;++i)
{
lowcost[i] = g.edges[v0][i]; //顶点v0到其余各定点的长度
vset[i] = 0; //vset全部置为0
}
vset[v0] = 1; //将v0并入树中
sum = 0; //sum清零用来累计树的权值
for(i=0;i<g.n-1;++i) //循环n-1次
{
min = INF;
/*下面这个循环用于选出侯选边中的最小值*/
for(j=0;j<g.n;++j)
{
if(vset[j] == 0 && lowcost[j] < min) //选出当前生成树到其余各顶点最短边中的最短的一条
{
min = lowcost[j];
k = j;
}
}
vset[k] = 1;
v = k;
sum += min; //这里用sum记录了最小生成树的权值
/*下面这个循环以刚并入的顶点v为媒介更新候选边*/
for(j=0;j<g.n;++j)
{
if(vset[j] == 0 && g.edges[v][j] < lowcost[j]) //并入生成树的顶点无需更新
lowcost[j] = g.edges[v][j];
}
}
}
2.克鲁斯卡尔算法
typedef struct
{
int a,b; //a,b为一条边所连的两个顶点
int w; //边的权值
}Road;
Road road[maxSize];
int v[maxSize]; //定义并查集数组
int getRoot(int a)
{
while(a != v[a])
a = v[a];
return a;
}
void Kruskal(MGraph g,int &sum,Road road[])
{
int i;
int N,E,a,b;
N = g.n; //顶点数
E = g.e; //边数
sum = 0;
for(i=0;i<N;++i)
v[i] = i;
sort(road,E); //对road数组中的E条边按其权值从小到大排序
for(i=0;i<E;++i)
{
a = getRoot(road[i].a);
b = getRoot(road[i].b);
if(a != b)
{
v[a] = b; //把b赋值给a,即将b作为a的父结点
sum += road[i].w;
}
}
}