图是一种灵活的数据结构,它多用于描述对象之间的关系和连接模型。
关于图的算法:最小生成树、最短路径、旅行商问题以及许多其他算法大都会使用到广度优先搜索和深度优先搜索,因为它们提供了一套系统地访问图数据结构的方法。
带权图,是指图的每条边上带有一个值或权,这些权用一个小的数字标记在边上。很多条件因素都可以作为权值,但通常它表示遍历这条边所产生的代价。
最小生成树简述我们做一个简单的模型,在一块木板上钉上一些钉子并用一些细绳连接起来,假设每一个钉子都经由一根或多根细绳连接起来。现在我们一根一根拿走细绳,直到用最少的细绳将所有钉子连接起来。这个模型的背后思想就是最小生成树。
正式的表述法是,给定一个无方向的带权图G=(V,E),最小生成树为集合T,T是以最小代价连接V中所有顶点所用边E的最小集合。集合T中的边能形成一颗树,这是因为每个顶点都能向上找到它的一个父节点(根结点除外)。
Prim算法Prim算法是一种产生最小生成树的方法。
Prim算法从任意一个顶点开始,每次选择一个与当前顶点最近的一个顶点,并将两个顶点之间的边加入到树中。
从根本上讲,Prim算法就是不断的选择顶点,并计算边的权值,同时判断是否还有更有效的连接方式。该算法类似广度优先搜索算法,因为在往图中更深的顶点探索之前,它首先要遍历与此顶点相关的所有顶点。每个阶段都要决定选择哪个顶点,所以需要维护顶点的颜色和键值。
开始,将所有顶点的色值设置为白色,键值设置为∞(它代表一个足够大的数,大于图中所有边的权值)。同时,将起始顶点的键值设置为0。随着算法的不断演进,在最小生成树中为每个顶点(除起始顶点外)指派一个父节点。只有当顶点的色值变为黑色时,此顶点才是最小生成树的一部分。
Prim算法的运行过程如下:
首先,在图中所有的白色顶点中,选择键值最小的顶点u。开始,键值被设置为0的那一顶点将作为起始顶点。
当选择此顶点之后,将其标记为黑色。
接下来,对于每个与u相邻的顶点v,设置v的键值为边(u,v)的权值,同时将u设置为v的父结点。
重复这个过程,直到所有的顶点都标记为黑色。
随着最小生成树的增长,该树会包含图中的所有的边(连接所有顶点最少数量边),且每条边的两端都有一个黑色的顶点。
下图展示了最小生成树的产生过程。在图中,键值和父结点都显示在每个顶点的旁边,用斜线分开。键值显示在斜线的左边,父结点显示在斜线的右边。浅灰色的边是最小生成树增长过程中的边。图中最小生成树总的权值为17。
最小生成树的接口定义
mst
int mst(Graph *graph, const MstVertex *start, List *span, int (*match)(const void *key1, const void *key2));
返回值:如果计算最小生成树成功,返回0,否则返回-1。
描述:为一个无方向的带权图graph计算最小生成树。
最小生成树从顶点start开始。
此操作会改变graph,所以如果有必要,在调用此操作之前先对图进行备份。
graph中的每个顶点必须包含MstVertex类型的数据。通过设置MstVertex结构体中的成员weight的值来指定每个边的权值,weight的值由传入graph_ins_edge的参数data2决定。用MstVertex结构体的成员data来保存与顶点相关的数据。
graph的match函数(此函数在用graph_init对图进行初始化时调用)用来比较MstVertex结构体中的data成员。此函数与传入mst中的参数match相同。
一旦计算完成,最小生成树的相关数据将会返回到span。span是存储MstVertex结构体的列表。在span中,父结点为NULL的顶点为最小生成树的根结点。其他每个顶点的parent成员都指向span中位于该顶点之前的那个顶点。
span中的顶点指向graph中的实际顶点,所以只要能够访问span,函数调用者就必须保证graph中内存空间有效。一旦不再使用span,就调用list_destroy销毁span。
复杂度:O(E V2),其中V是图中顶点的个数,E是边的条数。
最小生成树的实现与分析