左神算法学习日记——图

图的各种基础算法

head.h

#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<hash_map>
#include<hash_set>
#include<stack>
#include<queue>
#include<algorithm>
#include<xfunctional>
#include<sstream>
#include<list>
#include<memory>

using namespace std;

 unionfind.h

#include"head.h"
class Node;
class Edge; 
template<class _Ty>
class unionfinset
{
private:
	hash_map<_Ty, _Ty> father;
	hash_map<_Ty, int> size;
public:
	unionfinset() = default;
	unionfinset(vector<_Ty> data)//用数组data初始化并查集
	{
		father.clear();
		size.clear();
		for (_Ty var : data)
		{
			father.insert({ var, var });
			size.insert({ var, 1 });
		}
	}
	_Ty findrep(_Ty node)
	{
		_Ty f = father[node];
		if (node != f)
		{
			node = f;
			f = findrep(node);//通过递归来传递代表结点
		}
		father[node] = f;//将所有结点的父节点都设为代表结点
		return f;
	}
	bool issameset(_Ty a, _Ty b)
	{
		return findrep(a) == findrep(b);
	}
	void Union(_Ty a, _Ty b)
	{
		_Ty arep = findrep(a);
		_Ty brep = findrep(b);
		if (arep != brep)
		{
			int asize = size[arep];
			int bsize = size[brep];
			if (asize > bsize)
			{
				father[b] = a;
				size[a] += size[b];
			}
			else
			{
				father[a] = b;
				size[b] += size[a];
			}
		}
	}
};

Graph.h 

#include"unionfind.h"
class Graph
{
public:
	std::hash_map<int, std::shared_ptr<Node>> nodes;//记录了图中的所有结点
	std::hash_set<std::shared_ptr<Edge>> edges;//记录了图中所有的边
	Graph() = default;
};

Node.h 

#include"Graph.h"
class Node
{
public:
	int value;
	int in;//结点的入度
	int out;//结点的出度
	int sign;//该结点是否被访问过
	std::list<std::shared_ptr<Node>> nexts;//该结点所能到达的边
	std::list<std::shared_ptr<Edge>> edges;//以该结点为起始结点的边
	Node() = default;
	Node(int n);
};

 Edge.h

#include"Node.h"
class Edge
{
public:
	int w;
	std::shared_ptr<Node> from;//该边的起始点
	std::shared_ptr<Node> to;//该边的终止点
	Edge(std::shared_ptr<Node> f, std::shared_ptr<Node> to, int w);//以起始结点,终止点,权重初始化边
};

 tu.cpp

#include"edge.h"

Node::Node(int n)
{
	value = n;
	in = 0;
	out = 0;
	sign = 1;
}

Edge::Edge(shared_ptr<Node> f, shared_ptr<Node> t, int weight)
{
	from = f;
	to = t;
	w = weight;
}
//以(权重,起始点,终止点)的三元组代表一条边,用包含多个这样的三元组的数组初始化图
Graph creatGraph(vector<vector<int>> mat)
{
	Graph gra;
	for (vector<int> var : mat)//遍历每一条边
	{
		int weight = var[0];
		int from = var[1];
		int to = var[2];
		if (gra.nodes.find(from) == gra.nodes.end())
		{
			gra.nodes.insert({ from, make_shared<Node>(from) });//如果图中没有该起始结点则将其插入图的结点中
		}
		if (gra.nodes.find(to) == gra.nodes.end())
		{
			gra.nodes.insert({ to, make_shared<Node>(to) });//如果图中没有包含该终止结点则将其插入图的结点中
		}
		shared_ptr<Node> From = gra.nodes[from];
		shared_ptr<Node> To = gra.nodes[to];
		shared_ptr<Edge> edge = make_shared<Edge>(From, To, weight);//构造边
        //更新边的两个结点的出度和入度
		edge->from->out++;
		edge->to->in++;
		From->nexts.push_back(To);//将to插入到from的点集合中
		From->edges.push_back(edge);//将edge插入到from的边集合中
		gra.edges.insert(edge);//将edge插入到图的边集合中
	}
	return gra;
}

void bfs(shared_ptr<Node> node)
{
	queue<shared_ptr<Node>> q;
	q.push(node);
	cout << node->value<<"  ";//输出第一个结点
	node->sign = 0;
	while (!q.empty())
	{
		list<shared_ptr<Node>> l = q.front()->nexts;
		q.pop(); 
		for (shared_ptr<Node> next:l)
		{
			if (next->sign)
			{
				next->sign = 0;
				q.push(next);
				cout << next->value<<"  ";//将当前结点的边集合全部输出
			}
		}
	}
}

void dfs(shared_ptr<Node> node)
{
	stack<shared_ptr<Node>> s;
	s.push(node);
	cout << node->value;//输出第一个结点
	node->sign = 0;
	while (!s.empty())
	{
		shared_ptr<Node> cur = s.top();
		s.pop();//将当前结点弹出栈,在当前结点的点集合为空时,下一轮循环访问其父结点的点集合
		for (shared_ptr<Node> next:cur->nexts)
		{
			if (next->sign)
			{
				s.push(cur);//当前结点的点集合不为空且存在未被访问过的结点,将当前结点压入栈
				s.push(next);//压入下一个结点
				next->sign = 0;
				cout << next->value;//输出当前结点的边集合中的第一条边
				break;
			}
		}
	}
}

