生成树
所有顶点由边连接到一起,但不存在回路的图
特点:
- 一个图可以有许多课不同的生成树。
- 生成树的顶点个数与图的顶点个数相同。
- 生成树是图的极小连通子图,去掉一条边则非联通。
- 一个有n个顶点的连通图的生成树有n-1条边。
- 在生成树中再加一条边必然形成回路。
- 生成树中任意两个顶点间的路径是唯一的。
无向图的生成树
在遍历顶点的同时记录下经过的边,边所构成的集合+顶点集合就是连通图的生成树。
最小生成树
给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那棵生成树称为该网的最小生成树,也叫最小代价生成树。
最小生成树的典型用途
问题: 欲在n个城市间建立通信网,则n个城市应铺设n-1条线路,每条线路都会有对应的经济成本,而n个城市最多有n(n-1)/2条线路,那么,如何选择这n-1条线路,使总费用最少。
转化为数学模型:
顶点 | 表示城市,有n个 |
边 | 表示线路,有n-1条 |
边的权值 | 表示每条线路的经济代价 |
联通网 | 表示n个城市间通信网 |
构造最小生成树(Mimimum Spanning Tree)
MST性质
构造最小生成树的算法很多,其中多数算法都利用了MST的性质
MST性质:
设N=(V,E)是一个联通网,U是顶点集V的一个非空子集。若边(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。
MST性质解释:
在最小生成树构建过程中,图中n个顶点分属于两个集合:
- 已落在生成树上的顶点集 U
- 尚未落在生成树上的顶点集 V-U
接下来则应在所有连通U中顶点和V-U中顶点的边中选取权值最小的边,这样才能保证新加入的边和顶点能构成生成树且边的权值最小。
Prim算法
const int MAXN = 1000,INF = 0x3f3f3f3f; //定义一个INF表示无穷大。
int g[MAXN][MAXN],dist[MAXN],n,m,res;
//我们用g[][]数组存储这个图,dist[]储存到集合S的距离,res保存结果。
bool book[MAXN];//用book数组记录某个点是否加入到集合S中。
void prim()
{
dist[1] = 0; //把点1加入集合S,点1在集合S中,将它到集合的距离初始化为0
book[1] = true; //表示点1已经加入到了S集合中
for(int i = 2 ; i <= n ;i++)
dist[i] = min(dist[i],g[1][i]); //用点1去更新dist[]
for(int i = 2 ; i <= n ; i++)
{
int temp = INF; //初始化距离
int t = -1; //接下来去寻找离集合S最近的点加入到集合中,用t记录这个点的下标。
for(int j = 2 ; j <= n; j++)
{
if(!book[j]&&dist[j]<temp)
//如果这个点没有加入集合S,而且这个点到集合的距离小于temp就将下标赋给t
{
temp = dist[j];//更新集合V到集合S的最小值
t = j;//把点赋给t
}
}
if(t==-1){res = INF ; return ;}
//如果t==-1,意味着在集合V找不到边连向集合S,生成树构建失败
book[t] = true; //如果找到了这个点,就把它加入集合S
res+=dist[t]; //加上这个点到集合S的距离
for(int j = 2 ; j <= n ; j++)
dist[j] = min(dist[j],g[t][j]); //用新加入的点更新dist[]
}
}
Kruskal算法
- 我们要选择权值最小的边,就需要给所有边的权值进行排序;
- 要避免形成环,就需要设置辅助函数,利用并查集的思想做排除。
定义边结构和图结构:
typedef struct{
VerTexType vex[MVNum]; //顶点
ArcType arcs[MVNum][MVNum]; //边
int vexnum,arcnum; //图当前点数和边数
}AMGRAGH;
struct Edge{
int start;
int end;
int weight;
};
根据图录入边的信息:
Edge* IniEdge(AMGRAGH* G){ //初始化边,同时舍弃边值为maxlnt
int index = 0;
Edge* edge = new Edge[G->arcnum];
for(int i=0;i<G->vexnum;i++)
{
for(int j=i+1;j<G->vexnum;j++)
{
if(G->arcs[i][j] != Maxlnt)
{
edge[index].start = i;
edge[index].end = j;
edge[index].weight = G->arcs[i][j];
index++;
}
}
}
return edge;
}
利用冒泡排序,根据权值对边进行排序:
void SortEdge(Edge* edge , AMGRAGH* G) //冒泡排序
{
for(int i = 0; i < G->arcnum - 1; i++)
{
for(int j = 0; j < G->arcnum - 1 - i; j++)
{
if(edge[j].weight > edge[j+1].weight)
{
Edge temp = edge[j+1];
edge[j+1] = edge[j];
edge[j] = temp;
}
}
}
}
Kruskal函数:
void kruskal(AMGRAGH* G){
int* connected = new int[G->vexnum]; //建立辅助数组,判断是否成环
for(int i = 0; i < G->vexnum; i++)
{
connected[i] = i;
}
Edge* edge = IniEdge(G);
SortEdge(edge,G);
for(int i = 0; i < G->arcnum; i++)
{
int start = connected[edge[i].start];
int end = connected[edge[i].end];
if(start != end)
{
cout<<G->vex[edge[i].start]<<"->"<<G->vex[edge[i].end]<<"->";
for(int j = 0; j < G->vexnum; j++)
{
if(connected[j] == end)
{
connected[j] = start;
}
}
}
}
delete[] connected;
delete[] edge;
}
完整代码封装成Kruskal类,代码如下:
class Kruskal{
public:
typedef struct{
VerTexType vex[MVNum]; //顶点
ArcType arcs[MVNum][MVNum]; //边
int vexnum,arcnum; //图当前点数和边数
}AMGRAGH;
struct Edge{
int start;
int end;
int weight;
};
Edge x;
Kruskal(){};
Edge* IniEdge(AMGRAGH* G){ //初始化边,同时舍弃边值为maxlnt
int index = 0;
Edge* edge = new Edge[G->arcnum];
for(int i=0;i<G->vexnum;i++)
{
for(int j=i+1;j<G->vexnum;j++)
{
if(G->arcs[i][j] != Maxlnt)
{
edge[index].start = i;
edge[index].end = j;
edge[index].weight = G->arcs[i][j];
index++;
}
}
}
return edge;
}
void SortEdge(Edge* edge , AMGRAGH* G) //冒泡排序
{
for(int i = 0; i < G->arcnum - 1; i++)
{
for(int j = 0; j < G->arcnum - 1 - i; j++)
{
if(edge[j].weight > edge[j+1].weight)
{
Edge temp = edge[j+1];
edge[j+1] = edge[j];
edge[j] = temp;
}
}
}
}
void kruskal(AMGRAGH* G){
int* connected = new int[G->vexnum]; //建立辅助数组,判断是否成环
for(int i = 0; i < G->vexnum; i++)
{
connected[i] = i;
}
Edge* edge = IniEdge(G);
SortEdge(edge,G);
for(int i = 0; i < G->arcnum; i++)
{
int start = connected[edge[i].start];
int end = connected[edge[i].end];
if(start != end)
{
cout<<G->vex[edge[i].start]<<"->"<<G->vex[edge[i].end]<<"->";
for(int j = 0; j < G->vexnum; j++)
{
if(connected[j] == end)
{
connected[j] = start;
}
}
}
}
delete[] connected;
delete[] edge;
}
};