首先,我们先看一下什么是图,图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E),G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
顶点(Vertex):在图中的数据元素。 边:图中,任意两顶点之间都可能有关系,顶点之间的逻辑关系用边来表示。 无向边(Edge):若顶点Vi到Vj之间的边没有方向,则称这条边为无向边,用无序偶对(Vi,Vj)来表示。 无向边:若从顶点Vi到Vj的边有方向,则称这条边为有向边,也称为弧(Arc)。记作<Vi,Vj>,比如连接顶点A到D的有向边就是弧,A是弧尾,D是弧头,<A,D>表示弧,因为他是有方向的,所以不能写成<D,A>。 无向完全图:在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。 有向完全图:在有向图中,如果任意两个顶点之间都存在方向上互为相反的两条弧,则称该图为有向完全图。 有很少条边或弧的图称为稀疏图,反之称为稠密图。 权(Weight):与图的边或弧相关的数叫做权。 网(Network):带权的图通常称为网。 连通图:在无向图中,如果从一个顶点到另外一个顶点有路径,则称两个顶点之间是连通的。如果对于图中任意两个顶点之间哦都是连通的,则称图为连通图。
接下来就以无向图以及邻接矩阵为例,进行图的相关代码的实现。
这里我们的MGraph是一个全局变量,所以在后面的函数中就省略了形参里面G的定义。
由于这些操作比较简单,这里就直接给出代码。
void DestoryGraph(MGraph g) //销毁图,若有图g,则销毁。 { bool flag=false; for(int i=0;i<g.Vertexes;i++) for(int j=0;j<g.Vertexes;j++) if(g.arc[i][j]!=Infinity) { g.arc[i][j]=Infinity; flag=true; } if(flag) //如果邻接矩阵有边,则说明这是一个图,就需要把他的顶点全部删除。如果邻接矩阵没有边,则不需要删除顶点。 for(int i=0;i<g.Vertexes;i++) g.Vexs[i]=0; } int LocateVex(char u) //若图中存在顶点u,则返回图中的位置 { bool flag=false; int i; for(i=0;i<g.Vertexes;i++) if(g.Vexs[i]==u) { flag=true; break; } if(flag) return i; else return 0 ; } char GetVex(int v) //返回图中顶点v的值 { return g.Vexs[v]; } void PutVex(int v,char value) //将图中顶点v赋值value { g.Vexs[v]=value; } char FirstAdjVex(char v) //返回顶点v的一个邻接顶点,若顶点在图中无邻接顶点,返回空 { int i,j; bool flag=false; for(i=0;i<g.Vertexes;i++) if(g.Vexs[i]==v) break; for(j=0;j<g.Vertexes;j++) if(g.arc[i][j]!=Infinity) { flag=true; break; } if(flag) return g.Vexs[j]; else return 0; } char NextAdjVex(char v,char w) //返回顶点v相对于顶点w的下一个邻接顶点,若w是v的最有一个邻接点,则返回“空”。 { int j,i,k; bool flag=false; for(i=0;i<g.Vertexes;i++) if(g.Vexs[i]==v) break; for(j=0;j<g.Vertexes;j++) if(g.Vexs[j]==w) break; for(k=0;k<g.Vertexes;k++) if(g.arc[i][k]!=Infinity&&k!=j) { return k; break; } } void InsertVex(char v) //在图中增加新顶点v { g.Vexs[g.Vertexes]=v; for(int i=0;i<g.Vertexes;i++) g.arc[g.Vertexes][i]=g.arc[i][g.Vertexes]=Infinity; g.Vertexes++; } void DeleteVex(char v) //删除图中顶点v以及相关的弧 { int i; for(i=0;i<g.Vertexes;i++) if(g.Vexs[i]==v) break; for(int j=0;j<g.Vertexes;j++) g.arc[j][i]=g.arc[i][j]=Infinity; for(int j=i;j<g.Vertexes-1;j++) g.Vexs[j]=g.Vexs[j+1]; g.Vertexes--; } void InsertArc(char v,char w) //在图中增加弧<v,w>,若图为无向图,还需要增添对称弧<w,v> { int i,j; for(i=0;i<g.Vertexes;i++) if(g.Vexs[i]==v) break; for(j=0;j<g.Vertexes;j++) if(g.Vexs[j]==w) break; g.arc[i][j]=g.arc[j][i]=1; } void DeleteArc(char v,char w) //在图中删除弧<v,w>,若图为无向图,还要删除对称弧<w,v> { int i,j; for(i=0;i<g.Vertexes;i++) if(g.Vexs[i]==v) break; for(j=0;j<g.Vertexes;j++) if(g.Vexs[j]==w) break; g.arc[i][j]=g.arc[j][i]=Infinity; }要对图进行深度优先搜索,首先要先知道,深度优先搜索是什么,那深度优先搜素是什么呢? 如图所示,假设你需要完成一个任务假设,要求你在如图这样的一个迷宫中,从顶点A开始要走遍所有的图顶点并作上标记,注意不是简单地看着这样的平面图走哦,而是如同现实般地在只有高墙和通道的迷宫中去完成任务。 很显然我们是需要策略的,否则在这四通八达的通道中乱窜,要想完成任务那就只能是碰运气。如果你学过深度优先遍历,这个任务就不难完成了。 首先我们从顶点A开始,做上表示走过的记号后,面前有两条路,通向B和F,我们给自己定一个原则,在没有碰到重复顶点的情况下,始终是向右手边走,于是走到了B顶点。整个行路过程,可参看右图。此时发现有三条分支,分别通向顶点C、I、G,右手通行原则,使得我们走到了C顶点。就这样,我们一直顺着右手通道走,一直走到F顶点。当我们依然选择右手通道走过去后,发现走回到顶点A了,因为在这里做了记号表示已经走过。此时我们退回到顶点F,走向从右数的第二条通道,到了G顶点,它有三条通道,发现B和D都已经是走过的,于是走到H,当我们面对通向H的两条通道D和E时,会发现都已经走过了。 此时我们是否已经遍历了所有顶点呢?没有。可能还有很多分支的顶点我们没有走到,所以我们按原路返回。在顶点H处,再无通道没走过,返回到G,也无未走过通道,返回到F,没有通道,返回到E,有一条通道通往H的通道,验证后也是走过的,再返回到顶点D,此时还有三条道未走过,一条条来,H走过了,G走过了,I,哦,这是一个新顶点,没有标记,赶快记下来。继续返回,直到返回顶点A,确认你已经完成遍历任务,找到了所有的9个顶点。 反应快的同学一定会感觉到,深度优先遍历其实就是一个递归的过程,如果再敏感一些,会发现其实转换成如右图后,就像是一棵树的前序遍历,没错,它就是。它从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。事实上,我们这里讲到的是连通图,对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后,若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
void DFS(int i) { isvisited[i]=true; cout<<g.Vexs[i]<<endl; for(int j=0;j<g.Vertexes;j++) if(g.arc[i][j]!=Infinity&&!isvisited[j]) DFS(j); } void DFSTraverse() //对图中进行深度优先遍历,在遍历过程中对每个顶点调用 { cout<<"DFS:"<<endl; for(int i=0;i<g.Vertexes;i++) isvisited[i]=false; for(int i=0;i<g.Vertexes;i++) if(!isvisited[i]) DFS(i); }那什么又是广度优先遍历呢? 如果说图的深度优先遍历类似树的前序遍历,那么图的广度优先遍历就类似于树的层序遍历了。我们将图7-5-3的第一幅图稍微变形,变形原则是顶点A放置在最上第一层,让与它有边的顶点B、F为第二层,再让与B和F有边的顶点C、I、G、E为第三层,再将这四个顶点有边的D、H放在第四层,如第二幅图所示。此时在视觉上感觉图的形状发生了变化,其实顶点和边的关系还是完全相同的。 那么话不多说,已经理解了原理以后我们就可以将代码写出来。
void BFSTraverse() //对图中进行广度优先遍历,在遍历过程中对每个顶点调用 { queue<int>q; bool visited[g.Vertexes]; cout<<endl<<endl<<"BFS:"<<endl; for(int i=0;i<g.Vertexes;i++) visited[i]=false; for(int i=0;i<g.Vertexes;i++) { if(!visited[i]) { visited[i]=true; cout<<g.Vexs[i]<<endl; q.push(i); while(q.empty()==false) { i=q.front(); q.pop(); for(int j=0;j<g.Vertexes;j++) { if(g.arc[i][j]!=Infinity&&!visited[j]) { visited[j]=true; cout<<g.Vexs[j]<<endl; q.push(j); } } } } } }以上是我们关于无向图的内容的整理,其中也用到了数学上的邻接矩阵。 还有下面的一组数据,用做测试。 9 15 ABCDEFGHI 0 1 1 1 2 1 2 3 1 3 4 1 4 5 1 5 8 1 8 7 1 7 3 1 7 4 1 3 6 1 2 6 1 1 6 1 0 5 1 3 8 1 1 8 1
由于是第一次写博客,多有缺陷以及不足,请多多包含。希望各位大佬给我这个菜菜的码农一点包容,有什么问题也请大家指正。
