【数据结构】图论基础

news2024/10/2 17:57:10

在这里插入图片描述

文章目录

  • 图的概念
    • 图的基本概念
    • 图的类型
    • 图的表示方法
  • 图的相关基本概念
    • 1. 路径(Path)
    • 2. 连通性(Connectivity)
    • 3. 图的度(Degree)
    • 4. 子图(Subgraph)
    • 5. 生成树(Spanning Tree)
    • 6. 二分图(Bipartite Graph)
    • 7. 拓扑排序(Topological Sort)
    • 8. 欧拉图和哈密顿图
  • 实现图
    • 邻接矩阵实现图
      • 邻接矩阵的基本框架
      • 核心接口
    • 邻接表实现图
      • 邻接表实现图的基本框架
      • 核心接口
  • 总结

图的概念

图(Graph)是离散数学中的一种重要数据结构,用于描述对象(称为顶点,或节点)之间的关系(称为)。图可以表示各种事物之间的连接关系,比如网络中的路由器、社交网络中的用户、城市之间的道路等。

图的基本概念

  1. 顶点(Vertex)

    • 图中的基本单位,也称为节点(Node)。一个图通常由若干个顶点组成,用来表示实体或对象。
  2. 边(Edge)

    • 顶点之间的连接关系称为边。边可以是有方向的(有向图)或无方向的(无向图)。在无向图中,边表示双向关系;在有向图中,边表示单向关系。
  3. 有向图(Directed Graph, Digraph)

    • 在有向图中,边是有方向的,表示从一个顶点指向另一个顶点的单向连接。通常用有向边表示诸如“影响”或“依赖”的关系。
      在这里插入图片描述
  4. 无向图(Undirected Graph)

    • 无向图中的边没有方向,表示两个顶点之间的对称关系,如“相邻”或“连接”。
      在这里插入图片描述
  5. 邻接(Adjacency)

    • 如果两个顶点之间有一条边连接,这两个顶点称为邻接的。
  6. 度(Degree)

    • 一个顶点的度是连接到该顶点的边的数目。在有向图中,区分入度(In-degree)出度(Out-degree),分别表示进入该顶点和从该顶点发出的边的数量。

图的类型

  1. 稀疏图(Sparse Graph)

    • 边的数量远远少于顶点数量的平方(E << V²),大部分顶点之间没有边连接。
  2. 稠密图(Dense Graph)

    • 边的数量接近顶点数量的平方(E ≈ V²),大多数顶点之间有边连接。
  3. 加权图(Weighted Graph)

    • 图中的每条边带有一个权重,用来表示顶点之间的某种度量,如距离、成本或容量。
  4. 连通图(Connected Graph)

    • 对于无向图,若任意两个顶点之间都有路径相连,则称为连通图。有向图中则需区分强连通弱连通
  5. 简单图(Simple Graph)

    • 不包含自环和多重边的图。自环是指从顶点到自身的边,多重边是指两个顶点之间存在多条边。
  6. 完全图(Complete Graph)

    • 图中任意两个顶点之间都有边相连,通常表示为 K n K_n Kn,其中 n n n 是顶点数。

图的表示方法

  1. 邻接矩阵(Adjacency Matrix)

    • 使用一个二维矩阵来表示顶点之间的连接关系,矩阵中的元素表示边的存在性或权重。
  2. 邻接表(Adjacency List)

    • 对每个顶点,维护一个链表(或数组)来存储与之相邻的顶点列表,适合稀疏图。
  3. 边集列表(Edge List)

    • 直接列出图中所有的边,用边的起点和终点来描述,适合图的遍历或算法中的具体操作。

图的相关基本概念

在理解图的结构和操作时,有一些与图密切相关的概念有助于更好地分析和处理图的算法和应用。

1. 路径(Path)

