最小生成树第三弹“克鲁斯卡尔(Kruskal)算法”

距离上一次分享最小生成树算法,已经过去1个多月。今天,小编重新来分享下另外一个有关的最小生成树算法,也就是“克鲁斯卡尔(Kruskal)算法”。

对于任意一个连通网的最小生成树来说,在要求总的权值最小的情况下,最直接的想法就是将连通网中的所有边按照权值大小进行升序排序,从小到大依次选择。

由于最小生成树本身是一棵生成树,所以需要时刻满足以下两点:
(1)生成树中任意顶点之间有且仅有一条通路,也就是说,生成树中不能存在回路;
(2)对于具有 n 个顶点的连通网,其生成树中只能有 n-1 条边,这 n-1 条边连通着 n 个顶点。
连接 n 个顶点在不产生回路的情况下,只需要 n-1 条边。

所以克鲁斯卡尔算法的具体思路是:
将所有边按照权值的大小进行升序排序,然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。

判断是否会产生回路的方法为:
在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接。

我们直接看例子:

图.png

首先,我们设置一个结构体类,用来存储边的相关信息。

template <class M, class N>
class EData
{
    public:
        M start; // 边的起点
        M end;   // 边的终点
        N weight; // 边的权重
};

通过:

EData *edges;
edges = new EData[numEdges];

新建一个结构体数组edges,将有关边的信息存储进来,得到下表:


edges数组未排序

按照权值进行排序,重新得到下表:


edges数组排序

使用该算法找到最小生成树时。需要使用到“并查集”的知识。
我们新建一个数组:
int vends[numEdges];
for(int i=0;i<numEdges;i++)
{
    vends[i]=i;
}

进行初始化,刚开始默认每个顶点的老大都是它自己。


vends数组1

当然后续这些顶点的老大会更新,那如何查找它们的新的老大呢,这就要使用下面的函数:

template <class T, class E>
int GraphMatrix<T, E>::getEnd(int vends[], int i)
{
    if(vends[i]!=i)
    {
            return getEnd(vends,vends[i]);
    }else
    {
        return i;
    }
}

当然,如果两个顶点没有相同的老大,那我们可以把这两个团体并起来:

template <class T, class E>
void GraphMatrix<T, E>::unionn(int vends[],int x,int y)
{
     vends[x] = y;
}

那接下来我们就以上图为例子,将算法流程走一遍。
edges数组里的边权值按从小到大排序,我们新建一个数组来存储找到的最小路径的边的信息:

EData<T,E> rets[DefaultVertices];

1、我们先取出edges[0],可以得到edges[0].start=A,edges[0].end=B,edges[0].weight=4,通过顶点表可知A的位置为0,B的位置为1。我们分别查找这两个点的老大,getEnd(0)=0,getEnd(1)=1,老大不同,因此可以合并,使得vends[0]=1;同时rets[0]=edges[0];


vends数组2

2、取出edges[1],可以得到edges[1].start=C,edges[0].end=D,edges[0].weight=5,通过顶点表可知C的位置为2,D的位置为3。我们分别查找这两个点的老大,getEnd(2)=2,getEnd(3)=3,老大不同,因此可以合并,使得vends[2]=3;同时rets[1]=edges[1];


vends数组3

3、取出edges[2],可以得到edges[2].start=E,edges[2].end=G,edges[2].weight=6,通过顶点表可知E的位置为4,G的位置为6。我们分别查找这两个点的老大,getEnd(4)=4,getEnd(6)=6,老大不同,因此可以合并,使得vends[4]=6;同时rets[2]=edges[2];
vends数组4

4、取出edges[3],可以得到edges[3].start=F,edges[3].end=H,edges[3].weight=8,通过顶点表可知F的位置为5,H的位置为7。我们分别查找这两个点的老大,getEnd(5)=5,getEnd(7)=7,老大不同,因此可以合并,使得vends[5]=7;同时rets[3]=edges[3];


vends数组5

5、取出edges[4],可以得到edges[4].start=A,edges[4].end=F,edges[4].weight=10,通过顶点表可知A的位置为0,F的位置为5。我们分别查找这两个点的老大,getEnd(0)=1,getEnd(5)=7,老大不同,因此可以合并,使得vends[1]=7;同时rets[4]=edges[4];
vends数组6

