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

【数据结构——邻接表】

文章目录

      • 引入
      • 头文件
        • 结构组成
        • 具体功能
      • no(弧尾)与next的区别
      • 功能实现
        • 产生邻接表
        • 初始化
        • 新增边
        • 访问顶点
        • 深搜
        • 重置访问标记
        • 广搜
      • 功能调用

引入

上篇对邻接矩阵进行了实现,这篇要实现的是邻接表:

每个顶点维护一个链表,存储其相邻顶点。

在这里插入图片描述
上图中链表节点结构中的4、0、2(D后面的)都是弧尾。

头文件

结构组成
//邻接表的边结构
typedef struct arcEdge{int no;       //边的顶点编号,no始终存储弧头(边的终点)//在有向图中被指向的顶点称为弧头(可以理解为是箭头指向的,所以称弧头)//无向图中no的含义与有向图一致,表示该边连接的另一个顶点的编号(即相邻顶点)int weight; //该边的权值struct arcEdge* next; //指向下一条边结构
}ArcEdge;//顶点结构
typedef struct {int no;     //顶点编号char* show; //顶点显示内容(如A、B…)ArcEdge* firstEdge; //该顶点指向其他顶点的边的链表首地址(边存在链表中)//所以边结构里面的变量no存储的是该顶点指向的其他顶点(弧头)
}ArcNode; //Arc:有向图中的单向边,Edge:无向图中的边//邻接表来表示图
typedef struct {ArcNode* node; //图中顶点的集合(顶点是用数组存储的)int* visited;  //标记遍历过的顶点int nodeNum;   //图的顶点个数int edgeNum;   //边的个数int directed;  //当前图是否有向
}AGraph;
具体功能
//产生一个n个顶点的邻接表
AGraph* createAGraph(int n);
//释放邻接表
void releaseAGraph(AGraph* graph);
//初始化邻接表
void initAGraph(AGraph* graph,const char* names[], int num, int directed);
//在邻接表graph中,添加一条从顶点x指向顶点y且权值为w的边
void addAGraph(AGraph* graph, int x, int y, int w);
//访问当前顶点
void visitAGraphNode(const ArcNode*node);void DFS_AGraph(const AGraph* graph, int v);
void BFS_AGraph(const AGraph* graph, int v);//重置,防止连续遍历时受干扰
void resetAGraphVisited(AGraph* graph);
  • 在功能实现之前先理清ArcEdge结构里面的no与next的区别:

no(弧尾)与next的区别

在这里插入图片描述

  • 两者在进行边的添加操作时可以看成两部分:

no主要用于表示与所添加的边相关联的顶点的编号;next主要用来表示这个新增的边在存储边的链表中所处的节点位置与插入逻辑的实现。

在这里插入图片描述

新边是通过头插法实现的,如下图edge->next指向原第一条边:
在这里插入图片描述

功能实现