路径是在图中从一个顶点到另一个顶点的行进序列,它由一系列边和顶点组成。根据图的类型和要求,路径可以分为几类:

  • 简单路径(Simple Path):路径中的顶点不重复出现。
  • 回路(Cycle):路径的起点和终点是同一个顶点,且路径中的其他顶点不重复。
    在这里插入图片描述

2. 连通性(Connectivity)

图的连通性描述了图中顶点与顶点之间的可达性。

  • 连通图(Connected Graph):对于无向图,如果从任意一个顶点能到达其他所有顶点,则图是连通的。
  • 强连通图(Strongly Connected Graph):对于有向图,如果任意两个顶点之间都有路径相通,则图是强连通的。
  • 弱连通图(Weakly Connected Graph):对于有向图,如果将其所有边看作无向边,能够使整个图连通,则图是弱连通的。
    在这里插入图片描述

3. 图的度(Degree)

图中一个顶点的度表示与该顶点连接的边的数量。

  • 入度(In-degree):有向图中指向该顶点的边的数量。
  • 出度(Out-degree):有向图中从该顶点发出的边的数量。
    在这里插入图片描述

4. 子图(Subgraph)

子图是从原始图中选取部分顶点和边构成的图。常见的子图类型包括:

  • 生成子图(Spanning Subgraph):包含图的所有顶点,但边可能有所删减。
  • 诱导子图(Induced Subgraph):包含图中某些顶点及这些顶点之间的所有边。
    在这里插入图片描述

5. 生成树(Spanning Tree)

生成树是无向图的一个子图,包含图中的所有顶点,且是一个树形结构(无环、连通)。

  • 最小生成树(Minimum Spanning Tree, MST):在加权图中,生成树的边权和最小的生成树。

生成树:
在这里插入图片描述
最小生成树:
在这里插入图片描述

6. 二分图(Bipartite Graph)

二分图是一种特殊的图,可以将顶点集合分为两个不相交的子集,且所有边都连接两个不同子集中的顶点,而子集中没有内部连接。
在这里插入图片描述

7. 拓扑排序(Topological Sort)

对于有向无环图(DAG),拓扑排序是一种将顶点按依赖关系线性排序的算法,通常用于解决调度、依赖管理等问题。

8. 欧拉图和哈密顿图

  • 欧拉路径(Eulerian Path):经过图中每条边一次且仅一次的路径。如果这样的路径是闭合的,即路径的起点和终点相同,则称为欧拉回路(Eulerian Circuit)
  • 哈密顿路径(Hamiltonian Path):经过图中每个顶点一次且仅一次的路径。如果这样的路径是闭合的,则称为哈密顿回路(Hamiltonian Circuit)

实现图

邻接矩阵实现图

在这里插入图片描述
如果是无向图的话,那么邻接矩阵就是沿着对角线对称的。
在这里插入图片描述
如果是无向图的话,那么就可以通过压缩只存储一半。
除了需要一个存储权值的邻接矩阵我们还需要一个vector来存储顶点,如果涉及到邻接矩阵,那么就会涉及到下标,所以我们应该还需要一个顶点映射下标的map。

邻接矩阵的基本框架

	//         顶点     weight                       有向图/无向图   
	template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>
	class Graph
	{
	public:
	
	private:
		vector<V> _vertexs;         //顶点集合
		map<V, int> _indexMap;      //顶点对应下标的关系(顶点---->下标)
		vector<vector<W>> _matrix;  //邻接矩阵
	};
}

核心接口

初始化

Graph(const V* a, size_t n)
{
	// 开辟n个空间
	_vertexs.resize(n);
	for (size_t i = 0;i < n;i++)
	{
		_vertexs[i] = a[i];
		//顶点----->下标
		_indexMap[a[i]] = i;
	}
	_matrix.resize(n);
	//权值用MAX_W初始化
	for (size_t i = 0;i < n;i++) _matrix[i].resize(n, MAX_W);
}

获取顶点的下标

