最小生成树:
1.prime普里姆算法
思路:
1).输入:一个加权连通图(邻接矩阵存储),其中顶点集合为V(n个);
2).初始化:Vnew= {V0},其中V0为集合V中的任一节点(起始点),定义辅助矩阵edge[n-1],d初始化为V0到剩余节点【0,1,.......,n-1】的长度;
3)循环k=0:n-2共n-1次(确定n-1条边):
a.在辅助矩阵edge[k]---edge[n-1]选取权值最小的边<Vi,Vj>,其中Vi为集合Vnew中的元素,而Vj不在Vnew集合当中。
b.将edge[k]与edge[j]交换,即将Vj加入到了Vnew中d
c.更新到不在Vnew集合的顶点的最短路径。此时使用新加入的节点Vj到剩余节点的距离与已知的距离比大小去较小的即可。
4).输出:使用edge[n-1]描叙最小生成树。
注意:更新策略避免了每次都需要在Vnew和非Vnew集合中遍历寻找最短路径(O(n(n-k))),而是采用新加入的顶点到剩余顶点的路径,与已知的最短路径比较。这样单次循环更新时间花销减小为O(n-k)。
时间复杂度为O(n.^2),由于涉及到交换,空间复杂度为O(1)。
整体思路:即确定一个集合,开始仅仅包含顶点0。在n-1次循环中,确定该集合到剩余节点的最短距离,并将最短距离的终点添加进该集合(并无实质添加,程序中用交换即解决)。更新该集合到剩余顶点的最短距离。
代码:
- //交换函数
- void swap(edge& e1,edge& e2){
- edge temp=e1;
- e1=e2;
- e2=temp;
- }
- //prime算法求最小生成树
- void prime(int** adjMatrix,int n,edge* ct ){
- int minWeight,minIndex;
- int currentFrom,currentEnd;
- bool flag;
- for(int i=0;i<n-1;i++){
- ct[i].from=0;
- ct[i].end=i+1;
- ct[i].weight=adjMatrix[0][i+1];
- }
- for(int i=1;i<n;i++){//依次加入n-1个点(n-1条路径),因此有n-1个循环
- //ct中[i-1,n-2]区间段中寻找最小长度的路径,并将其与i-1的路径交换
- minWeight=ct[i-1].weight;
- minIndex=i-1;
- flag=false;
- for(int j=i;j<n-1;j++)
- if(ct[j].weight<minWeight){
- minWeight=ct[j].weight;
- minIndex=j;
- flag=true;
- }
- if(flag)
- swap(ct[i-1],ct[minIndex]);
- //比较新的顶点到剩余节点的路径与当前到该节点的路径,更新到剩余节点的最短路径
- currentFrom=ct[i-1].end;
- for(int j=i;j<n-1;j++){
- currentEnd=ct[j].end;
- if(adjMatrix[currentFrom][currentEnd]<ct[j].weight){
- ct[j].from=currentFrom;
- ct[j].weight=adjMatrix[currentFrom][currentEnd];
- }
- }
- }
- }
2.Kruskal克鲁斯卡尔算法
思路:
定义集合标号数组vexSet。
(1)基于图的边集数组存储来完成,实现需要依照权重从小大道排列边集数组。
(2)初始化每一个顶点为单独集合。
(3)遍历边集数组:
a.如果路径两端节点不在一个集合内,储存该路径并将遍历终节点所属的集合全部合并到起始节点所在集合。
b.如果路径两端节点在一个集合内,跳过,继续遍历。
注意:使用标号来标示集合,不在同一个集合保证了无回路存在.
由于需要辅助数组标记顶点所属集合,故算法的空间复杂度为O(n)。而内嵌的循环中,合并集合需要执行n次,故时间复杂度最坏为为O(ne),最好为O(n.^2)。但是注意到,边集数组减小了图的空间复杂度为O(e),其中e为边数。
整体思路:集合合并问题。初始化每个顶点单独成为一个集合。每一轮循环中,确定顶点终点不在同一集合的最短边,保存该最短边,将顶点和终点所属集合合并(此时同一用边的顶点序号来标定同一集合)。
代码:
- //根据邻接矩阵获得升序排列的边集数组
- void getSet(edge* &edgeSet,int &size,int** adjMatrix,int n){
- for(int i=1;i<n;i++){
- for(int j=0;j<i;j++)
- if(adjMatrix[i][j]!=MAX){
- size++;
- if(!edgeSet){
- edgeSet=(edge*)malloc(sizeof(edge));
- edgeSet[0].from=j;
- edgeSet[0].end=i;
- edgeSet[0].weight=adjMatrix[i][j];
- }
- else{
- edgeSet=(edge*)realloc(edgeSet,size*sizeof(edge));
- int k=size-2;
- while(k>=-1&&edgeSet[k].weight>adjMatrix[i][j]){
- edgeSet[k+1]=edgeSet[k];
- k--;
- }
- edgeSet[k+1].from=j;
- edgeSet[k+1].end=i;
- edgeSet[k+1].weight=adjMatrix[i][j];
- }
- }
- }
- }
- //克鲁斯卡尔算法求最小生成树
- void Kruskal(int** adjMatrix,int n,edge* ct){
- edge* edgeSet=NULL;
- int size=0;
- getSet(edgeSet,size,adjMatrix,n);
- int i,k;
- int currentEnd;
- int* vexSet=new int[n];//集合标记辅助数组
- for(i=0;i<n;i++)
- vexSet[i]=i;
- for(i=0,k=0;i<size&&k<n-1;i++){//至多有n-1条边
- if(vexSet[edgeSet[i].from]!=vexSet[edgeSet[i].end]){//如果起点终点处于不同的集合
- ct[k++]=edgeSet[i];
- currentEnd=vexSet[edgeSet[i].end];
- for(int j=0;j<n;j++)//遍历终节点所属的集合全部合并到起始节点所在集合
- if(vexSet[j]==currentEnd)
- vexSet[j]=vexSet[edgeSet[i].from];
- }
- }
- free(edgeSet);
- delete []vexSet;
- }
3.主程序
使用数组矩阵模拟邻接矩阵,假设最大权重为100。
- #include<iostream>
- using namespace std;
- #define MAX 100
- #include"algo.h"
- void main(){
- //构建邻接矩阵
- int** a=new int*[7];
- for(int i=0;i<7;i++){
- a[i]=new int[7];
- for(int j=0;j<7;j++)
- a[i][j]=100;
- }
- a[3][0]=a[0][3]=5;
- a[1][0]=a[0][1]=8;
- a[2][1]=a[1][2]=12;
- a[3][1]=a[1][3]=3;
- a[4][1]=a[1][4]=10;
- a[4][2]=a[2][4]=6;
- a[5][2]=a[2][5]=2;
- a[5][3]=a[3][5]=7;
- a[6][3]=a[3][6]=15;
- a[5][4]=a[4][5]=9;
- //最小生成树
- edge* ct=new edge[7];
- //prime(a,7,ct);
- Kruskal(a,7,ct);
- for(int i=0;i<6;i++){
- cout<<ct[i].from<<"--"<<ct[i].end<<": "<<ct[i].weight<<endl;
- }
- for(int i=0;i<7;i++){
- delete[]a[i];
- }
- delete []a;
- delete []ct;
- }