Prim 算法 C++

Posted

技术标签:

【中文标题】Prim 算法 C++【英文标题】:Prim's Algorithm C++ 【发布时间】:2014-05-11 02:54:47 【问题描述】:

我正在用 C++ 实现 Prim 算法的一个简单版本,并且在将算法转换为与我非常基本的图形实现一起使用时遇到了一些困难。

我有一个边结构如下:

struct edge 

    int src;
    int dest;
    int weight;
;

我只是将边推送到图形类中的向量。我不确定这是否是实现 Prim 的最佳方式,但它似乎对我的最终结果是最佳的,它能够简单地打印出访问过的顶点数量和最小生成树的总权重。

我想我了解 Prim's 的基本概念,但我对几点有疑问。我所要求的只是朝着正确的方向前进。不过,特定于我的使用的伪代码将是理想的。

1) 选择一个起始顶点

2) 在从源顶点到所有其他顶点的 while 循环中,将边权重添加到某种堆中(这让我感到困惑,一些算法说初始化为无穷大,但 STL 优先级队列没有保留方法...)

3) 获取最低边缘(从优先级队列中弹出或在我考虑不周的实现中遍历向量以获得最低值...)

4) 如果已访问过最小值的目的地,则丢弃该边并尝试下一条。如果没有,则将边添加到最小生成树并标记它已访问。

我没有太多的代码,而且收益递减,所以任何建议都值得赞赏。这是我得到的:

vector<edge> graph::prims(int source, vector<edge> vt)

    int current;
    vector<bool> visited(numVert, false);
    vector<unsigned int> minWeight;
    minWeight.reserve(numVert);

    // Intilize minWeight to a large number
    for(int j=0; j < numEdges; j++)
    
        minWeight[j] = 0xffffffff;
    

    current = source; 

    // Will this even work? What if I pick the nth node...
    while (current <= numEdges)
    
        // This should add the edge weights to the current vertex 
        // to a vector so the minimum can be found
        for(int i = 0; i < numVert; i++)
        
            if(vt[i].src == current && visted[i] == false)
            
                minWeight.push_back(vt[i].weight);
            
        
    

    // Need to finish Prim's


    return vt;

我整天都在尝试从互联网上获取代码并进行修改,但它让我无处可去。所以我最终决定自己尝试一下。不算多,但这花了我大约两个小时......

我的代码可以在Google Drive找到。

【问题讨论】:

【参考方案1】:

我查看了您的代码,您的设计似乎在某些方面不完整。一起来看看吧。

您的graph 类如下所示:

class graph 
public:
        ... function prototypes ...
private:
        int numVert;
        int numEdges;
;

这个类声明缺少一些相当重要的信息:特别是,虽然存储了顶点数和边数,但从未存储过关于属于给定 graph 实例的顶点或边的信息。

您已经有 edgecity 结构,可分别用于边和顶点。这些可以存储在您的graph 类的std::vector&lt;...&gt; 成员中,并且-如果您小心保留相关的顺序-您可以通过数字索引来解决这些问题并且(大部分)很好。完成此操作后,您可以调整 addEdgeaddCity 以使它们实际执行应做的操作 - 即分别向 graph 实例添加边或顶点。

要考虑的另一件事是您实际上希望如何存储边缘。 “最佳”方法取决于问题的性质,但对于大多数应用程序,将边存储在以顶点 ID 为键的结构中(例如 std::map&lt;std::vector&lt;...&gt; &gt; 实例)或作为每个顶点对象中的子字段(例如通过将std::vector&lt;...&gt; 成员字段添加到您的city 类)。


除此之外,让我们解决您尝试实现的实际算法。

Prim 的算法本质上是对这条规则的迭代应用:

将最小权重边添加到候选最小生成树中,将树内的某个顶点连接到树外的某个顶点。

也就是说,在每一步,我们都这样做:

