邻接表介绍

邻接矩阵是不错的一种图存储结构,但是我们也发现,对于边数相对顶点较少的图,这种结构比较较浪费存储空间如果不想浪费存储空间,大家肯定会先到链表。需要空间的时候再才想内存去申请,同样适用于图的存储。我们把这种数组与链表相结合的存储方式成为邻接表(Adjacency List)。关于邻接矩阵的详细介绍请看:图:图的邻接矩阵创建、深度优先遍历和广度优先遍历详解 。

邻接表创建图

我们创建边表的单链表时,可以使用头插法和尾插法创建,不过尾插法要麻烦一点,需要先创建头结点,最后还要释放头结点,不过2种方法 效果一样

//邻接表创建无向网图
GraphAdjList* CreateGraphAdjList()
{GraphAdjList* graph = (GraphAdjList*)malloc(sizeof(GraphAdjList));int vexNum, edgeNum;printf("请输入顶点和边数(用逗号隔开):");scanf("%d,%d",&vexNum,&edgeNum);graph->edgeNun = edgeNum;graph->vexNum = vexNum;getchar();//消除上面的换行符printf("请输入顶点的值:");输入顶点值//for (int i = 0; i < vexNum; i++)//{//  scanf("%c", &graph->list[i].data);//   graph->list[i].first = NULL;//初始化first指针域//}输入边表数据//for (int k = 0; k < edgeNum; k++)//{//    int i, j, w;//  printf("请输入(Vi,Vj)对应的顶点下标和权值(用逗号隔开):");// scanf("%d,%d,%d", &i, &j, &w);//  //i -> j i出度到j// EdgeNode* edge = (EdgeNode*)malloc(sizeof(EdgeNode));//创建边结点// edge->vexIndex = j;//   edge->weight = w;// edge->next = graph->list[i].first;//头插法 往单链表插入结点//   graph->list[i].first = edge;//  //由于是无向的,实现上是双向的,故边数据(Vj,Vi)也要创建//    //j -> i j出度到i// edge = (EdgeNode*)malloc(sizeof(EdgeNode));//  edge->vexIndex = i;//   edge->weight = w;// edge->next = graph->list[j].first;;//    graph->list[j].first = edge;//}//每个单链表的尾指针数组EdgeNode** tailArr = (EdgeNode**)malloc(sizeof(EdgeNode*)*vexNum);//输入顶点值for (int i = 0; i < vexNum; i++){scanf("%c", &graph->list[i].data);graph->list[i].first = (EdgeNode*)malloc(sizeof(EdgeNode));//创建头结点,采用尾插入graph->list[i].first->next = NULL;tailArr[i] = graph->list[i].first;}//输入边表数据for (int k = 0; k < edgeNum; k++){int i, j, w;printf("请输入(Vi,Vj)对应的顶点下标和权值(用逗号隔开):");scanf("%d,%d,%d", &i, &j, &w);//i -> j i出度到jEdgeNode* edge = (EdgeNode*)malloc(sizeof(EdgeNode));//创建边结点edge->vexIndex = j;edge->weight = w;edge->next = NULL;tailArr[i]->next = edge;    //采用尾插法tailArr[i] = edge;edge = (EdgeNode*)malloc(sizeof(EdgeNode));edge->vexIndex = i;edge->weight = w;edge->next = NULL;tailArr[j]->next = edge;tailArr[j] = edge;}//将头结点释放for (int i = 0; i < vexNum; i++){EdgeNode* head = graph->list[i].first;graph->list[i].first = head->next;free(head);}return graph;
}

邻接表的深度优先遍历

