树:在图中,常常将树定义为一个无回路连通的无向图。只选定图中某个顶点做根,以树根为起点对每条边定向,就能将他们变成通常的树。树中悬挂的顶点称为树叶,其他顶点称为分支点,一个非连通图,如果它的各个连通分量都是树,则这种非连通图称为森林。树是森林的特殊情况,树中无回路,因此树中必然没有自身环也无重边,否则就出现回路不是树了。
生成树和生成森林:
生成树:连通图的一个子图如果是一棵包含所有顶点的树,则该子图称为图的生成树。由于n个顶点的连通图至少有n-1条边,而所包含n-1条边及n个顶点的连通图都是无回路的树,所以生成树是连通图的极小连通图。所谓极小是指边数最小,若在生成树中去掉任何一条边,都会使之变为非连通图,若在生成树上任意添加一条边,就必定出现回路。DFS是图的深度搜索得到的生成树称为深度优先生成树。BFS图的广度搜索得到的生成树称为广度优先生成树。
生成森林:非连通的无向图,则要若干次从外部调用DFS(BFS)算法,才能完成对图的遍历,每一次外部调用,只能访问到图的一个连通分量的顶点集,这些顶点和遍历时所经过的边构成了连通分量的一棵DFS(BFS)生成树。图的各个连通分量的DFS(BFS)组成了DFS(BFS)生成森林。类似的,若图是非强连通的有向图,且初始出发点又不是有向图的根,则遍历时一般也只能得到该有向图的生成森铃。
最小生成树:图的生成树不是唯一的,从不同的顶点出发进行遍历,可以得到不同的生成树。对于连通网络,边是带权的,因而图的生成树的各边也是带权的。我们把生成树的权值总和称为生成树的权,并把权最小的生成树称为图的最小生成树。
最小生成树算法:
构造最小生成树应遵循以下规则:
(1)只能使用原图中的边来构造最小生成树
(2)只能使用且仅使用n-1条边来连接图中n个顶点。
(3)所有产生回路的边均不得使用。
普利姆算法:假设G=(V,E)是连通网络,即V={1,2,....n}。设所求的最小生成树为T = (U,TE),其中U是T的顶点集,TE是T的边集。并且将G中边上的权看做是长度。
基本思想:
(1)开始的时候U={v0}(v0属于V),TE={};
(2)在所有u属于U,v属于V-U的边(u-v)属于E中,找出一条权值最小的边(u0,v0)并入集合TE,同时将v0并入集合U;
(3)重复执行b操作:直至U=V,此时TE必有n-1条边,则T=(U,TE)是G的一棵最小生成树。
template<class T>
void AdjMatrixGraph<T>::MinSpanTree_prim(Edge *mst) //构造带权图最小生成树的普里姆算法
{
int n = vertcount,i,j;
for(i = 0;i<n-1;i++)
{
Edge e= {0,i+1,dajmatrix[0][i+1]};
mst[i] = e;
}
cout<<"mst数组初值:"<<print(mst,n-1);
for(i = 0;i<n-1;i++)
{
int minweight = MAX_WEIGHT;
int main = i;
for(j = i;j<n-1;j++) //寻找当前最小权值的边的顶点
if(mst[j].weight<minweight)
{
minweight = mst[j].weight; //更新最小权值
min = j; //保存当前最小权值边的终点序号
}
Edge temp = mst[i]; //交换最小权值的边
mst [i] = mst[min];
mst [min] = temp;
int u = mst[i].dest; //刚并入u的顶点
for(j = i+1;j<n-1;j++) //调整mst[i+1]及其后元素为权值最小的边
{
int v = mst[j].dest; //原边在v-u中的终点
if(adjmatrix[u][v]<mst[j].weight) //若有权值更小的边(u,v)边替换原边
{
mst[j].weight = adjmatrix[u][v];
mst[j].start = u;
}
}
cout<<"\nast 数组:"; print(mst,n-1);
}
}