当前位置: 首页 > news >正文

数据结构:图(Graph)

目录

图(Graph)的诞生——我们为什么要发明“图”?

定义图(Graph)的核心元素

图的分类——丰富图的表达能力

无向图 (Undirected Graph) vs. 有向图 (Directed Graph)

 无权图 (Unweighted Graph) vs. 带权图 (Weighted Graph)

描述图的常用术语

关于顶点的术语:度 (Degree)

关于顶点序列的术语:路径 (Path) 和 环 (Cycle)

关于图整体的术语:连通性 (Connectivity)

简单 C/C++ 代码框架的思考

第一步:定义顶点 (Vertex)

第二步:定义边 (Edge)

第三步:组合成图 (Graph)

总结一下今天的内容


图(Graph)的诞生——我们为什么要发明“图”?

在开始学习任何新知识之前,最重要的问题是:“我们为什么需要它?它解决了什么问题?”

想象一下,我们生活中有各种各样的“事物”,这些“事物”之间也存在着千丝万缕的“关系”。

  • 事物: 一个人、一个城市、一个网页、一个路口、一个任务...

  • 关系: 朋友关系、城市间的航线、网页间的链接、路口间的道路、任务间的依赖关系...

我们的大脑很擅长处理这些关系,但计算机不擅长。计算机只擅长处理数字和结构化的数据。

如果我们想让计算机解决类似“从北京到上海的最短路线”或者“在朋友圈里,我和马化腾之间隔了几个人”这样的问题,我们就必须找到一种方法,把这些“事物”和“关系”翻译成计算机能理解的语言。

 所有这些问题的本质,都可以抽象为两个核心元素:

  1. 点 (Point/Node):用来代表那些独立的“事物”。

  2. 线 (Line/Link):用来代表事物之间的“关系”。

把“点”和“线”组合在一起,就构成了一个可以描述万物关系的通用模型。这个模型,就是我们今天要讲的 图 (Graph)

所以,图的本质就是:一种用来描述“多对多”(many-to-many)关系的数据结构。


定义图(Graph)的核心元素

现在我们正式定义图。一个图 G (Graph) 由两个基本集合组成:

  1. 顶点集合 V (Vertex Set): 包含了所有“事物”的集合。我们把里面的每一个元素叫做 顶点 (Vertex)

  2. 边集合 E (Edge Set): 包含了所有“关系”的集合。我们把里面的每一个元素叫做 边 (Edge)

所以,一个图可以表示为 G = (V, E)

我们来看一个具体的例子。假设我们有四个城市:北京、上海、广州、武汉。

  • 顶点集合 V 就是这四个城市:V = {北京, 上海, 广州, 武汉}

  • 假设它们之间有航班线路,这些线路就是“关系”。比如:

  • 北京和上海之间有航班

  • 北京和广州之间有航班

  • 上海和广州之间有航班

  • 广州和武汉之间有航班

  • 那么 边集合 E 就是这些航班线路:E = {(北京, 上海), (北京, 广州), (上海, 广州), (广州, 武汉)}

这里的 (北京, 上海) 就表示一条连接北京和上海的边。

现在,我们把这个定义画出来,你就有了对图的第一个直观印象。

1️⃣:一个简单的城市交通图

      (北京) --------- (上海)|               /|              /|             /(广州) --------- (武汉)

在这个图里:

  • 圆圈圈起来的城市名就是 顶点 (Vertex)

  • 连接两个顶点的线段就是 边 (Edge)


图的分类——丰富图的表达能力

上面的图很简单,但现实世界的关系更复杂。比如:

  • 朋友关系是双向的:我是你的朋友,你也是我的朋友。

  • 关注关系是单向的:我关注了你,但你未必关注我。

  • 城市间的道路有长有短,距离不同。

为了描述这些更复杂的关系,我们需要对图进行分类。

无向图 (Undirected Graph) vs. 有向图 (Directed Graph)

关系的“方向性”是一个核心区别。朋友关系没有方向,而关注关系有方向。

