C++ 数据结构之图:从理论到实践

news2025/4/15 22:27:50

一、图的基本概念

1.1 图的定义与组成

图(Graph)由顶点(Vertex)边(Edge)组成,形式化定义为:

G = (V, E)
  • 顶点集合 V:表示实体(如城市、用户)

  • 边集合 E:表示实体间关系(如道路、社交关系)

1.2 图的分类

类型特点应用场景
无向图边无方向性社交网络
有向图边有方向性网页链接
加权图边带权值路径规划
有环图包含环路状态机
连通图所有顶点连通网络拓扑

二、图的存储结构

2.1 邻接矩阵

使用二维数组存储顶点间关系

class AdjMatrixGraph {
private:
    vector<vector<int>> matrix;  // 存储边权
    int vertexCount;

public:
    AdjMatrixGraph(int n) : vertexCount(n), matrix(n, vector<int>(n, 0)) {}

    void addEdge(int from, int to, int weight = 1) {
        matrix[from][to] = weight;
        // 无向图需添加 matrix[to][from] = weight;
    }

    void print() {
        for (auto& row : matrix) {
            for (int val : row) cout << val << " ";
            cout << endl;
        }
    }
};

特点

  • 空间复杂度 O(V²)

  • 适合稠密图

  • 快速判断顶点是否邻接


2.2 邻接表

使用链表/数组存储邻接关系

struct EdgeNode {
    int dest;
    int weight;
    EdgeNode* next;
};

class AdjListGraph {
private:
    struct VertexNode {
        EdgeNode* head = nullptr;
    };

    vector<VertexNode> vertices;
    int vertexCount;

public:
    AdjListGraph(int n) : vertexCount(n), vertices(n) {}

    void addEdge(int from, int to, int weight = 1) {
        EdgeNode* newNode = new EdgeNode{to, weight, vertices[from].head};
        vertices[from].head = newNode;
    }

    ~AdjListGraph() {
        for (auto& v : vertices) {
            while (v.head) {
                EdgeNode* temp = v.head;
                v.head = v.head->next;
                delete temp;
            }
        }
    }
};

特点

  • 空间复杂度 O(V + E)

  • 适合稀疏图

  • 高效遍历邻接顶点


三、图的遍历算法

3.1 深度优先搜索(DFS)

void dfs(const AdjListGraph& graph, int v, vector<bool>& visited) {
    visited[v] = true;
    cout << "Visit: " << v << endl;

    EdgeNode* curr = graph.getEdges(v);
    while (curr) {
        if (!visited[curr->dest]) {
            dfs(graph, curr->dest, visited);
        }
        curr = curr->next;
    }
}

3.2 广度优先搜索(BFS)

void bfs(const AdjListGraph& graph, int start) {
    vector<bool> visited(graph.size(), false);
    queue<int> q;

    visited[start] = true;
    q.push(start);

    while (!q.empty()) {
        int v = q.front();
        q.pop();
        cout << "Visit: " << v << endl;

        EdgeNode* curr = graph.getEdges(v);
        while (curr) {
            if (!visited[curr->dest]) {
                visited[curr->dest] = true;
                q.push(curr->dest);
            }
            curr = curr->next;
        }
    }
}

四、经典图算法实现

4.1 Dijkstra 最短路径算法

vector<int> dijkstra(const AdjListGraph& graph, int src) {
    const int INF = INT_MAX;
    vector<int> dist(graph.size(), INF);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;

    dist[src] = 0;
    pq.emplace(0, src);

    while (!pq.empty()) {
        auto [d, u] = pq.top();
        pq.pop();

        if (d > dist[u]) continue;

        EdgeNode* curr = graph.getEdges(u);
        while (curr) {
            int v = curr->dest;
            int w = curr->weight;
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                pq.emplace(dist[v], v);
            }
            curr = curr->next;
        }
    }
    return dist;
}

4.2 Prim 最小生成树算法

