数据结构之邻接表及广度优先遍历

2023-10-27

一、邻接表的概念

邻接表是图的一种最主要存储结构(相当于图的压缩存储),用来描述图上的每一个点。图的邻接表存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。表结点存放的是邻接顶点在数组中的索引。对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。

广度优先搜索遍历类似于树的按层次遍历,对于用邻接表做存储结构的图,从某个给定 顶点出发的图的遍历得到的访问结点顶点次序,随建立的邻接表的不同而可能不同

二、代码步骤

1、创建图

2、图的初始化

3、创建链队列

4、队列的初始化

5、判断队列是否为空

6、入队

7、出队

8、邻接表的结构体定义

9、定义遍历数组

10、将图转为邻接表

11、打印邻接表

12、广度遍历

13、测试代码

14、程序入口

15、运行结果


三、代码功能

1、创建图

typedef struct Graph
{
	int** connections;
	int numNodes;
} *GraphPtr;

2、图的初始化

GraphPtr initGraph(int paraSize, int** paraData) 
{
	int i, j;
	GraphPtr resultPtr = (GraphPtr)malloc(sizeof(struct Graph));
	resultPtr -> numNodes = paraSize;
	resultPtr -> connections = (int**)malloc(paraSize * sizeof(int*));
	for (i = 0; i < paraSize; i ++)
	{
		resultPtr -> connections[i] = (int*)malloc(paraSize * sizeof(int));
		for (j = 0; j < paraSize; j ++) 
		{
			resultPtr -> connections[i][j] = paraData[i][j];
		}
	}
	
	return resultPtr;
}

3、创建链队列

typedef struct GraphNodeQueue
{
	int* nodes;
	int front;
	int rear;
}GraphNodeQueue, *QueuePtr;

4、队列的初始化

QueuePtr initQueue()
{
	QueuePtr resultQueuePtr = (QueuePtr)malloc(sizeof(struct GraphNodeQueue));
	resultQueuePtr->nodes = (int*)malloc(QUEUE_SIZE * sizeof(int));
	resultQueuePtr->front = 0;
	resultQueuePtr->rear = 1;
	return resultQueuePtr;
}

5、判断队列是否为空

bool isQueueEmpty(QueuePtr paraQueuePtr)
{
	if ((paraQueuePtr->front + 1) % QUEUE_SIZE == paraQueuePtr->rear) 
	{
		return true;
	}

	return false;
}

6、入队

void enqueue(QueuePtr paraQueuePtr, int paraNode)
{
	if ((paraQueuePtr->rear + 1) % QUEUE_SIZE == paraQueuePtr->front % QUEUE_SIZE) 
	{
		printf("Error, trying to enqueue %d. queue full.\r\n", paraNode);
		return;
	}
	paraQueuePtr->nodes[paraQueuePtr->rear] = paraNode;
	paraQueuePtr->rear = (paraQueuePtr->rear + 1) % QUEUE_SIZE;
}

7、出队

int dequeue(QueuePtr paraQueuePtr)
{
	if (isQueueEmpty(paraQueuePtr)) 
	{
		printf("Error, empty queue\r\n");
		return NULL;
	}
	paraQueuePtr->front = (paraQueuePtr->front + 1) % QUEUE_SIZE;

	return paraQueuePtr->nodes[paraQueuePtr->front];
}

8、邻接表的结构体定义

typedef struct AdjacencyNode 
{
	int column;
	AdjacencyNode* next;
}AdjacencyNode, *AdjacentNodePtr;

/**
 * Aajacent list.
 */
typedef struct AdjacencyList 
{
	int numNodes;
	AdjacencyNode* headers;
}AdjacencyList, *AdjacencyListPtr;

9、定义遍历数组

int* visitedPtr;

10、将图转为邻接表

AdjacencyListPtr graphToAdjacentList(GraphPtr paraPtr) 
{
	//Allocate space.
	int i, j, tempNum;
	AdjacentNodePtr p, q;
	tempNum = paraPtr->numNodes;
	AdjacencyListPtr resultPtr = (AdjacencyListPtr)malloc(sizeof(struct AdjacencyList));
	resultPtr->numNodes = tempNum;
	resultPtr->headers = (AdjacencyNode*)malloc(tempNum * sizeof(struct AdjacencyNode));
	
	//Fill the data.
	for (i = 0; i < tempNum; i ++)
	{
		//Initialize headers.
		p = &(resultPtr->headers[i]);
		p->column = -1;
		p->next = NULL;

		for (j = 0; j < tempNum; j ++) 
		{
			if (paraPtr->connections[i][j] > 0) 
			{
				//Create a new node.
				q = (AdjacentNodePtr)malloc(sizeof(struct AdjacencyNode));
				q->column = j;
				q->next = NULL;
				p->next = q;
				p = q;
			}
		}
	}
	return resultPtr;
}

