zoukankan      html  css  js  c++  java
  • 最小生成树之Kruskal算法和Prim算法

    依据图的深度优先遍历和广度优先遍历,能够用最少的边连接全部的顶点,并且不会形成回路。

    这样的连接全部顶点并且路径唯一的树型结构称为生成树或扩展树。实际中。希望产生的生成树的全部边的权值和最小,称之为最小生成树。

    常见的最小生成树算法有Kruskal算法和Prim算法。

    Kruskal算法每次选取权值最小的边。然后检查是否增加后形成回路,假设形成回路则须要放弃。终于构成最小生成树。n个顶点的图最小生成树过程例如以下:

    边的权值升序排序。

    选取全部未遍历的边中权值最小的边,推断增加后是否形成回路,若形成回路,放弃之。又一次从未被遍历的边中选择。

    反复上述步骤,直到选中n-1条边。

    /*****************Kruskal算法********************/
    struct Edge
    {
    	int v1,v2;//顶点
    	int w;//边v1--v2权值
    	struct Edge *next;//指向下一条边
    }
    //h为按边的权值升序排序的单链表
    int Kruskal(Edge *h, int *visited)
    {
    	int edgenum = 0;//记录生成树中边的个数
    	int weight = 0;//权值和
    	Edge *p = h;
    	printf("最小生成树:(顶点1,顶点2,权值)
    ");
    
    	while(edgenum != maximum)//maximum=顶点数,当边数=顶点数-1时结束
    	{
    		if(visited[p->v1] == 0 ||visited[p->v2]==0)
    			//新增边至少有一个顶点没有被訪问过
    		{
    			printf("(%d,%d,%d)->",p->v1,p->v2,p->w);
    			weight = weight + p->w; //权值和累加
    			visited[p->v1] = 1;
    			visited[p->v2] = 1;
    			edgenum++;//边数+1
    		}
    		p = p->next;
    		if(p==NULL)//无边可增加
    		{
    			printf("spanning tree fail
    ");
    			break;
    		}
    	}
    	return weight;
    }


    Prim算法

    相比于Kruskal选边生成,Prim算法选择顶点生成最小生成树。

    从某个顶点v開始,列出顶点 v 全部邻接点的边 选择权值最小的边(vi-->vj)增加到最小生成树中,并标记该边已被訪问过。
    再从vj開始 列出顶点vj全部邻接点的边,从中选择全部未被訪问过的边中权值最小的边 vj-->vk  增加到最小生成树中,并标记该边已被訪问过。

    反复上述操作。直到找到n-1条边为止。

    以下直接贴代码:

    /*******************Prims算法******************/
    struct Edge
    {
    	int v1,v2;//顶点
    	int w;//边v1--v2权值
    	int marked;//标识该边是否已经被加入到最小生成树中
    	struct Edge *next;
    }
    //h为边节点构成的链表,index表示開始顶点
    void Prim(Edge *h,int * visited, int index)
    {
    	Edge *p,*min;//min每次指向剩余边中中权值最小且   与上一个边共享顶点v1
    	int i;
    	int edgenum =0;//已连接边数
    	int weight =0;//权值
    	int vertex;
    	min = (Edge*)malloc(sizeof(Edge));
    	/***加入第一条边*******/
    	min->w = h->w;//最小边权值初值(可随意指定但小于全部边的权值,当然比越大越好)
    	p = h;
    	while(p!=NULL)
    	{
    		if(p->v1 ==index && (p->w < min->w))
    			min = p; //找到以index开头 且权值最小的边结点
    		p = p->next;
    	}
    	min->marked = 1;//该边已被訪问
    	visited[min->v1] = 1;
    	visited[min->v2] = 1; //该边两个顶点被訪问过
    	edgenum++;
    	weight = min->w;
    	printf("(%d,%d,%d)->",min->v1,min->v2,min->w);//输出选中边
    	/***加入其余边*******/
    	while(edgenum != maximum)
    	{
    		min->w = h->w;
    		p = h;
    		while(p != NULL)
    		{
    			if(p->marked==0 && visited[p->v1]+visited[p->v2]==1)//边没有被訪问过 且有且仅仅有一个顶点被訪问,还有一顶点没有被訪问过
    				if(p->w < min->w)
    					min = p; //找到权值最小的边
    			p = p->next;
    		}
    		min->marked = 1;
    		visited[min->v1] =1;
    		visited[min->v2] =1;
    		edgenum++;
    		weight += min->w;
    		printf("(%d,%d,%d)->",min->v1,min->v2,min->w);
    	}
    	printf("
    总权值为:%d",weight);
    }


  • 相关阅读:
    vi删除前n个字符
    ubuntu自定义登录session和桌面环境
    linux出错处理
    获取用户id
    c关键字控制语句运算符小结
    红帽Linux启动流程详解
    C语言中的文本流与二进制流的区别
    c简单排序字符串
    linux挂载磁盘优盘
    strcpy之段错误
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6748136.html
Copyright © 2011-2022 走看看