图
在数学中,图是描述于一组对象的结构,其中某些对象对在某种意义上是“相关的”。这些对象对应于称为顶点的数学抽象(也称为节点或点),并且每个相关的顶点对都称为边(也称为链接或线)。通常,图形以图解形式描绘为顶点的一组点或环,并通过边的线或曲线连接。 图形是离散数学的研究对象之一。 ------百度百科
6.1 基本术语
图:记为 G=( V, E )
其中:V 是G的顶点集合,是有穷非空集;
E 是G的边集合,是有穷集
6.2 存储结构
- 图的特点:非线性结构
- 图的顺序存储结构:无(多个定点,无序可言),但可以用数组描述元素间的关系。(设计为邻接矩阵)
- 链式存储结构:可以用多重链表(邻接表,邻接多重表、十字链表)
下面结论面介绍:图的数组表示法(邻接矩阵)和链式表示法(邻接表)
图的数组表示法(邻接矩阵为例)
直接给出存储结构代码
#define MAX_VERTEX_NUM 100 /*最大顶点数设为100*/ /*---图的邻接矩阵表示---*/ typedef struct { int vexs[MAX_VERTEX_NUM]; /*顶点向量*/ int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*邻接距阵*/ int vexnum,arcnum; /*顶点数和边数*/ int kind; /*---图的类型(1:有向图 0:无向图)---*/ }Mgraph;
解释
给出边的矩阵表示形式
记住以上规律,对于有向图和无向图的创建,有很大帮助
图的链式存储结构(邻接表为例)
/*-------------- 图的邻接表--------*/ #define MAX_VERTEX_NUM 100 typedef struct ArcNode{ int adjvex; //该弧所指向的顶点位置 struct ArcNode *nextarc; //指向下一条弧的指针 int weight; }ArcNode; //表结点 (狐) typedef struct VNode{ int data; //顶点信息 ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 }VNode; //顶点 typedef struct { Vnode adjlist [MAX_VERTEX_NUM]; /*邻接表*/ int vexnum,arcnum; //顶点数,边数 int kind; }ALGraph; //图的邻接表
解释
6.3 图的遍历
遍历定义:从已给的图中某一顶点出发,沿着一些边,访遍图中所有的顶点,且 使每个顶点仅被访问一次,就叫做图的遍历。
遍历实质:找每个顶点的邻接点的过程。
图的特点:图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。
解决思路:可设置一个辅助数组 visited [n ],用来标记每个被访问过的顶点。它的初始状态为0,在图的遍历过程中,一旦某一个顶点i 被访问,就立即改 visited [i]为1,防止它被多次访问。
图的常用遍历方式:1.深度优先搜索 2.广度优先搜索
图的遍历之深度优先遍历
深度优先遍历(Depth First Search),也有称为深度优先搜索,简称为DFS。
其原理如下:
- 在访问图中某一起始顶点 v 后,由 v 出发,访问它的任一邻接顶点 w1;
- 再从 w1 出发,访问与 w1邻接但还未被访问过的顶点 w2;
- 然后再从 w2 出发,进行类似的访问,…
- 如此进行下去,直至到达所有的邻接顶点都被访问过的顶点 u 为止。
- 接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它未被访问的邻接顶点。
- 如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;
- 如果没有,就再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止
https://ucc.alicdn.com/images/user-upload-01/img_convert/db2ae28f722c0721d79859729e63a32a.gif
https://ucc.alicdn.com/images/user-upload-01/img_convert/7b3d90a4765577fa0c03043291f4677b.gif
- 注:作图做的不好,最好慢理解文字吧
不难从文字中看出,这是一个递归思想。
给出代码(邻接矩阵):
void DFS( Mgraph g,int v,int visited[]) {/* 邻接矩阵存储,从顶点v出发,对图g进行深度优先搜索*/ int j; printf("%d ",g.vexs[v]); visited[v]=1; /*标识v被访问过*/ for(j=0;j<g.vexnum;j++); /* */ { if( g.arcs[v][j]==1&&visited[j]==0)/*j为v的邻接点,未被访问过*/ DFS(g,j,visited); /*从j出发递归调用DFS*/ }/*for*/ }/*DFS*/ void DFSTraverse(Mgraph g) { /*邻接矩阵 深度优先搜索*/ int v; int visited[MAX_VERTEX_NUM]; for(v=0;v<g.vexnum;v++) visited[v]=0; /*初始化visited数组*/ for(v=0;v<g.vexnum;v++) if(visited[v]==0) DFS(g,v,visited); /*从未被访问过的v出发,DFS搜索*/ }
给出代码(邻接表):
void DFS(ALGraph *g,int v,int visited[]) {/*从顶点v出发,对图g进行深度优先搜索*/ ArcNode *p; int w; printf("%d ",g->adjlist[v].data); visited[v]=1; /*标识v被访问过*/ p=g->adjlist[v].firstarc; /*p指向第v个单链表的头指针*/ while(p) { w=p->adjvex; /*w为v的邻接点*/ if(visited[w]==0) /*若w未被访问*/ DFS(g,w,visited); /*从w出发递归调用DFS*/ p=p->nextarc; /*找v的下一个邻接点*/ }/*while*/ }/*DFS*/ void DFSTraverse(ALGraph *g) { /*邻接表 深度优先搜索*/ int v; int visited[MAX_VERTEX_NUM]; for(v=0;v<g->vexnum;v++) visited[v]=0; /*初始化visited数组*/ for(v=0;v<g->vexnum;v++) if(visited[v]==0) DFS(g,v,visited); /*从未被访问过的v出发,DFS搜索*/ }
DFS算法的性能分析
DFS算法是一个递归算法,需要借助一个递归工作栈,故其空间复杂度为O ( V ) O(V)O(V)。
对于n个顶点e条边的图来说,邻接矩阵由于是二维数组,要查找每个顶点的邻接点需要访问矩阵中的所有元素,因此都需要O ( V 2 ) O(V^2)O(V 2 )的时间。而邻接表做存储结构时,找邻接点所需的时间取决于顶点和边的数量,所以是O ( V + E ) O(V+E)O(V+E)。 显然对于点多边少的稀疏图来说,邻接表结构使得算法在时间效率上大大提高。
对于有向图而言,由于它只是对通道存在可行或不可行,算法上没有变化,是完全可以通用的。
图的遍历之广度优先遍历
广度优先遍历(Breadth First Search),又称为广度优先搜索,简称BFS。
基本思想:——仿树的层次遍历过程。
遍历步骤:在访问了起始点v之后,依次访问 v的邻接点;然后再依次(顺序)访问这些点(下一层)中未被访问过的邻接点;直到所有顶点都被访问过为止。
广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有回退的情况。
因此,广度优先搜索不是一个递归的过程,其算法也不是递归的
给出代码(邻接矩阵):
void BFSTraverse(Mgraph *mgraph, int n) { int v,w,u; int visited[n]; int Q[n+1],r,f; //Q为队列, 其中r,f分别为队尾指针和对头指针 //初始化visited数组和队列 int i; for(i = 0;i<n;i++) { visited[i] = 0; } f = 0; r = 0; for(v = 0;v< mgraph->vexnum;v++) { if(!visited[v]) { visited[v] = 1;//默认从v = 0 开始访问(第一访问) printf("%d ",mgraph->vexs[v]);//打印 Q[r] = v;//入队列 r = (r+1)%(n+1); while(f<r) //队列非空时 { u = Q[f];f = (f+1)%(n+1);//出队并将出队数赋值给u for(w = 0;w<mgraph->vexnum;w++) { if(mgraph->arcs[u][w]==1 && visited[w] == 0) //遍历该顶点所有邻接点,找该顶点是否存在没有访问过的邻接点 {//找到并输出 和入队 visited[w] = 1; printf("%d ",mgraph->vexs[w]); Q[r] = w;r = (r+1)%(n+1); } } } } } }
给出代码(邻接表):
void BFSTraverse(ALGraph *g ,int n) { int v,w,u; int Q[n+1]; int visited[g->vexnum]; int r = 0 ,f = 0; ArcNode *p; for(int i = 0;i<n+1;i++) Q[i] = 0; for(v=0;v<g->vexnum;v++) visited[v]=0 ;//初始化visited数组 //队列Q初始化 for(v=0;v<g->vexnum ;v++) //从v出发广度优先搜索 { if (!visited[v]) { visited[v]=1; printf("%d ",v); //输出v Q[r] = v; r = (r+1)%(n+1); //v入队 while(f<r || f>r) //队列Q不空 { u = Q[f]; f = (f+1)%(n+1);// u出队 p=g->adjlist[u].firstarc ; while(p) //依次访问u的邻接点 { w=p->adjvex; if(!visited[w]) { visited[w]=1; printf("%d ",w); Q[r] = w; r = (r+1)%(n+1); //w 入队 } p=p->nextarc ; //p指向下一个邻接点 }//end of while }//end of while }//end of if }//end of for }//end of BFSTravers
附———图的创建以及遍历完整代码(邻接矩阵和邻接表)
邻接矩阵(带注释)
点击查看代码
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_VERTEX_NUM 100 /*最大顶点数设为100*/ /*---图的邻接矩阵表示---*/ typedef struct { int vexs[MAX_VERTEX_NUM]; /*顶点向量*/ int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*邻接距阵*/ int vexnum,arcnum; /*顶点数和边数*/ int kind; /*---图的类型(1:有向图 0:无向图)---*/ }Mgraph; bool InitMgraph(Mgraph *mgraph,int vexnum,int arcnum,int kind) { if(!mgraph) { return false; } /*---初始化边的一些基本信息---*/ mgraph->vexnum = vexnum; mgraph->arcnum = arcnum; mgraph->kind = kind; int i; int j; for(i=0;i<MAX_VERTEX_NUM;i++) { mgraph->vexs[i] = 0; for(j=0;j<MAX_VERTEX_NUM;j++) { mgraph->arcs[i][j] = 0; } } return true; } bool CreatMgraph(Mgraph *mgraph) { if(!mgraph) { return false; } int i,j; for(i=0;i<mgraph->vexnum;i++) { mgraph->vexs[i] = i; } int count = 0; while(count<mgraph->arcnum) { scanf("%d,%d",&i,&j); if(mgraph->kind==0) { mgraph->arcs[i][j] = 1; mgraph->arcs[j][i] = 1; } else mgraph->arcs[i][j] = 1; count++; } return true; } void printMgraph(Mgraph *mgraph) { printf("++++++++++++++++++++++++\n"); printf("+++++图的基本信息+++++++\n"); printf("+顶点数:%d \n",mgraph->vexnum); printf("+边数 :%d \n",mgraph->arcnum); printf("邻接矩阵:\n"); int i; int j; for(i = 0;i<mgraph->vexnum;i++) { for(j = 0;j<mgraph->vexnum;j++) { printf(" %d",mgraph->arcs[i][j]); } printf("\n"); } } void DFS(Mgraph *m,int i,int visited[]) { printf("%d",m->vexs[i]); visited[i] = 1; for(int j = 0;j<m->vexnum;j++) { if(m->arcs[i][j] == 1 && visited[j] == 0) { DFS(m,j,visited); } } } void DFST(Mgraph *m) { int visited[m->vexnum]; for(int i = 0;i<m->vexnum;i++) { visited[i] = 0; } for(int i = 0;i<m->vexnum;i++) { if(visited[i]==0) DFS(m,i,visited); } } void BFST(Mgraph *m) { int u,v,w; int visited[m->vexnum]; int Q[m->vexnum+1]; int r,f; r = 0; f = 0; for(int i = 0;i<m->vexnum;i++) { visited[i] = 0; } for(int i = 0;i<m->vexnum;i++) { if(!visited[i]) { printf("%d",m->vexs[i]); visited[i] = 1; Q[r] = i; r = (r+1)%(m->vexnum+1); while(r!=f) { u = Q[f];f = (f+1)%(m->vexnum+1); for(int j = 0;j<m->vexnum;j++) { if(m->arcs[u][j] == 1 && visited[j] == 0) { visited[j] = 1; printf("%d",m->vexs[j]); Q[r] = j; r = (r+1)%(m->vexnum+1); } } } } } } int main() { Mgraph *m1; m1 = (Mgraph*)malloc(sizeof(Mgraph)); int num,s,kind; scanf("%d",&kind); scanf("%d,%d",&num,&s); InitMgraph(m1,num,s,kind); CreatMgraph(m1); DFST(m1); BFST(m1); return 0; }
### 邻接表(带注释) 点击查看代码
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> /*-------------- 图的邻接表--------*/ #define MAX_VERTEX_NUM 100 typedef struct ArcNode{ int adjvex; //该弧所指向的顶点位置 struct ArcNode *nextarc; //指向下一条弧的指针 int weight; }ArcNode; //表结点 (狐) typedef struct VNode{ int data; //顶点信息 ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 }Vnode; //顶点 typedef struct { Vnode adjlist [MAX_VERTEX_NUM]; /*邻接表*/ int vexnum,arcnum; //顶点数,边数 int kind; }ALGraph; //图的邻接表 bool InitALGraph(ALGraph *algmraph,int vexnum,int arcnum,int kind) { if(!algmraph) { return false; } algmraph->vexnum = vexnum; algmraph->arcnum = arcnum; algmraph->kind = kind; for(int i = 0;i<algmraph->vexnum;i++) { algmraph->adjlist[i].firstarc = NULL; algmraph->adjlist[i].data = i; } return true; } bool CreatALGraph(ALGraph *algraph) { if(!algraph) { return false; } int count = 0; ArcNode *p; int i,j; while(count<algraph->arcnum) { scanf("%d,%d",&i,&j); p = (ArcNode *)malloc(sizeof(ArcNode)); p->adjvex = j; p->nextarc = algraph->adjlist[i].firstarc; algraph->adjlist[i].firstarc = p; if(algraph->kind==0) { p = (ArcNode *)malloc(sizeof(ArcNode)); p->adjvex = i; p->nextarc = algraph->adjlist[j].firstarc; algraph->adjlist[j].firstarc = p; } count++; } return true; } void printALGraph(ALGraph *algraph) { printf("++++++++++++++++++++++++++\n"); printf("+边数:%d 定点数:%d+\n",algraph->vexnum,algraph->arcnum); printf("邻接表:\n"); ArcNode *p; for(int i = 0;i<algraph->vexnum;i++) { p = algraph->adjlist[i].firstarc; while(p!=NULL) { printf("(%d,%d) ",i,p->adjvex); p = p->nextarc; } printf("\n"); } } void DFS(ALGraph *m,int i, int visited[]) { ArcNode *p; printf("%d",m->adjlist[i].data); p = m->adjlist[i].firstarc; visited[i] = 1; while(p) { if(visited[p->adjvex] == 0) { DFS(m,p->adjvex,visited); } p = p->nextarc; } } void DFST(ALGraph *m) { int visited[m->vexnum]; for(int i = 0;i<m->vexnum;i++) visited[i] = 0; for(int i = 0;i<m->vexnum;i++) { if(!visited[i]) { DFS(m,i,visited); } } } void BFST(ALGraph *m) { int Q[m->vexnum+1]; int r,f; r = 0;f = 0; int w; ArcNode *p; int visited[m->vexnum]; for(int i = 0;i<m->vexnum;i++) visited[i] = 0,Q[i] = 0; for(int i = 0;i<m->vexnum;i++) { if(!visited[i]) { printf("%d",i); Q[r] = i;r = (r+1)%(m->vexnum+1); visited[i] = 1; while(r!=f) { w = Q[f];f = (f+1)%(m->vexnum+1); p = m->adjlist[w].firstarc; while(p) { if(!visited[p->adjvex]) { printf("%d",m->adjlist[p->adjvex].data); Q[r] = p->adjvex;r = (r+1)%(m->vexnum+1); visited[p->adjvex] = 1; } p = p->nextarc; } } } } } int main() { ALGraph *al; al = (ALGraph*)malloc(sizeof(ALGraph)); int kind; scanf("%d",&kind); int i,j; scanf("%d,%d",&i,&j); InitALGraph(al,i,j,kind); CreatALGraph(al); DFST(al); BFST(al); printALGraph(al); return 0; }