11、打印邻接表

void printAdjacentList(AdjacencyListPtr paraPtr) 
{
	int i;
	AdjacentNodePtr p;
	int tempNum = paraPtr->numNodes;

	printf("This is the graph:\r\n");
	for (i = 0; i < tempNum; i ++) 
	{
		p = paraPtr->headers[i].next;
		while (p != NULL) 
		{
			printf("%d, ", p->column);
			p = p->next;
		}
		printf("\r\n");
	}
}

12、广度遍历

void widthFirstTranverse(AdjacencyListPtr paraListPtr, int paraStart)
{
	printf("width first \r\n");
	//Use a queue to manage the pointers
	int i, j, tempNode;
	AdjacentNodePtr p;
	i = 0;

	//Initialize data
	visitedPtr = (int*) malloc(paraListPtr->numNodes * sizeof(int));
	
	for (i = 0; i < paraListPtr->numNodes; i ++)
	{
		visitedPtr[i] = 0;
	}//Of for i

	QueuePtr tempQueuePtr = initQueue();
	printf("%d\t", paraStart);
	visitedPtr[paraStart] = 1;
	enqueue(tempQueuePtr, paraStart);
	while (!isQueueEmpty(tempQueuePtr)) 
	{
		tempNode = dequeue(tempQueuePtr);

		for (p = &(paraListPtr->headers[tempNode]); p != NULL; p = p->next) 
		{
			j = p->column;
			if (visitedPtr[j]) 
				continue;

			printf("%d\t", j);
			visitedPtr[j] = 1;
			enqueue(tempQueuePtr, j);
		}//Of for
	}//Of while
	printf("\r\n");
}

13、测试代码

void testGraphTranverse()
{
	int i, j;
	int myGraph[5][5] = { 
		{0, 1, 0, 1, 0},
		{1, 0, 1, 0, 1}, 
		{0, 1, 0, 1, 1}, 
		{1, 0, 1, 0, 0}, 
		{0, 1, 1, 0, 0}};
	int** tempPtr;
	printf("Preparing data\r\n");
		
	tempPtr = (int**)malloc(5 * sizeof(int*));
	for (i = 0; i < 5; i ++)
	{
		tempPtr[i] = (int*)malloc(5 * sizeof(int));
	}
	 
	for (i = 0; i < 5; i ++)
	{
		for (j = 0; j < 5; j ++)
		{

			tempPtr[i][j] = myGraph[i][j];

		}
	}
 
	printf("Data ready\r\n");
	
	GraphPtr tempGraphPtr = initGraph(5, tempPtr);
	AdjacencyListPtr tempListPtr = graphToAdjacentList(tempGraphPtr);

	printAdjacentList(tempListPtr);

	widthFirstTranverse(tempListPtr, 4);
}

14、程序入口

int main()
{
	testGraphTranverse();
	return 1;
}

15、运行结果

Preparing data
Data ready
This is the graph:
1, 3,
0, 2, 4,
1, 3, 4,
0, 2,
1, 2,
width first
4       1       2       0       3

四、整体代码


#include <stdio.h>
#include <malloc.h>
#define QUEUE_SIZE 10


int* visitedPtr;

/**
 * The structure of a graph.
 */
typedef struct Graph
{
	int** connections;
	int numNodes;
} *GraphPtr;

/**
 * Initialize a graph.
 */
GraphPtr initGraph(int paraSize, int** paraData) 
{
	int i, j;
	GraphPtr resultPtr = (GraphPtr)malloc(sizeof(struct Graph));
	resultPtr -> numNodes = paraSize;
	resultPtr -> connections = (int**)malloc(paraSize * sizeof(int*));
	for (i = 0; i < paraSize; i ++)
	{
		resultPtr -> connections[i] = (int*)malloc(paraSize * sizeof(int));
		for (j = 0; j < paraSize; j ++) 
		{
			resultPtr -> connections[i][j] = paraData[i][j];
		}
	}
	
	return resultPtr;
}//Of initGraph

/**
 * A queue with a number of indices.
 */
typedef struct GraphNodeQueue
{
	int* nodes;
	int front;
	int rear;
}GraphNodeQueue, *QueuePtr;

