news 2026/5/1 8:24:20

图的基础概念操作与遍历

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
图的基础概念操作与遍历

一、图的基础概念与术语

  1. 概念:图是一种非线性数据结构,由顶点和边组成,相较于线性关系(链表)和分治关系(树),网络关系(图)的自由度更高,因而更为复杂。

  2. 常见类型

    1. 是否有方向:无向图与有向图

      • 在无向图中,边表示两顶点之间的“双向”连接关系
      • 在有向图中,边具有方向性,即A→B和B→A两个方向的边是相互独立的
    2. 所有顶点是否连通:连通图和非连通图

      • 对于连通图,从某个顶点出发,可以到达其余任意顶点。
      • 对于非连通图,从某个顶点出发,至少有一个顶点无法到达。
    3. 我们还可以为边添加“权重”变量,从而得到有权图

  3. 术语:

    1. 邻接:当两顶点之间存在边相连时,称这两顶点“邻接”。
    2. 路径:从顶点 A 到顶点 B 经过的边构成的序列被称为从 A 到 B 的“路径”。
    3. 度:一个顶点拥有的边数。对于有向图,入度表示有多少条边指向该顶点,出度表示有多少条边从该顶点指出。

二、图的表示

  1. 邻接矩阵:设图的顶点数量为 ,邻接矩阵使用一个 大小的矩阵来表示图,每一行(列)代表一个顶点,矩阵元素代表边,用0或1表示两个顶点之间是否存在边。

  1. 邻接表:邻接表(adjacency list)使用n个链表来表示图,链表节点表示顶点。第i个链表对应顶点i,其中存储了该顶点的所有邻接顶点(与该顶点相连的顶点)。

三、图的基础操作

  1. 基于邻接矩阵的实现
/* 基于邻接矩阵实现的无向图类 */ class GraphAdjMat { vector<int> vertices; vector<vector<int>> adjMat; public: /* 构造方法 */ GraphAdjMat(const vector<int> &vertices, const vector<vector<int>> &edges) { // 添加顶点 for (int val : vertices) { addVertex(val); } // 添加边 for (const vector<int> &edge : edges) { addEdge(edge[0], edge[1]); } } /* 获取顶点数量 */ int size() const { return vertices.size(); } /* 添加顶点 */ void addVertex(int val) { int n = size(); vertices.push_back(val); adjMat.emplace_back(vector<int>(n, 0)); for (vector<int> &row : adjMat) { row.push_back(0); } } /* 删除顶点 */ void removeVertex(int index) { if (index >= size()) { throw out_of_range("顶点不存在"); } vertices.erase(vertices.begin() + index); adjMat.erase(adjMat.begin() + index); for (vector<int> &row : adjMat) { row.erase(row.begin() + index); } } /* 添加边 */ // 参数 i, j 对应 vertices 元素索引 void addEdge(int i, int j) { if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { throw out_of_range("顶点不存在"); } adjMat[i][j] = 1; adjMat[j][i] = 1; } /* 删除边 */ void removeEdge(int i, int j) { if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { throw out_of_range("顶点不存在"); } adjMat[i][j] = 0; adjMat[j][i] = 0; } /* 打印邻接矩阵 */ void print() { cout << "顶点列表 = "; printVector(vertices); cout << "邻接矩阵 =" << endl; printVectorMatrix(adjMat); } };
  1. 基于邻接表的实现
/* 基于邻接表实现的无向图类 */ class GraphAdjList { public: unordered_map<Vertex *, vector<Vertex *>> adjList; /* 在 vector 中删除指定节点 */ void remove(vector<Vertex *> &vec, Vertex *vet) { for (int i = 0; i < vec.size(); i++) { if (vec[i] == vet) { vec.erase(vec.begin() + i); break; } } } /* 构造方法 */ GraphAdjList(const vector<vector<Vertex *>> &edges) { for (const vector<Vertex *> &edge : edges) { addVertex(edge[0]); addVertex(edge[1]); addEdge(edge[0], edge[1]); } } /* 获取顶点数量 */ int size() { return adjList.size(); } /* 添加边 */ void addEdge(Vertex *vet1, Vertex *vet2) { if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) throw invalid_argument("不存在顶点"); adjList[vet1].push_back(vet2); adjList[vet2].push_back(vet1); } /* 删除边 */ void removeEdge(Vertex *vet1, Vertex *vet2) { if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) throw invalid_argument("不存在顶点"); remove(adjList[vet1], vet2); remove(adjList[vet2], vet1); } /* 添加顶点 */ void addVertex(Vertex *vet) { if (adjList.count(vet)) return; adjList[vet] = vector<Vertex *>(); } /* 删除顶点 */ void removeVertex(Vertex *vet) { if (!adjList.count(vet)) throw invalid_argument("不存在顶点"); adjList.erase(vet); for (auto &adj : adjList) { remove(adj.second, vet); } } /* 打印邻接表 */ void print() { cout << "邻接表 =" << endl; for (auto &adj : adjList) { const auto &key = adj.first; const auto &vec = adj.second; cout << key->val << ": "; printVector(vetsToVals(vec)); } } };

