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

前言:本文笔者详细介绍了并查集的概念和实现, 图的基本概念和存储结构。

并查集

引入:以社交网络为例,并查集被用于处理人际关系、社群发现等问题。通过将每个人看作一个节点,并使用并查集来管理他们之间的社交关系,当两个人成为朋友时,即执行合并(Union)操作,将它们放在同一个连通分量中。当需要判断两个人是否属于同一个社群时,只需执行发现(Find)操作,判断它们是否属于同一个连通分量即可。并查集主要应用于连通性问题中

所有位置抽象到一个数组中时,

1. 数组的下标对应集合中元素的编号
2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
3. 数组中如果为非负数,代表该元素的最近父节点在数组中的下标

并查集的实现

class unionFindSet
{
public:
	unionFindSet(size_t sz)
		:_ufs(sz,-1)
	{}
	void Union(int x, int y)
	{
		int rootX = findRoot(x);
		int rootY = findRoot(y);
		/*当x和y在同一个集合中的时候,就不用合并直接返回即可*/
		if (rootX != rootY)
		{
			_ufs[rootX] += _ufs[rootY];
			_ufs[rootY] = rootX;
		}
	}
	int findRoot(int x)
	{
		int root = x;
		while (_ufs[root] >= 0)
		{
			root = _ufs[root];
		}
		return root;
	}
	/*显示当前并查集中有几个集合*/
	size_t size()const
	{
		int _count = 0;
		for (auto x : _ufs)
		{
			if (x < 0) ++_count;
		}
		return _count;
	}
private:
	vector<int> _ufs;
};

图的基本概念:

非人话:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成的一种数据结构,用于表示多对多的关系。图可以表示为G(V, E),其中G表示一个图,V是图G中顶点的集合,E是图G中边的集合。线性表和树都可以看作是图的特殊形式。(树是一种特殊的图),更多的图的分类之后提到再细说。

人话:简单来说,图是一种表示型数据结构,意在表示某种场景,为了表示不同的场景衍生出了各种类型的图,图的顶点可以看作是一个元素,图的边可以看作元素与元素之间的某种关系(并不一定是单一的关系).

图的存储结构的实现(邻接矩阵)

邻接矩阵:

图解:1表示相连,0表示不相连(以无向图为例)

代码实现

namespace adjacencyMatrices
{
	template<class V, class W, W WMAX = INT_MAX, bool judgMent = false>
	class Graph
	{
	public:
		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(const V& src, const V& des, const W& edge)
		{
			size_t targetSrc = getIndex(src);
			size_t targetDes = getIndex(des);
			_edges[targetSrc][targetDes] = edge;
			if (judgMent == false)//判断是否是无向图
			{
				_edges[targetDes][targetSrc] = edge;
			}
		}
		void printTest()
		{
			for (int i = 0; i < _vectex.size(); i++)
			{
				cout << i << "-->" << _vectex[i] << endl;
			}
			cout << endl;
			/*打印横下标*/
			cout << "  ";
			for (int i = 0; i < _edges.size(); i++)
			{
				cout << i <<" ";
			}
			cout << endl;
			/*打印矩阵*/
			for (int i = 0; i < _edges.size(); ++i)
			{
				/*打印竖下标*/
				cout << i << " ";
				for (int j = 0; j < _edges[i].size(); ++j)
				{
					if (_edges[i][j] != WMAX)
						cout << _edges[i][j] << " ";
					else
						cout << "#" << " ";
				}
				cout << endl;
			}
			cout << endl << endl;
			// 打印所有的边
			for (int i = 0; i < _edges.size(); ++i)
			{
				for (int j = 0; j < _edges[i].size(); ++j)
				{
					if (i < j && _edges[i][j] != WMAX)
					{
						cout << _vectex[i] << "-" << _vectex[j] << ":" <<
							_edges[i][j] << endl;
					}
				}
			}
		}
     private:
		vector<V> _vectex;
		map<V, int> _indexMap;
		vector<vector<int>> _edges;
	};
}

邻接矩阵的优势:

1.邻接矩阵的存储方式适合稠密图。

2.O(1)的时间复杂度判断2个顶点的连接关系,并取到权值。

邻接矩阵的缺点:

1.不适合稀疏图

2.不适合判断一个顶点连接的边数 -->O(N)

邻接表

为了解决邻接矩阵不擅长的问题,引入了邻接表的结构

图示:使用链表将自己链接顶点的边都挂在自己下面(以无向图为例)

代码实现:

namespace LinkTable
{
	template<class W>
	struct LinkEdge
	{
		int _srcIndex;
		int _dstIndex;
		W _w;
		LinkEdge<W>* _next;
		LinkEdge(const W& w)
			: _srcIndex(-1)
			, _dstIndex(-1)
			, _w(w)
			, _next(nullptr)
		{}
	};
	template<class V, class W, bool Direction = false>
	class Graph
	{
		typedef LinkEdge<W> Edge;
	public:
		Graph(const V* vertexs, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(vertexs[i]);
				_vIndexMap[vertexs[i]] = i;
			}
			_linkTable.resize(n, nullptr);
		}
		size_t GetVertexIndex(const V& v)
		{
			auto ret = _vIndexMap.find(v);
			if (ret != _vIndexMap.end())
			{
				return ret->second;
			}
			else
			{
				throw invalid_argument("不存在的顶点");
				return -1;
			}
		}
		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srcindex = GetVertexIndex(src);
			size_t dstindex = GetVertexIndex(dst);
			// 0 1
			Edge* sd_edge = new Edge(w);
			sd_edge->_srcIndex = srcindex;
			sd_edge->_dstIndex = dstindex;
			sd_edge->_next = _linkTable[srcindex];
			_linkTable[srcindex] = sd_edge;
			// 1 0
			// 无向图
			if (Direction == false)
			{
				Edge* ds_edge = new Edge(w);
				ds_edge->_srcIndex = dstindex;
				ds_edge->_dstIndex = srcindex;
				ds_edge->_next = _linkTable[dstindex];
				_linkTable[dstindex] = ds_edge;
			}
		}
	private:
		map<string, int> _vIndexMap;
		vector<V> _vertexs; // 顶点集合
		vector<Edge*> _linkTable;
	};
}

邻接表的缺点:不适合判断2个定点是否相连

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值