size_t GetVertexIndex(const V& v)
{
	auto it = _indexMap.find(v);
	if (it != _indexMap.end()) return it->second;
	else
	{
		// 抛异常出去
		throw invalid_argument("顶点不存在");
		// 过编译器逻辑
		return -1;
	}
}

通过map的接口find找到v对应的下标,如果it为end(),说明没有这个顶点,直接抛异常,如果存在,则返回对应的下标
添加边

//                   原点        目标点       权值
void AddEdge(const V& src, const V& dst, const W& w)
{
	// 获取原点下标
	size_t srci = GetVertexIndex(src);
	size_t dsti = GetVertexIndex(dst);
	_matrix[srci][dsti] = w;
	// 无向图添加两个权值
	if (Direction == false) _matrix[dsti][srci] = w;
}

找到原点和目标点对应的下标,如果是有向图的话,只需要添加原点到目标点的边,如果是无向图的话,就需要反向添加一下目标点到原点的边了。

邻接表实现图

邻接表是图的一种存储表示方法,常用于存储稀疏图(即边数相对较少的图)。它通过每个顶点对应一个链表或数组来记录与其相邻的顶点。邻接表的空间复杂度相对较小,适合存储大规模图。
在这里插入图片描述
对于无向图来说不存在入度和出度的概念,所以只需要一个邻接表表示,下标的邻接表就是用来表示边的集合。
在这里插入图片描述
拿第一个点为例,这里使用一个结构体来维护的,这个结构体中有指向一下下一个边集的指针,还有一个权值和目标点的下标。
我们说形象一点:
在这里插入图片描述

类似于哈希桶的那个做法

邻接表实现图的基本框架

template<class W>
struct Edge
{
	int _dsti;  //目标点的下标
	W _w;       //权值
	Edge<W>* _next;
	Edge(int dsti,const W& w)
		:_dsti(dsti)
		,_w(w)
		,_next(nullptr)
	{}
};
//         顶点   weight         有向图/无向图   
template<class V, class W,bool Direction = false>
class Graph
{
	typedef Edge<W> Edge;
public:

private:
	vector<V> _vertexs;         //顶点集合
	map<V, int> _indexMap;      //顶点对应下标的关系(顶点---->下标)
	vector<Edge*> _tables;       //邻接表
};

核心接口

初始化

Graph(const V* a, size_t n)
{
	// 开辟n个空间
	_vertexs.resize(n);
	for (size_t i = 0;i < n;i++)
	{
		_vertexs[i] = a[i];
		//顶点----->下标
		_indexMap[a[i]] = i;
	}
	_tables.resize(n,nullptr);//给tables开辟n个空间(n个顶点)
}

获取顶点对应下标

size_t GetVertexIndex(const V& v)
{
	auto it = _indexMap.find(v);
	if (it != _indexMap.end()) return it->second;
	else
	{
		// 抛异常出去
		throw invalid_argument("顶点不存在");
		// 过编译器逻辑
		return -1;
	}
}

添加边

void AddEdge(const V& src, const V& dst, const W& w)
{
	// 获取原点下标
	size_t srci = GetVertexIndex(src);
	size_t dsti = GetVertexIndex(dst);

	//1 --> 2
	//原点到目标的权值
	Edge* eg = new Edge(dsti, w);
	//这里进行头插
	eg->_next = _tables[srci];
	_tables[srci] = eg;
	//无向图进行特殊处理
	//2 --> 1
	if (Direction == false)
	{
		//反向指向
		Edge* eg = new Edge(srci, w);
		eg->_next = _tables[dsti];
		_tables[dsti] = eg;
	}
}

这个和邻接矩阵就不一样,邻接表中添加边应该先创建一个对应边的结构体,然后将这个结构体头插到原点对应下标的边集的位置上,如果是无向图的话,需要反向添加一条目标点到原点的边。注意:头插之后需要改变头的位置

总结