list<shared_ptr<Node>> Topsort(Graph& gra)
{
	stack<shared_ptr<Node>> zeroin;
	for (auto var : gra.nodes)
	{
		if (var.second->in == 0)
		{
			zeroin.push(var.second);//将所有入度为0的结点压入栈中
		}
	}
	list<shared_ptr<Node>> res;
	while (!zeroin.empty())
	{
		shared_ptr<Node> cur = zeroin.top();
		res.push_back(zeroin.top());
		zeroin.pop();
		for (auto next : cur->nexts)//遍历当前结点的点集合,并将它们的入度减1
		{
			next->in--;
			if (next->in == 0)//当当前结点的点集合更新后,出现入度为0的结点,则将其压入
				zeroin.push(next);
		}
	}
	return res;
}

struct sm
{
	bool operator() (shared_ptr<Edge>& _left, shared_ptr<Edge>& _right)//设置小顶堆的比较器
	{
		return _left->w > _right->w;
	}
};

vector<shared_ptr<Edge>> kruskal(Graph gra)
{
	vector<shared_ptr<Node>> data;
	for (auto var : gra.nodes)
	{
		data.push_back(var.second);
	}
	unionfinset<shared_ptr<Node>> uni(data);//初始化点的并查集
	priority_queue<shared_ptr<Edge>,vector<shared_ptr<Edge>>,sm> small;
    //将所有边输入,按照权重建立小顶堆
	for (auto var : gra.edges)
	{
		small.push(var);
	}
	vector<shared_ptr<Edge>> res;
	while (!small.empty())
	{
		shared_ptr<Edge> edge = small.top();
		small.pop();
		if (!uni.issameset(edge->from, edge->to))//如果当前边的两个点不在一个并查集中,则说明加入它没有构成环路
		{
			res.push_back(edge);
			uni.Union(edge->from, edge->to);//将当前边的两个结点的并查集合在一起
		}
	}
	return res;
}

set<shared_ptr<Edge>> prim(Graph gra)
{
	shared_ptr<Node> node = gra.nodes.begin()->second;
	set<shared_ptr<Edge>> res;
	if (node->sign)
	{
		priority_queue<shared_ptr<Edge>,vector<shared_ptr<Edge>>,sm> small;
        //建立当前结点的边集合的小顶堆
		for (auto var : node->edges)
		{
			small.push(var);
		}
		node->sign = 0;
		while (!small.empty())
		{
			shared_ptr<Edge> edge = small.top();
			small.pop();
			node = edge->to;
			if (node->sign)//查看当前结点的最小权重的边的邻接结点是否被访问过,访问过则说明会构成环路
			{
				node->sign = 0;
				res.insert(edge);
				for (auto var : node->edges)
				{
					small.push(var);//当邻接点没有被访问过将邻接点的边集合输入到小顶堆中
				}
			}
		}
	}
	return res;
}

list<shared_ptr<Edge>> dijstra(shared_ptr<Node>& node)
{
	priority_queue<pair<int, shared_ptr<Edge>>, vector<pair<int, shared_ptr<Edge>>>, dij> small;
	int dis = 0;
	small.push(make_pair(dis,make_shared<Edge>(node,node,0)));//记录每个结点从第一个结点出发所需要的最小距离和最后一条边,并放入小顶堆中
	node->sign = 0;//已知最小距离的结点标记为0,表示已达到最优
	list<shared_ptr<Edge>> res;
	while (!small.empty())
	{
		pair<int, shared_ptr<Edge>> cur = small.top();//获取当前剩余边中终止结点距离第一个结点距离最小的边
		small.pop();
		if (cur.second->to->sign)
		{
            res.push_back(cur.second);//如果该具有最小距离的边的终止结点没有被访问过则将其放入返回链表中中
		    cur.second->to->sign = 0;//并将其终止结点标记为已访问过
        }//
		dis += cur.second->w;
		for (auto var : cur.second->to->edges)
		{
			if (var->to->sign)
			{
				small.push(make_pair(dis + var->w, var));//将当前边的终止结点所邻接的边中其终止结点没有被访问过的边和该终止结点到第一个结点的距离放入小顶堆中
			}
		}
	}
	return res;
}

int main()
{
	vector<vector<int>> mat = { { 7, 1, 2 }, { 7, 2, 1 }, { 3, 1, 3 }, { 3, 3, 1 }, { 4, 1, 4 }, { 4, 4, 1 },
	{ 8, 2, 3 }, { 8, 3, 2 }, { 2, 2, 7 }, { 2, 7, 2 }, { 1, 7, 3 }, { 1, 3, 7 },
	{ 7, 3, 5 }, { 7, 5, 3 }, { 9, 4, 6 }, { 9, 6, 4 } };
	Graph gra = creatGraph(mat);
	vector<shared_ptr<Edge>> e = kruskal(gra);
	for (auto var : e)
	{
		cout << var->w << "  " << var->from->value << "  " << var->to->value << endl;
	}
}

猜你喜欢

转载自blog.csdn.net/w275412237/article/details/89460109