实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法
实现图的深度优先搜索、广度优先搜索
实现 Dijkstra 算法、A* 算法
实现拓扑排序的 Kahn 算法、DFS 算法
一、图的分类与表示方法
图是由顶点和边组成的一种数据结构,一组顶点V 表示顶点的集合,一组边E 表示边的集合。
图按照不同的特征可以分为以下类别:
有向图、无向图
有权图、 无权图
连通图、非连通图
图的表示
图在程序中表示一般有两种方式
-
邻接矩阵:在n个顶点的图中有一个n*n大小的矩阵。在一个无权图中, 矩阵坐标中每个位置值为1代表两个点是相连的, 0表示两个点是不相连的。在一个有权图中,矩阵坐标中每个位置值代表该两点之间的权重,0 表示该两点不相连。在无向图中, 邻接矩阵关于对角线相等
-
邻接表:对于每个点, 存储着一个链表,用来指向所有该店直接相连的点。对于有权图来说, 链表中元素值对应着权重。
通常采用邻接表表示法,因为用这种方法表示稀疏图(图中边数远小于点个数)比较紧凑。但当遇到稠密图(|E|接近于|V|^2)或必须很快判别两个给定顶点手否存在连接边时,通常采用邻接矩阵表示法,例如求最短路径算法中,就采用邻接矩阵表示。
邻接表表示法也有潜在的不足之处,即如果要确定图中边(u,v)是否存在,只能在顶点u邻接表Adj[u]中搜索v,除此之外没有其他更快的办法。这一不足可通过图的邻接矩阵表示法来弥补,但要(在渐进意义下)以占用更多的存储空间为代价。
邻接表和邻接矩阵的关系就像是链表和数组,在时间上和空间上分别占有优势。邻接表相当于链表,邻接矩阵相当于数组,其比较关系见前面的文字数据结构二 https://mp.csdn.net/postedit/89175155
二、图的广度优先搜索(BFS)和深度优先搜索(DFS)算法
和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图中所有顶点进行访问,且仅访问一次。
但是图的遍历相对树而言要更为复杂。因为图中的任意顶点都可能与其他顶点相邻,所以在图的遍历中必须记录已被访问的顶点,避免重复访问。
根据搜索路径的不同,我们可以将遍历图的方法分为两种:广度优先搜索和深度优先搜索。
1 广度优先搜索(BFS)
广度优先搜索依赖的是队列解决问题。队列中的每一个节点需要包含记录以下内容:该节点到起点的距离dist,该节点的前驱节点past,该节点在当前路径下是否被访问过visit(0表示没有访问过,1表示当前路径下正在访问,2表示该节点周围的所有节点都已经被访问过)。
步骤如下:
初始化:
起点值初始化(past=NULL,dist=0,visit=1)
其他节点值初始化(past=NULL,dist=无穷,visit=0)
起点入队
循环1:直到队列中没有元素
从队伍中输出一个节点作为当前节点
循环2:访问与当前节点连通但是【没有被访问过】的节点(visit=0的节点)
将即将访问的节点记为正在访问的状态
将即将访问的节点的状态更新(past=当前节点,dist=即将访问的节点到当前节点的距离,visit=1)
即将访问的节点入队
将当前节点的visit记为2(因为与它连接的所有节点都被访问过)
邻接矩阵存储表示
typedef struct AMGraph
{
char vexs[MVNum]; //顶点表
int arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum; //当前的顶点数和边数
}AMGraph;
/*找到顶点v的对应下标*/
int LocateVex(AMGraph &G, char v)
{
int i;
for (i = 0; i < G.vexnum; i++)
if (G.vexs[i] == v)
return i;
}
/*采用邻接矩阵表示法,创建无向图G*/
int CreateUDG_1(AMGraph &G)
{
int i, j, k;
char v1, v2;
cin>> &G.vexnum>> &G.arcnum; //输入总顶点数,总边数
for (i = 0; i < G.vexnum; i++)
cin>> &G.vexs[i]; //依次输入点的信息
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
G.arcs[i][j] = 0; //初始化邻接矩阵边,0表示顶点i和j之间无边
for (k = 0; k < G.arcnum; k++)
{
cin>> &v1>> &v2; //输入一条边依附的顶点
i = LocateVex(G, v1); //找到顶点i的下标
j = LocateVex(G, v2); //找到顶点j的下标
G.arcs[i][j] = G.arcs[j][i] = 1; //1表示顶点i和j之间有边,无向图不区分方向
}
return 1;
}
/*采用邻接矩阵表示图的广度优先遍历*/
void BFS_AM(AMGraph &G, char v0)
{
/*从v0元素开始访问图*/
int u, i, v, w;
v = LocateVex(G, v0); //找到v0对应的下标
printf("%c ", v0); //打印v0
visited[v] = 1; //顶点v0已被访问
q.push(v0); //将v0入队
while (!q.empty())
{
u = q.front(); //将队头元素u出队,开始访问u的所有邻接点
v = LocateVex(G, u); //得到顶点u的对应下标
q.pop(); //将顶点u出队
for (i = 0; i < G.vexnum; i++)
{
w = G.vexs[i];
if (G.arcs[v][i] && !visited[i])//顶点u和w间有边,且顶点w未被访问
{
printf("%c ", w); //打印顶点w
q.push(w); //将顶点w入队
visited[i] = 1; //顶点w已被访问
}
}
}
}
邻接表法
/*找到顶点对应的下标*/
int LocateVex(ALGraph &G, char v)
{
int i;
for (i = 0; i < G.vexnum; i++)
if (v == G.vertices[i].data)
return i;
}
/*邻接表存储表示*/
typedef struct ArcNode //边结点
{
int adjvex; //该边所指向的顶点的位置
ArcNode *nextarc; //指向下一条边的指针
int info; //和边相关的信息,如权值
}ArcNode;
typedef struct VexNode //表头结点
{