6、取出edges[5],可以得到edges[5].start=B,edges[5].end=G,edges[5].weight=11,通过顶点表可知B的位置为1,G的位置为6。我们分别查找这两个点的老大,getEnd(1)=7,getEnd(6)=6,老大不同,因此可以合并,使得vends[7]=6;同时rets[5]=edges[5];
vends数组7

7、取出edges[6],可以得到edges[6].start=C,edges[6].end=E,edges[6].weight=15,通过顶点表可知C的位置为2,E的位置为4。我们分别查找这两个点的老大,getEnd(2)=3,getEnd(4)=6,老大不同,因此可以合并,使得vends[3]=6;同时rets[6]=edges[6];
vends数组8

8、取出edges[7],可以得到edges[7].start=G,edges[7].end=H,edges[7].weight=17,通过顶点表可知G的位置为6,H的位置为7。我们分别查找这两个点的老大,getEnd(6)=6,getEnd(7)=6,老大相同,处于同一个团体,意味着形成环路,故这条边不加入数组rets中。
vends数组9

9、取出edges[8],可以得到edges[8].start=A,edges[8].end=D,edges[8].weight=20,通过顶点表可知A的位置为0,D的位置为3。我们分别查找这两个点的老大,getEnd(0)=6,getEnd(3)=6,老大相同,处于同一个团体,意味着形成环路,故这条边不加入数组rets中。


vends数组10

此时,所有边都遍历了一遍,因此算法的查找过程结束,接下来就是将找到的最小生成树打印出来而已:
for (i = 0; i < index; i++)
     length += rets[i].weight;
cout << "Kruskal=" << length << ": ";
for (i = 0; i < index; i++)
     cout << "(" << rets[i].start << "," << rets[i].end << ") ";

采用此方法得到最小生成树的路径和以及具体信息。
由于小编的代码采用了模板,因此可能直接看文章有点难理解。这里小编没有优化算法,进行路径压缩,各位小伙伴们就自己动动手实现下。小编将完整的代码提供如下:

#include <iostream>
using namespace std;

const int DefaultVertices = 30; // 默认最大顶点数
template <class M, class N>
class EData
{
    public:
        M start; // 边的起点
        M end;   // 边的终点
        N weight; // 边的权重

    public:
        EData(){}
        EData(M s, M e, N w):start(s),end(e),weight(w){}
};
template <class T, class E>
class GraphMatrix {
public:
    const E maxWeight = 100000; // 代表无穷大的值(=∞)
    GraphMatrix(int sz=DefaultVertices); // 构造函数
    ~GraphMatrix(); // 析构函数
    void inputGraph(); // 创建基于邻接矩阵的图
    void outputGraph(); // 输出图的所有顶点和边信息
    T getValue(int i); // 取顶点i的值,i不合理返回0
    E getWeight(int v1, int v2); // 取边(v1, v2)上的权值
    int getFirstNeighbor(int v); // 取顶点v的第一个邻接顶点
    int getNextNeighbor(int v, int w); // 取v的邻接顶点w的下一个邻接顶点
    bool insertVertex(const T& vertex); // 插入顶点vertice
    bool insertEdge(int v1, int v2, E cost); // 插入边(v1, v2)权值为cost,有向图 
    bool insertUEdge(int v1, int v2, E cost); // 插入边(v1, v2)权值为cost,无向图 
    bool removeVertex(int v); // 删去顶点v和所有与它相关联的边
    bool removeEdge(int v1, int v2); // 在图中删去边(v1, v2)
    int getVertexPos(T vertex); // 给出顶点vertice在图中的位置
    void DFS();//深度度优先搜索 
    void DFS1(int i, int *visited); 
    void BFS();//广度优先搜索 
    void Dijkstra(int k);//Dijkstra最短路径算法 
    void Prim(char x); //Prim(普利姆算法) 
    int getPosition(T ch);   // 返回ch在矩阵中的位置
    EData<T,E>* getEdges();     // 获取图中的边
    void sortEdges(EData<T,E>* edges, int elen); // 对边按照权值大小进行排序(由小到大)
    int getEnd(int vends[], int i);  // //找集体老大,并查集的一部分 ,获取i的终点
    void unionn(int vends[],int x,int y);//加入团体,并查集的一部分
    void kruskal(); // 克鲁斯卡尔(Kruskal)最小生成树
private:
    int maxVertices; // 图中最大的顶点数
    int numEdges; // 当前边数
    int numVertices; // 当前顶点数
    T *VerticesList; // 顶点表
    E **Edge; // 邻接矩阵
    
};

