数据结构 -图 -基础

博客主页:【夜泉_ly

本文专栏:【数据结构

欢迎点赞👍收藏⭐关注❤️

文章目录

今天简单讲讲图的相关概念。

常见概念

有向无向

首先,

图主要被分成了两种,
无向有向

大概长这样:

无向图的一条边是双向的,

有向图的一条边是单向的,有方向。

因此,

无向图中,

相邻的两个点之间最多一条边;

而有向图,

相邻的两个点之间最多两条边。

完全图

我们把图中所有的点全部通过边两两相连,

这就是完全图

然后,用等差数列就可以得出,

无向完全图的边有n(n - 1)/2条,

有向n(n - 1)。

子图

我们把图中的点或线随便拆掉一些,

新图就是老图的子图 :(图片)

不过要记住,

你只能删,

不能增或改。

连通/强连通/弱连通图

对于无向图,

如果任意两点都有路径相连,

没有分什么小团体,

那这就叫连通图

下面的红、蓝就是连通图,而黄、绿不是:

对于有向图,

如果任意一个点都有走到其余点的路径,

这就叫强连通图

如果把边的方向去掉,

即,把它当作无向图后,

还是一个连通图,

那这就叫弱连通图

(很明显,

强连通必是弱连通,

但弱不一定是强)

生成树

生成树,

首先得是个连通图,

然后得是这个连通图的最小联通子图

联通图刚刚讲了,子图也讲了,

剩个最小没讲,

这个最小不代表它的点变少了,

而代表它没有多余的边,

具体讲,

n个顶点的连通图,

其生成树只能是n个点、n-1条边 的连通子图。

存储结构

没学图之前,

我还以为图是一堆Node的组合,

就像二叉树那样,

然后每个Node又存了相邻Node的指针。。。

不过现在一看,

搞个数组建立映射关系就行了,

在这之后,点的问题就解决了:

一个下标对应一个点。

那么,现在只需要知道如何表示边。

最主要的方法有两个:
邻接矩阵邻接表

先看邻接矩阵,

这里用到的是二维数组,

刚刚不是把点对应到了数组下标吗,

那么这里,

每个格子的横纵坐标就代表两个点,

格子存的值对应的是边的权值

(无穷代表该处没有边)

上面这是无向图的,

可以看到整个格子轴对称,

也就是每次需在对称的两个位置填数。

而有向图,

每次只在轴的一边填数,

另一边保持不变就行。

再来看看邻接表,

大概长这样:

有向图,你也可以用两个表,

一个记录这个点指向谁,

一个记录谁指向这个点。

知道了邻接矩阵和邻接表是什么,

我们还得关注一下它们的特点:

邻接矩阵 邻接表
便于判断两个点间是否有边
便于计算各个顶点的度
便于增加删除顶点
便于统计边的数量
较高的空间效率

概念大致理解了,

现在开始敲代码。

先写邻接矩阵。

模板:
template<class V, class W, W W_MAX = INT_MAX, bool Direction = false>

点类型、权值类型、权值的最大值、是否有向

成员变量:
std::vector<V> _v;
std::unordered_map<V, int> _index; 下标
std::vector<std::vector<W>> _matrix; 矩阵

简单搭个架子,能跑就行:

cpp 复制代码
namespace AdjacencyMatrix
{
template<class V, class W, W W_MAX = INT_MAX, bool Direction = false>
class Graph
{
public:
	Graph() = default;
	Graph(const std::vector<V>& v)
		: _v(v), _matrix(v.size(), std::vector<W>(v.size(), W_MAX)) 
	{
		for (int i = 0; i < v.size(); i++)
		{
			_index[v[i]] = i;
		}
	}
	void AddEdge(const V& src, const V& dst, const W& w)
	{
		int i_src = _index[src], i_dst = _index[dst];
		_matrix[i_src][i_dst] = w;
		if (!Direction)
		{
			_matrix[i_dst][i_src] = w;
		}
	}
	void Show() const
	{
		std::cout << "   ";
		for (const auto& e : _v)
			std::cout << e << "  ";
		std::cout << std::endl;
		for (int i = 0; i < _matrix.size(); i++)
		{
			std::cout << _v[i] << "  ";
			for (int j = 0; j < _matrix.size(); j++)
			{
				if (_matrix[i][j] == W_MAX) std::cout << "∞ ";
				else std::cout << _matrix[i][j] << "  ";
			}
			std::cout << std::endl;
		}
	}
	~Graph(){}
private:
	std::vector<V> _v;
	std::unordered_map<V, int> _index;
	std::vector<std::vector<W>> _matrix;
};

void Test1()
{
	Graph<char, int, INT_MAX, true> g(std::vector<char>{'A', 'B', 'D', 'C'});
	g.AddEdge('A', 'C', 2);
	g.AddEdge('B', 'D', 0);
	g.AddEdge('D', 'B', 2);
	g.AddEdge('C', 'A', 5);
	g.Show();
}
} // namespace AdjacencyMatrix

