5.4.1图的应用(最小生成树)

5.4.1图的应用(最小生成树)

连通图的生成树:极小的连通子图。包含图中全部的顶点,但只有足以构成一棵树的n-1条边。

最小生成树:总权值最小的的生成树。

[外链图片转存失败(img-fNWrjauC-1566910228717)(C:\Users\liuhao\AppData\Roaming\Typora\typora-user-images\1566890602805.png)]

最小生成树的求解算法:

[外链图片转存失败(img-4ZJsXIRj-1566910228718)(C:\Users\liuhao\AppData\Roaming\Typora\typora-user-images\1566890631681.png)]

普利姆算法

算法思想:

需要两个数组: lowcost[n] , adjvex (n是图中的顶点数)

1)从图中找第一个起始顶点V0,作为生成树的第一个顶点,然后从这个顶点到其他所有边中挑选一条权值最小的边。然后与把这条边的另一个顶点也加入到生成树中。

2)对剩余其他的所有顶点,分别检查这些顶点与顶点v的权值是否比这些顶点在lowcost数组中对应的权值小,如果更小,则用较小的权值更新lowcost数组。

3)从更新后的lowcost数组中继续挑选权值最小而且不在生成树中边,然后加入到生成树中。

4)反复执行2)3)直到所有顶点都加入到生成树中。

Void MiniSpanTree_Prim(MGraph G)  //用邻接矩阵存的图
{
    int min,i,j,k;
    int adjvex[MAXVEX];
    int lowcost[MAXVEX];
    /*初始化*/
    lowcost[0]=0;   // 下边为0的顶点加入到生成树中
    adjvex[0]=0;
    for(i=1 ; i<G.vexnum; i++) //出下边为0的所有顶点
    {
        lowcost[i]=G[0][i];	//将与下标为0的顶点有边权值存入loccost数组
        adjvex[i]=0;  // 这些顶点的adjvex数组全部初始化为0
    }
    /*算法核心*/
    for(i=1; i<G.vexnum; i++)  //只需要循环N-1次
    {
        min =65535;
        j=0;
        k=0;
        /*找顶点*/
        //找出lowcost中最小的  最小的权值给min 下标给k
        while(j<G.vexnum)  //从一号开始找
        {
            if(lowcost[j]!=0&&lowcost[j]<min)//不在生成树中的顶点而且权值更小的的
            {
                min =lowcost[j];  //更新权值
                k=j;    //找到了新的点下标给k
            }
            j++;  // 在看下一个顶点
        }
        printf("%d->%d",adjvex[k],k);
        lowcost[k]=0;  // 将这个顶点加入生成树
        //生成树加入新的顶点,熊下标为1的顶点开始更新lowcost数组值
        for(j=0; j<G.vexnum; j++)
        {
            if(lowcst[j]!=0; &&G.arc[k][j]<lowcost[j])
            {
                lowcost[j]=G.arc[k][j];
                adjvex[j]=k;  //修改这条边邻接的顶点
            }
        }
    }
}

举例:

[外链图片转存失败(img-92C9PX6q-1566910228719)(assets/1566892755100.png)]

[外链图片转存失败(img-quzZ3UZJ-1566910228720)(assets/1566892778202.png)]

[外链图片转存失败(img-t81RDSJg-1566910228721)(assets/1566892795443.png)]

[外链图片转存失败(img-qe3KLwm2-1566910228722)(assets/1566892807937.png)]

[外链图片转存失败(img-8BTUL3Y7-1566910228723)(assets/1566892864250.png)]

[外链图片转存失败(img-BfcdasKW-1566910228724)(assets/1566892882785.png)]

[外链图片转存失败(img-rJwQyzet-1566910228725)(assets/1566892899501.png)]

[外链图片转存失败(img-BDPPOQ75-1566910228726)(assets/1566892949488.png)]

[外链图片转存失败(img-fIqpWPYR-1566910228727)(assets/1566892958906.png)]

[外链图片转存失败(img-HwuPk3YJ-1566910228729)(assets/1566893013881.png)]

[外链图片转存失败(img-SZU1cO2v-1566910228730)(assets/1566893039408.png)]

[外链图片转存失败(img-Q2iGnKeX-1566910228732)(assets/1566893059414.png)]

[外链图片转存失败(img-oeg4vXQG-1566910228733)(assets/1566893070348.png)]

[外链图片转存失败(img-Ew2kHxlK-1566910228735)(assets/1566893088611.png)]

[外链图片转存失败(img-Jdq7u2sY-1566910228735)(assets/1566893101483.png)]

