Kruskal算法
同Prim算法,Kruskal算法用于求解连通图中最小生成树问题。
该算法从直观上比较好理解,其过程为:
- 将图中的所有边按照权值大小从小到大排序
- 依次选择最小的边,只要该边加入生成树中不会成环,就将这条边加入生成树
Kruskal算法的实现
实现分辨顶点加入树中是否会导致成环的方法是将所有顶点分类,在一棵树中的为同样一类,其过程见http://data.biancheng.net/view/41.html
这里用的方法和上边资料的方法相同但在表述上稍有差别。
建立一个数组,命名为parent[N],N为顶点个数,存放每个顶点所连接到的上一个顶点(即父结点),开始时初始化为-1,表示没有父结点,为树根。
int parent[N];
for (int i = 0; i < N; i++) {
parent[i] = -1;
}
为查找某一结点所连接的树根,定义find(int i)函数,其作用为循环查找i结点的父节点,直到树根为止。
查找每一个结点所在子树的树根就是查找这个结点属于哪一个分类的过程!
inline int find(int k) // 返回值为结点k的树根
{
while (parent[k] != -1)
k = parent[k];
return k;
}
来举一个例子:
目前的状态为:
parent[N] = {1, 5, 8, 7, 7, 8, -1, -1, 6}
从图上可知下一个要加入生成树的边为edges[7],按照find函数检查V6的树根find(6),结果为6,是他本身,而V7的树根find(7)为6,说明这两个点在一颗生成树上,连接会成环!所以跳过这一边。
接下来检查edges[8],find(8)返回6,find(2)返回6,同样跳过。
然后是edges[9],find(6)返回6,find(7)返回7,说明V6、V7不在同一生成树上,可以将edges[9]加入生成树。注意到此时V7的parent为-1,即V7为根节点,为将子树{V3, V4, V7}加入,令parent[7] = 6,即将V7的树根设置为6。
这样V3、V4、V7按照find函数查找的结果都是6,它们被连接到一颗生成树上了。当然反过来让V6的树根为V7也是可以的。
按照上述过程直到有n - 1个边加入生成树算法即执行完毕。
以下是代码:
MatrixGraph.h
#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>
#include <assert.h>
using namespace std;
#ifndef _MATRIXGRAPH
#define _MATRIXGRAPH
const int INF { 65535 };
template <class T>
class MGraph
{
private:
vector<T> vexs;
vector<vector<int>> arc;
int m_vexsnum, m_edgenum;
bool* visited;
int* k_parent; // use for kruskal
private:
inline int m_Kruskal_find(int);
public:
MGraph(bool direction);
MGraph(const char* in_file);
void Print();
bool Save(const char* out_name);
void Prim();
void Kruskal();
};
#endif
MatrixGraph.cpp
#include "MatrixGraph.h"
using namespace std;
#define _INPUT 0
template <class T>
MGraph<T>::MGraph(bool direction)
{
cout << "Please input the number of vertexs:";
cin >> m_vexsnum;
vexs.resize(m_vexsnum);
arc.resize(m_vexsnum);
for (register int i = 0; i < m_vexsnum; i++) {
arc[i].resize(m_vexsnum);
}
for(register size_t i = 0; i < m_vexsnum; i++)
{
for(register size_t j = 0; j < m_vexsnum; j++)
{
arc[i][j] = (i == j) ? 0 : INF;
}
}
cout << "Please input the number of edges:";
cin >> m_edgenum;
// cout << "Please input vertexs:" << endl;
for(register size_t i = 0; i < m_vexsnum; i++)
{
// cin >> vexs[i];
vexs[i] = i;
}
cout << "Please input [in out weight] of edges:" << endl;
for(register size_t i = 0; i < m_edgenum; i++)
{
int ti{ 0 }, tj{ 0 }, temp{ 0 };
cin >> ti;
cin >> tj;
cin >> temp;
arc[ti][tj] = temp;
if (!direction) { arc[tj][ti] = temp; }
}
visited = nullptr;
}
template<class T>
MGraph<T>::MGraph(const char* in_file)
{
ifstream ifile(in_file, ios::binary);
assert(ifile.is_open());
ifile.read((char*)&m_vexsnum, sizeof(m_vexsnum));
ifile.read((char*)&m_edgenum, sizeof(m_edgenum));
vexs.resize(m_vexsnum);
arc.resize(m_vexsnum);
for (register int i = 0; i < m_vexsnum; i++) {
arc[i].resize(m_vexsnum);
}
for (register int i = 0; i < m_vexsnum; i++) {
ifile.read((char*)&vexs[i], sizeof(T));
}
for (int i = 0; i < m_vexsnum; i++)
{
for (int j = 0; j < m_vexsnum; j++)
{
ifile.read((char*)&arc[i][j], sizeof(int));
}
cout << endl;
}
ifile.close();
visited = nullptr;
}
template<class T>
void MGraph<T>::Print()
{
cout << "Elements:" << endl;
for (register int i = 0; i < m_vexsnum; i++) {
cout << vexs[i] << " ";
}
cout << endl << "Edges:" << endl;
for (register int i = 0; i < m_vexsnum; i++)
{
for (register int j = 0; j < m_vexsnum; j++)
{
cout << setw(7) << arc[i][j] << " ";
}
cout << endl;
}
}
template<class T>
bool MGraph<T>::Save(const char* out_name)
{
ofstream ofile(out_name, ios::binary);
assert(ofile.is_open());
ofile.write((char*)&m_vexsnum, sizeof(m_vexsnum));
ofile.write((char*)&m_edgenum, sizeof(m_edgenum));
for (register int i = 0; i < m_vexsnum; i++) {
ofile.write((char*)&vexs[i], sizeof(T));
}
cout << endl;
for (int i = 0; i < m_vexsnum; i++)
{
for (int j = 0; j < m_vexsnum; j++)
{
cout << setw(3) << arc[i][j] << " ";
ofile.write((char*)&arc[i][j], sizeof(int));
}
cout << endl;
}
ofile.close();
return true;
}
template<class T>
void MGraph<T>::Prim()
{
typedef struct {
int adjvex;
int lowcost;
}close_node;
vector<close_node> close(m_vexsnum);
// 初始化,起始点为0,存储其余点到0点的距离
close[0].lowcost = 0;
for (int i = 0; i < m_vexsnum; i++) {
close[i].adjvex = 0;
close[i].lowcost = arc[0][i];
}
for (int i = 0; i < m_vexsnum; i++) {
// 查找生成树外的点到生成树中点的最近距离
int k{ 0 }, min{ INF };
for (int j = 0; j < m_vexsnum; j++) {
if (close[j].lowcost > 0 && close[j].lowcost < min) {
k = j;
min = close[j].lowcost;
}
}
// 输出信息
cout << "(" << close[k].adjvex << "," << k << ")" << endl;
// 将距树中点距离最小的点加入生成树
close[k].lowcost = 0;
// 更新列表,看看那些点距离新加入的k比之前近
for (int j = 0; j < m_vexsnum; j++) {
if (close[j].lowcost > 0 && arc[k][j] < close[j].lowcost) {
close[j].adjvex = k;
close[j].lowcost = arc[k][j];
}
}
// 更新之后,close之中永远存的是各点到生成树的最小距离和连接的点
}
}
template<class T>
inline int MGraph<T>::m_Kruskal_find(int k)
{
while (k_parent[k] != -1)
k = k_parent[k];
return k;
}
template<class T>
void MGraph<T>::Kruskal()
{
typedef struct {
int begin;
int end;
int weight;
}Edge;
// 将边加入列表
Edge* edges = new Edge[m_edgenum]; assert(edges);
Edge* p = edges;
for (int i = 0; i < m_vexsnum - 1; i++) {
for (int j = i + 1; j < m_vexsnum; j++) {
if (arc[i][j] == INF)
continue;
*(p++) = { i, j, arc[i][j] };
}
}
// 按weight冒泡排序
for (int i = 0; i < m_edgenum; i++) {
for (int j = 0; j < (m_edgenum - 1 - i); j++) {
if (edges[j].weight > edges[j + 1].weight) {
Edge temp = edges[j + 1];
edges[j + 1] = edges[j];
edges[j] = temp;
}
}
}
cout << "Edges after order:" << endl;
for (int i = 0; i < m_edgenum; i++) {
cout << edges[i].begin << " " << edges[i].end << " " << edges[i].weight << endl;
}
// 初始化parent数组
k_parent = new int[m_vexsnum]; assert(k_parent);
for (int i = 0; i < m_vexsnum; i++)
k_parent[i] = -1;
cout << "\nResult:" << endl;
for (int i = 0; i < m_edgenum; i++) {
// 查找当前边两端结点的树根
int n = m_Kruskal_find(edges[i].begin);
int m = m_Kruskal_find(edges[i].end);
// 若不为同一颗树,将该边加入生成树并将这两颗子树相连
if (m != n) {
// 输出
cout << edges[i].begin << " " << edges[i].end << " " << edges[i].weight << endl;
// 连接子树
k_parent[m] = n;
}
}
delete[] k_parent;
}
int main(void)
{
#if _INPUT
MGraph<int> g(false);
g.Print();
g.Save("Prim_test.dat");
#endif
MGraph<int> g1("Prim_test.dat");
g1.Print();
cout << "\nDepth first search:" << endl;
g1.DFS();
cout << "\nBreadth first search:" << endl;
g1.BFS();
cout << "\n\nPrim:" << endl;
g1.Prim();
cout << "\n\nKruskal:" << endl;
g1.Kruskal();
return 0;
}