调用AdjacencyMatrix::Test1(),输出:

然后CV一下,稍稍改改,

就成了邻接表:

cpp 复制代码
namespace AdjacencyList
{
template<class W>
struct Edge
{
	Edge(const W& w) :_index(-1), _w(w), _next(nullptr) {}

	int _index;
	W _w;
	Edge<W>* _next;
};

template<class V, class W, W W_MAX = INT_MAX, bool Direction = false>
class Graph
{
public:
	typedef Edge<W> Edge;
public:
	Graph() = default;
	Graph(const std::vector<V>& v)
		: _v(v), _list(v.size(), nullptr)
	{
		for (int i = 0; i < v.size(); i++)
		{
			_index[v[i]] = i;
		}
	}

	void _AddEdge(int src, int dst, const W& w)
	{
		Edge* pNew = new Edge(w);
		pNew->_index = dst;
		pNew->_next = _list[src];
		_list[src] = pNew;
	}

	void AddEdge(const V& src, const V& dst, const W& w)
	{
		_AddEdge(_index[src], _index[dst], w);
		if (!Direction) _AddEdge(_index[dst], _index[src], w);
	}

	void Show() const
	{
		for (int i = 0; i < _list.size(); i++) 
		{
			std::cout << _v[i] << " :: ";
			Edge* cur = _list[i];
			while (cur) 
			{
				std::cout << _v[cur->_index] << ':' << cur->_w << "->";
				cur = cur->_next;
			}
			std::cout << "nullptr" << std::endl;
		}
	}

	~Graph() 
	{
		for (auto e : _list) 
		{
			while (e) 
			{
				auto tmp = e->_next;
				delete e;
				e = tmp;
			}
		}
	}

private:
	std::vector<V> _v;
	std::unordered_map<V, int> _index;
	std::vector<Edge*> _list;
};

void Test1()
{
	Graph<char, int, INT_MAX, true> g(std::vector<char>{'A', 'B', 'D', 'C'});
	g.AddEdge('A', 'C', 2);
	g.AddEdge('B', 'D', 0);
	g.AddEdge('D', 'B', 2);
	g.AddEdge('C', 'A', 5);
	g.Show();
}
} // namespace AdjacencyList

调用AdjacencyList::Test1(),输出:


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!

本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

相关推荐
z26373056111 小时前
Redis常用数据结构及命令详解:从基础到进阶
数据结构·数据库·redis
熊峰峰1 小时前
数据结构第六节:二叉搜索树(BST)的基本操作与实现
开发语言·数据结构·c++·算法
Dante7984 小时前
【数据结构】二叉搜索树、平衡搜索树、红黑树
数据结构·c++·算法
shinelord明4 小时前
【软件设计】23 种设计模式解析与实践指南
数据结构·设计模式·软件工程
橘颂TA7 小时前
【C++】数据结构 栈的实现
开发语言·数据结构·c++··学生
曼诺尔雷迪亚兹14 小时前
2025年四川烟草工业计算机岗位备考详细内容
数据结构·数据库·计算机网络·算法
Simon5231415 小时前
数据结构---八大排序
java·数据结构·排序算法
_GR16 小时前
2019年蓝桥杯第十届C&C++大学B组真题及代码
c语言·数据结构·c++·算法·蓝桥杯
WW_千谷山4_sch16 小时前
MYOJ_4204:迷宫(图论-网格图基础,dfs,bfs在网格图中应用)
数据结构·c++·深度优先·图论·广度优先