无向图G是一个彻底图。html
路径:链接图中两个顶点的边的序列,能够由多条边组成。java
无向图中的路径是双向的。ios
连通图:无向图中 任意两个顶点间都有路径。例如:git
彻底图必定是连通图,连通图不必定是彻底图。算法
其中,(b),(c)是(a)的子图。数组
有向图:顶点之间有序链接,边是顶点的有序对。网络
边(A,B)和(B,A)方向不一样。数据结构
有向图中的有序对经常使用序偶表示,例如:函数
上图中路径 V00→V22→V33 是从V00到V33的路径,可是反过来再也不成立。学习
上图中左图为联通图,右图不联通,由于从任何顶点到顶点1都没有路径。
有向树是一个有向图,其中指定一个元素为根,则具备下列特性:
任何顶点到根都没有链接。
到达每一个非根元素的链接都只有一个。
从根到每一个顶点都有路径。
网络能够是无向的也能够是有向的。
在深度优先遍历中,须要两个栈,这里能够看出深度优先遍历带有递归的性质。一个栈用来辅助遍历,即用来保存遍历过程当中里面的顶点,另外一个栈用来保存遍历的顺序。之因此另外须要一个栈来保存遍历的顺序的缘由 与 广度优先遍历 中须要用另外一个队列来保存 遍历顺序 的缘由相同。当深度优先遍历到某个顶点时,若该顶点的全部邻接点均已经被访问,则发生回溯,即返回去遍历 该顶点 的 前驱顶点 的 未被访问的某个邻接点。
图的深度优先遍历与广度优先遍历的惟一不一样是,他使用的是栈而不是队列来管理遍历。
一般构造最小生成树的算法有两种:
它是从点的方面考虑构建一颗MST,大体思想是:设图G顶点集合为U,首先任意选择图G中的一点做为起始点a,将该点加入集合V,再从集合U-V中找到另外一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;以此类推,如今的集合V={a,b},再从集合U-V中找到另外一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V,直至全部顶点所有被加入V,此时就构建出了一颗MST。由于有N个顶点,因此该MST就有N-1条边,每一次向集合V中加入一个点,就意味着找到一条MST的边。
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点当作是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。以后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不一样的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
相对应的邻接矩阵表示以下:
#include<iostream> #include<string> #define maxSize 10 using namespace std; //在此声明 不用template 模板 class Graph{ public: Graph(string str[],int vertex,int arc1); ~Graph(){ //因为没有动态申请内存 没法对改内存进行释放(在此处作声明) cout<<"析构函数调用成功!"; }; void DFS(int v); //深度优先遍历 void FdgDFS(int v); //非递归的深度优先遍历 void BFS(int v); //广度优先遍历 private: int verNum; int arcNum; string verName[maxSize]; //对于邻接矩阵构造无向图确定少不了邻接矩阵 即二维数组 int arc[maxSize][maxSize]; //遍历时,对于没有访问过的顶点,须要创建flag 标识 int visited[maxSize]={0}; }; Graph::Graph(string str[],int vertex,int arc1){ verNum=vertex; //顶点的赋值 for(int i=0;i<verNum;i++){ verName[i]=str[i]; } for(int i=0;i<verNum;i++) //初始化邻接矩阵 for(int j=0;j<verNum;j++) arc[i][j]=arc[j][i]=0; //对边进行构造 输入依附于边的邻接点的下标 arcNum=arc1; for(int k=0;k<arcNum;k++){ int i=0; int j=0; cout<<"请输入依附于边的邻接点下标 "<<endl; cin>>i>>j; //无向图的邻接矩阵是对称的 arc[i][j]=arc[j][i]=1; } } //递归的深度遍历 void Graph::DFS(int v){ cout<<verName[v]; visited[v]=1; for(int j=0;j<verNum;j++){ if(arc[v][j]==1 && visited[j]==0) DFS(j); } } //非递归的深度优先遍历 void Graph::FdgDFS(int v){ int Stack[maxSize]; int top=-1; cout<<verName[v]; visited[v]=1; Stack[++top]=v; while(top!=-1){ v=Stack[top]; for(int j=0;j<verNum;j++){ if(arc[v][j]==1 && visited[j]==0){ cout<<verName[j]; visited[j]=1; Stack[++top]=j; break; } if(j==verNum-1) top--; } } } //广度优先遍历 void Graph::BFS(int v){ //定义队列进行遍历 int Queue[maxSize]; int front=-1; int rear=-1; cout<<verName[v]; visited[v]=1; Queue[++rear]=v; while(front!=rear){ //出队 v=Queue[++front]; for(int j=0;j<verNum;j++) if(arc[v][j]==1 && visited[j]==0){ cout<<verName[j]; visited[j]=1; //入队 Queue[++rear]=j; } } } int main(){ string mystr[4]={"v0","v1","v2","v3"}; Graph myGraph(mystr,4,4); //深度优先遍历 myGraph.DFS(2); //非递归深度优先遍历 myGraph.FdgDFS(2); //广度优先遍历 myGraph.BFS(2); return 0; }
我看到一个大佬的博客解决了这个问题,并且我阅读以后也有了比较深入的理解
大佬的博客————图的深度优先遍历和广度优先遍历理解
这两道题改了选项,我当时没注意到群里面的,好气哦
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | |
---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 |
第一周 | 0/0 | 1/1 | 8/8 |
第二周 | 671/671 | 1/2 | 17/25 |
第三周 | 345/1016 | 1/3 | 15/40 |
第四周 | 405/1421 | 2/5 | 23/63 |
第五周 | 1202/2623 | 1/5 | 20/83 |
第六周 | 1741/4364 | 1/6 | 20/103 |
第七周 | 400/4764 | 1/7 | 20/123 |
第八周 | 521/5285 | 2/9 | 24/147 |
第九周 | 1622/6907 | 2/11 | 17/164 |