查看: 60|回复: 0

手搓邻接表:从零开始理解图数据结构(适合新手小白)

[复制链接]
  • TA的每日心情

    5 小时前
  • 签到天数: 33 天

    [LV.5]常住居民I

    32

    主题

    0

    回帖

    63

    积分

    注册会员

    Rank: 2

    积分
    63
    发表于 2025-6-29 10:37:02 | 显示全部楼层 |阅读模式
    一、简介和特点
    邻接表是一种用于存储(Graph)的数据结构,特别适合稀疏图(边数较少的图)。它通过链表的方式为每个节点维护其相邻节点的信息,既能高效节省空间,又能灵活支持图的动态操作。本文将基于您手写的C++邻接表类,通过代码注释与步骤解析,帮助新手小白理解其核心实现逻辑。
    二、优点
    1. 空间效率:相较于邻接矩阵(二维数组),邻接表在稀疏图中仅需存储实际存在的边,避免大量无效空间的浪费。
    2. 动态扩展:支持节点和边的随时添加,无需提前确定图规模。
    3. 操作灵活:通过链表快速定位节点的出边,便于实现深度/广度优先遍历等算法
    三、实现步骤
    1. 定义边和节点的结构体:
        edge:每条边包含指向下一节点的索引(nextnum)、边权值(data)和指向下一条边的指针(next)。
        listnode:每个节点包含节点数据(data)和指向第一条边的指针(first)。
    2. 构建图类(graph):
        使用节点数组(g)存储所有节点,节点数(n)记录当前节点数量。
        提供两种构造函数:指定节点数初始化或默认初始化为1001个节点。
    3. 添加边(addedge(head, tail, data)):
        从节点head指向节点tail,边权值为data。
        若head节点无出边,直接创建第一条边;否则遍历链表找到末尾,添加新边。
    4. 添加节点(addnode(k, data)):
        在索引k处创建新节点,存储数据data,并更新节点数。
    5. 打印图结构(print()):
        遍历所有节点,输出节点数据及其所有出边的目标节点和权值。
    四、代码和注释
    1. #include <iostream>
    2. using namespace std;

    3. // 边结构体:存储指向下一节点的索引、边权值、指向下一条边的指针
    4. struct edge {
    5.     int nextnum;      // 下一节点的索引
    6.     int data;         // 边权值(可根据需求修改类型)
    7.     edge* next = nullptr;  // 指向下一条边的指针
    8. };

    9. // 节点结构体:存储节点数据、指向第一条边的指针
    10. struct listnode {
    11.     int data;          // 节点数据
    12.     edge* first = nullptr;   // 指向第一条出边
    13. };

    14. // 图类:维护节点数组和节点数
    15. class graph {
    16.     listnode* g;        // 节点数组
    17.     int n = 0;          // 当前节点数量

    18. public:
    19.     // 构造函数1:指定节点数初始化数组
    20.     graph(int num) {
    21.         g = new listnode[num];
    22.     }

    23.     // 构造函数2:默认初始化1001个节点
    24.     graph() {
    25.         g = new listnode[1001];
    26.     }

    27.     // 添加边方法
    28.     void addedge(int head, int tail, int data) {
    29.         // 获取head节点的第一条边
    30.         edge* tmp = g[head].first;
    31.         if (!tmp) {          // 若head无出边,创建第一条边
    32.             g[head].first = new edge;
    33.             g[head].first->data = data;
    34.             g[head].first->nextnum = tail;
    35.         } else {             // 若已有出边,遍历到末尾添加新边
    36.             while (tmp->next)
    37.                 tmp = tmp->next;
    38.             tmp->next = new edge;
    39.             tmp->next->data = data;
    40.             tmp->next->nextnum = tail;
    41.         }
    42.     }

    43.     // 添加节点方法
    44.     void addnode(int k, int data) {
    45.         g[k].data = data;       // 设置节点数据
    46.         n++;                    // 更新节点数量
    47.     }

    48.     // 打印图结构方法
    49.     void print() {
    50.         for (int i = 0; i < n; i++) {  // 遍历所有节点
    51.             cout << g[i].data << "->";   // 输出节点数据
    52.             edge* tmp = g[i].first;     // 获取当前节点的第一条边
    53.             while (tmp) {               // 遍历所有出边
    54.                 cout << tmp->nextnum << "(" << tmp->data << ") -> ";
    55.                 tmp = tmp->next;
    56.             }
    57.             cout << "null" << endl;      // 末尾标记
    58.         }
    59.     }
    60. };
    复制代码

    五、总结
    邻接表作为图数据结构的核心实现方式之一,通过链表巧妙平衡了空间与效率。理解其底层逻辑不仅有助于处理图算法问题,更能培养对动态数据结构的抽象思维。本文代码虽为简化版,但已涵盖基础操作框架,可作为学习图数据结构的入门参考。建议进一步探索双向链表优化、权重存储调整等扩展应用。
    参考:信奥自学

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    快速回复 返回顶部 返回列表