高阶数据结构-->图(下)

前言:本文笔者主要解释最小生成树以及最小生成树的一个算法-->Kruskal算法 

最小生成树:

连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树
就不在连通;反之,在其中引入任何一条新边,都会形成一条回路。
若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边。
构造最小生成树的三大准则:
1. 只能使用图中的边来构造最小生成树
2. 只能使用恰好n-1条边来连接图中的n个顶点
3. 选用的n-1条边不能构成回路
构造最小生成树的方法:Kruskal算法Prim算法。这两个算法都采用了逐步求解的贪心策略。
贪心算法:是指在问题求解时,总是做出当前看起来最好的选择。也就是说贪心算法做出的不是
整体 最优的的选择,而是某种意义上的局部最优解。贪心算法不是对所有的问题都能得到整体最优解。

Kruskal算法:

每次贪心(选最小权值的边)选定一条边之后,连接2个顶点之前,判断这2个顶点相连会不会构成环,如果不构成则连接,然后继续贪心,反之就直接放弃这条边,继续贪心下一条边重复上述判断操作,直到不构成环就相连。

在判断是否成环这里有个问题:如何判断2个顶点相连会成环?

首先暴力遍历效率太慢并且代码实现会过于复杂,回想一下之前笔者写的这篇:高阶数据结构-->图(上)-CSDN博客中,实现的并查集,用并查集判断2个顶点之间的集合关系,如果2个顶点在同一个集合则相连后必定构成环。

Kruskal算法代码实现

/*最小生成树*/
struct Edge
{
	int _srci;
	int _dsti;
	W _w;
	Edge(const int srci,const int dsti,const W& w)
		:_srci(srci)
		,_dsti(dsti)
		,_w(w)
	{}
};
struct cmp
{
	bool operator()(const Edge& px, const Edge& py)
	{
		return px._w > py._w;
	}
};
W Kruskal(self& minTree)
{
	minTree._vectex = _vectex;
	minTree._indexMap = _indexMap;
	priority_queue <Edge,vector<Edge>,cmp> q1;
	int n = _vectex.size();
	minTree._edges.resize(n);
	for (int i = 0; i < n; i++)
	{
		minTree._edges[i].resize(n, WMAX);
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (_edges[i][j] != WMAX && i < j)
			{
				q1.push(Edge(i, j,_edges[i][j]));
			}
		}
	}
	int size = 0;
	W weightSum = W();
	unionFindSet ufs(n);
	while (q1.size())
	{
		Edge front = q1.top();
		q1.pop();
		if (ufs.isInSet(front._srci, front._dsti) == false)
		{
			minTree._addEdges(front._srci, front._dsti, front._w);
			ufs.Union(front._srci, front._dsti);
			weightSum += front._w;
			++size;
		}
	}

	if (size == n - 1) return weightSum;
	else return W();
}

加上图的完整代码:

template<class V, class W, W WMAX = INT_MAX, bool judgMent = false>
class Graph
{
	typedef Graph<V, W, WMAX, judgMent> self;
public:
	Graph() = default;
	Graph(const V* vectex, size_t n)
	{
		_vectex.reserve(n);
		for (int i = 0; i < n; i++)
		{
			_vectex.push_back(vectex[i]);
			_indexMap[vectex[i]] = i;
		}
		_edges.resize(n);
		for (int i = 0; i < n; i++)
		{
			_edges[i].resize(n, WMAX);
		}
	}
	size_t getIndex(const V& queriedNum)
	{
		auto it = _indexMap.find(queriedNum);
		if (it != _indexMap.end())
		{
			return it->second;
		}
		else
		{
			throw "输入的顶点错误!";
			return -1;
		}
	}
	void _addEdges(int targetSrc, int targetDes, const W& edge)
	{
		_edges[targetSrc][targetDes] = edge;
		if (judgMent == false)//判断是否是无向图
		{
			_edges[targetDes][targetSrc] = edge;
		}
	}
	void AddEdge(const V& src, const V& des, const W& edge)
	{
		size_t targetSrc = getIndex(src);
		size_t targetDes = getIndex(des);
		_addEdges(targetSrc, targetDes,edge);
	}
    	/*最小生成树*/
	struct Edge
	{
		int _srci;
		int _dsti;
		W _w;
		Edge(const int srci,const int dsti,const W& w)
			:_srci(srci)
			,_dsti(dsti)
			,_w(w)
		{}
	};
	struct cmp
	{
		bool operator()(const Edge& px, const Edge& py)
		{
			return px._w > py._w;
		}
	};
	W Kruskal(self& minTree)
	{
		minTree._vectex = _vectex;
		minTree._indexMap = _indexMap;
		priority_queue <Edge,vector<Edge>,cmp> q1;
		int n = _vectex.size();
		minTree._edges.resize(n);
		for (int i = 0; i < n; i++)
		{
			minTree._edges[i].resize(n, WMAX);
		}
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				if (_edges[i][j] != WMAX && i < j)
				{
					q1.push(Edge(i, j,_edges[i][j]));
				}
			}
		}
		int size = 0;
		W weightSum = W();
		unionFindSet ufs(n);
		while (q1.size())
		{
			Edge front = q1.top();
			q1.pop();
			if (ufs.isInSet(front._srci, front._dsti) == false)
			{
				minTree._addEdges(front._srci, front._dsti, front._w);
				ufs.Union(front._srci, front._dsti);
				weightSum += front._w;
				++size;
			}
		}

		if (size == n - 1) return weightSum;
		else return W();
	}
 private:
	vector<V> _vectex;
	map<V, int> _indexMap;
	vector<vector<W>> _edges;
};

可以测试一下:

int main()
{
	const char* str = "abcdefghi";
	adjacencyMatrices::Graph<char, int> g(str, strlen(str));
	g.AddEdge('a', 'b', 4);
	g.AddEdge('a', 'h', 8);
	//g.AddEdge('a', 'h', 9);
	g.AddEdge('b', 'c', 8);
	g.AddEdge('b', 'h', 11);
	g.AddEdge('c', 'i', 2);
	g.AddEdge('c', 'f', 4);
	g.AddEdge('c', 'd', 7);
	g.AddEdge('d', 'f', 14);
	g.AddEdge('d', 'e', 9);
	g.AddEdge('e', 'f', 10);
	g.AddEdge('f', 'g', 2);
	g.AddEdge('g', 'h', 1);
	g.AddEdge('g', 'i', 6);
	g.AddEdge('h', 'i', 7);
	adjacencyMatrices::Graph<char, int> kminTree;
	cout << "Kruskal:" << g.Kruskal(kminTree) << endl;
	return 0;
}

输出:

Prim算法和最短路径问题会单独开一章讲述,敬请期待!!

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值