图
定义
多对多的关系。
无向图:每条边都没有方向
有向图:每条边都有方向
完全图:任意两个点都有一条边相连(无向完全图:n个顶点,有n(n-1)/2条边;有向完全图:n个顶点,有n(n-1)条边)
稀疏图:有很少边(无向图)或弧(有向图)的图(e<nlogn)
邻接:有边/弧相连的两个顶点之间的关系。
存在(vi , vj),则称vi和vj互为邻接点
存在<vi , vj>,则称vi邻接到, vj邻接于vi
顶点的度:与该顶点相关联的边的数目。
在有向图中,顶点的度等于该顶点的入度与出度之和。
顶点v的入度是以v为终点的有向边的条数。
顶点v的出度是以v为始点的有向边的条数。
连通图(强连通图:有向图):从无(有)向图G=(V , {E})中,若对任何两个顶点v、u都存在从v到u的路径,则称G为连通图(强连通图)
在有向图的邻接矩阵中,
第i行含义:以结点vi为尾的弧(即出度边);
第i列含义:以结点vi为头的弧(即入度边)。
代码:
#define MVNum 100 //最大顶点数
typedef struct{ //图的结构体定义
char vexs[MVNum]; //存放顶点的一维数组
int arcs[MVNum][MVNum]; //邻接矩阵
int vexnum,arcnum; //图的顶点数和边数
}MGraph;
void CreatMGraph(MGraph *G){
scanf("%d %d",&G->vexnum,&G->arcnum); // 输入顶点数和边数
getchar();
for(int i = 0;i<G->vexnum;i++){
scanf("%c",&G->vexs[i]);
}
for(int i = 0;i<G->vexnum;i++){
for(int j = 0;j<G->vexnum;j++){
G->arcs[i][j] = 0; // 初始化矩阵为0
}
}
getchar();
for(int i = 0;i<G->arcnum;i++){ // 构造邻接矩阵
char v1,v2;
scanf("%c %c",&v1,&v2);
getchar();
int x = locate(G,v1);
int y = locate(G,v2);
G->arcs[x][y] = 1; // 有路径则矩阵为1
G->arcs[y][x] = 1; // 无向图具有对称性,路径具有互通性(有向图则去除这行代码)
}
}
int locate(MGraph *G,char v){ // 查找图中顶点v,存在则返回下标
for(int i = 0;i<G->vexnum;i++){
if(v == G->vexs[i]){
return i;
}
}
return -1; // 否则返回-1
}
无向图的邻接表如此实现,其特点为:
- 邻接表不唯一
- 若无向图中有n个结点、e条边,则其邻接表需要n个头结点和2e个表结点,适宜存储稀疏图。
- 无向图中顶点vi的度为第i个单链表中的节点数。
代码:
#define MVNum 100 //最大顶点数
typedef struct ArcNode{ //表结点
int adjvex; //邻接点的位置
struct ArcNode *nextarc; //指向下一个表结点的指针
}ArcNode;
typedef struct VNode{
char data; //顶点信息
ArcNode *firstarc; //指向第一个表结点的指针
}VNode, AdjList[MVNum]; //AdjList表示邻接表类型
typedef struct{
AdjList vertices; //头结点数组
int vexnum, arcnum; //图的顶点数和边数
}ALGraph;
void CreatMGraph(ALGraph &G){ // 使用邻接表创建无向图
int x, y;
char v1, v2;
cin >> G.vexnum >> G.arcnum;
for(int i=0; i<G.vexnum; i++){
cin >> G.vertices[i].data;
G.vertices[i].firstarc=NULL;//初始化
}
for(int i=0; i<G.arcnum; i++){
cin >> v1 >> v2;
int x = locate(G,v1);
int y = locate(G,v2);
//头插法
ArcNode* p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = y;
p->nextarc = G.vertices[x].firstarc;
G.vertices[x].firstarc = p;
p = (ArcNode*)malloc(sizeof(ArcNode)); // 无向图具有对称性
p->adjvex = x;
p->nextarc = G.vertices[y].firstarc;
G.vertices[y].firstarc = p;
}
}
int locate(ALGraph G,char v){ // 获取v头结点的下标
for(int i = 0;i<G.vexnum;i++){
if(G.vertices[i].data==v){
return i;
}
}
return -1;
}
图的遍历主要分为两种:广度优先搜索(DFS)、深度优先搜索(BFS)
深度优先搜索
遍历方法:
1.访问图中某一个起始顶点v后,从v出发,访问其任一邻接顶点w1。
2.再从w1出发,访问与w1邻接但未被访问过的顶点w2。
3.然后再从w2出发,重复第二步直至到达所有的邻接顶点都被访问过的顶点u为止。
4.从u顶点回退一步至刚访问过的顶点,看是否还有其它未被访问的邻接节点。
5.如果有,则访问此顶点,重复2、3步。
6.如果没有,则重复第4步。
7.重复5、6步直至连通图中所有顶点都被访问过为止。
代码:
#define MVNum 100 //最大顶点数
typedef struct{ //图的结构体定义
char vexs[MVNum]; //存放顶点的一维数组
int arcs[MVNum][MVNum]; //邻接矩阵
int vexnum,arcnum; //图的顶点数和边数
}MGraph;
void DFS(MGraph G,int v){ // 访问第v个顶点
cout << v;
visited[v] = true;
for(int i = 0;i<G.vexnum;i++){
if(G.arcs[v][i]!=0 && (!visited[i])){
dfs(G,i); // i是v的邻接点,如果i未被访问,则递归调用dfs
}
}
}
广度优先搜索
遍历方法:
- 从图的某一个节点开始,首先依次访问该节点的所有邻接顶点vi1、vi2、vi3,… ,vin。
- 再按这些顶点被访问的先后次序依次访问它们想邻接的所有未被访问的顶点。
- 重复上述步骤直至所有顶点均被访问为止。
代码:
#define MVNum 100 //最大顶点数
typedef struct{ //图的结构体定义
char vexs[MVNum]; //存放顶点的一维数组
int arcs[MVNum][MVNum]; //邻接矩阵
int vexnum,arcnum; //图的顶点数和边数
}Graph;
void BFS(Graph G,int v){ // 按照广度优先非递归遍历连通图G
cout << v;
visited[v] = true; // 访问第v个顶点
InitQueue(Q); // 使用队列辅助遍历,初始化队列Q
EnQueue(Q,v); // v进队
while(!QueueEmpty(Q)){ // 队列非空
DeQueue(Q,u); // 队头元素出队并赋值给u
for(w = FirstAdjVex(G,u);w>=0;w = NextAdjVex(G,u,w)) // 依次找弧
if(!visited[w]){ // w为u的尚未访问过的邻接顶点
cout << w;
visited[w] = true;
EnQueue(Q,w); // w进队
}
}
}