int primMST(const AdjListGraph& graph) {
    const int INF = INT_MAX;
    vector<int> key(graph.size(), INF);
    vector<bool> inMST(graph.size(), false);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;

    key[0] = 0;
    pq.emplace(0, 0);
    int totalWeight = 0;

    while (!pq.empty()) {
        auto [k, u] = pq.top();
        pq.pop();

        if (inMST[u]) continue;
        inMST[u] = true;
        totalWeight += k;

        EdgeNode* curr = graph.getEdges(u);
        while (curr) {
            int v = curr->dest;
            int w = curr->weight;
            if (!inMST[v] && w < key[v]) {
                key[v] = w;
                pq.emplace(key[v], v);
            }
            curr = curr->next;
        }
    }
    return totalWeight;
}

五、图的应用场景

5.1 社交网络分析

  • 顶点:用户

  • 边:关注/好友关系

  • 算法应用:推荐系统(BFS扩展好友)、影响力分析(PageRank)

5.2 路径规划系统

  • 顶点:地点

  • 边:道路(权重=距离/时间)

  • 算法应用:最短路径(Dijkstra)、实时导航(A*算法)

5.3 任务调度系统

  • 顶点:任务

  • 边:依赖关系(有向边)

  • 算法应用:拓扑排序检测循环依赖


六、性能优化技巧

6.1 数据结构选择策略

图类型推荐存储结构理由
稠密图邻接矩阵快速访问任意边
稀疏图邻接表节省内存
动态变化图邻接表高效增删边操作
需要频繁判断邻接邻接矩阵O(1)时间判断

6.2 内存管理优化

// 使用智能指针管理边节点
class SafeAdjListGraph {
private:
    struct EdgeNode {
        int dest;
        shared_ptr<EdgeNode> next;
    };

    vector<shared_ptr<EdgeNode>> vertices;
};

6.3 并行计算优化

// 使用OpenMP并行处理边
void parallelBFS(const AdjListGraph& graph, int start) {
    // ...
    #pragma omp parallel for
    for (EdgeNode* curr = graph.getEdges(u); curr; curr = curr->next) {
        // 并行处理邻接节点
    }
}

七、现代C++图实现示例

template <typename VertexType, typename EdgeType = int>
class Graph {
private:
    unordered_map<VertexType, list<pair<VertexType, EdgeType>>> adjList;

public:
    void addVertex(const VertexType& v) {
        adjList.emplace(v, list<pair<VertexType, EdgeType>>());
    }

    void addEdge(const VertexType& from, const VertexType& to, EdgeType weight = 1) {
        adjList[from].emplace_back(to, weight);
    }

    auto dijkstra(const VertexType& start) -> unordered_map<VertexType, EdgeType> {
        // 实现Dijkstra算法...
    }

    // 其他算法实现...
};

// 使用示例
Graph<string> cityGraph;
cityGraph.addVertex("Beijing");
cityGraph.addVertex("Shanghai");
cityGraph.addEdge("Beijing", "Shanghai", 1200); // 公里数

八、总结与学习资源

8.1 关键要点

  1. 存储结构选择:根据场景选择矩阵或邻接表

  2. 算法复杂度认知:DFS/BFS O(V+E),Dijkstra O(E log V)

  3. 现代C++实践:使用STL容器和智能指针

  4. 性能优化方向:并行处理、内存布局优化

8.2 推荐学习路径

  1. 基础理论:《算法导论》图论章节

  2. 算法可视化:VisuAlgo.net 图算法演示

  3. 高性能实现:Boost Graph Library (BGL)

  4. 领域应用:社交网络分析、推荐系统论文

“图是描述复杂关系的终极工具——从微小的分子结构到浩瀚的宇宙网络,皆可用图建模。” —— 匿名算法工程师

通过掌握图的存储结构与经典算法,开发者可以解锁解决复杂系统问题的能力。建议结合具体应用场景进行实践,逐步深入理解这一强大的数据结构。

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

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

相关文章

linux多线(进)程编程——(5)虚拟内存与内存映射

