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

图的基础概念操作与遍历

一、图的基础概念与术语

  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; }
http://www.jsqmd.com/news/88337/

相关文章:

  • 52、系统性能调优指南
  • Unity学习笔记(十七)GUI控件(一)
  • 台达DVPEH3系列PLC与欧姆龙E5CC温控器通讯及控制实现
  • 192KHz 双声道输入 24 位 AD 转换器国产品牌DP8340兼容CS5340
  • Origin科研绘图——手把手教你“分段拟合”
  • XPM与IP模式下FIFO的比较
  • 53、Linux 系统优化与命令行操作指南
  • Cameralink采集卡软件EspeedGrab使用讲解:3 保存采集参数
  • C#+VisionMaster 学习笔记(目录)-目录
  • 54、Linux命令行与软件管理全攻略
  • MySQL数据处理(增删改)
  • 电科毕设 stm32 wifi远程可视化与农业灌溉系统(源码+硬件+论文)
  • 55、Ubuntu 系统软件管理全攻略
  • 34、Bash脚本中的循环控制与故障排查
  • 2025年年终无人机吊运公司推荐:不同预算与项目规模下的性价比分析与5家服务商对比 - 品牌推荐
  • vue基于Spring Boot框架的在线音乐推荐排行榜网站_q46lgu0x
  • 摄影的光线运用
  • 56、Linux内核与模块管理全解析
  • 英语_阅读_CIMON 2_待读
  • 35、脚本开发中的故障排除、流程控制与参数处理
  • 一文搞懂CNN - LSTM - Attention回归预测:新手友好实战
  • 如何选择靠谱的无人机吊运服务商?2025年年终最新市场深度解析及5家实力公司推荐! - 品牌推荐
  • vue基于Spring Boot框架的学生干部选举管理系统的设计与实现_4q46dzc1
  • 26、GNOME开发中的实用组件与功能详解
  • Kubernetes Node 管理完全指南:从入门到生产实践
  • 27、GNOME开发:Druids、会话管理及Glade使用指南
  • Cameralink采集卡软件EspeedGrab使用讲解:4图像处理
  • Kubernetes RBAC 权限控制实战指南:从零开始掌握每个细节
  • 36、脚本编程中的参数、循环与数据处理
  • const引用