通过本篇文章的介绍,我们初步了解了图的基本概念、图的表示方法(如邻接矩阵和邻接表)、以及图中的各种基本性质。图论作为计算机科学和数学中的一个重要分支,其应用范围广泛,从网络设计到路径规划,都有着广泛的应用场景。

在接下来的学习中,我们可以进一步探讨更高级的图算法,如最短路径算法(Dijkstra、Bellman-Ford 等)、图的遍历算法(深度优先搜索、广度优先搜索)、以及图的连通性和最小生成树等高级主题。这些内容将为解决更复杂的实际问题提供坚实的理论基础。

图论的世界广阔而有趣,期待你能在之后的学习和实践中不断挖掘其奥秘!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2184817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW提高开发效率技巧----快速实现原型和测试

在LabVIEW开发中&#xff0c;DAQ助手&#xff08;DAQ Assistant&#xff09;和Express VI为快速构建原型和测试功能提供了极大的便利&#xff0c;特别适合于简单系统的开发和早期验证阶段。 DAQ助手&#xff1a;是一种可视化配置工具&#xff0c;通过图形界面轻松设置和管理数据…

CSS3渐变

一、线性渐变 通过background-image: linear-gradient(...)设置线性渐变 语法&#xff1a; linear-gradient(direction,color1,color2, . . ) direction&#xff1a;渐变方向&#xff0c;默认从上到下&#xff0c;可选值&#xff1a; 简单选取&#xff1a; ① to right&…

Python和C++及MATLAB和R时间序列中数学物理金融气象运动和电子材料

&#x1f3af;要点 小波分析&#xff0c;量化噪声概率分布和统计推理物理量和化学量数值计算确定性非线性系统金融资本市场和市场流动性波形传播气象建模评估 Python时间序列数学 时间序列分析是一种强大的统计技术&#xff0c;广泛应用于经济学、金融学、环境科学和工程学…

基于SSM+Vue技术的定制式音乐资讯平台

文未可获取一份本项目的java源码和数据库参考。 一、选题的背景与意义&#xff1a; 随着个人计算机的普及和互联网技术的日渐成熟&#xff0c;网络正逐渐成为人们获取信息及消费的主要渠道。然而在当前这个信息时代&#xff0c;网络中的信息种类和数量呈现爆炸性增长的趋势&a…

基于Node.js+Express+MySQL+VUE实现的在线电影视频点播网站管理系统的设计与实现部署安装

目录 1. 引言 1.1开发背景 1.2开发意义 1.3国内外研究 2. 需求分析 3. 系统架构设计 4. 关键技术选型 5. 功能模块设计 5.1功能图 5.2界面介绍 6. 总结 1. 引言 随着互联网技术的快速发展和普及&#xff0c;人们获取信息的方式发生了巨大变化&#xff0c;其中在…

PCL库简单的icp配准

#include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/registration/icp.h>int main(int argc, char** argv) {// 确保提供了两个PCD文件作为输入if (argc ! 3) {PCL_ERROR("请提供两个PCD文件作为输入。\n");return (-1);}// …

dcatadmin 自定义登录页面

一、问题&#xff1a; 在后台管理系统中&#xff0c;不同的项目想要不同的登录页面&#xff0c;但是框架自带的登录页面就只有一个。 解决&#xff1a; 由芒果系统改造的dcatadmin登录插件&#xff0c;实现一键安装改变登录页面。 项目介绍 基于Laravel和Vue的快速开发的后台管…

html5 + css3(上)

目录 HTML认知web标准vscode的简介和使用注释标题和段落换行和水平线标签文本格式化标签图片图片-基本使用图片-属性 绝对路径相对路径音频标签视频标签超链接 HTML基础列表列表-无序和有序列表-自定义 表格表格-使用表格-表格标题和表头单元格表格-结构标签&#xff08;了解&a…

CentOS 6文件系统