考虑到目前为止我们已经建立的树中的顶点;将此集合称为 A。(一开始,这是您选择的单个顶点。最后,这是包含初始顶点的连通分量的顶点集合;如果您的图是连通的,则这是所有顶点。 ) 考虑我们图中不在树中的顶点;将此集合称为 B。 查看连接集合 A 中的任何顶点与集合 B 中的任何顶点的所有可能边(在图论语言中。图的割 (A, B) 的割集)。如果没有,你就完成了。否则,选择其中权重最小的边并将其添加到树中。

您可以证明,如果原始图是连通的,则该算法会生成总边权重最小的生成树。试着为自己证明这一点。


那么,你现在在哪里?虽然我无法读懂你的想法,但我可以告诉你你的代码在告诉我什么:

您正在尝试跟踪您已经访问过的顶点集。 这很好;你会需要的。 您正在尝试跟踪割集,即退出访问集的边。 您也需要这个。 我不知道你为什么只使用std::vector&lt;unsigned int&gt; 来达到这个目的,因为对于给定的边,你需要跟踪三个量:'from' 顶点、'to' 顶点和权重。为此,您可能需要 std::vector&lt;edge&gt;。 您似乎正在尝试填充此集合。 您可能需要重做此位,具体取决于您是否更改了数据结构的布局。 主要思想 - 找到从已访问节点到未访问节点的边 - 存在,但我不确定当前代码建议的方法是否可行。

那么,算法中缺少什么?

一旦你有了一个有效的割集,你就需要在集中找到权重最小的边。 您需要将此边添加到返回集。 您需要将此边连接到的顶点标记为已访问。将此节点称为 N。 您需要更新割集。特别是,将 N 连接到先前访问过的顶点的任何边都需要删除,并且需要添加将 N 连接到某个未访问顶点的任何边。

我希望这对您有所帮助。如果您对任何事情感到困惑,需要更多解释,或者认为我所说的内容不准确或不正确,请告诉我,我会更新此答案。

【讨论】:

【参考方案2】:
//Prims Algorithm implementation using matrix in simpler way

#include<iostream>
#include<limits.h>
using namespace std;
int n=5;

//Min Node will return the node having minimum weight
int min_node(int matrix[5][5],bool visited[5])
    int result;
    int min_value=INT_MAX;

    for(int i=0;i<n;i++)
        if(visited[i])
            for(int j=0;j<n;j++)
                if(matrix[i][j]<min_value && !visited[j])//If the node is not in visited array then consider it,otherwise not,
                                                            //to avoid the loop in the minimum spanning tree
                    min_value=matrix[i][j];//update the min value
                    result=i*10 + j;
                //endl inner if structure
            //end inner for loop
        //end outer if structure
    //end outer for loop

    return result;


int main()
cout<<"Hello world\n";
int matrix[5][5]=
                    0,18,15,0,0,
                    18,0,12,0,13,
                    15,12,0,20,0,
                    0,0,20,0,11,
                    0,13,0,11,0
                ;

//Display the matrix
for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
        cout<<matrix[i][j]<<"\t";
     cout<<"\n";


//Make the disconnected node weight = MAX
for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
        if(matrix[i][j]==0)
            matrix[i][j]=INT_MAX;
    


//Take an visited array
bool visited[5];
//Make all the entries of visited array false
for(int i=0;i<5;i++)
    visited[i]=false;

int source;
cout<<"Enter the source vertex\n";
cin>>source;

visited[source]=true;

//Tree having 'n' vertices will have 'n-1' edges in the minimum spanning tree
int t=4;
while(t>0)
    int result=min_node(matrix,visited);
    int i=result/10;
    int j=result%10;
    visited[j]=true;//Now add the new node the visited, to consider the its edeges in the condition
    cout<<"Path "<<i<<"- "<<j<<" ="<<matrix[i][j]<<endl;

t--;


return 0;

【讨论】:

以上是关于Prim 算法 C++的主要内容,如果未能解决你的问题,请参考以下文章

Prim算法

最小生成树Prim算法实现C++

C++ 实现无向图的最小生成树Prim算法(附完整代码)

Prim 的算法不适用于某个实现

C++ 图进阶系列之 kruskal 和 Prim 算法_图向最小生成树的华丽转身

欧几里德 平面最小生成树算法