最小生成树

生成树

所有顶点由边连接到一起,但不存在回路的图

 特点:

  • 一个图可以有许多课不同的生成树。
  • 生成树的顶点个数与图的顶点个数相同
  • 生成树是图的极小连通子图,去掉一条边则非联通。
  • 一个有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;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值