前言&#xff08;前情回顾&#xff09; 进程君开发了管道这门技术后&#xff0c;修真界的各种沟通越来越频繁&#xff0c;这天进程君正与自己的孩子沟通&#xff0c;进程君的孩子说道&#xff1a; “爸爸&#xff0c;昨天我看他们斗法&#xff0c;小明一拳打到了小刚的肚子上&…

SpringBoot 动态路由菜单 权限系统开发 菜单权限 数据库设计 不同角色对应不同权限

介绍 系统中的路由配置可以根据用户的身份、角色或其他权限信息动态生成&#xff0c;而不是固定在系统中。不同的用户根据其权限会看到不同的路由&#xff0c;访问不同的页面。对应各部门不同的权限。 效果 [{"id": 1,"menuName": "用户管理"…

[dp8_子数组] 乘积为正数的最长子数组长度 | 等差数列划分 | 最长湍流子数组

目录 1.乘积为正数的最长子数组长度 2.等差数列划分 3.最长湍流子数组 写代码做到&#xff0c;只用维护好自己的一小步 1.乘积为正数的最长子数组长度 链接&#xff1a;1567. 乘积为正数的最长子数组长度 给你一个整数数组 nums &#xff0c;请你求出乘积为正数的最长子数…

【图像处理基石】什么是通透感?

一、画面的通透感定义 画面的通透感指图像在色彩鲜明度、空间层次感、物体轮廓清晰度三方面的综合表现&#xff0c;具体表现为&#xff1a; 色彩鲜明&#xff1a;颜色纯净且饱和度适中&#xff0c;无灰暗或浑浊感&#xff1b;层次分明&#xff1a;明暗过渡自然&#xff0c;光…

无锡无人机超视距驾驶证怎么考?

无锡无人机超视距驾驶证怎么考&#xff1f;在近年来&#xff0c;无人机技术的迅猛发展使得无人机的应用场景变得愈发广泛&#xff0c;其不仅在环境监测、农业喷洒、快递配送等领域展现出真金白银的价值&#xff0c;同时也推动了无人机驾驶证的需求。尤其是在无锡&#xff0c;随…

213、【图论】有向图的完全联通(Python)

题目描述 原题链接&#xff1a;105. 有向图的完全联通 代码实现 import collectionsn, k list(map(int, input().split())) adjacency collections.defaultdict(list) for _ in range(k):head, tail list(map(int, input().split()))adjacency[head].append(tail)visited_…

图像形态学操作对比(Opencv)

形态学基于图像的形状进行操作&#xff0c;用于处理二值化图像&#xff0c;主要包括腐蚀和膨胀两种基本操作。这些操作通常用于去除噪声、分隔或连接相邻的元素以及寻找图像中显著的最大点和最小点。 1. 形态学操作 import cv2 import numpy as np import matplotlib.pyplot …

复刻系列-星穹铁道 3.2 版本先行展示页

复刻星穹铁道 3.2 版本先行展示页 0. 视频 手搓&#xff5e;星穹铁道&#xff5e;展示页&#xff5e;&#xff5e;&#xff5e; 1. 基本信息 作者: 啊是特嗷桃系列: 复刻系列官方的网站: 《崩坏&#xff1a;星穹铁道》3.2版本「走过安眠地的花丛」专题展示页现已上线复刻的网…

Linux:进程理解1(查看进程,创造进程,进程状态)

进程理解 &#xff08;一&#xff09;查看进程通过系统调用获取进程标示* &#xff08;二&#xff09;创造进程&#xff08;fork&#xff09;1. 创造的子进程的PCB代码数据怎么来&#xff1f;2.一个函数为什么有两个返回值&#xff1f;3. 为什么这里会有 两个 id值&#xff1f;…

异形遮罩之QML中的 `OpacityMask` 实战

文章目录 &#x1f327;️ 传统实现的问题&#x1f449; 效果图 &#x1f308; 使用 OpacityMask 的理想方案&#x1f449;代码如下&#x1f3af; 最终效果&#xff1a; ✨ 延伸应用&#x1f9e0; 总结 在 UI 设计中&#xff0c;经常希望实现一些“异形区域”拥有统一透明度或颜…