由冯诺依曼在 1945 年提出的计算机五大组成部分&#xff1a;运算器&#xff0c;控制器&#xff0c;存储器&#xff0c;输入设 备&#xff0c;输出设备。 1. 硬盘结构&#xff1a; &#xff08;1&#xff09;机械硬盘结构&#xff1a; 磁盘拆解图&#xff1a; 扇区&#xff0c;…

白杨SEO:抖音上做自然搜索流量怎么挖掘出抖音流量关键词及布局进去?【举例】

前言&#xff1a;为什么想到再分享这个&#xff1f;因为发现很多人在抖音做搜索流量时怎么挖掘抖音关键词这个基础以及怎么布局进去不太清楚&#xff0c;所以再来写下&#xff0c;希望对大家有帮助。 文章大纲&#xff1a; 1、抖音搜索流量如何确定业务词&#xff1f; 2、抖音…

Ubuntu下安装Zookeeper集群

Zookeeper集群是一个开源的分布式协调服务系统&#xff0c;它由Apache软件基金会维护&#xff0c;旨在为分布式应用提供一致性和可靠性的服务。 在Zookeeper集群中&#xff0c;服务器可以扮演三种角色——领导者&#xff08;Leader&#xff09;、跟随者&#xff08;Follower&a…

开放式耳机哪个品牌好?值得选购的开放式蓝牙耳机推荐

2024年&#xff0c;蓝牙耳机市场迎来了开放式耳机的热潮。但其实对于许多消费者来说&#xff0c;如何选择合适的开放式耳机仍然充满疑问&#xff1a;佩戴稳固舒适的开放式耳机应该怎么选择&#xff1f;开放式耳机的蓝牙版本该怎么选择&#xff1f;又有哪些开放式耳机品牌是可靠…

SkyWalking 高可用

生产环境中,后端应用需要支持高吞吐量并且支持高可用来保证服务的稳定,因此需要高可用集群管理。 集群方案 Skywalking集群是将 skywalking oap 作为一个服务注册到nacos上,只要skywalking oap服务没有全部宕机,保证有一个skywalking oap在运行,就可以提供服务。 高可用…

【mmsegmentation】Loss模块(进阶)自定义自己的LOSS

1、定义自己的loss driving\models\losses\shuai_loss.py import torch from torch import nn from mmseg.models import LOSSESLOSSES.register_module() class ShuaiLoss(nn.Module):def __init__(self,loss_weight1.0):super().__init__()self.ce_loss nn.CrossEntropyLo…

躺平成长:微信小程序运营日记第二天

在进行属于生活的开源之后&#xff0c;自己更加感受到自己存在的渺茫&#xff0c;同时更加开始深刻领会&#xff0c;开源的重要性&#xff0c;在开源&#xff0c;开放&#xff0c;创造&#xff0c;再创新的思维模式下&#xff0c;不发布八部金刚功相关的训练视频&#xff0c;自…

详解Java中的Collection单列集合(从底层到用法超详细解析和细节分析)

⭕在 Java 中&#xff0c;集合框架是开发过程中最常用的数据结构之一&#xff0c;其中 Collection 接口是整个集合框架的基础。Collection 是处理单列数据的接口&#xff0c;它定义了一些通用的操作&#xff0c;允许对一组对象进行操作。今天我们将深入介绍 Java 中的单列集合 …

ECharts图表图例4

jave 用eclipse软件 代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <!-- 引入ECharts脚本 --> <script src"js/echarts.js"></script> <title>绘制堆积面积图</title&g…

Unraid的cache使用btrfs或zfs?

Unraid的cache使用btrfs或zfs&#xff1f; 背景&#xff1a;由于在unraid中添加了多个docker和虚拟机&#xff0c;因此会一直访问硬盘。然而&#xff0c;单个硬盘实在难以让人放心。在阵列盘中&#xff0c;可以通过添加校验盘进行数据保护&#xff0c;在cache中无法使用xfs格式…

Leecode热题100-283.移动零

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出: […

【Python报错已解决】ImportError: No module named ‘module‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…