图的邻接表表示
邻接表
-
G[N]为指针数组,对应矩阵每行一个链表,只存非0元素。
-
邻接表的优点
- 方便找任一顶点的所有“邻接点”
- 节约稀疏图的空间
- 需要N个头指针+ 2E个结点(每个结点至少2个域)
- 方便计算任一顶点的“度”?
- 对无向图:是的
- 对有向图:只能计算“出度”;需要构造“逆邻接表”(存指向自己的边)来方便计算“入度” -
邻接表的缺点
- 不方便检查任意一对顶点间是否存在边
BFS广度优先搜索(Breadth First Search, BFS)
-
运用队列,将顶点V的每个邻接点进队。(类似于树的层先遍历)
-
若有N个顶点、E条边,时间复杂度是
- 用邻接表存储图,有O(N+E)
- 用邻接矩阵存储图,有O(N^2)
DFS深度优先搜索索(Depth First Search, DFS)
-
用递归(类似于树的先序遍历)。
-
ListComponents 图不连通时,列出各连通分量。
-
若有N个顶点、E条边,时间复杂度是
- 用邻接表存储图,有O(N+E)
- 用邻接矩阵存储图,有O(N^2)
测试代码:
/*!
* \file 图的邻接表表示.cpp
*
* \author ranjiewen
* \date 2017/04/12 8:57
*
*
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
using namespace std;
/* 图的邻接表表示法 */
#define MaxVertexNum 100 /*最大顶点数设为100*/
#define INFINITY 65535 /*设为双字节无符号整数的最大值为65535*/
typedef int Vertex; /*用顶点下标表示顶点,为整型*/
typedef int WeightType; /*边的权值设为整型*/
typedef char DataType; /*顶点存储的数据类型设为字符型*/
/*边的定义*/
typedef struct ENode* PtrToENode;
struct ENode
{
Vertex V1, V2; //有向边<v1,v2>
WeightType Weight;//权重
};
typedef PtrToENode Edge;
/*邻接点的定义*/
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode
{
Vertex AdjV; //邻接点下标
WeightType Weight; //边权重
PtrToAdjVNode Next; //指向下一个邻接点的指针
};
/*顶点表头结点的定义*/
typedef struct VNode
{
PtrToAdjVNode FirstEdge; //边表头指针
DataType Data; //存顶点的数据
//注意:很多时候,顶点无数据,此时Data可以不出现
}AdjList[MaxVertexNum];
/*图结点的定义*/
typedef struct GNode *PtrToGNode;
struct GNode
{
int Nv; //顶点树
int Ne; //边数
AdjList G; //邻接表
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */
LGraph CreateGraph(int VertexNum)
{
/*初始化一个有VertexNum个顶点但没有边的图*/
Vertex V, W; /*顶点的下标*/
LGraph Graph;
Graph = (LGraph)malloc(sizeof(struct GNode)); /*建立图*/
Graph->Nv = VertexNum;
Graph->Ne = 0;
//初始化邻接表
//注意:这里默认顶点编号从0开始到(Graph->Nv - 1)
for (V = 0; V < Graph->Nv;V++)
{
Graph->G[V].FirstEdge = NULL;
}
return Graph;
}
void InsertEdge(LGraph Graph,Edge E)
{
PtrToAdjVNode NewNode;
//插入边<v1,v2>
//为V2建立新的邻接点
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V2;
NewNode->Weight = E->Weight;
//将v2插入v1的表头
NewNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = NewNode;
//若是无向图,还有插入边<v2,v1>
//为v1建立新的邻接点
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V1;
NewNode->Weight = E->Weight;
//将v1插入v2的表头
NewNode->Next = Graph->G[E->V2].FirstEdge;
Graph->G[E->V2].FirstEdge = NewNode;
}
LGraph BuildGraph()
{
LGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf("%d", &Nv); /*读入顶点个数*/
Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */
scanf("%d", &(Graph->Ne)); /*读入边数*/
if (Graph->Ne!=0) //如果有边
{
E = (Edge)malloc(sizeof(struct ENode)); //建立边结点
//读入边,格式为:起点,中点,权重;插入邻接
for (i = 0; i < Graph->Ne;i++)
{
scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
//注意:如果权重不是整型,weight的读入格式要改变
InsertEdge(Graph, E);
}
}
//如果顶点有数据的话,读入数据
for (V = 0; V < Graph->Nv;V++)
{
//scanf("%c", &(Graph->G[V].Data));
}
return Graph;
}
/* 邻接矩阵存储的图 - BFS */
bool Visited[MaxVertexNum] = { false };
void InitVisited()
{
for (int i = 0; i < MaxVertexNum;i++)
{
Visited[i] = false;
}
}
void Visit(Vertex v)
{
printf("%d ", v);
}
//连通下的DFS和BFS
void DFS(LGraph Graph, Vertex V, void(*Visit)(Vertex))
{
/*以V为出发点对邻接表存储的图Graph进行DFS搜索*/
PtrToAdjVNode W;
Visit(V); //访问第V个顶点
Visited[V] = true;
for (W = Graph->G[V].FirstEdge; W;W=W->Next) //对V的每个邻接点访问
{
if (!Visited[W->AdjV])
{
DFS(Graph, W->AdjV, Visit);
}
}
}
//非连通下的遍历
Vertex listDFS(LGraph Graph, void(*Visit)(Vertex))
{
Vertex i;
for (i = 0; i < Graph->Nv; i++) {
if (Visited[i] == false)
break;
}
if (i == Graph->Nv)
return 0;
DFS(Graph, i, Visit);
printf("\n");
return listDFS(Graph, Visit);
}
//图不连通时 列出各连通分量
void DFSListComponents(LGraph Graph, void(*Visit)(Vertex))
{
for (Vertex i = 0; i < Graph->Nv; i++) {
if (Visited[i] == false) {
DFS(Graph, i, Visit);
printf("\n");
}
}
}
void BFS(LGraph Graph, Vertex S, void(*Visit)(Vertex))
{
/* 以S为出发点对邻接表存储的图Graph进行BFS搜索 */
queue<Vertex> Q;
Vertex V;
Visit(S);
Visited[S] = true;
Q.push(S);
while (!Q.empty()) {
V = Q.front();
Q.pop();
for (PtrToAdjVNode tempV = Graph->G[V].FirstEdge; tempV;tempV=tempV->Next) /* 对W的每个邻接点tempV->AdjV */
/* 若W是V的邻接点并且未访问过 */
if (!Visited[tempV->AdjV] )
{
/* 访问顶点W */
Visit(tempV->AdjV);
Visited[tempV->AdjV] = true;
Q.push(tempV->AdjV);
}
}
}
void BFSListComponents(LGraph Graph, void(*Visit)(Vertex))
{
for (Vertex i = 0; i < Graph->Nv; i++) {
if (Visited[i] == false) {
BFS(Graph, i, Visit);
printf("\n");
}
}
}
int main()
{
LGraph graph;
graph = BuildGraph();
InitVisited();
listDFS(graph, &Visit);
InitVisited();
DFSListComponents(graph, &Visit);
InitVisited();
//BFS(graph,0,&Visit);
BFSListComponents(graph, &Visit);
return 0;
}
结果:
Reference
C/C++基本语法学习
STL
C++ primer