600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > C语言《数据结构》(朱战立):图

C语言《数据结构》(朱战立):图

时间:2018-12-20 19:02:15

相关推荐

C语言《数据结构》(朱战立):图

C语言数据结构:图

一、图的基本概念

1、图的定义

G=(V,E)

V{x|x∈某个数据元素集合}:表示顶点E{(x,y)|x,y∈V}:表示路径,若指定Path(x,y),说明x到y是一条单向通路,若不指定则是无向的。

2、图的基本术语

顶点和边:图中结点称为顶点,两个顶点相关联,这称两顶点间有条边

有向图和无向图:<x,y>,<y,x>表示边有方向,(x,y)表示边无方向

无向完全图:在n个顶点的无向图中,任意两个顶点间有且只有一条边(n(n-1)/2条边)

有向完全图:在n个顶点的有向图中,任意两个顶点之间有且只有方向相反的两条边(n(n-1))

邻接顶点:一条边的两个点称为邻接顶点

顶点的度:指与某一顶点相关联的边的条数,有几条边就有多少度

路径:从一个顶点到另外一个顶点的序列

权:边的附带信息

路径长度:不带权的图,路径的长度就行路径上边的条数。带权的图,路径的长度,是边上权的总和

子图:G1={V1,E2}和G2{V2,E2},若V2∈V1,E2属于E1,则G2是G1的子图

连通图:无向图中,任意两个顶点都能连通

强连通图,有向图中,任意两个顶点都能连通

生成树:一个连通图最小的连通子图。有n个顶点的连通图的生成树有n个顶点和n-1条边

简单路径:路径上的顶点不重复

回路:路径上的第一个顶点和最后一个顶点重合,称为回路

3、图的抽象数据类型

① 数据集合

数据集合由一组顶点{v}和一组边{e}集合组成,当为带权图时,还包含边上的权集合{w}

② 操作集合

//初始化图Initiate(G);//插入顶点InsertVertex(G,vertex);//插入边InsertEdge(G,V1,V2,weight);//删除边DeleteEdge(G,V1,V2)//删除顶点DeleteVertex(G,vertex);//取第一个领接顶点GetFirstVex(G,int,V1,V2);//取下一个领接顶点GetNextVex(G,int,V1,V2);//遍历DepthFirstSearch(G);

二、图的存储结构

1、图的邻接矩阵存储结构

适用于稠密矩阵,当为稠密矩阵时最高效

顶点信息用一维数组存储,边信息的邻接矩阵用二维数组存储,无向图的邻接矩阵一定是对称矩阵。有向图一般非对称。

① 无向图和其邻接矩阵

考试写法如下:

② 带权无向图和其邻接矩阵

自己和自己为0,非邻接顶点为∞,权为权值

③ 有向图和其邻接矩阵
④ 带权有向图和其邻接矩阵

2、图的邻接表存储结构

适合稀疏矩阵,为稀疏矩阵时更有效

将稀疏矩阵存储在行指针数组结构的三元组链表中

三、图的实现

1、邻接矩阵存储结构图的操作实现

① 结点结构体

#inclede"SeqList.h"typedef struct{SeqList vertices;//存放顶点的顺序表int edge[MaxVertices][MaxVertices];//存放边的邻接矩阵int numOfEdges;//边的条数}AdjMGraph;

② 初始化

