文章目录
- 一.图邻接矩阵数据结构
- 二.kruskal算法
-
- 三.Prim算法
-
一.图邻接矩阵数据结构
- 以STL
vector
和unordered_map
为适配容器实现图数据结构:
namespace Graph_Structure
{
template<class Vertex, class Weight = int, Weight MAX_W = INT_MAX, bool Direction = false>
class Graph
{
typedef Graph<Vertex, Weight, MAX_W, Direction> Self;
public:
Graph() = default;
Graph(const Vertex* a, size_t n)
{
_vertexs.reserve(n);
_indexMap.rehash(n);
_matrix.resize(n, std::vector<Weight>(n, MAX_W));
for (size_t i = 0; i < n; ++i)
{
_vertexs.push_back(a[i]);
_indexMap[a[i]] = i;
}
}
size_t GetVertexIndex(const Vertex& vertex)
{
if (_indexMap.find(vertex) == _indexMap.end())
{
throw "invalued_para";
return -1;
}
return _indexMap[vertex];
}
void _AddEdge(size_t srci, size_t dsti, const Weight& w)
{
if (Direction == false)
{
_matrix[dsti][srci] = w;
}
_matrix[srci][dsti] = w;
}
void AddEdge(const Vertex& src, const Vertex& dst, const Weight& w)
{
if (_indexMap.find(src) == _indexMap.end() || _indexMap.find(dst) == _indexMap.end())
{
throw "invalued_para";
}
size_t srci_index = GetVertexIndex(src);
size_t dst_index = GetVertexIndex(dst);
_AddEdge(srci_index, dst_index, w);
}
void Print()
{
for (auto e : _vertexs)
{
std::cout << e << '[' << _indexMap[e] << ']' << std::endl;
}
std::cout << " ";
for (int i = 0; i < _vertexs.size(); ++i)
{
std::cout << i << " ";
}
std::cout << std::endl;
int i = 0;
for (auto arry : _matrix)
{
std::cout << i++ << ' ';
for (auto e : arry)
{
if (e == MAX_W)
{
printf("%4c ", '*');
}
else
{
printf("%4d ", e);
}
}
std::cout << std::endl;
}
}
void BFS(const Vertex& src)
{
size_t begin = GetVertexIndex(src);
std::queue<int> QNode;
std::vector<bool> Label(_vertexs.size(), false);
QNode.push(begin);
Label[begin] = true;
size_t Level = 0;
while (!QNode.empty())
{
size_t LevelSize = QNode.size();
for (size_t i = 0; i < LevelSize; ++i)
{
size_t front = QNode.front();
QNode.pop();
std::cout << _vertexs[front] << '[' << front << ']' << std::endl;
for (int j = 0; j < _vertexs.size(); ++j)
{
if (Label[j] == false && _matrix[front][j] != MAX_W)
{
QNode.push(j);
Label[j] = true;
}
}
}
}
}
void DFS(const Vertex& src)
{
std::vector<bool> visited(_vertexs.size(), false);
_DFS(GetVertexIndex(src), visited);
}
private:
void _DFS(size_t srci, std::vector<bool>& visited)
{
visited[srci] = true;
std::cout << _vertexs[srci] << '[' << srci << ']' << std::endl;
for (int i = 0; i < _vertexs.size(); ++i)
{
if (_matrix[srci][i] != MAX_W && visited[i] == false)
{
_DFS(i, visited);
}
}
}
private:
std::vector<Vertex> _vertexs;
std::unordered_map<Vertex, size_t> _indexMap;
std::vector<std::vector<Weight>> _matrix;
};
}
- 一个无向连通图的生成树指的是包含该图所有顶点的无环连通子图
- 最小生成树指的是图的所有生成树中边权值之和最小的生成树
二.kruskal算法
kruskal
算法求图的最小生成树的基本思想是依次选出图中权值最小的边来构建生成树,图中某条边加入生成树之前,要判断生成树中是否会形成环(这是该算法的一个难点),如果会形成环,则跳过这条边继续选下一条权值最小边.若连通图共有N
个顶点,则选出N-1
条边则最小生成树构建完成
算法实现思想
- 选最小边的方法:将图的所有边加入到小堆数据结构(STL
priority_queue
的底层结构)中,选边时可以从堆顶选到最小边,选N
条边的时间复杂度为O(NlogN)
- 某条边加入生成树之前,判断生成树中是否会出现环的方法:采用并查集实现判环,选边的过程中将生成树中互相连通的顶点在并查集中合并到同一个连通分量中.
- 最小生成树构建的过程中,如果选出的权值最小边的起点和终点位于并查集的同一个连通分量中,则该边不能加入最小生成树(原理:两个顶点在构建最小生成树的过程中如果两次连通则生成树中必定会出现环)
- 采用并查集判环,每次判环的时间复杂度接近
O(1)
kruskal算法接口实现
- 定义一个描述边的内部类(记录边的起点和终点以及边的权值)
class Edge
{
public:
size_t _srci;
size_t _dsti;
Weight _weight;
Edge(size_t srci, size_t dsti, Weight weight)
:_srci(srci),
_dsti(dsti),
_weight(weight)
{}
bool operator>(const Edge& edge) const
{
return _weight > edge._weight;
}
};
Weight Kruskal(Self& MinTree)
{
MinTree._vertexs = _vertexs;
MinTree._indexMap = _indexMap;
MinTree._matrix.resize(_vertexs.size(), std::vector<Weight>(_vertexs.size(), MAX_W));
std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge>> Qedge;
for (int i = 0; i < _matrix.size(); ++i)
{
for (int j = 0; j < _matrix.size(); ++j)
{
Qedge.push(Edge(i, j, _matrix[i][j]));
}
}
std::vector<int> FindSet(_vertexs.size(), -1);
auto FindRoot = [&FindSet](size_t root) {
while (FindSet[root] >= 0)
{
root = FindSet[root];
}
return root;
};
Weight totalweight = Weight();
size_t Edgenums = 0;
while (!Qedge.empty() && Edgenums < _vertexs.size() - 1)
{
Edge MinEdge = Qedge.top();
Qedge.pop();
size_t root1 = FindRoot(MinEdge._srci);
size_t root2 = FindRoot(MinEdge._dsti);
if (root1 != root2)
{
MinTree._AddEdge(MinEdge._srci, MinEdge._dsti, MinEdge._weight);
std::cout << '[' << _vertexs[MinEdge._srci] << ']' << "->" << '[' << _vertexs[MinEdge._dsti] << ']' << std::endl;
if (FindSet[root2] > FindSet[root1])
std::swap(root1, root2);
FindSet[root2] = root1;
++Edgenums;
totalweight += MinEdge._weight;
}
}
if (Edgenums == _vertexs.size() - 1)
{
return totalweight;
}
else
{
return Weight();
}
}
void TestGraphMinTree()
{
const char str[] = "abcdefghi";
Graph_Structure::Graph<char, int> g(str, strlen(str));
g.AddEdge('a', 'b', 4);
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);
Graph_Structure::Graph<char, int> kminTree;
std::cout << "Kruskal:" << g.Kruskal(kminTree) << std::endl;
kminTree.Print();
}
三.Prim算法
Prim
最小生成树算法思想:用一个顶点集合来记录已经加入最小生成树的顶点,选定一个构建最小生成树的起始顶点(并将该顶点加入顶点集合),将与顶点集合中的所有点相连的边加入堆数据结构中,从堆顶依次选取权值最小边加入最小生成树,然后更新最小生成树顶点集合和堆,如此往复。
- 若某条边的起点和终点已经在最小生成树顶点集合中,则跳过该边,取下一条权值最小边,通过这种方式可以保证最小生成树中不会出现环
Prim算法接口实现
Weight Prim(Self& MinTree, Vertex start)
{
MinTree._vertexs = _vertexs;
MinTree._indexMap = _indexMap;
MinTree._matrix.resize(_vertexs.size(), std::vector<Weight>(_vertexs.size(), MAX_W));
std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge>> Qedge;
size_t starti = GetVertexIndex(start);
std::vector<bool> InMinTree(_vertexs.size(), false);
InMinTree[starti] = true;
for (size_t i = 0; i < _vertexs.size(); ++i)
{
if (_matrix[starti][i] != MAX_W)
{
Qedge.push(Edge(starti, i, _matrix[starti][i]));
}
}
Weight totalweight = Weight();
size_t Edgenums = 0;
while (!Qedge.empty() && Edgenums < _vertexs.size() - 1)
{
Edge MinEdge = Qedge.top();
Qedge.pop();
if (InMinTree[MinEdge._dsti] == false)
{
InMinTree[MinEdge._dsti] = true;
MinTree._AddEdge(MinEdge._srci, MinEdge._dsti, MinEdge._weight);
for (size_t i = 0; i < _vertexs.size(); ++i)
{
if (_matrix[MinEdge._dsti][i] != MAX_W && InMinTree[i] == false)
{
Qedge.push(Edge(MinEdge._dsti, i, _matrix[MinEdge._dsti][i]));
}
}
++Edgenums;
totalweight += MinEdge._weight;
}
}
if (Edgenums == _vertexs.size() - 1)
{
return totalweight;
}
else
{
return Weight();
}
}
void TestGraphMinTree()
{
const char str[] = "abcdefghi";
Graph_Structure::Graph<char, int> g(str, strlen(str));
g.AddEdge('a', 'b', 4);
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);
Graph_Structure::Graph<char, int> pminTree;
std::cout << "Prim:" << g.Prim(pminTree, 'a') << std::endl;
pminTree.Print();
std::cout << std::endl;
}