[外链图片转存失败(img-1NW6HO4b-1566910228736)(assets/1566893107710.png)]

算法分析:

双重循环,外层循环次数为n-1,内层并列两个循环次数都是n。故普利姆算法时间复杂度为O(n^2),而且时间复杂度只和n有关系 ,所以适合稠密图(顶点相对于边来说少)。

克鲁斯卡尔算法:

生成树包含n个顶点,n-1条边。

算法思路:

将图中边按权值从小到大排列,然后从最小的边开始扫描,设置一个边的集合来记录,如果该边并入不构成回路的话,则将该边并入当前生成树。直到所有的边都检测完为止。

排序:堆排序

不构成回路:并查集

并查集:

[外链图片转存失败(img-q1lMKjwI-1566910228737)(assets/1566907344623.png)]

[外链图片转存失败(img-2MPBz9FB-1566910228738)(assets/1566907351775.png)]

[外链图片转存失败(img-h7fRGX5x-1566910228739)(assets/1566907384208.png)]

合并:一个集合的根结点为另一个集合根结点的孩子

查:两个节点的根结点相同,那它们是联通的,为同一集合中的,相连就会产生回路。

//查找某个集合的根结点:
int Find(int *parent,int x){
		while(parent[x]>=0)
            x=parent[x];
    return x;
}
//合并两个集合
void Union(int *parent,int root1,int root2){
    parent[root2]=root1;
}

克鲁斯卡尔代码:

#define MaxSize 100
typedef struct{
	int a,b; //变得两个顶点
    int weight;  //边的权值
}Edge;  //边结构体
int Find(int *parent, int x){
	while(parent[x]>0)
        x=parent[x];
    return x;
}    
Edge edges[MaxEdge];  // 边数组
int parent[MaxVex];   // 父亲顶点数组(并查集)
int MiniSpanTree_Kruskal(MGraph G){
	int i, n,m;
    sort(edges); //按权值从小到大排序
    for(i=0;i<G.vexnum;i++){
		parent[i] =-1//初始化
    }
    for(i=0;i<G.arcnum;i++){  //扫描每条边
		n=Find(parent,edges[i].a);// n是这条边第一个顶点的根顶点所在的下标	
         m=Find(parent,edges[i].b); //n是这条边第二个顶点的根顶点所在的下标	
         if(n!=m){
			parent(n)=m;//并操作
             printf("(%d->%d)",deges[i].a,deges[i].a)
         }
    }
}



[外链图片转存失败(img-oS3cuv9s-1566910228740)(assets/1566908588249.png)]

边数组:

[外链图片转存失败(img-Q4wCYU05-1566910228740)(assets/1566908598541.png)]

初始化的父亲顶点数组:

[外链图片转存失败(img-DxkviuTh-1566910228741)(assets/1566908654980.png)]

第一次:边为1 - 4 n=1 m=4 n!=m parent[1]=4 打印 1- 4

[外链图片转存失败(img-GSHBfvdk-1566910228741)(assets/1566908932720.png)]

第二次:边为5 -6 n=5 m=6 n!=m parent[5]=6 打印 5-6

[外链图片转存失败(img-mFuN6ofd-1566910228742)(assets/1566908954579.png)]

第三次:边为4-5 n=4 m=6 n!=m parent[4]=6 打印 4-5

[外链图片转存失败(img-1RpZ3X29-1566910228742)(assets/1566909073973.png)]

第四次:边为0-1 n=0 m=6 n!=m parent[0]=6 打印0-1

[外链图片转存失败(img-8QhYa4cm-1566910228743)(assets/1566909129733.png)]

第五次:边为1-5 n=6 m=6 n==m

[外链图片转存失败(img-tx6maZRX-1566910228743)(assets/1566909202788.png)]

第六次:边为0-2 n=6 m=2 n!=m parent[6]=2 打印0-2

[外链图片转存失败(img-7fgCAGjK-1566910228744)(assets/1566909275083.png)]

第七次:边为1-3 n=2 m=3 n!=m parent[2]=3 打印0-1

[外链图片转存失败(img-NWiEY12I-1566910228745)(assets/1566909334795.png)]

已经够六条边够了。

算法分析:

克鲁斯卡尔算法操作分为对边权值排序部分和一个单重for循环,它们是并列关系,由于排序耗费时间大于单重循环,所以主要用时为排序上。排序和图中的边的数量有关系,所以适合稀疏图。

最小生成树:

[外链图片转存失败(img-5idBR84k-1566910228746)(assets/1566909605016.png)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值