数据结构 | 您所在的位置:网站首页 › vis数组 › 数据结构 |
文章目录
定义基本术语图的存储邻接矩阵法结构体创建图打印邻接矩阵
邻接表法结构体建立邻接表打印邻接表
图的遍历DFS方法代码
BFS方法代码
最小生成树普利姆算法思想图示代码
克鲁斯卡尔算法思想图示代码
定义
图G是由一个非空的顶点集合V和一个描述顶点之间的关系即边集E的一种数据结构,记为G = (V,E) 1、图G不可以为空,E可以为空,但V一定是非空集 2、图分为无向图和有向图 ① 无向图:若E为无向边的有限集合,则G为无向图。 边是顶点的无序对,记为 ( v 1 , v 2 ) (v_1,v_2) (v1,v2)或 ( v 2 , v 1 ) (v_2,v_1) (v2,v1),且 ( v 1 , v 2 ) = ( v 2 , v 1 ) (v_1,v_2) = (v_2,v_1) (v1,v2)=(v2,v1),v1和v2互为邻接点 G可以表示为: G = (V,E) V = {A,B,C,D,E} E = {(A,B),(B,D),(B,E),(C,D),(C,E)} 如图: ② 有向图:若E为有向边(也称弧)的有限集合,则G为有向图。 弧是顶点的有序对,记为 < v i , v j > ,其中 v i v_i vi是弧尾, v j v_j vj是弧头 G可以表示为: G = (V,E) V = {A,B,C,D,E} E = {,,,,,,,} 如图: 基本术语1、顶点的度、入度、出度 ① 无向图:顶点的度是指该顶点拥有的边数,记为TD(v) 如上图中:TD(A) = 1,TD(B) = 3,TD© = 2,TD(D) = 2,TD(E) = 2 对于无向图而言,有: ∑ i = 1 n T D ( v i ) = 2 ∣ E ∣ \displaystyle \sum^{n}_{i=1}{TD(v_i)} = 2|E| i=1∑nTD(vi)=2∣E∣ ② 有向图:入度是以该顶点为终点的有向边的数目,记为ID(v),出度是以该顶点为起点的有向边的数目,记为OD(v) 顶点v的度等于入度和出度之和,即TD(v) = ID(v) + OD(v) 如上图中: ID(A) = 1,OD(A) = 4,TD(A) = 5 ID(B) = 1,OD(B) = 3,TD(B) = 4 ID© = 2,OD© = 1,TD© = 3 ID(D) = 2,OD(D) = 0,TD(D) = 2 ID(E) = 2,OD(E) = 0,TD(E) = 2 对于有向图而言,有: ∑ i = 1 n I D ( v i ) = ∑ i = 1 n O D ( v i ) = ∣ E ∣ \displaystyle \sum^{n}_{i=1}{ID(v_i)}=\displaystyle \sum^{n}_{i=1}{OD(v_i)} = |E| i=1∑nID(vi)=i=1∑nOD(vi)=∣E∣ 2、路径 顶点 v i v_i vi到 v j v_j vj之间的一条路径是指顶点序列, v i , v 1 , v 2 , v 3 . . . v j v_i,v_1,v_2,v_3...v_j vi,v1,v2,v3...vj 3、回路 第一个顶点和最后一个顶点相同的路径称为回路或环 4、简单路径 在路径序列中,顶点不重复出现的路径称为简单路径 5、简单回路 除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路 6、路径长度 路径上边的数目 7、连通图 无向图中,若从顶点 v i v_i vi到顶点 v j v_j vj有路径存在,则称 v i v_i vi和 v j v_j vj是连通的,任意两个顶点都是连通的图称为连通图 对于n个顶点的无向图G: 若G为连通图,则最少有n-1条边 若G为非连通图,则最多可能有 C n − 1 2 C_{n-1}^2 Cn−12条边 8、强连通图 有向图中,若从顶点 v i v_i vi到顶点 v j v_j vj和从顶点 v j v_j vj到顶点 v i v_i vi都有路径存在,则称 v i v_i vi和 v j v_j vj是强连通的,任意两个顶点都是强连通的图称为强连通图 对于n个顶点有向图G: 若G为强连通图,则最少有n条边(形成回路) 9、子图 设有两个图G1 = (V1,E1)和G2 = (V2,E2),若V2是V1的子集,且E2是E1的子集,则称G2是G1的子图 若G2中包含G1的所有顶点(可以不包含所有的边),则称G2为G1的生成子图 10、连通分量 无向图中的极大连通子图称为连通分量 如图: 11、强连通分量 有向图中的极大强连通子图称为有向图的强连通分量 如图: 12、生成树 连通图的生成树是包含图中全部顶点的一个极小连通子图 如图: 生成树是不唯一的 若顶点数为n,则生成树含有n-1条边 对于生成树,去掉一条边,则会变成非连通图,加上一条边则会形成一个回路 13、权 边的权:在一个图中,每条边都可以标上具有某种含义的数值,称为该边的权值 带权图/网:边上带有权值的图称为带权图或者网 带权路径长度:当图是带权图时,一条路径上所有边的权值之和,称为该路径的带权路径长度 14、特殊图 无向完全图:无向图中任意两个顶点之间都存在边 有向完全图:有向图中任意两个顶点之间都存在方向相反的两条弧 若无向图顶点数 ∣ V ∣ = n |V| = n ∣V∣=n,则 ∣ E ∣ ∈ [ 0 , C n 2 ] = [ 0 , n ( n − 1 ) 2 ] |E|\in[0,C^2_n]=[0,\frac{n(n-1)}{2}] ∣E∣∈[0,Cn2]=[0,2n(n−1)] 若有向图顶点数 ∣ V ∣ = n |V| = n ∣V∣=n,则 ∣ E ∣ ∈ [ 0 , 2 C n 2 ] = [ 0 , n ( n − 1 ) ] |E|\in[0,2C^2_n]=[0,n(n-1)] ∣E∣∈[0,2Cn2]=[0,n(n−1)] 图的存储 邻接矩阵法邻接矩阵法是表示顶点之间相邻关系的矩阵 如下图: 结构体 #define MaxVertexNum 100 struct Graph { char vex[MaxVertexNum]; //顶点表 int edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表 int vexNum, edgeNum; //顶点数、边数 }; 创建图 void CreateGraph(Graph* G) { int i, j, k; char ch1, ch2; cout G->vexNum >> G->edgeNum; cout for (j = 0; j vexNum; j++) { G->edge[i][j] = 0; } } for (k = 0; k edgeNum; k++) { getchar(); cout if (ch1 == G->vex[i] && ch2 == G->vex[j]) { G->edge[i][j] = 1; G->edge[j][i] = 1; } } } } } 打印邻接矩阵 void PrintGraph(Graph *G) { int i, j; cout cout //定义顶点表结点 char data; //顶点域 EdgeNode* firstEdge; //指向第一条边结点 }; struct AdjList { VexNode adjList[Max]; //邻接表头结点数组 int vexNum, edgeNum; //顶点数,边数 }; 建立邻接表 void CreateGraph(AdjList* G, int flag) { int i, j, k; EdgeNode* p; if (flag) { cout // getchar(); cin >> G->adjList[i].data; G->adjList[i].firstEdge = NULL; //点的边表头指针设为NULL } cout p = new EdgeNode; p->adjVex = i; //邻接点序号为i p->next = G->adjList[j].firstEdge; //将新结点p插入到顶点vi边表头 G->adjList[j].firstEdge = p; } } } 打印邻接表 void PrintGraph(AdjList* G) { int i; EdgeNode* p; cout cout if (vis[p->adjVex] == 0) { DFS(G, p->adjVex); } p = p->next; } } BFS图的广度优先搜索(Breadth-First-Search)类似于树的层序遍历 方法1、假设从图中某顶点 v 出发,在访问了 v 之后依次访问 v 的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使 “先被访问的顶点的邻接点” 先于“后被访问的顶点的邻接点” 被访问,直至图中所有已被访问的顶点的邻接点都被访问到 2、若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上 述过程,直至图中所有顶点都被访问到为止3、换句话说,广度优先搜索遍历图的过程中以 v 为起始点,由近至远依次访问和 v 有路径相通且路径长度为 1,2,… 的顶点, 广度优先遍历也需要用到标志数组 vis[N] 以避免重复访问,还需要用到队列来存放已经访问过的各相邻顶点 同一个图的邻接矩阵表示方式唯一,BFS遍历序列唯一 同一个图的邻接表表示方式不唯一,BFS遍历序列不唯一 代码 //从顶点vex开始遍历 void BFS(AdjList* G, int vex) { int i, v; int q[Max], front = 0, rear = 0; //定义循环队列 EdgeNode* p; for (i = 0; i vexNum; i++) { vis[i] = 0; } cout if (vis[p->adjVex] == 0) { //未访问过 vis[p->adjVex] = 1; //访问数组该元素置1,变成已访问 cout for (j = 0; j cout v1 >> v2 >> w; cost[v1][v2] = w; //将(v1,v2)设权值为w cost[v2][v1] = w; //将(v1,v2)设权值为w } return vexNum; //返回顶点数 } void Prim(int c[MAX][MAX], int n) { int i, j, k, min; int lowCost[MAX]; //lowCost用来保存集合V-U中各顶点到集合U中各顶点构成的边中具有最小权值的边的权值 int closest[MAX]; //closest用来保存依附于该边的在集合U中的顶点 for (i = 1; i //从U之外求离U中某一顶点最近的顶点 min = M; k = i; for (j = 0; j min = lowCost[j]; //最小值设为当前边的权值 k = j; //此边依附顶点赋为j } } cout lowCost[j] = c[k][j]; closest[j] = k; } } } } int main() { int n; //图中顶点个数 int cost[MAX][MAX]; //邻接矩阵数组 n = CreateCost(cost); //调用建立邻接矩阵函数 cout int vexNum, i; cout vexNum; for (i = 0; i int i, j; Edge t; for (i = 0; i if (E[i].w > E[j].w) { swap(E[i], E[j]); } } } } //在边表中查看顶点v在哪个连通集合中 int seek(int set[], int v) { int i = v; while (set[i] > 0) { i = set[i]; } return i; } void Kruskal(Edge E[], int n) { int set[MAX]; //辅助标志数组 int v1, v2, i; for (i = 0; i //按边权递增顺序,逐边检查该边是否应加入到生成树中 v1 = seek(set, E[i].u); //确定顶点v所在的连通集 v2 = seek(set, E[i].v); if (v1 != v2) { //当v1,v2不在同一顶点集合,确定该边应当选入生成树 cout |
CopyRight 2018-2019 实验室设备网 版权所有 |