// 构造函数
template <class T, class E>
GraphMatrix<T, E>::GraphMatrix(int sz) {
    int i, j;
    
    maxVertices = sz;
    numVertices = 0;
    numEdges = 0;
    VerticesList = new T[maxVertices]; // 创建顶点表数组
    Edge = new E*[maxVertices]; // 创建邻接矩阵数组
    for(i = 0; i < maxVertices; i++)
        Edge[i] = new E[maxVertices];
    for(i = 0; i < maxVertices; i++) { // 邻接矩阵初始化
        for(j = 0; j < maxVertices; j++)
        {
            if(i == j) // 矩阵对角处,即为同一顶点
                Edge[i][j] = 0;
            else // 不是同一顶点的,即两顶点一开始没有边相连,为无穷大∞
                Edge[i][j] = maxWeight;
        }
    }
}
 
// 析构函数
template <class T, class E>
GraphMatrix<T, E>::~GraphMatrix() {
    delete []VerticesList; // 释放动态分配的空间
    delete []Edge;
}
 
// 创建基于邻接矩阵的图
template <class T, class E>
void GraphMatrix<T, E>::inputGraph() {
    int i, j, k,h;
    int n, m; // 要输入的顶点数和边数
    T e1, e2; // 边的两端顶点
    E weight; // 边对应的权值
    
    cout << "请输入顶点数和边数:" << endl;
    cin >> n >> m;
    cout << "请输入顶点:" << endl;
    for(i = 0; i < n; i++) { // 建立顶点表数据
        cin >> e1;
        insertVertex(e1); // 插入
    } 
    cout<<"有向图选0,无向图选1:"<<endl;
    cin>>h; 
    cout << "请输入边的两端顶点和权值:" << endl;
    i = 0;
    while(i < m){ // 输入边
        cin >> e1 >> e2 >> weight; // 输入端点信息
        j = getVertexPos(e1); // 查顶点号
        k = getVertexPos(e2);
        if(j == -1 || k == -1)
            cout << "边两端点信息有误,重新输入!" << endl;
        else {
            if(h==1)
                insertUEdge(j, k, weight);
            else if(h==0)
                insertEdge(j, k, weight);
            else
                cout<<"输入错误";
            i++;
        }
    } // for结束
}
 
// 输出图的所有顶点和边信息
template <class T, class E>
void GraphMatrix<T, E>::outputGraph() {
    int i, j, n, m;
    T e1, e2;
    E w;
    
    n = numVertices;
    m = numEdges;
    cout << "顶点数为:" << n << ",边数为:" << m << endl;
    for(i = 0; i < n; i++) {
        for(j = i+1; j < n; j++) {
            w = getWeight(i, j); // 取边上权值
            if(w > 0 && w < maxWeight) { // 有效,即这两顶点存在边
                e1 = getValue(i);
                e2 = getValue(j);
                cout << "(" << e1 << "," << e2 << "," << w << ")" << endl;
            }
        }
    } // for
}
 
// 给出顶点vertice在图中的位置
template <class T, class E>
int GraphMatrix<T, E>::getVertexPos(T vertex) {
    for(int i = 0; i < numVertices; i++)
        if(VerticesList[i] == vertex)
            return i;
    return -1;
}
 
// 取顶点i的值,i不合理返回NULL
template <class T, class E>
T GraphMatrix<T, E>::getValue(int i) {
    if(i >= 0 && i < numVertices)
        return VerticesList[i];
    return NULL;
}
 
// 取边(v1, v2)上的权值
template <class T, class E>
E GraphMatrix<T, E>::getWeight(int v1, int v2) {
    if(v1 != -1 && v2 != -1) // 存在这两个顶点
        return Edge[v1][v2];
    return 0;
}
 
// 取顶点v的第一个邻接顶点
template <class T, class E>
int GraphMatrix<T, E>::getFirstNeighbor(int v) {
    if(v != -1) {
        for(int col = 0; col < numVertices; col++)
            if(Edge[v][col] > 0 && Edge[v][col] <maxWeight)
                return col;
    }
    return -1;
}
 
