图的十字链表存储法:高效表示有向图与有向带权图

引言

        图是离散数学中一个重要的数据结构,广泛应用于计算机科学和其他领域。在实际应用中,经常需要处理有向图有向带权图,这就需要一种高效的图的存储方法。十字链表存储法是一种常用的表示有向图和有向带权图的数据结构,它结合了邻接表和逆邻接表,使得查找入边出边的操作更加高效

十字链表存储法的概念和原理

        在十字链表中,每个顶点对应一个顶点结点,下图为顶点结点的结构图

        data:是顶点存储的数据

        firstIn:指向此顶点的第一条入边 

        firstOut:指向此顶点的第一条出边 

        所以,顶点结点只存储它的顶点数据,和它的一条入边和一条出边


        在十字链表中,每条边对应一个边结点,下图为边结点的结构图

        tailVex:这条边的边尾(没有箭头)顶点的数组下标,相当于标识它的边尾顶点

        headVex:这条边的边头(有箭头)顶点的数组下标,相当于标识它的边头顶点

 这样就确定了这条边的头和尾了

        headLink:指向边头与此边的边头相同的一条边

        tailLink:指向边尾与此边的边尾相同的一条边

        weight:边的权

这样就包含了所有边的信息,顶点和边的信息都能够表示,所以十字链表能够表示一个图

        理解了十字链表的数据结构和它的概念及原理,那么对图的操作就很简单了,其实就是对链表和指针的操作

十字链表存储法的优点

        高效查找:十字链表存储法使得查找某个顶点的出边和入边的操作变得高效,时间复杂度为O(d),其中d为该顶点的度数。

        节省空间:相比邻接矩阵法,十字链表存储法在空间上更加节省,尤其在稀疏图的情况下,可以大大减少存储空间。

        支持有向带权图:十字链表存储法不仅适用于有向图,还可以方便地表示有向带权图,为实际应用提供了更多的可能性。

十字链表数据结构的定义

        下面用C语言实现十字链表的数据结构

#define MAX_VERTICES 100
typedef int VertexDataType;

// 定义边结构
typedef struct Edge {
    int tailVex;         // 边的起点,顶点的数组下标
    int headVex;         // 边的终点,顶点的数组下标
    int weight;          // 边的权重(如果是有权图)
    Edge* headLink;  // 指向下一条具有相同起点的边的指针
    Edge* tailLink;  // 指向下一条具有相同终点的边的指针
} Edge;

// 定义顶点结构
typedef struct Vertex {
    VertexDataType data; // 顶点数据
    Edge* firstOut;  // 指向第一个出边的指针
    Edge* firstIn;   // 指向第一个入边的指针
} Vertex;

// 定义图的结构
typedef struct Graph {
    int num_vertices;    // 图中顶点的数量
    int num_edges;       // 图中边的数量
    Vertex vertices[MAX_VERTICES];  // 存储顶点的数组
} Graph;

基本操作

// 初始化图
void initGraph(Graph* graph, int num_vertices) {
    graph->num_vertices = num_vertices;
    graph->num_edges = 0;
    for (int i = 0; i < num_vertices; ++i) {
        graph->vertices[i].data = i;  // 以顶点索引作为顶点数据
        graph->vertices[i].firstOut = NULL; // 初始化顶点的出边链表为空
        graph->vertices[i].firstIn = NULL;  // 初始化顶点的入边链表为空
    }
}

// 添加一条边
void addEdge(Graph* graph, int tail, int head, int weight) {
    // 创建新的边节点
    Edge* newEdge = (Edge*)malloc(sizeof(Edge));
    newEdge->tailVex = tail;
    newEdge->headVex = head;
    newEdge->weight = weight;
    newEdge->headLink = graph->vertices[head].firstIn;   // 新边的headLink指向终点为head的第一个入边
    newEdge->tailLink = graph->vertices[tail].firstOut;  // 新边的tailLink指向起点为tail的第一个出边

    // 更新顶点的出边链表和入边链表
    graph->vertices[head].firstIn = newEdge;    // 将新边添加到终点为head的入边链表的头部
    graph->vertices[tail].firstOut = newEdge;   // 将新边添加到起点为tail的出边链表的头部

    // 边数量加1
    graph->num_edges++;
}

// 获取顶点的入度
int getInDegree(Graph* graph, int vertex) {
    int in_degree = 0;
    Edge* edge = graph->vertices[vertex].firstIn;
    while (edge != NULL) {
        in_degree++;
        edge = edge->headLink;
    }
    return in_degree;
}

// 获取顶点的出度
int getOutDegree(Graph* graph, int vertex) {
    int out_degree = 0;
    Edge* edge = graph->vertices[vertex].firstOut;
    while (edge != NULL) {
        out_degree++;
        edge = edge->tailLink;
    }
    return out_degree;
}

总结

        十字链表存储法是一种高效表示有向图和有向带权图的数据结构。通过它,我们可以方便地查找某个顶点的出边和入边,同时节省存储空间。在实际应用中,十字链表存储法在处理有向图和有向带权图时表现出色,是一个值得掌握和应用的数据结构。

        希望本篇博文能够帮助读者理解十字链表存储法,并在实际应用中发挥其优势,提高图相关问题的处理效率。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不想学习啊啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值