无向图 (Undirected Graph)

  • 定义:图中的边没有方向。边 (u, v) 和边 (v, u) 表示的是同一条边。

  • 适用场景:描述相互、对等的关系。例如:朋友关系、城市间的双向铁路。

  • 图示:我们上面画的 图1 就是一个典型的无向图。连接北京和上海的线,你既可以看作从北京到上海,也可以看作从上海到北京。

有向图 (Directed Graph / Digraph)

  • 定义:图中的边有明确的方向。从顶点 u 指向顶点 v 的边,我们称之为一条 弧 (Arc),记作 <u, v>。这里 u 称为 弧尾 (Tail)v 称为 弧头 (Head)<u, v><v, u> 是两条完全不同的弧。

  • 适用场景:描述单向的关系。例如:社交网络中的“关注”、网页间的超链接、任务的依赖(必须先完成A才能做B)。

2️⃣:一个简单的社交关注图

假设:A关注了B,B关注了C,C关注了D,D关注了A。

  • V = {A, B, C, D}

  • E = {<A, B>, <B, C>, <C, D>, <D, A>}

画出来就是这个样子:

      (A) --------> (B)^             ||             ||             v(D) <-------- (C)

注意图中的 箭头,它明确地指出了关系的方向。


 无权图 (Unweighted Graph) vs. 带权图 (Weighted Graph)

关系的“强度”或“成本”是另一个核心区别。

我和你是朋友,这是关系的有无。但我们之间可能关系很好(亲密度90),也可能只是点赞之交(亲密度10)。

从北京到上海的航班,有的要2小时,有的要3小时,这个“时长”或“票价”就是关系的属性。

无权图 (Unweighted Graph)

  • 定义:所有的边都被认为是等价的,没有量的差别。我们只关心顶点之间“是否”相连,不关心连接的“代价”是什么。

  • 图示:上面讲的 图1图2 都是无权图。

带权图 (Weighted Graph / Network)

  • 定义:图中的每一条边都被赋予一个数值,这个数值被称为 权重 (Weight)。这个权重可以代表各种意义,如距离、时间、成本、容量等。

  • 适用场景:需要考虑成本或度量的问题。例如:地图导航(距离或时间)、网络路由(带宽或延迟)。

3️⃣:带权重的城市交通图 (单位: 公里)

我们把 图1 加上城市间的距离,它就变成了一个带权图。

          1213(北京) --------- (上海)|               /|              / 14632123 |             /|            /(广州) --------- (武汉)1067

这里的数字 1213, 1463 等就是对应边的 权重 (Weight)


描述图的常用术语

为了方便地讨论和研究图,我们需要学习一些“行话”。这些术语都是基于上面最基本的定义演化而来的。

关于顶点的术语:度 (Degree)

 一个顶点有多重要?一个直观的衡量标准就是看有多少条边和它相连。这个“相连边的数量”就是“度”。

在无向图中:

顶点的 度 (Degree) 就是指和该顶点相关联的边的数量。

在 图1 中:

  • Degree(北京) = 2

  • Degree(上海) = 2

  • Degree(广州) = 3

  • Degree(武汉) = 1

在有向图中:

因为边有方向,所以度的概念也需要细分。

  • 入度 (In-degree): 指向该顶点的弧的数量(有多少箭头指向我)。

  • 出度 (Out-degree): 从该顶点出发的弧的数量(我发出了多少箭头)。

  • 度 (Degree) = 入度 + 出度

在 图2 中:

  • In-degree(A) = 1, Out-degree(A) = 1, Degree(A) = 2

  • In-degree(B) = 1, Out-degree(B) = 1, Degree(B) = 2

  • ... 以此类推,所有顶点的入度和出度都是1。

对于任何图,所有顶点的度数之和,等于边总数的两倍。

因为每一条边(无论有向还是无向)都有两个端点。当我们计算所有顶点的度数之和时,每一条边都会被它的两个端点各自计算一次。

所以,边的总数 |E| 和度数总和的关系是:Σ(Degree(v)) = 2 * |E|。不信你可以用上面的图1验证一下:(2+2+3+1) = 8,而边总数是4,正好是2倍。


关于顶点序列的术语:路径 (Path) 和 环 (Cycle)

我们研究图,很多时候是为了解决“从A点能不能到B点?”以及“怎么走?”的问题。这就引出了“路径”的概念。