void Initiate(AdjMGraph *G,int n){int i,j;for(i=0;i<n;i++){for(j=0;j<n;j++){if(i==j) G->edge[i][j] = 0;else G->edge[i][j] = MaxWeight;//表示无穷大}G->numOfEdges = 0;ListInitiate(&G->Vertices);}

③ 插入顶点

void InsertVertex(AdjMGraph *G,DataType vertex){ListInsert(&G->Vertices,G->Vertices.size,vertex);}

④ 插入边

void InsertEdge(AdjMGraph *G,int v1,int v2,int weight){if(v1<0 || v1 >= G->Vertives.size || v2<0 || v2>=G->Vertices.size){printf("参数v1或v2越界出错!\n");return;}G->edge[v1][v2]=weight;G->numOfEdges++;}

⑤ 删除边

void DeleteEdge(AdjMGraph *G,int v1,int v2){if(v1<0 || v1>=G->Vertices.size || V1==V2){printf("参数v1或v2出错!\n");return;}if(G->edge[v1][v2]==MaxWeight||v1==v2){printf("该边不存在!\n");return;}G->edge[v1][v2]=MaxWeight;G->numOfEdges--;}

⑦ 取第一个邻接顶点

int GetFirstVex(AdjMGraph G,int v){int col;if(v<0 || V>= G.Vertices.size){printf("参数v1越界出错");return -1;}for(col=0;col<G.Vertices.size;col++)if(G.edge[v][col]>0 && G.edge[v][col]<MaxWeight) return col;return -1;}

⑧ 取下一个邻接结点

int GetNextVex(AdjMGraph G,int v1,int v2){int col;if(v1<0 || v1>=G.Vertices.size || V2<0 || V2>=G.Vertices.size){printf("参数v1或v2越界出错!\n");return -1;}for(col=v2+1;col<G.Vertices.size;col++)if(G.edge[v1][col]>0 && G.edge[v1][col]<MaxWeight) retutn col;return -1;}

2、创建图函数

typedef struct{int row;//行int col;//列int weight;//权值}RowColWeight;void GreatGraph(AdjMGraph *G,DataType V[],int n,RowColWeight E[],int e){int i,k;Initiate(G,n);for(i=0;i<n;i++)InsertVertex(G,V[i]);//插入顶点for(k=0;k<e;k++)InsertEdge(G,E[K].row,E[k].col.E[k].weight);//插入边}

3、邻接表存储结构图的操作实现

① 邻接表的存储结构

typedef struct Node{int dest;//邻接边弧头顶点序号struct Node *next;}Edge;typedef struct{DataType data;//顶点数据元素int source;//邻接边弧尾顶点序号Edge *adj;//邻接边的头指针}AdjHeight;typedef struct{AdjLHeight a[MaxVertices];//邻接表数组int numOfVerts;//顶点个数int numOfEdges;//边个数}AdjLGraph;

② 初始化

AdjInitiate(AdjLGraph *G){int i;G->numOfVerts=0;G->numOfEdfes=0;for(i=0;i<MaxVertices;i++){G->a[i].source = i;G->a[i].adj = NULL;}}

③ 插入顶点

void InsertVertex(adjLGraph *G,int i,DataType vertex){if(i>=0 && i<Maxvertices){G->a[i].data=vertex;G->numOfVerts++;}else printf("顶点越界");}

④ 插入边

void InsertEdge(AdjLGraph *G,int v1,int v2){Edge *p;if(v1<0 || v1>=G->numOfVerts || v2<0 || v2 >= G->numOfVerts){printf("参数v1或v2越界出错!");return;}p = (Edge *)malloc(sizeof(Edge));p->dest = v2;p->next = G->a[v1].adj;G->a[v1].adj = p;G->numOfEdges++;}

⑤ 删除边

void DeleteEdge(AdjLGraph *G,int v1,int v2){Edge *curr,*pre;if(v1<0 || v1>= G->numOfVerts || v2<0 || v2>=G->numOfVerts){printf("参数v1或v2越界出错!");return;}pre = NULL;curr = G->a[v1].adj;while(curr != NULL && curr->dest != v2){pre = curr;curr = curr->next;}if(curr!=NULL && curr->dest !=v2){pre = curr;curr = G->a[v1].adj;while(curr!=NULL && curr->dest != v2){pre = curr;curr = curr->next;}if(curr != NULL && curr->dest == v2 && pre ==NULL){G->a[v1].adj=crtt->next;free(curr);G->numOfEdges--;}else if(curr != NULL && curr->dest == v2 && pre != NULL){pre->next = curr->next;free(curr);G->numOfEfges--;}elseprintf("边<v1,v2>不存在!")}

四、图的遍历

1、图的遍历关键点

图无首尾之分,要指定访问的第一个结点设计要考虑遍历路径为回路的情况,避免死循环要使一个顶点的所有邻接顶点按照某种次序都被访问到

2、图的深度优先遍历

顺着顶点一直找邻接结点,然后再返回到顶点,再找他的邻接结点一直顺着找,他没有就顺着他的邻接结点找

关键:在图的所有邻接顶点中,每次都在访问完当前顶点后,首先访问当前顶点的第一个邻接顶点。

① 访问顶点v并标记顶点v为已访问

② 查找顶点v的第一个邻接顶点w

③ 若顶点v的邻接顶点w存在,则继续执行,否则算法结束

④ 若顶点w尚未被访问,则深度优先遍历递归访问顶点w

⑤ 查找顶点v的w邻接顶点的下一个邻接顶点w,转到步骤3

void DepthFSearch(AdjMGraph G,int v,int visited[],void Visit(DataType)){int w;Visit(G.Vertices.list[v]);visited[v]=1;w=GetFirstVex(G,v);while(w!=-1){if(!visited[w])DepthFSearch(G,w,visited,Visit);w=GetNextVex(G,v,w);}}

3、图的广度优先遍历

顺着顶点先找完它的邻接结点,一层一层的寻找

关键:从指定顶点开始,按照到该顶点的路径长度由短到长的顺序,依次访问图中的其余顶点

① 访问初始顶点v并标记顶点v为已访问

② 顶点v入队列

③ 若队列非空,则继续执行,否则算法结束

④ 出队列取得队头顶点u

⑤ 查找顶点u的第一个邻接顶点w

⑥ 若顶点u的邻接顶点w不存在,则转到步骤③,否则循环执行

(a)若顶点w尚未被访问,则访问顶点w并标记顶点w为已访问

(b)顶点w入队列

(c)查找顶点u的w邻接顶点后的下一个邻接顶点w,转到步骤⑥

#include "SeqCQueue.h"//导入循环队列void BroadFSearch(AdjMGraph G,int v,int visited[],void Visit(DataType item)){int u,w;SeqCQueue queue;Visit(G.Vertices.list[v]);visitrd[v]=1;QueueInitiate(&queue);QueueAppend(&queue,v);while(QueueNotEmpty(queue)){QueueDelete(&queue,&u);w=GetFirstVex(G,u);while(w!=-1){if(!visited[w]){Visit(G.Vertices.list[w]);visited[w]=1;QueueAppend(&queue,w);}w=GetNextVex(G,u,w);}}}

五、最小生成树

1、生成树

一个有n个顶点的连通图的生成树是原图的极小连通子图,有以下特点

无论它的生成树的形状如何,一定是:n个顶点,n-1条边

删除生成树一条边,导致其不是连通图

增加生成树一条边,会存在回路

连通图的生成树可以有许多,不同寻找方法生成不同生成树

2、最小生成树

如果有n个顶点的无向连通图是带权图,则它所有生成树必有一棵权值总和最小,则称为最小生成树

最小生成树必须包含n个顶点有且只有n-1条边不存在回路

3、构成最小生成树的方法

① Prim(普里姆)算法

普里姆(Prim)算法是一某个顶点为起点,逐步找各顶点最小权值的边来构建最小生成树。

步骤:

以一个点为顶点,找和它连通权值最小的点,形成新树找和刚才两个点形成的树连通权值最小的点,形成新树依次循环,直到所有点都连通

② Kruskal(克鲁斯卡尔)算法

将所有边的权值按从小到大的顺序进行排序,然后先连通最小的边形成树,不能出现回路,直到形成最小生成树。

找权值最小的边,连通两点,形成树重复上一步直到形成最小生成树

六、最短路径

即距离最短,权值总和最低,非带权图可视为权值皆为1

1、Dijkastra算法:从源点到各个顶点的最短路径

按路径长度递增的顺序逐步产生最短路径的构造方法(动态规划)

① 设置distance[],path[]

distance[]:用来存放得到的从源点v到其余各顶点的最短距离数值path[]:用来存放得到的从源点v到其余各顶点的最短路径上到目标顶点的前一个顶点的下标

② 从源点v开始,找到到达邻接结点u的最短距离(存入distance),确定到达该点的前一个顶点为源点(存入path)

③ 找从源点v到u的邻接结点u1的最短路径(存入distance),确定(更新)到达该点的前一个顶点为源点(存入path)

④ 循环③,直到找完从源点到各个点的最短路径

2、Floyd算法:每对顶点间的最短路径

① 设置两个二维数组,weight和path

weight存放每对顶点之间的最短路径path存放到目标顶点的前顶点的序号

② 具体如图

③ 解释

weight记录的是当前最短路径

找从源点v到u的邻接结点u1的最短路径(存入distance),确定(更新)到达该点的前一个顶点为源点(存入path)

④ 循环③,直到找完从源点到各个点的最短路径

2、Floyd算法:每对顶点间的最短路径

① 设置两个二维数组,weight和path

weight存放每对顶点之间的最短路径path存放到目标顶点的前顶点的序号

② 具体如图

③ 解释

weight记录的是当前最短路径

Path记录的是这个点到另外一个点当前最短路径最后一个中间点

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。