/**
 * Initialize the queue.
 */
QueuePtr initQueue()
{
	QueuePtr resultQueuePtr = (QueuePtr)malloc(sizeof(struct GraphNodeQueue));
	resultQueuePtr->nodes = (int*)malloc(QUEUE_SIZE * sizeof(int));
	resultQueuePtr->front = 0;
	resultQueuePtr->rear = 1;
	return resultQueuePtr;
}

/**
 * Is the queue empty?
 */
bool isQueueEmpty(QueuePtr paraQueuePtr)
{
	if ((paraQueuePtr->front + 1) % QUEUE_SIZE == paraQueuePtr->rear) 
	{
		return true;
	}

	return false;
}

/**
 * Add a node to the queue.
 */
void enqueue(QueuePtr paraQueuePtr, int paraNode)
{
	if ((paraQueuePtr->rear + 1) % QUEUE_SIZE == paraQueuePtr->front % QUEUE_SIZE) 
	{
		printf("Error, trying to enqueue %d. queue full.\r\n", paraNode);
		return;
	}
	paraQueuePtr->nodes[paraQueuePtr->rear] = paraNode;
	paraQueuePtr->rear = (paraQueuePtr->rear + 1) % QUEUE_SIZE;
}

/**
 * Remove an element from the queue and return.
 */
int dequeue(QueuePtr paraQueuePtr)
{
	if (isQueueEmpty(paraQueuePtr)) 
	{
		printf("Error, empty queue\r\n");
		return NULL;
	}
	paraQueuePtr->front = (paraQueuePtr->front + 1) % QUEUE_SIZE;

	return paraQueuePtr->nodes[paraQueuePtr->front];
}

typedef struct AdjacencyNode 
{
	int column;
	AdjacencyNode* next;
}AdjacencyNode, *AdjacentNodePtr;

/**
 * Aajacent list.
 */
typedef struct AdjacencyList 
{
	int numNodes;
	AdjacencyNode* headers;
}AdjacencyList, *AdjacencyListPtr;

/**
 * Construct an adjacent list.
 */
AdjacencyListPtr graphToAdjacentList(GraphPtr paraPtr) 
{
	//Allocate space.
	int i, j, tempNum;
	AdjacentNodePtr p, q;
	tempNum = paraPtr->numNodes;
	AdjacencyListPtr resultPtr = (AdjacencyListPtr)malloc(sizeof(struct AdjacencyList));
	resultPtr->numNodes = tempNum;
	resultPtr->headers = (AdjacencyNode*)malloc(tempNum * sizeof(struct AdjacencyNode));
	
	//Fill the data.
	for (i = 0; i < tempNum; i ++)
	{
		//Initialize headers.
		p = &(resultPtr->headers[i]);
		p->column = -1;
		p->next = NULL;

		for (j = 0; j < tempNum; j ++) 
		{
			if (paraPtr->connections[i][j] > 0) 
			{
				//Create a new node.
				q = (AdjacentNodePtr)malloc(sizeof(struct AdjacencyNode));
				q->column = j;
				q->next = NULL;
				p->next = q;
				p = q;
			}
		}
	}
	return resultPtr;
}

/**
 * Print an adjacent list.
 */
void printAdjacentList(AdjacencyListPtr paraPtr) 
{
	int i;
	AdjacentNodePtr p;
	int tempNum = paraPtr->numNodes;

	printf("This is the graph:\r\n");
	for (i = 0; i < tempNum; i ++) 
	{
		p = paraPtr->headers[i].next;
		while (p != NULL) 
		{
			printf("%d, ", p->column);
			p = p->next;
		}
		printf("\r\n");
	}
}

/**
 * Width first tranverse.
 */
void widthFirstTranverse(AdjacencyListPtr paraListPtr, int paraStart)
{
	printf("width first \r\n");
	//Use a queue to manage the pointers
	int i, j, tempNode;
	AdjacentNodePtr p;
	i = 0;

	//Initialize data
	visitedPtr = (int*) malloc(paraListPtr->numNodes * sizeof(int));
	
	for (i = 0; i < paraListPtr->numNodes; i ++)
	{
		visitedPtr[i] = 0;
	}//Of for i

	QueuePtr tempQueuePtr = initQueue();
	printf("%d\t", paraStart);
	visitedPtr[paraStart] = 1;
	enqueue(tempQueuePtr, paraStart);
	while (!isQueueEmpty(tempQueuePtr)) 
	{
		tempNode = dequeue(tempQueuePtr);

		for (p = &(paraListPtr->headers[tempNode]); p != NULL; p = p->next) 
		{
			j = p->column;
			if (visitedPtr[j]) 
				continue;

			printf("%d\t", j);
			visitedPtr[j] = 1;
			enqueue(tempQueuePtr, j);
		}//Of for
	}//Of while
	printf("\r\n");
}//Of widthFirstTranverse

/**
 * Test graph tranverse.
 */
void testGraphTranverse()
{
	int i, j;
	int myGraph[5][5] = { 
		{0, 1, 0, 1, 0},
		{1, 0, 1, 0, 1}, 
		{0, 1, 0, 1, 1}, 
		{1, 0, 1, 0, 0}, 
		{0, 1, 1, 0, 0}};
	int** tempPtr;
	printf("Preparing data\r\n");
		
	tempPtr = (int**)malloc(5 * sizeof(int*));
	for (i = 0; i < 5; i ++)
	{
		tempPtr[i] = (int*)malloc(5 * sizeof(int));
	}
	 
	for (i = 0; i < 5; i ++)
	{
		for (j = 0; j < 5; j ++)
		{

			tempPtr[i][j] = myGraph[i][j];

		}
	}
 
	printf("Data ready\r\n");
	
	GraphPtr tempGraphPtr = initGraph(5, tempPtr);
	AdjacencyListPtr tempListPtr = graphToAdjacentList(tempGraphPtr);

	printAdjacentList(tempListPtr);

	widthFirstTranverse(tempListPtr, 4);
}

int main()
{
	testGraphTranverse();
	return 1;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构之邻接表及广度优先遍历 的相关文章

  • std::shared_ptr 详解

    一 介绍 shared ptr是一种智能指针 smart pointer 作用有如同指针 但会记录有多少个shared ptrs共同指向一个对象 这便是所谓的引用计数 reference counting 一旦最后一个这样的指针被销毁 也就
  • 解决win10桌面文件刷新后示问题

    网上很多解决方案是在注册表编辑器中新建项 尝试发现没有效果 多次搜索后找到如下解决方案 1 在桌面上新建txt文件 然后将下面的代码粘贴进去 重命名为UpdateMode reg 然后双击执行导入注册表即可 Windows Registry
  • C开发工程师

    岗位职责 1 完成软件系统代码的实现 编写代码注释和开发文档 2 辅助进行系统的功能定义 程序设计 3 根据设计文档或需求说明完成代码编写 调试 测试和维护 4 分析并解决软件开发过程中的问题 5 协助测试工程师制定测试计划 定位发现的问题

随机推荐

  • 51入门详解教程系列之IO口的输入和输出

    一 IO口的输入 1 分类 1 基本输入IO电路 2 施密特触发输入电路 3 弱上拉输入电路 2 各种的优缺点 1 基本输入IO电路 1 gt 优点 不接VCC GND 在低功耗模式下 不费电 2 gt 缺点 输入不稳定 发生抖动 所以一般
  • Scanner中next与nextLine区别

    区别一 String st1 scanner nextLine String st2 scanner next System out println nextLine方式输入 st1 System out println next方式输入
  • unity中暂停游戏

    做做笔记 本方法出自unity官方案例精讲中的第十二章 private bool paused false void Update if Input GetKeyUp KeyCode P paused paused if paused Ti
  • Flink CDC 基于mysql binlog 实时同步mysql表(无主键)

    环境说明 flink 1 15 2 mysql 版本5 7 注意 需要开启binlog 因为增量同步是基于binlog捕获数据 windows11 IDEA 本地运行 具体前提设置 请看这篇 包含 binlog 设置 Maven Flink
  • server2008 服务器文件共享加密,使用secWall端口加密控制Server2016的文件共享服务...

    使用secWall端口加密控制Server2016的文件共享服务 万华数据 环境 文件服务器 Windows Server 2016 2012 2008 客户端 所有支持的系统 目标 通过在服务器端设置secWall端口加密 让没有正常登录
  • http协议与Apache

    1 http协议 0 监听 扩展 yum install nc 双端安装 nc l 80 服务器监听80端口 nc 192 168 64 100 80 客户端访问80端口 1 http概念 互联网 是网络的网络 是所有类型网络的母集 因特网
  • 喜讯!AntDB数据库入围上海信创公共服务平台产品目录

    近日 AntDB数据库完成信息技术应用创新产品适配 成功入围上海信创公共服务平台产品目录 此次产品目录的入围 再次验证了AntDB数据库符合信息技术应用创新产品可控性要求 也肯定了AntDB数据库团队近20年的数据库研发 服务能力 图1 A
  • GT20L16S1Y字库IC驱动

    GT20L16S1Y字库IC驱动 file GT20L16S1Y c date 2020 7 7 author aron566 copyright None brief GD20L16S1Y字库驱动 details version V1 0
  • ctfshow--网络迷踪

    前言 记录一下做题过程 如有不当之处 还望指正 如有疑问 欢迎留言 目录 前言 1 新手上路 2 初学乍练 3 初学又练 4 初学再练 5 现拉现吃 6 初窥门径 7 狗哥去哪 8 国足加油 9 致我超吧 10 山外有山 11 密集恐惧 1
  • C++Primer第五版课后习题答案目录

    本帖用来记录我在看C Primer第五版时课后习题的代码以及书中一些问题的思考 仅供参考 水平有限 如有错误之处 请大家不吝指教 谢谢 目录 第一章 开始 第二章 变量和基本类型 第三章 字符串 向量和数组 第四章 表达式 第五章 语句
  • Linux 命令之 - scp(从远端机器拉取数据)

    scp是secure copy的简写 用于在Linux下进行远程拷贝文件的命令 和它类似的命令有cp 不过cp只是在本机进行拷贝不能跨服务器 而且scp传输是加密的 命令格式 scp 参数 原路径 目标路径 从本地服务器复制到远程服务器 需
  • 网易滑块验证

    之前在写瑞数专题一时就想发一篇关于网易滑块验证的案例 奈何现在的大佬好像比较喜欢瑞数 不管咋样 还是来水一篇网易滑块验证相关的文章 首先是获取图片的部分参数 fp cb callback这三个都是加密而来 图片验证这里的acToken可以不
  • 聊聊分布式任务调度系统

    我看过那么多所谓的教程 大部分都是教 如何使用工具 的 没有多少是教 如何制作工具 的 能教 如何仿制工具 的都已经是凤毛麟角 中国 软件行业 缺的是真正可以 制作工具 的程序员 而绝对不缺那些 使用工具 的程序员 这个业界最不需要的就是
  • 二、三层转发原理(多例详解,图文相结合说明ping过程)

    首先要了解 源主机在发起通信之前 会将自己的IP与目的主机的IP进行比较 如果两者位于同一网段 用网络掩码计算后具有相同的网络号 那么源主机发送arp请求广播报 请求目的主机的mac地址 在收到目的主机的ARP应答后获得对方的物理层 MAC
  • mysql 错误代码1171

    在创建主键id的时候没有取消上图的允许空值 导致报错1171 Error All part of primary key must be not null when installing flag module 转载于 https www
  • 一位股市天才的肺腑独白:一直只用MACD指标来炒股

    在股市投资中 MACD指标作为一种技术分析的手段 得到了投资者的认知 但如何使用MACD指标 才能使投资收益达到最佳境界 却是知者甚微 在股市操作中 MACD指标在保护投资者利益方面 远超过它发现投资机会的功效 如何巧用MACD指标 在股海
  • linux 重启服务器命令

    Linux有如下的关机和重启命令 shutdown reboot halt poweroff 那么它们有什么区别呢 shutdown 建议使用的命令 shutdown是最常用也是最安全的关机和重启命令 它会在关机之前调用fsck检查磁盘 其
  • 计算机系统基础摘记——程序的链接

    目录 1 初探链接 1 1 可执行文件的生成过程 1 2 链接器的由来 1 3 概述链接器的关键作用 1 4 链接带来的好处 2 目标文件 2 1 一些基本概念 2 2 可重定位文件 2 2 1 可重定位文件的格式 2 2 2 ELF头的格
  • 基础算法题——Harder Gcd Problem(数论、思维)

    题目 题目链接 给定一个 n 将 2 n 内的数进行一对一匹配 每个数仅能利用一次 假设 a 与 b 匹配 则 gcd a b 1 现求 2 n 内最大匹配数量 并输出匹配数对 输入 T代表输入组数 下面T行 每一行一个数字n 输出 输出最
  • 数据结构之邻接表及广度优先遍历

    一 邻接表的概念 邻接表是图的一种最主要存储结构 相当于图的压缩存储 用来描述图上的每一个点 图的邻接表存储方法跟树的孩子链表示法相类似 是一种顺序分配和链式分配相结合的存储结构 如这个表头结点所对应的顶点存在相邻顶点 则把相邻顶点依次存放