void DFTGraphAdjList(GraphAdjList* graph,int vexIndex)
{//访问过不再访问if (g_visited[vexIndex] == TRUE){return;}g_visited[vexIndex] = TRUE;VisitGraphAdjListVertex(graph->list[vexIndex].data);EdgeNode* node = graph->list[vexIndex].first;while (node){DFTGraphAdjList(graph, node->vexIndex);node = node->next;}return;
}//深度优先遍历邻接表
void TraverseGraphAdjList(GraphAdjList* graph)
{if (NULL == graph){return;}for (int i = 0; i < graph->vexNum; i++){g_visited[i] = FALSE;}for (int i = 0; i < graph->vexNum; i++){if (g_visited[i] == FALSE){DFTGraphAdjList(graph, i);}}return;
}

邻接表的广度优先遍历

//广度优先遍历邻接表
void BFTGraphAdjList(GraphAdjList* graph)
{if (NULL == graph){return;}Queue queue;InitQueue(&queue);//初始化顶点都没有访问过for (int i = 0; i < graph->vexNum; i++){g_visited[i] = FALSE;}for (int i = 0; i < graph->vexNum; i++){if (g_visited[i] == FALSE){g_visited[i] = TRUE;VertexType vex = graph->list[i].data;VisitGraphAdjListVertex(vex);//访问顶点数据EnQueue(&queue, i);//将访问过的顶点下标入队//为什么这里要循环出队呢?出队获取已经访问过结点的下标,在内层的for继续访问其相关联结点,将减少外层for循环进入if次数while (!EmptyQueue(&queue)){int vexIndex;DeQueue(&queue, &vexIndex);//将访问过的顶点下标出队EdgeNode* node = graph->list[vexIndex].first;//将该节点的连接的结点且没有被访问过的结点进行访问,然后入队while (node != NULL && g_visited[node->vexIndex] == FALSE){g_visited[node->vexIndex] = TRUE;VertexType vex = graph->list[node->vexIndex].data;VisitGraphAdjListVertex(vex);EnQueue(&queue, node->vexIndex);node = node->next;}}}}return;
}

代码汇总

Queue.h

#pragma once
#ifndef __QUEUE_H__
#define __QUEUE_H__typedef int EleType;//元素类型
typedef enum { ERROR, OK } Status;
typedef enum {FALSE,TRUE} Boolean;//队列结点
typedef struct QueueNode
{EleType data;struct QueueNode* next;
}QueueNode;//队列
typedef struct Queue
{QueueNode* front;QueueNode* tail;
}Queue;//队列初始化
void InitQueue(Queue* queue);//入队
int EnQueue(Queue* queue, EleType data);//出队
int DeQueue(Queue* queue, EleType* data);//队列是否为空
int EmptyQueue(Queue* queue);#endif // !__QUEUE_H__

Queue.c