// 取v的邻接顶点w的下一个邻接顶点
template <class T, class E>
int GraphMatrix<T, E>::getNextNeighbor(int v, int w) {
    if(v != -1 && w != -1) {
        for(int col = w+1; col < numVertices; col++) {
            if(Edge[v][col] > 0 && Edge[v][col] < maxWeight)
                return col;
        }
    }
    return -1;
}
 
// 插入顶点vertice
template <class T, class E>
bool GraphMatrix<T, E>::insertVertex(const T& vertex) {
    if(numVertices == maxVertices) // 顶点表满
        return false;
    VerticesList[numVertices++] = vertex;
    return true;
}
 
// 插入边(v1, v2)权值为cost
template <class T, class E>
bool GraphMatrix<T, E>::insertEdge(int v1, int v2, E cost) {
    if(v1 > -1 && v1 < numVertices && v2 > -1 && v2 < numVertices && Edge[v1][v2] == maxWeight)
     { // 顶点v1,v2都存在,并且v1,v2没有边
        Edge[v1][v2]=cost;
        numEdges++;
        return true;
    }
    return false;
}
 // 插入边(v1, v2)权值为cost
template <class T, class E>
bool GraphMatrix<T, E>::insertUEdge(int v1, int v2, E cost) {
    if(v1 > -1 && v1 < numVertices && v2 > -1 && v2 < numVertices && Edge[v1][v2] == maxWeight)
     { // 顶点v1,v2都存在,并且v1,v2没有边
        Edge[v1][v2] = Edge[v2][v1] = cost;
        numEdges++;
        return true;
    }
    return false;
}
 
// 删去顶点v和所有与它相关联的边
template <class T, class E>
bool GraphMatrix<T, E>::removeVertex(int v) {
    if(v < 0 && v > numVertices) // v不在图中,不删除
        return false;
    if(numVertices == 1) // 只剩一个顶点,不删除
        return false;
    int i, j;
    
    VerticesList[v] = VerticesList[numVertices-1]; // 用最后一个顶点替代当前要删的顶点
    // 删除与v相关联边数
    for(i = 0; i < numVertices; i++) {
        if(Edge[i][v] > 0 && Edge[i][v] < maxWeight)
            numEdges--;
    }
    // 用最后一列,填补第v列
    for(i = 0; i < numVertices; i++)
        Edge[i][v] = Edge[i][numVertices-1];
    numVertices--; // 顶点数减1
    // 用最后一行,填补第v行
    for(j = 0; j < numVertices; j++)
        Edge[v][j] = Edge[numVertices][j];
    return true;
}
 
// 在图中删去边(v1, v2)
template <class T, class E>
bool  GraphMatrix<T, E>::removeEdge(int v1, int v2) {
    if(v1 > -1 && v1 < numVertices && v2 > -1 && v2 < numVertices && Edge[v1][v2] < maxWeight) {
        Edge[v1][v2] = Edge[v2][v1] = maxWeight;
        numEdges--; // 边数减1
        return true;
    }
    return false;
}

// 
template <class T, class E>
void  GraphMatrix<T, E>::DFS() {
    int i;
    int visited[30];       // 顶点访问标记

    // 初始化所有顶点都没有被访问
    for (i = 0; i < numVertices; i++)
        visited[i] = 0;

    cout << "DFS: ";
    for (i = 0; i < numVertices; i++)
    {
        //printf("\n== LOOP(%d)\n", i);
        if (!visited[i])
            DFS1(i, visited);
    }
    cout << endl;
}

template <class T, class E>
void  GraphMatrix<T, E>::DFS1(int i, int *visited) {
    int w;
    visited[i] = 1;
    cout << VerticesList[i] << " ";
    // 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走
    for (w = getFirstNeighbor(i); w >= 0; w = getNextNeighbor(i, w))
    {
        if (!visited[w])
            DFS1(w, visited);
    }
}

template <class T, class E>
void  GraphMatrix<T, E>::BFS() {
    int head = 0;
    int rear = 0;
    int queue[DefaultVertices];     // 辅组队列
    int visited[DefaultVertices];   // 顶点访问标记
    int i, j, k;

    for (i = 0; i < numVertices; i++)
        visited[i] = 0;

    cout << "BFS: ";
    for (i = 0; i < numVertices; i++)
    {
        if (!visited[i])
        {
            visited[i] = 1;
            cout <<VerticesList[i] << " ";
            queue[rear++] = i;  // 入队列
        }
        while (head != rear) 
        {
            j = queue[head++];  // 出队列
            for (k = getFirstNeighbor(j); k >= 0; k = getNextNeighbor(j, k)) //k是为访问的邻接顶点
            {
                if (!visited[k])
                {
                    visited[k] = 1;
                    cout <<VerticesList[k] << " ";
                    queue[rear++] = k;
                }
            }
        }
    }
    cout << endl;
}

