data structure——图
图的存储结构:
- 图的顺序存储结构:邻接矩阵的结构型
有两个数组:一个一维数组存储图中的顶点信息;一个二维数组存储图中的边或弧的信息。
缺点:如果边数相对于顶点数较少,那么边数组的存储空间是一个很大的浪费。邻接矩阵事例:1
2
3
4
5
6
7
8
9
10
11
12
13typedef struct
{
int no; //顶点编号
char info; //顶点的其他信息,视情况而定写不写
}VertexType;
typedef struct //图的定义
{
int edges[maxSize][maxSize]; //邻接矩阵定义,可看做边表
int n,e; //顶点数和边数
VertexType vex[maxSize]; //顶点表,存放结点信息
}MGraph; //图的邻接矩阵类型
- 图的链式存储结构:邻接表
数组与链表相结合;图中顶点用一维数组来存储(也可用单链表);图中的每个顶点的邻接点用单链表来存储。
1 |
|
邻接表事例:
ArcNode是“边”(边结点)的结构体,是用点与点之间的关系来表示边的。其中的adjvex(Adjacent vertices邻接顶点的缩写)这条边所指的顶点,是顶点在数组中的下标,*next是指向下一个边结点的指针。
VNode是“顶点”的结构体,AGraph是“图”的结构体,其中定义了一个VNode型的数组来储存顶点,n,e是结点数和边数。
邻接表中,如果是有向图的话,邻接表是存储的是这个结点引出的边,也即“出度”,而如果存储的是指向这个结点的边,也即“入度”的话,这个存储结构就是“逆邻接表”。
- 有向图的十字链表存储结构
如果我们想即存储出度又存储入度呢?我们可以这样:
其中的firstIn是指向入边链表中第一个结点的指针,firstOut指向第一个出边链表的指针。
start是指当前边起点的数据域,end是当前边终点的数据域,nextIn指向这个顶点的下一个入边,nextOut指向这个结点的下一个出边。
这就是有向图的十字链表存储结构。
- 无向图的邻接多重表
我们在看到用邻接表存储无向图的时候,会出现重复存储的情况,那么我可以这样:
左上方是顶点的结构体设计,右上方是边结点的结构体设计。
其中的Vi是边的起点,Vj是边的终点。ViNext是表示与Vi相关的下一个边结点,VjNext是表示与Vj相关的下一个边结点。
遍历
这里dfs和bfs的代码所对应的都是图的邻接表的存储方式的遍历。
- 深度优先遍历(DFS)
很类似于树的先序遍历算法,
这里用了一个visit[]数组,设置为一个标记,如果这个结点已经访问过了就设置为1,如果没有访问过默认为0。
注意:Visit函数和visit[]数组不是同一个东西,第10句中的下一个结点指的是在邻接表中当前结点的下一个结点。
1 | void DFS(int c,AGraph *G) |
- 广度优先遍历(BFS)
类似于树的层次遍历,遍历过程会用到一个列表。主要过程是:取一个顶点进行访问,入队,并将这个点标记为已访问;接着,访问这个结点的下一个结点,没有访问过就访问,标记为1,入队,如此循环将邻接点都入队,p为空时就出队,然后循环对这个出队顶点的所有没有被访问过的邻接顶点进行访问,标记,入队,依次循环最后,直至出队使得队列为空,结束循环。
1 | void BFS(AGraph *G,int v,int visit[maxsize]) |
- 最小生成树
由一个图按照某种规则导出的一个树,叫生成树。最小生成树:构成这个生成树的所有分支的权值和最小。
prime算法
求最小生成树的代码:
1 | void Prim(int n ,float MGraph[][n],int v0,float &sum)//顶点个数,带权图,构造生成树的起始顶点, |
lowCost[]数组是用来存储当前生成树到图中其余顶点的边的最小权值。vSet[i]为1时就说明已经被并入生成树中,为0就没有并入。
v指向刚并入的顶点。
v0是最小生成树的初始结点,所以v=v0
kruskal算法
把当前未被并入的,且并入后不会产生环的最小边进行并入。
如何检测并入后到底会不会产生环呢?就用到了“并查集”。所谓的并查集,就是通过这些图中的点构造出来的树,每一个结点在相连的时候,要一直查到到根结点,如果根结点不相同,说明图中选取边之后不会产生环,所以可以选择这个这个结点的边。
相关的存储结构: