数据结构 您所在的位置:网站首页 vis数组 数据结构

数据结构

2024-06-09 18:48| 来源: 网络整理| 查看: 265

文章目录 定义基本术语图的存储邻接矩阵法结构体创建图打印邻接矩阵 邻接表法结构体建立邻接表打印邻接表 图的遍历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∑n​TD(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∑n​ID(vi​)=i=1∑n​OD(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 实验室设备网 版权所有