路径 (Path): 从顶点 u 到顶点 v 的一条路径,指的是一个顶点序列 u, v1, v2, ..., v,其中序列中任意相邻的两个顶点之间都存在一条边。

路径长度 (Path Length): 路径上边的数量(对于无权图)或权重之和(对于带权图)。

在 图1 中,从“北京”到“武汉”的一条路径是:北京 -> 广州 -> 武汉。这条路径的长度是 2 (经过了2条边)。

环 (Cycle / Circuit): 如果一条路径的起点和终点是同一个顶点,那么这条路径就构成一个 环。

  • 在 图1 中,北京 -> 上海 -> 广州 -> 北京 就是一个环。

  • 在 图2 中,A -> B -> C -> D -> A 也是一个环。


关于图整体的术语:连通性 (Connectivity)

一个图是“一个整体”还是“几块分离的碎片”?这是描述图的整体结构的重要属性。

  • 连通图 (Connected Graph): 这是针对 无向图 说的。如果一个无向图中,任意两个顶点之间都存在至少一条路径,那么这个图就是连通图。我们上面画的 图1 和 图3 都是连通图。

  • 连通分量 (Connected Component): 如果一个无向图不是连通的,那么它可以被分成若干个独立的、内部连通的子图,每一个这样的子图就是一个连通分量。

  • 强连通图 (Strongly Connected Graph): 这是针对 有向图 说的。如果一个有向图中,任意两个顶点 uv,都既存在从 uv 的路径,也存在从 vu 的路径,那么这个图就是强连通图。我们上面画的 图2 就是一个强连通图。

4️⃣:一个非连通图,它有两个连通分量

  (A) --- (B)           (E) --- (F)|       |             ||       |             |(C) --- (D)           (G)

在这个图中,你无法从顶点A找到一条路径到达顶点E。{A, B, C, D} 和 {E, F, G} 分别是这个图的两个连-通分量。


简单 C/C++ 代码框架的思考

虽然我们还不涉及图的具体存储方式,但我们可以从概念出发,思考一下如果用C/C++来描述一个“顶点”和一个“边”,它大概会是什么样子。这是一个从概念到代码的初步映射。

第一步:定义顶点 (Vertex)

一个顶点最核心的信息可能就是它的编号或者名字。我们还可以给它附加一些数据。

// 为了简单,我们先用一个整数ID来唯一标识一个顶点
// 假设我们有一个顶点,它里面可以存储一些数据,比如城市的名称
struct Vertex {int id;          // 顶点的唯一标识char* data;    // 指向顶点存储的数据,比如 "北京"// ... 未来可能还会有其他信息
};

第二步:定义边 (Edge)

一条边最核心的信息是它连接了哪两个顶点。如果是带权图,还需要一个权重。

// 定义一条无向、带权的边
struct Edge {int from_vertex_id;   // 边的起点IDint to_vertex_id;     // 边的终点IDint weight;           // 边的权重// ... 未来可能还会有其他信息
};

注意: 在无向图中,fromto 是可以互换的。在有向图中,方向是固定的。

第三步:组合成图 (Graph)

一个图就是一堆顶点和一堆边的集合。

// 一个最最基础的图的结构框架
#define MAX_VERTICES 100 // 假设最多有100个顶点
#define MAX_EDGES 1000   // 假设最多有1000条边struct Graph {Vertex vertices[MAX_VERTICES]; // 顶点数组Edge edges[MAX_EDGES];         // 边数组int num_vertices; // 当前图中顶点的数量int num_edges;    // 当前图中边的数量
};

这个代码框架非常初级,甚至可以说是简陋的,而且在实际使用中效率不高。但它很好地反映了我们前面学到的图的定义:G = (V, E)。一个 Graph 结构体,里面包含了一个顶点数组 V 和一个边数组 E

我们今天先不深入这个代码,因为如何高效地表示和存储这些顶点和边,就是下一章 "图的表示 (Representation of Graphs)" 要解决的核心问题(例如邻接矩阵和邻接表)。


