C++之最小生成树算法(Prim)

在讲述Prim算法之前让我们先了解一下什么是最小生成树?以下是我摘自维基百科:

最小生成树是一副连通加权无向图中一棵权值最小的生成树。

在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即 {(u,v)\in E}(u,v)\in E),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集 且 (V, T) 为树,使得

{w(T)={(u,v)\in T}w(u,v)}w(T)=\sum _{(u,v)\in T}w(u,v)
的 w(T) 最小,则此 T 为 G 的最小生成树。

最小生成树其实是最小权重生成树的简称。

通俗点讲就是:我们用线段把图所有的顶点进行连接,连接时不能产生圈,并且所有边权值的和为最小。因此我们可知,最小生成树可能不是唯一的。

今天我们先来讲述最小生成树的其中的一种算法:Prim算法。

Prim算法的思想(摘自维基百科):

Prim算法的每一步都会为一棵生长中的树添加一条边,该树最开始只有一个顶点,然后会添加V-1个边。每次总是添加生长中的树和树中除该生长的树以外的部分形成的切分的具有最小权值的横切边。

 什么意思呢?通俗点说就是我们选择一个root顶点,然后让其慢慢的生成一个树,也就是给其加边,每次加边时所选择的root顶点是来自于其前一个顶点,直到图中的所有顶点加到树上。Prim算法的核心思想其实是贪婪算法,与Dijkstra的算法思想一样,其实现几乎也是一模一样的哦~。在Dijkstra算法里面dis的作用是存储起始顶点到访问顶点的最小权值,对它进行更新,维护。但是Prim算法就不一样了哦~为什么呢?前面我们提到过,我们贪婪的选择最小的边生成一颗树。因此Prim算法里面dis的作用是存储未知顶点到已知顶点的权值。还有一个重要点:加入的边的数量比顶点数少一哦~~

Prim的核心代码:

void Graph::Prim(size_t v) {
	verptr->disweight[v] = 0;
	size_t count = 0;
	while (count!=verptr->vertexnumber-1) {
		size_t min = Infinity;
		for (int i = 0; i < verptr->vertexnumber; ++i) {
			if (verptr->visited[i] == false && min > verptr->disweight[i]/*选择边数里最小的权值*/) {
				min = verptr->disweight[i];
				v = i;
			}
		}
		verptr->visited[v] = true;  //对顶点进行标记
		++count; //对增加的边数进行更新
		auto beg = lists[v].begin();
		while (beg != lists[v].end()) {
			if (verptr->visited[*beg] == false)
				if (MIN(verptr->disweight[*beg], weight[v][*beg])) {
					verptr->disweight[*beg] = weight[v][*beg]; //对dis进行更新
					verptr->path[*beg] = v;
				}
			++beg;
		}
	}
}

Prim算法的时间复杂度:Prim算法与Dijkstra算法几乎是一样的,因此其时间复杂度也是一样,所以请看我的这一篇博客对Prim算法的时间复杂度的介绍。 

 

 全部代码如下:

#include<iostream>
#include<memory>
#include<list>
#include<vector>
const size_t Infinity = 999;
#define MIN(a,b) (a>b)
struct Vertex {
	size_t    vertexnumber;
	bool*     visited;
	size_t*   disweight;
	size_t*   path;
};

class Graph {
public:
	Graph(const size_t _vernumber) :verptr(std::make_unique<Vertex>()), lists(new std::list<size_t>[_vernumber]()), weight(std::vector<std::vector<size_t>>(_vernumber, std::vector<size_t>(_vernumber, 0))) {
		verptr->vertexnumber = _vernumber;
		verptr->visited = new bool[_vernumber]();
		verptr->disweight = new size_t[_vernumber]();
		verptr->path = new size_t[_vernumber]();
		for (int i = 0; i < verptr->vertexnumber; ++i) {
			verptr->disweight[i] = Infinity;
		}
	}
	~Graph() {
		if (verptr->visited && verptr->disweight && verptr->path && lists) {
			delete[] verptr->visited;
			delete[] verptr->disweight;
			delete[] verptr->path;
			delete[] lists;
		}
	}
public:
	void AddEdge(const size_t v, const size_t w, const size_t _weight);
	void Prim(size_t v);
	void Display()const;

private:
	std::unique_ptr<Vertex>verptr;
	std::list<size_t>*lists;
	std::vector<std::vector<size_t>>weight;
};

void Graph::AddEdge(const size_t v, const size_t w, const size_t _weight) {
	lists[v].push_back(w);
	lists[w].push_back(v);
	weight[v][w] = _weight;
	weight[w][v] = _weight;
}

void Graph::Prim(size_t v) {
	verptr->disweight[v] = 0;
	size_t count = 0;
	while (count!=verptr->vertexnumber-1) {
		size_t min = Infinity;
		for (int i = 0; i < verptr->vertexnumber; ++i) {
			if (verptr->visited[i] == false && min > verptr->disweight[i]) {
				min = verptr->disweight[i];
				v = i;
			}
		}
		verptr->visited[v] = true;
		++count;
		auto beg = lists[v].begin();
		while (beg != lists[v].end()) {
			if (verptr->visited[*beg] == false)
				if (MIN(verptr->disweight[*beg], weight[v][*beg])) {
					verptr->disweight[*beg] = weight[v][*beg];
					verptr->path[*beg] = v;
				}
			++beg;
		}
	}
}

void Graph::Display()const {
	size_t sum = 0;
	for (int i = 0; i < verptr->vertexnumber; ++i) {
		std::cout << "( " << i << " , " << verptr->path[i] << " )" << " weight:  " << verptr->disweight[i] << std::endl;
		sum += verptr->disweight[i];
	}
	std::cout << "The minimum spanning tree is: " << sum << std::endl;
}

int main(void)
{
	const size_t vertexnumber = 7;
	Graph graph(vertexnumber);
	graph.AddEdge(0, 1, 2);
	graph.AddEdge(0, 2, 4);
	graph.AddEdge(0, 3, 1);
	graph.AddEdge(1, 3, 3);
	graph.AddEdge(1, 4, 10);
	graph.AddEdge(2, 5, 5);
	graph.AddEdge(3, 2, 2);
	graph.AddEdge(3, 5, 8);
	graph.AddEdge(3, 6, 4);
	graph.AddEdge(3, 4, 2);
	graph.AddEdge(4, 6, 6);
	graph.AddEdge(6, 5, 1);
	graph.Prim(0);
	graph.Display();
	system("pause");
}

 

参考《数据结构与算法分析——C语言描述》

发布了50 篇原创文章 · 获赞 11 · 访问量 4082

猜你喜欢

转载自blog.csdn.net/qq_43145594/article/details/103428268
今日推荐