四、图的遍历

  1. 广度优先搜索:广度优先遍历是一种由近及远的遍历方式,从某个节点出发,始终优先访问距离最近的顶点,并一层层向外扩张
  2. 代码实现:
vector<Vertex *> graphBFS(GraphAdjList &graph, Vertex *startVet) { vector<Vertex *> res; unordered_set<Vertex *> visited = {startVet}; queue<Vertex *> que; que.push(startVet); while (!que.empty()) { Vertex *vet = que.front(); que.pop(); res.push_back(vet); for (auto adjVet : graph.adjList[vet]) { if (visited.count(adjVet)) continue; que.push(adjVet); visited.emplace(adjVet); } } return res; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 17:43:58

wangEditor处理微信公众号音视频嵌入转存

以下是针对党政事业单位项目需求的完整技术解决方案&#xff0c;包含信创环境适配、跨框架兼容、云存储集成等核心内容&#xff0c;采用买断式授权模式&#xff0c;源代码完全可控&#xff1a; 一、系统架构设计 1. 技术栈选型 前端框架&#xff1a;Vue2/Vue3/React 通用适配…

作者头像 李华
网站建设 2026/4/21 2:10:02

Vue3如何整合第三方插件支持大文件批量上传?

大文件上传解决方案 各位同行大佬们好&#xff0c;作为一个在广东摸爬滚打多年的前端"老油条"&#xff0c;最近接了个让我差点秃顶的项目——20G大文件上传系统&#xff0c;还要兼容IE9&#xff01;这感觉就像让我用竹篮子去打水还要不漏一样刺激… 需求分析&#…

作者头像 李华
网站建设 2026/5/1 7:42:24

jQuery如何实现网页大文件上传的进度回传功能?

.NET程序员的20G文件上传历险记 大家好&#xff0c;我是甘肃的一名苦逼.NET程序员&#xff0c;最近接了个外包项目&#xff0c;客户的需求简直是要我老命啊&#xff01;来给大家扒一扒这个"价值连城"的项目需求&#xff1a; 项目需求&#xff1a;地狱级难度 大文件…

作者头像 李华
网站建设 2026/4/22 17:21:15

JSP如何支持跨平台的大文件分片上传?

大文件传输系统建设方案 一、需求分析与技术选型 作为福建IT行业软件公司项目负责人&#xff0c;针对公司当前大文件传输需求&#xff0c;经过详细技术调研和业务分析&#xff0c;我提出以下技术方案&#xff1a; 1.1 核心需求痛点 现有开源组件无法满足&#xff1a; 文件夹…

作者头像 李华
网站建设 2026/4/17 23:14:43

探索现代永磁同步电机控制原理:从理论到Matlab仿真实践

现代永磁同步电机控制原理&#xff0c;包含书和matlab 仿真程序&#xff0c;包含直接转矩控制&#xff0c;滑膜观测器&#xff0c;高频注入等仿真模型。在电机控制领域&#xff0c;永磁同步电机&#xff08;PMSM&#xff09;凭借其高效、节能、功率密度大等优势&#xff0c;在工…

作者头像 李华
网站建设 2026/4/29 17:24:07

53、Linux 系统优化与命令行操作指南

Linux 系统优化与命令行操作指南 1. MySQL 优化建议 在处理 MySQL 数据库时,为了提升性能和效率,有一些实用的优化建议: - 字段声明 :创建表时,将字段声明为 NOT NULL ,这样可以节省空间并提高查询速度。 - 默认值设置 :为字段提供默认值,并在合适的地方使用…

作者头像 李华