总结一下今天的内容

  1. 图是什么? 图是用来描述“事物”(顶点)和“关系”(边)的数学模型,本质是多对多关系。

  2. 图的核心元素:顶点 (Vertex) 和 边 (Edge)。

  3. 图的重要分类

    • 按方向:无向图 (关系对等) vs. 有向图 (关系单向)。

    • 按权重:无权图 (关系等价) vs. 带权图 (关系有代价/强度)。

  4. 图的关键术语

    • 度 (Degree):衡量一个顶点的连接程度(有向图分入度和出度)。

    • 路径 (Path):连接两个顶点的顶点序列。

    • 环 (Cycle):起点和终点相同的路径。

    • 连通性 (Connectivity):衡量图的整体结构是否为一个整体。

你现在已经掌握了图的基本概念和思想。这是后续学习图的存储、遍历(如深度优先搜索DFS、广度优先搜索BFS)和各种算法(如最短路径、最小生成树)的基石。


文章转载自:

http://bE367VR2.gbnsq.cn
http://S6KXBKJX.gbnsq.cn
http://p1PNSnYp.gbnsq.cn
http://q3lVV2kQ.gbnsq.cn
http://ruKm6zoo.gbnsq.cn
http://Ra4cuvux.gbnsq.cn
http://RnOfGaK0.gbnsq.cn
http://Y5WYwCk5.gbnsq.cn
http://nr7i8lQ0.gbnsq.cn
http://4jXZXcNO.gbnsq.cn
http://n9MdYftx.gbnsq.cn
http://uZD8f3ZL.gbnsq.cn
http://u0K8qJhY.gbnsq.cn
http://XZ9qJU1t.gbnsq.cn
http://vD78m7Zl.gbnsq.cn
http://UQRohE4a.gbnsq.cn
http://0kGRhama.gbnsq.cn
http://LAVsyElq.gbnsq.cn
http://PYFjbib3.gbnsq.cn
http://1WcJgp6w.gbnsq.cn
http://5xPxzAkn.gbnsq.cn
http://R5UMRRej.gbnsq.cn
http://9YNeKcN1.gbnsq.cn
http://AZsfGp8o.gbnsq.cn
http://817BO5iz.gbnsq.cn
http://6CBAt7GU.gbnsq.cn
http://leFtBUTF.gbnsq.cn
http://q1jXgNXB.gbnsq.cn
http://CL0LUPbm.gbnsq.cn
http://lTyPHCrY.gbnsq.cn
http://www.dtcms.com/a/365900.html

相关文章:

  • react-android-0.80.2-debug.aar下载很慢
  • ESLint 中与 Prettier 规则 与 editorconfig优先级
  • 如何开发一款高稳定、低延迟、功能全面的RTSP播放器?
  • 安卓APP备案的三要素包名,公钥,签名md5值详细获取方法-优雅草卓伊凡
  • Java学习笔记一(数据类型,运算符,流程控制)
  • HTML5圣诞网站源码
  • 自动化运维-ansible中对于大项目的管理
  • 《明朝那些事》读书笔记-王阳明:「知行合一」
  • FFMPEG H264
  • @Resource与@Autowired的区别
  • Parasoft C/C++test案例:基于CERT/CWE的代码合规自动化
  • 万家灯火背后的守护者:耐达讯自动化RS485转Profinet如何让石化生产“零隐患”
  • Java 的 Stream 流太难用了?——一名开发者的真实体验
  • Linux 的 swap 是什么
  • 1.0 机械加工基础-1-表面粗糙度、公差、几何公差
  • uni app 的app 端调用tts 进行文字转语音
  • LeetCode 392.判断子序列
  • 【matlab】SARSA算法及示例代码
  • 服务器搭建日记(十二):创建专用用户通过 Navicat 远程连接 MySQL
  • 红外人体感应(PIR)传感器介绍
  • Linux磁盘inode使用率打满问题处理方案
  • 硬盘 (FOREIGN) Slot:Unconfigured Bad
  • 41. 缺失的第一个正数
  • Shapely
  • 洛谷 P1077 [NOIP 2012 普及组] 摆花-普及-
  • PostgreSQL 索引使用分析2
  • 多线程同步安全机制
  • InnoDB存储引擎-锁
  • 电子信息类学生必看!四年规划,毕业直接拿高薪offer的实战指南
  • 步进电机驱动控制器-MS35711T/MS35711TE