template <class T, class E>
void  GraphMatrix<T, E>::Dijkstra(int k)
{
    int* final = new int[numVertices];
    int* path = new int[numVertices];
    int* distance = new int[numVertices];
    int i;
    // 初始化结点
    for (i = 0; i < numVertices; i++) {
        path[i] = -1;
        final[i] = 0;
        distance[i] = maxWeight;
    }
    distance[k] = 0; // 初始化源点
    for (i = 0; i < numVertices; i++) { // 寻找不同长度的最短路径
        int min = maxWeight; // 当前所知里顶点j的最短距离
        int index = -1;     // 最短距离对应的下标
        int j;
        for (j = 0; j < numVertices; j++) {
            if (!final[j]) {    // j在V-S中
                if (distance[j] < min) {
                    min = distance[j];
                    index = j;
                }
            }
        }// 寻找最短路径
        if (index == -1) break;
        final[index] = 1;  // 离j最近的顶点Index并入S,第一次一定是v0
        for (j = 0; j < numVertices; j++) { // 更新距离矩阵,一定要判断是否达,即arc[][]!=OO,否则会整数溢出
            if (!final[j] && Edge[index][j] != INT_MAX && (min + Edge[index][j] < distance[j])) {
                distance[j] = min +Edge[index][j];
                path[j] = index;
            }
        }
    }
    for (i = 0; i < numVertices; i++)
    {
        cout<<VerticesList[k]<<"到顶点"<<VerticesList[i]<<"的最短路径距离为"<<distance[i]<<endl;
        cout<<"逆序输出路径:" ;
        int index=0;
        int j=i;
        while(path[j]!=-1)
        {
            cout<<VerticesList[j]<<"<-";
            j=path[j];
        }
        cout<<VerticesList[k];
        cout<<endl;
    }
    delete[] final;

} 

template <class T, class E>
void  GraphMatrix<T, E>::Prim(char x)//普利姆算法(参数:起点(即第一个生成的点,可随便取))
{
    int lowcost[DefaultVertices], closest[DefaultVertices], i, min, j, k;
    int v=getVertexPos(x);
    /***初始化lowcost数组,closest数组(即从起点开始设置lowcost数组,closest数组相应的值,以便后续生成使用)***/
    for (i = 0; i < numVertices; i++)//赋初值,即将closest数组都赋为第一个节点v,lowcost数组赋为第一个节点v到各节点的权重
    {
        closest[i] = v;
        lowcost[i] = Edge[v][i];//Edge[v][i]的值指的是节点v到i节点的权重
    }
 
    /**********************************开始生成其他的节点*********************************/
    for (i = 1; i < numVertices; i++)//接下来找剩下的n-1个节点(g.n是图的节点个数)
    {
 
        /*****找到一个节点,该节点到已选节点中的某一个节点的权值是当前最小的*****/
        min = maxWeight;//INF表示正无穷(每查找一个节点,min都会重新更新为INF,以便获取当前最小权重的节点)
        for (j = 0; j < numVertices; j++)//遍历所有节点
        {
            if (lowcost[j] != 0 && lowcost[j] < min)//若该节点还未被选且权值小于之前遍历所得到的最小值
            {
                min = lowcost[j];//更新min的值
                k = j;//记录当前最小权重的节点的编号
            }
        }
 
        /****************输出被连接节点与连接节点,以及它们的权值***************/
        printf("边(%c,%c)权为:%d\n", VerticesList[closest[k]],VerticesList[k], min);
 
        /***********更新lowcost数组,closest数组,以便生成下一个节点************/
        lowcost[k] = 0;//表明k节点已被选了(作标记)
        //选中一个节点完成连接之后,作数组相应的调整
        for (j = 0; j <numVertices ; j++)//遍历所有节点
        {
            /* if语句条件的说明:
             * (1)Edge[k][j] != 0是指k!=j,即跳过自身的节点
             * (2)Edge[k][j]是指刚被选的节点k到节点j的权重,lowcost[j]是指之前遍历的所有节点与j节点的最小权重。若Edge[k][j] < lowcost[j],则说明当前刚被选的节点k与节点j之间存在更小的权重,则需要更新
             * (3)有人会问:为什么只跳过掉自身的节点(即k==j),而不跳过所有的已选节点?当然我们可以在if语句条件中增加跳过所有的已选节点的条件(即lowcost[j] == 0),而在本程序中我们只跳过了自身的节点?(注意:我们假设图中的边的权值大于0)但其实不是,Edge[k][j] < lowcost[j]条件已包含跳过所有的已选节点,原因是在邻接矩阵中权值为0是最小的,即Edge[k][j]>=0,而已选节点满足lowcost[j] == 0,则已选节点j是不满足Edge[k][j] < lowcost[j],则会被跳过
             */
            if (Edge[k][j] != 0 && Edge[k][j] < lowcost[j])
            {
                //更新lowcost数组,closest数组
                lowcost[j] = Edge[k][j];//更新权重,使其当前最小
                closest[j] = k;//进入到该if语句里,说明刚选的节点k与当前节点j有更小的权重,则closest[j]的被连接节点需作修改为k
            }
        }
    }
}

template <class T, class E>
int GraphMatrix<T, E>::getPosition(T ch)
{
    int i;
    for(i=0; i<numVertices; i++)
        if(VerticesList[i]==ch)
            return i;
    return -1;
}

template <class T, class E>
EData<T,E>* GraphMatrix<T, E>::getEdges()
{
    int i,j;
    int index=0;
    EData<T,E> *edges;

    edges = new EData<T,E>[numEdges];
    for (i=0; i < numVertices; i++)
    {
        for (j=i+1; j < numVertices; j++)
        {
            if (Edge[i][j]!=maxWeight)
            {
                edges[index].start  = VerticesList[i];
                edges[index].end    = VerticesList[j];
                edges[index].weight = Edge[i][j];
                index++;
            }
        }
    }
    cout<<"edges数组未排序:"<<endl;
    for(i=0;i<index;i++)
    {
        cout<<edges[i].start<<"-->"<<edges[i].end<<"="<<edges[i].weight<<endl;  
    } 
    
    cout<<endl;
    return edges;
}

template <class T, class E>
void GraphMatrix<T, E>::sortEdges(EData<T,E>* edges, int elen)
{
    int i,j;
    for (i=0; i<elen; i++)
    {
        for (j=i+1; j<elen; j++)
        {
            if (edges[i].weight > edges[j].weight)
            {
                // 交换"边i"和"边j"
                swap(edges[i], edges[j]);
            }
        }
    }
    cout<<"edges数组排序:"<<endl;
    for(i=0;i<elen;i++)
    {
        cout<<edges[i].start<<"-->"<<edges[i].end<<"="<<edges[i].weight<<endl;  
    } 
}

template <class T, class E>
int GraphMatrix<T, E>::getEnd(int vends[], int i)
{
    if(vends[i]!=i)
    {
        return getEnd(vends,vends[i]);
    }else 
    {
        return i;   
    }
}

template <class T, class E>
void GraphMatrix<T, E>::unionn(int vends[],int x,int y)
{
     vends[x] = y;
}

template <class T, class E>
void GraphMatrix<T, E>::kruskal()
{
    int i,m,n,p1,p2;
    int length;
    int index = 0;          // rets数组的索引
    int vends[DefaultVertices];     // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
    for(int i=0;i<numEdges;i++)
    {
        vends[i]=i; 
    } 
    EData<T,E> rets[DefaultVertices];        // 结果数组,保存kruskal最小生成树的边
    EData<T,E> *edges;           // 图对应的所有边
    // 获取"图中所有的边"
    edges = getEdges();
    // 将边按照"权"的大小进行排序(从小到大)
    sortEdges(edges, numEdges);

    for (i=0; i<numEdges; i++)
    {
        p1 = getPosition(edges[i].start);      // 获取第i条边的"起点"的序号
        p2 = getPosition(edges[i].end);        // 获取第i条边的"终点"的序号

        m = getEnd(vends, p1);                 // 获取p1在"已有的最小生成树"中的终点,找自己的老大 
        n = getEnd(vends, p2);                 // 获取p2在"已有的最小生成树"中的终点,找自己的老大 
        // 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路
        if (m != n)//假如不在一个团体 
        {
            unionn(vends,m,n);                      // 设置m在"已有的最小生成树"中的终点为n
            rets[index++] = edges[i];           // 保存结果
        }
    }
    delete[] edges;

    // 统计并打印"kruskal最小生成树"的信息
    length = 0;
    for (i = 0; i < index; i++)
        length += rets[i].weight;
    cout << "Kruskal=" << length << ": ";
    for (i = 0; i < index; i++)
        cout << "(" << rets[i].start << "," << rets[i].end << ") ";
    cout << endl;
}