产生邻接表
AGraph* createAGraph(int n) {AGraph* graph = (AGraph*)malloc(sizeof(AGraph));if (graph == NULL) {return NULL;}graph->directed = 0;graph->edgeNum = 0;graph->nodeNum = n;graph->node = (ArcNode*)malloc(sizeof(ArcNode));graph->visited = (int*)malloc(sizeof(int)*n);if (graph->node == NULL || graph->visited == NULL) {free(graph);return NULL;}memset(graph->node, 0,sizeof(ArcNode)*n);memset(graph->visited, 0,sizeof(int)*n);return graph;
}
初始化
void initAGraph(AGraph* graph,const char* name[], int num, int directed) {graph->directed = directed;for (int i = 0; i < num; i++) {graph->node[i].no = i;graph->node[i].show = (char*)name[i];graph->node[i].firstEdge = NULL;}
}
新增边
static ArcEdge* createArcEdge(int v, int w) {ArcEdge* edge = (ArcEdge*)malloc(sizeof(ArcEdge));if (edge == NULL) {return NULL;}edge->no = v;edge->weight = w;edge->next = NULL;return edge;
}void addAGraph(AGraph* graph, int x, int y, int w) {if (x < 0 || x >= graph->nodeNum || y < 0 || y >= graph->nodeNum) {return;}if (x == y) return;ArcEdge* edge = createArcEdge(y, w);edge->next = graph->node[x].firstEdge; //理解成两个不同区域的数据整理(点记点的数据,边记边)//将新边添加到顶点x的邻接链表中,隐式声明了这条边的起点(弧尾)是xgraph->node[x].firstEdge = edge;graph->edgeNum++;if (graph->directed == 0) {edge = createArcEdge(x, w);edge->next = graph->node[y].firstEdge;graph->node[y].firstEdge=edge;graph->edgeNum++;}//可以理解为弧尾是谁就将新边添加到谁的边链表中(无向添加两次)
}
访问顶点
void visitAGraphNode(const ArcNode* node) {printf("\t%s", node->show);
}
深搜
void DFS_AGraph(const AGraph* graph, int v) {ArcEdge* p;graph->visited[v] = 1;         //先标记再访问visitAGraphNode(&graph->node[v]);p = graph->node[v].firstEdge;  // p指向顶点v的第一条邻接边while (p) {   // 检查该边指向的顶点是否被访问过if (graph->visited[p->no] == 0) {DFS_AGraph(graph, p->no);}p = p->next;}
}
重置访问标记
void resetAGraphVisited(AGraph* graph) {memset(graph->visited, 0, sizeof(int) * graph->nodeNum);
}
广搜
void BFS_AGraph(const AGraph* graph, int v) {int* que = (int*)malloc(sizeof(int) * graph->nodeNum);int front = 0, rear = 0;ArcEdge* p;int cur;rear = (rear + 1) % graph->nodeNum;que[rear] = v;graph->visited[v] = 1;  //入队即一定会被访问,所以一入对就标记while (front != rear) {front = (front + 1) % graph->nodeNum;cur = que[front];visitAGraphNode(&graph->node[cur]);p = graph->node[cur].firstEdge; //传入链表头给pwhile (p) {if (graph->visited[p->no] == 0) {rear = (rear + 1) % graph->nodeNum;que[rear] = p->no;graph->visited[p->no] = 1;}p = p->next;}}free(que); 
}

实现对象:
在这里插入图片描述

功能调用

static void setupGraph(AGraph* graph) {
const char* name[] ={"A", "B", "C", "D", "E"};initAGraph(graph, name, sizeof(name) / sizeof(name[0]), 1);addAGraph(graph, 0, 4, 1);addAGraph(graph, 0, 3, 1);addAGraph(graph, 0, 1, 1);addAGraph(graph, 1, 4, 1);addAGraph(graph, 1, 2, 1);addAGraph(graph, 2, 0, 1);addAGraph(graph, 3, 2, 1);
}int main() {int n = 5;AGraph* graph = createAGraph(n);setupGraph(graph);printf("图的边数: %d\n", graph->edgeNum);printf("图的深度遍历:");DFS_AGraph(graph, 0);printf("\n图的广度遍历:");resetAGraphVisited(graph);BFS_AGraph(graph, 0);return 0;
}
http://www.dtcms.com/a/391826.html

相关文章:

  • RNA-seq分析之单基因Wilcoxon秩和检验
  • 四网络层IP-子网掩码ARP CIDR RIP OSPF BGP 路由算法-思考题
  • [重学Rust]之智能指针
  • 团体程序设计天梯赛-练习集 L1-036 A乘以B
  • H2数据库(tcp 服务器模式)调优
  • C# 面试记录
  • 深度学习(十):逻辑回归的代价函数
  • FreeRTOS学习笔记(六):汇编指令笔记
  • 【复刻】中国城市数字经济发展对环境污染的影响及机理研究(2011-2021年)
  • Blazer:一个免费开源、基于SQL的数据分析与可视化工具
  • 软件体系架构——系统架构评估与ATAM
  • sam2 docker部署
  • 深度学习------卷积神经网络
  • Amazon SES + NestJS 实战:零成本打造高送达率邮箱验证方案
  • MySQL 8.0临时表空间深度解析
  • 低秩矩阵:揭示高维数据中的简约之美
  • QR Wizard for Mac 好用的二维码生成器
  • 【redis】redis知识点
  • C语言模版(机试666)
  • 高通camx架构学习(二)——深入理解高通Camx Hal
  • 戴尔笔记本的奇怪功能
  • Linux文件系统结构与用户管理完全指南
  • 鸿蒙保存图片到相册
  • 【C语言】喝汽水问题分析:20元能喝多少瓶汽水?
  • 二物理层-ADSL-思考题
  • PyQt6之滑动条
  • 虚拟机ubuntu安装中文输入法
  • 康奈尔大学视觉-语言-动作模型全面综述:概念、进展、应用与挑战
  • 单片机--中断实验
  • 嵌入式 - GPIO