如何为您的设计应用选择高速连接器

电气应用的设计过程需要考虑诸多因素&#xff0c;尤其是在设计高速网络时。许多连接器用户可能没有意识到&#xff0c;除了在两个互连之间组装导电线路之外&#xff0c;还需要考虑各种工艺。在建立高速连接并确保适当的信号完整性时&#xff0c;必须考虑蚀刻、公差、屏蔽等因素…

【论文阅读】UniAD: Planning-oriented Autonomous Driving

一、Introduction 传统的无人驾驶采用了区分子模块的设计&#xff0c;即将无人驾驶拆分为感知规划控制三个模块&#xff0c;这虽然能够让无人驾驶以一个很清晰的结构实现&#xff0c;但是感知的结果在传达到规划部分的时候&#xff0c;会导致部分信息丢失&#xff0c;这势必会…

upload-labs二次打

1(前端js绕过) 弹窗&#xff0c;先看看有没有js有&#xff0c;禁用js 禁用后就可以上传php文件了&#xff0c;然后我们就去访问文件&#xff0c;成功 2&#xff08;MIME绕过&#xff09; 先上传一个php文件试试&#xff0c;不行&#xff0c;.htaccess不行, 试试MIME类型&am…

提权实战!

就是提升权限&#xff0c;当我们拿到一个shell权限较低&#xff0c;当满足MySQL提权的要求时&#xff0c;就可以进行这个提权。 MySQL数据库提权&#xff08;Privilege Escalation&#xff09;是指攻击者通过技术手段&#xff0c;从低权限的数据库用户提升到更高权限&#xff…

ChromeOS 135 版本更新

ChromeOS 135 版本更新 一、ChromeOS 135 更新内容 1. ChromeOS 电池寿命优化策略 为了延长 Chromebook 的使用寿命&#xff0c;ChromeOS 135 引入了一项全新的电池充电限制策略 —— DevicePowerBatteryChargingOptimization&#xff0c;可提供更多充电优化选项&#xff0c…

javaSE.Lambda表达式

如果一个接口中有且只有一个待实现的抽象方法&#xff0c;那么我们可以将匿名内部类简写为Lambda表达式。 简写规则 标准格式&#xff1a; &#xff08;【参数类型 参数名称&#xff0c;】...&#xff09; -> {代码语句&#xff0c; 包括返回值} 只有一行花括号{}可以省略。…

【随身wifi】青龙面板保姆级教程

0.操作前必看 本教程基于Debian系统&#xff0c;从Docker环境。面板安装&#xff0c;到最后拉取脚本的使用。 可以拉库跑狗东京豆&#xff0c;elm红包等等&#xff0c;也可以跑写自己写的脚本&#xff0c;自行探索 重要的号别搞&#xff0c;容易黑号&#xff0c;黑号自己负责…

Android 之美国关税问题导致 GitHub 403 无法正常访问,责任在谁?

这几天各国关税问题导致世界动荡不安&#xff0c;如今GitHub又无法正常访问&#xff0c;是不是Google到时候也无法正常使用了。

4月13日星期日早报简报微语报早读

4月13日星期日&#xff0c;农历三月十六&#xff0c;早报#微语早读。 1、北京处置倒伏树木843棵&#xff0c;已全部处置完毕&#xff1b; 2、山西大同“订婚强奸案”本月16日二审宣判&#xff0c;一审男方被判3年刑&#xff1b; 3、今年我国快递业务量已突破500亿件&#xf…

动态路由, RIP路由协议,RIPv1,RIPv2

动态路由 1、回顾 路由&#xff1a;从源主机到目标主机的过程 源主机发送数据给目标主机&#xff0c;源主机会查看自身的路由信息 如果目标主机是自己同网段&#xff0c;源主机查看的是直连路由 如果目标主机和自己不同网段&#xff0c;源主机查看的是静态路由、动态路由、默…