int main(int argc, const char * argv[]) {
    GraphMatrix<char, int> st; // 声明对象
    bool finished = false;
    int choice;
    char e1, e2, next;
    int weight;
    
    while(!finished) {
        cout << "[1]创建基于邻接矩阵的图" << endl;
        cout << "[2]输出图的所有顶点和边信息" << endl;
        cout << "[3]取顶点v的第一个邻接顶点" << endl;
        cout << "[4]取v的邻接顶点w的下一个邻接顶点" << endl;
        cout << "[5]插入顶点" << endl;
        cout << "[6]无向图插入边" << endl;
        cout << "[7]有向图插入边" << endl;
        cout << "[8]删除顶点" << endl;
        cout << "[9]删除边" << endl;
        cout << "[10]深度优先搜索DFS" << endl;
        cout << "[11]广度优先搜索BFS" << endl;
        cout << "[12]Dijkstra最短路劲" << endl;
        cout << "[13]Prim最小生成树算法" << endl;
        cout << "[14]kruskal最小生成树算法" << endl;
        cout << "[15]退出" << endl;
        cout << "请输入选择[1-15]:";
        cin >> choice;
        switch(choice) {
            case 1:
                st.inputGraph();
                break;
            case 2:
                st.outputGraph();
                break;
            case 3:
                cout << "请输入顶点:";
                cin >> e1;
                e2 = st.getValue(st.getFirstNeighbor(st.getVertexPos(e1)));
                if(e2)
                    cout << "顶点" << e1 << "的第一个邻接顶点为:" << e2 << endl;
                else
                    cout << "顶点" << e1 << "没有邻接顶点!" << endl;
                break;
            case 4:
                cout << "请输入顶点v和邻接顶点w:";
                cin >> e1 >> e2;
                next = st.getValue(st.getNextNeighbor(st.getVertexPos(e1), st.getVertexPos(e2)));
                if(next)
                    cout << "顶点" << e1 << "的邻接顶点" << e2 << "的下一个邻接顶点为:" << next << endl;
                else
                    cout << "顶点" << e1 << "的邻接顶点" << e2 << "没有下一个邻接顶点!" << endl;
                break;
            case 5:
                cout << "请输入要插入的顶点:";
                cin >> e1;
                if(st.insertVertex(e1))
                    cout << "插入成功!" << endl;
                else
                    cout << "表已满,插入失败!" << endl;
                break;
            case 6:
                cout << "请输入要插入的边的信息:" << endl;
                cin >> e1 >> e2 >> weight;
                st.insertUEdge(st.getVertexPos(e1), st.getVertexPos(e2), weight);
                break;
            case 7:
                cout << "请输入要插入的边的信息:" << endl;
                cin >> e1 >> e2 >> weight;
                st.insertEdge(st.getVertexPos(e1), st.getVertexPos(e2), weight);
                break;
            case 8:
                cout << "请输入要删除的顶点:";
                cin >> e1;
                if(st.removeVertex(st.getVertexPos(e1)))
                    cout << "顶点" << e1 << "已删除!" << endl;
                else
                    cout << "顶点" << e1 << "不在图中!"  << endl;
                break;
            case 9:
                cout << "请输入要删除的边的两个端点:" << endl;
                cin >> e1 >> e2;
                st.removeEdge(st.getVertexPos(e1), st.getVertexPos(e2));
                break;
            case 10:
                st.DFS();
                break; 
            case 11:
                st.BFS();
                break; 
            case 12:
                st.Dijkstra(1);
                break; 
            case 13:
                cout<<"请输入起始点:"<<endl;
                cin>>e1; 
                st.Prim(e1);
                break;
            case 14: 
                st.kruskal();
                break; 
            case 15: 
                finished = true;
                break;
            default:
                cout << "选择输入错误,请重新输入!" << endl;
        }
    }
    return 0;
}

运行截图
2020-06-01_092619.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342