#include <stdlib.h>
#include "Queue.h"//队列初始化
void InitQueue(Queue* queue)
{queue->front = NULL;queue->tail = NULL;return;
}//入队
int EnQueue(Queue* queue, EleType data)
{if (NULL == queue){return ERROR;}QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));node->data = data;node->next = NULL;if (queue->front == NULL){queue->front = queue->tail = node;}else{queue->tail->next = node;queue->tail = node;}return OK;
}
//出队
int DeQueue(Queue* queue, EleType* data)
{if (NULL == queue){return ERROR;}if (!EmptyQueue(queue)){QueueNode* node = queue->front;*data = node->data;queue->front = queue->front->next;if (NULL != node){free(node);node = NULL;}//队列的最后一个元素出队列后,tail指针也要置为空if (EmptyQueue(queue)){queue->tail = queue->front;}}return OK;
}//队列是否为空
int EmptyQueue(Queue* queue)
{if (NULL == queue){return ERROR;}if (queue->front == NULL){return TRUE;}return FALSE;
}

GraphAdjList.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include "Queue.h"
#define MAX_VERTEX 100
typedef char VertexType;//顶点类型
typedef int EdgeType;//边上权值类型
//边表结点数据结构
typedef struct EdgeNode
{int vexIndex;//顶点下标EdgeType weight;//权值struct EdgeNode* next;  //指向下一个结点
}EdgeNode;
Boolean g_visited[MAX_VERTEX] = { 0 };//全局变量 顶点访问标志位数组
//顶点表数据结构
typedef struct VextexNode
{VertexType data;EdgeNode* first;//边表第一个结点
}VextexNode,AdjList[MAX_VERTEX];typedef struct GraphAdjList
{AdjList list;int vexNum, edgeNun;//顶点,边 的数量
}GraphAdjList;//邻接表创建无向网图
GraphAdjList* CreateGraphAdjList()
{GraphAdjList* graph = (GraphAdjList*)malloc(sizeof(GraphAdjList));int vexNum, edgeNum;printf("请输入顶点和边数(用逗号隔开):");scanf("%d,%d",&vexNum,&edgeNum);graph->edgeNun = edgeNum;graph->vexNum = vexNum;getchar();//消除上面的换行符printf("请输入顶点的值:");输入顶点值//for (int i = 0; i < vexNum; i++)//{//  scanf("%c", &graph->list[i].data);//   graph->list[i].first = NULL;//初始化first指针域//}输入边表数据//for (int k = 0; k < edgeNum; k++)//{//    int i, j, w;//  printf("请输入(Vi,Vj)对应的顶点下标和权值(用逗号隔开):");// scanf("%d,%d,%d", &i, &j, &w);//  //i -> j i出度到j// EdgeNode* edge = (EdgeNode*)malloc(sizeof(EdgeNode));//创建边结点// edge->vexIndex = j;//   edge->weight = w;// edge->next = graph->list[i].first;//头插法 往单链表插入结点//   graph->list[i].first = edge;//  //由于是无向的,实现上是双向的,故边数据(Vj,Vi)也要创建//    //j -> i j出度到i// edge = (EdgeNode*)malloc(sizeof(EdgeNode));//  edge->vexIndex = i;//   edge->weight = w;// edge->next = graph->list[j].first;;//    graph->list[j].first = edge;//}//每个单链表的尾指针数组EdgeNode** tailArr = (EdgeNode**)malloc(sizeof(EdgeNode*)*vexNum);//输入顶点值for (int i = 0; i < vexNum; i++){scanf("%c", &graph->list[i].data);graph->list[i].first = (EdgeNode*)malloc(sizeof(EdgeNode));//创建头结点,采用尾插入graph->list[i].first->next = NULL;tailArr[i] = graph->list[i].first;}//输入边表数据for (int k = 0; k < edgeNum; k++){int i, j, w;printf("请输入(Vi,Vj)对应的顶点下标和权值(用逗号隔开):");scanf("%d,%d,%d", &i, &j, &w);//i -> j i出度到jEdgeNode* edge = (EdgeNode*)malloc(sizeof(EdgeNode));//创建边结点edge->vexIndex = j;edge->weight = w;edge->next = NULL;tailArr[i]->next = edge;    //采用尾插法tailArr[i] = edge;edge = (EdgeNode*)malloc(sizeof(EdgeNode));edge->vexIndex = i;edge->weight = w;edge->next = NULL;tailArr[j]->next = edge;tailArr[j] = edge;}//将头结点释放for (int i = 0; i < vexNum; i++){EdgeNode* head = graph->list[i].first;graph->list[i].first = head->next;free(head);}return graph;
}
//打印 邻接表的无向图
void PrintGraphAdjList(GraphAdjList* graph)
{printf("顶点数据:\n");//顶点数据for (int i = 0; i < graph->vexNum; i++){printf("%c ",graph->list[i].data);}printf("\n边数据:\n");EdgeNode* temp = NULL;//边数据for (int i = 0; i < graph->vexNum; i++){temp = graph->list[i].first;while (temp){printf("%d\t",temp->weight);temp = temp->next;}printf("\n");}return;
}
//访问顶点元素
void VisitGraphAdjListVertex(VertexType data)
{printf("%c ", data);return;
}
void DFTGraphAdjList(GraphAdjList* graph,int vexIndex)
{//访问过不再访问if (g_visited[vexIndex] == TRUE){return;}g_visited[vexIndex] = TRUE;VisitGraphAdjListVertex(graph->list[vexIndex].data);EdgeNode* node = graph->list[vexIndex].first;while (node){DFTGraphAdjList(graph, node->vexIndex);node = node->next;}return;
}//深度优先遍历邻接表
void TraverseGraphAdjList(GraphAdjList* graph)
{if (NULL == graph){return;}for (int i = 0; i < graph->vexNum; i++){g_visited[i] = FALSE;}for (int i = 0; i < graph->vexNum; i++){if (g_visited[i] == FALSE){DFTGraphAdjList(graph, i);}}return;
}
//广度优先遍历邻接表
void BFTGraphAdjList(GraphAdjList* graph)
{if (NULL == graph){return;}Queue queue;InitQueue(&queue);//初始化顶点都没有访问过for (int i = 0; i < graph->vexNum; i++){g_visited[i] = FALSE;}for (int i = 0; i < graph->vexNum; i++){if (g_visited[i] == FALSE){g_visited[i] = TRUE;VertexType vex = graph->list[i].data;VisitGraphAdjListVertex(vex);//访问顶点数据EnQueue(&queue, i);//将访问过的顶点下标入队//为什么这里要循环出队呢?出队获取已经访问过结点的下标,在内层的for继续访问其相关联结点,将减少外层for循环进入if次数while (!EmptyQueue(&queue)){int vexIndex;DeQueue(&queue, &vexIndex);//将访问过的顶点下标出队EdgeNode* node = graph->list[vexIndex].first;//将该节点的连接的结点且没有被访问过的结点进行访问,然后入队while (node != NULL && g_visited[node->vexIndex] == FALSE){g_visited[node->vexIndex] = TRUE;VertexType vex = graph->list[node->vexIndex].data;VisitGraphAdjListVertex(vex);EnQueue(&queue, node->vexIndex);node = node->next;}}}}return;
}int main(int argc, char *argv[])
{GraphAdjList* graph = CreateGraphAdjList();PrintGraphAdjList(graph);printf("深度优先遍历邻接表:\n");TraverseGraphAdjList(graph);printf("\n广度优先遍历邻接表:\n");BFTGraphAdjList(graph);printf("\n");return 0;
}

代码运行测试

我们来创建如下图的一个图,图是教材上的。

代码运行结果:

图:图的邻接表创建、深度优先遍历和广度优先遍历代码实现相关推荐

  1. 图2——利用邻接表创建有向图

    图2--利用邻接表创建有向图 图 假设以邻接表作为图的存储结构,编写算法,创建有向图并输出邻接表. 主要考查对邻接表的理解.图的邻接表分为两个部分:表头结点和边表结点,因此创建有向图也分成两部分:一是 ...

  2. 数据结构之图:邻接矩阵和邻接表、深度优先遍历和广度优先遍历

    简介 线性表是一种线性结构,除了头结点和尾节点,线性表的每个元素都只有一个前取节点和一个后继节点.而树结构则相较于线性表更加复杂,它描述的关系为数据元素之间的父子关系,也是现实世界父子关系的缩影, 一 ...

  3. 根据邻接表求深度优先搜索和广度优先搜索_深度优先搜索/广度优先搜索与java的实现...

    度:某个顶点的度就是依附于该顶点的边的个数 子图:一幅图中所有边(包含依附边的顶点)的子集 路径:是由边顺序连接的一系列定点组成 环:至少含有一条边且终点和起点相同的路径 连通图:如果图中任一个到另一 ...

  4. 【 第11关:基于邻接表的深度优先遍历】【编程题实训-图】【头歌】【bjfu-282】

    任务描述 一个连通图采用邻接表作为存储结构.设计一个算法,实现从顶点v出发的深度优先遍历的非递归过程. 编程要求 输入 多组数据,每组m+2数据行.第一行有两个数字n和m,代表有n个顶点和m条边.顶点 ...

  5. 图:图的邻接矩阵创建、深度优先遍历和广度优先遍历详解

    邻接矩阵介绍 直接说,邻接矩阵是图的一种存储结构.那么图是什么呢?图是一种逻辑结构,和线性结构.树形结构.集合结构一样 是一种逻辑结构用来描述数据对象中的数据元素之间的关系.来看下图的定义:图(Gra ...

  6. 【算法学习】图相关算法编程实现-深度优先遍历和广度优先遍历

    一.图的表示 图G=(V,E).要表示一个图,通常有两种方法:邻接表和邻接矩阵.两种方法都既可以表示有向图,也可以表示无向图. 邻接表表示由一个包含|V|个列表的数组组成,其中每个列表对应V中的一个顶 ...

  7. 大话数据结构 17:图的深度优先遍历和广度优先遍历

    深度优先遍历 主要思路是从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点,再从另一条路开始走到底-,不断递归重复此过程,直到所有的顶点都遍历完成,它的特点 ...

  8. 广度优先搜索生成树怎么画_图的深度优先遍历与广度优先遍历以及最小生成树...

    图的深度优先遍历 题目:写出附从每个顶点出发的一次深度优先搜索遍历序列.在纸上画出遍历过程和序列,提交截图. 错误回答 从A点开始遍历:0124-01324-0134-0324-034 从B点开始遍历 ...

  9. 用邻接表存储图c语言,邻接表、邻接多重表、十字链表及C语言实现

    上一节介绍了如何使用顺序存储结构存储邻接多重表和 邻接的意思是顶点之间有边或者弧存在,通过当前顶点,可以直接找到下一个顶点. 邻接表 使用邻接表存储图时,对于图中的每一个顶点和它相关的邻接点,都存储到 ...

最新文章

  1. UDP,你要耗子喂汁呀!
  2. notepad python配置_Notepad++怎么配置python?
  3. 解决ModuleNotFoundError: No module named ‘numpy.core._multiarray_umath‘ 错误
  4. 网易MCtalk Live:漫谈短视频平台概况,全面解读头部内容
  5. Mysql中的递归层次查询(父子查询)
  6. Unity C# namespace 命名空间的使用
  7. WMRouter:美团外卖Android开源路由框架
  8. java 静态方法 多线程_Java静态方法的线程安全性问题
  9. java程序员编程过程中的基本问题
  10. 无基础学python能干什么-呼市学Python语言能干什么
  11. 网络常用协议 SSH、SSL
  12. [复杂网络博弈] 第二章 演化博弈动力学基础
  13. 计算机tpu定义,tpu材料
  14. 密码忘用计算机解开,电脑密码忘了怎么办,详细教您电脑开机密码忘记了怎么解决...
  15. 都有哪些较好用的项目管理软件?
  16. 300美元课程就能帮你获得93000美元的薪水,高等教育的路在何方?
  17. 占书明:电脑只能上扣扣QQ和微信,无法打开网页,解决办法!
  18. ANSYS apdl软件学习指令(建立三维模型)
  19. 创建型模式——原型模型(Prototype Pattern)
  20. 学习笔记-主成分分析法

热门文章

  1. java immutable系列_Java Immutable类代码示例
  2. arcengine遍历属性表_Redis源码解析四--跳跃表
  3. ThinkPHP5访问去除/public/index.php
  4. php 云端桌面,开源云桌面程序DZZ v1.2新版功能预览
  5. 小爱同学app安卓版_小爱课程表3.0全新升级 课表倒入更简单所有手机能用
  6. MySQL笔记-解决...mysql.sock (13)(两种情况会产生此问题)
  7. C++笔记-利用远程线程注入获取PC版微信个人昵称
  8. RabbitMQ笔记-使用rabbitmq-c让生产者发送数据
  9. WEB安全基础-CSRF漏洞
  10. 7.3图的遍历(深度优先)-理论