数据结构之图(C语言)

这篇具有很好参考价值的文章主要介绍了数据结构之图(C语言)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

图的概念

图是由节点(也称为顶点)和边(也称为边缘)组成的非线性数据结构。节点是图的基本单元,边则用于连接节点之间的关系。图可以用于许多不同的应用程序,包括社交网络、网络拓扑、地图和生物学。

在图中,节点和边可以具有不同的属性。例如,一个社交网络图可能具有节点属性表示用户的姓名和年龄,边属性可能表示用户之间的关系,如朋友或家庭关系。图可以是有向或无向的,也可以是加权或未加权的。有向图具有指向边的方向,而无向图则没有。加权图则对边进行加权,这些权重可以表示距离、成本或其他数据。

图的定义:图G由顶点集V和边集E组成,G = (V,E);

有向图与无向图

如果给图的每条边规定一个方向,那么该图称为有向图。在有向图中,从一个顶点出发的边数称为该点的出度,而指向一个顶点的边数称为该点的入度。相反,边没有方向的图称为无向图。

在有向图中,边指向的结点是唯一的,而无向图的边所连接的结点相互连通;

有权图与无权图

如果图中的边都有各自的权重,则称该图为有权图。反之,如果图中的边无权重(各边的权重相同),则该图为无权图;

图的存储

常用的存储方式有两种:邻接矩阵和邻接表。得分

邻接矩阵:

图的邻接矩阵是一个二维矩阵,矩阵的行和列分别对应图中的顶点。如果两个顶点之间有边,则矩阵中对应的元素为1;否则为0。对于无向图来说,邻接矩阵是对称的。而对于有向图来说,邻接矩阵则不一定对称。因此邻接矩阵的表示相当的直观,而且对于查找某一条边是否存在、权重多少非常快。但其比较浪费空间,对稠密图来说,会比较适合。

代码实现步骤:

1、构建图的基本元素与图的初始化

#define Maxcount 100

struct Node{        //图
	int Nv; //顶点数
	int Ne; //边数
	int data[Maxcount];  //顶点数据
	int wight[Maxcount][Maxcount];  //边的权重
};


struct Nodes //边
{
	int V1, V2; //边所连接的顶点
	int wight;  //边的权重
};


void initialize(struct Node *graph ,int Vexcount)  //初始化图
{
	graph->Nv = Vexcount;   //顶点数
	graph->Ne = 0;         //边数置零
	for (int i = 0; i <= Vexcount; i++) 
	{
		for (int j = 0; j <= Vexcount; j++)
		{
			graph->wight[i][j] = 0;  //初始化每条边的比重
		}
	}
}

2、插入边的权重

void insertedge(struct Node* graph, struct Nodes* edge) //插入边的权重(无向图)
{
	graph->wight[edge->V1][edge->V2] = edge->wight;
	graph->wight[edge->V2][edge->V1] = edge->wight;
}

3、主函数

int main()
{
	printf("邻接矩阵实现图!\n");
	int count;
	printf("图的顶点数:");
	scanf_s("%d/n", &count);
	struct Node* graph = (struct Node*)malloc(sizeof(struct Node)); 
	initialize(graph, count);
	int edgecount;
	printf("图的边数:");
	scanf_s("%d", &edgecount);
	if (edgecount != 0)
	{
		printf("请依次输入顶点与权重:\n");
		for (int i = 0; i < edgecount; i++)
		{
			struct Nodes* edge = (struct Nodes*)malloc(sizeof(struct Nodes));
			scanf_s("%d %d %d", &edge->V1, &edge->V2, &edge->wight);
			insertedge(graph, edge);
		}
		printf("请输入顶点的数据:\n");
		for (int j = 1; j <= count; j++)
			scanf_s("%d", &graph->data[j]);
	}

	printf("打印邻接矩阵图!\n");
	for (int i = 1; i <= count; i++) //从1开始遍历
	{
		for (int j = 1; j <= count; j++)
		{
			printf("%5d ", graph->wight[i][j]);
		}
		printf("\n");
	}
	printf("打印图中各点数据!\n");
	for (int i = 1; i <= count; i++)
		printf("%3d ", graph->data[i]);
	printf("\n");
    return 0;
}

注意:以上代码创建的是无向图;

代码执行后,结果如下图:
c语言图结构,数据结构,c语言

邻接表

图的邻接表是一种表示图的数据结构,它使用一个一维数组来存储顶点,使用链表的形式来存储该顶点与其他顶点的链接,链表内存储着顶点的数组下标,因此,邻接表比较适合稀疏图;

代码实现步骤:
1、基本元素的构建与图的初始化

#define Maxcount 100

struct edgeNode {   //边结点
	int wight;  //权重
	struct edgeNode* next;   //指向下一结点的指针
	int local;   //边结点本身数值
};

struct VNode {   //顶点
	int data;  //顶点数据
	struct edgeNode* firstnext;  //指向边结点的指针
};

struct Adjgarph {  //图
	int Nv;  //顶点个数
	int Ne;  //边数
	struct VNode List[Maxcount];  //顶点数组
	
};

void initialize(struct Adjgarph* G) //图的初始化
{
	printf("请输入顶点数:");
	scanf_s("%d", &G->Nv);
	printf("请输入边数:");
	scanf_s("%d", &G->Ne);
	printf("请依次输入顶点数据:\n");
	for (int i = 1; i <= G->Nv; i++) //写入顶点数据
	{
		scanf_s("%d/n", &G->List[i].data);
		G->List[i].firstnext = NULL;  //指针指向空
	}
}

2、插入边的权重

void creatgraph(struct Adjgarph *G) //权重插入
{
	printf("请输入顶点与权重:\n");
	for (int i = 1; i <= G->Ne; i++) //每次插入边的数据
	{
		struct edgeNode* p = (struct edgeNode*)malloc(sizeof(struct edgeNode)); //边的创建
		int W; //边结点的父节点
		scanf_s("%d %d %d", &W, &p->local, &p->wight);
		p->next = G->List[W].firstnext; //p结点的指针指向顶点的指向
		G->List[W].firstnext = p; //顶点指针指向p
		 //该图为无向图,因此要p指向q,q也指向p,要插入两次;
		struct edgeNode* q = (struct edgeNode*)malloc(sizeof(struct edgeNode));
		q->local = W;
		q->wight = p->wight;
		q->next = G->List[p->local].firstnext;
		G->List[p->local].firstnext = q;
	}
}

3、主函数

int main()
{
	printf("邻接表实现图!\n");
	struct Adjgarph* graph = (struct Adjgraph*)malloc(sizeof( struct Adjgarph)); //给图创建空间
	initialize(graph); //初始化图
	creatgraph(graph); //权重插入
	printf("打印邻接表的示意图!\n");
	for (int i = 1; i <= graph->Nv; i++) 
	{
		printf("%d->", i); //打印顶点数据
		struct VNode* p = graph->List[i].firstnext;  //保存最初结点位置
		while (graph->List[i].firstnext != NULL)
		{
			printf("%d -- wight:%d ->", graph->List[i].firstnext->local, graph->List[i].firstnext->wight); //打印i顶点的边结点与权重
			graph->List[i].firstnext = graph->List[i].firstnext->next; // i顶点指针指向下一个边结点
		}
		if (graph->List[i].firstnext == NULL)
			printf("NULL");
		printf("\n");
		graph->List[i].firstnext = p; //赋回原来位置
	}
  return 0;
}

代码执行后,结果如下图:
c语言图结构,数据结构,c语言

图的遍历 

图的遍历最常用的有两种:深度优先搜索(Depth-first Search, DFS)和广度优先搜索(Breadth-First Search, BFS)。 

深度优先遍历

深度优先遍历(DFS):类似于树的先序遍历,尽可能深的搜索图,从某个顶点出发,沿着一条边走到底,直到不能走为止,然后返回上一层,再走其他的路径,直到遍历完整个图。DFS通常使用递归来实现。

代码实现:

1、邻接矩阵实现的图

//深度遍历(邻接矩阵实现图)
void depthsearch(struct Node* graph, int V, int visit[]) //V为起始点,visit数组为标记数组
{
	int i;
	visit[V] = 1;  //标记被遍历过的元素
	printf("%d ", graph->data[V]); //打印
	for (i = 1; i <= graph->Nv; i++) //遍历V行的每一列
	{
		if (graph->wight[V][i] != 0 && visit[i] != 1) //权重不为0 且 i元素未被遍历过
			depthsearch(graph, i, visit); //将i元素传入并递归
	}
}

主函数变化:

	//深度遍历
	int vin; //初始点
	int *visit = (int*)malloc(sizeof(int) * (graph->Nv)); // 建立标记数组
	printf("请输入深度遍历的初始点:");
	scanf_s("%d/n", &vin);
	depthsearch(graph, vin, visit); //传入数据

2、 邻接表实现的图

//深度遍历(邻接表实现图)
void depthsearch(struct Adjgarph* graph, int V, int* visit)
{
	visit[V] = 1; //标记
	printf("%d ", graph->List[V].data);
	struct edgeNode* p = graph->List[V].firstnext; //代替指针移动
	while (p != NULL) //指针指向不为NULL
	{
		if (visit[p->local] == 0) //未被遍历过
			depthsearch(graph, p->local, visit);
		p = p->next;
	}
}

int locatvex(struct VNode* arr, int vex) //查找顶点在数组中的下标
{
	for (int i = 0; i < Maxcount; i++)
	{
		if (arr[i].data == vex)
			return i;
	}
	return -1;
}

主函数变化:

//深度遍历
	int vin;
	int* visit = (int*)malloc(sizeof(int) * ((graph->Nv)+1)); //创建下标数组
	for (int t = 0; t <= graph->Nv; t++) //置零
		visit[t] = 0;
	printf("请输入初始点:");
	scanf_s("%d/n", &vin);
	depthsearch(graph, locatvex(graph->List,vin), visit);

广度优先遍历

广度优先遍历(BFS):类似于树的层序遍历,一层一层的遍历,依次访问它的所有邻接点,再依次访问邻接点的所有未访问过的邻接点,直到遍历完整个图。为实现逐层遍历,BFS通常借用队列来实现。

代码实现:

1、邻接矩阵实现图:

//广度遍历
void breadsearch(struct Node* graph, int V, int visit[])
{
	int que[Maxcount]; //数组实现队
	int front = 1;
	int rear = 1;
	que[rear++] = V;
	while (front < rear)  //终止条件
	{
		printf("%d ", que[front++]);
		visit[V] = 1;
		for (int j = 1; j <= graph->Nv; j++) 
		{
			if (visit[j] == 0 && graph->wight[front][j] != 0) //结点未被遍历 且 权重不为零
			{
				printf("%d ", graph->data[j]);
				visit[j] = 1;
				que[rear++] = j; //rear向后移
			}
		}
	}
}

主函数变化:

//广度遍历
	int vin;
	int* visit = (int*)malloc(sizeof(int) * (graph->Nv));
	for (int i = 0; i <= graph->Nv; i++)
		visit[i] = 0;
	printf("请输入广度遍历的初始点:");
	scanf_s("%d/n", &vin);
	breadsearch(graph, vin, visit);

2、邻接表实现图:

//链表建立队列
struct Node {
	int data;
	struct Node* next;
};

struct Node* front = NULL;
struct Node* rear = NULL;
int target = 0;

void push(int vin) //入队
{
	struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
	temp->data = vin;
	temp->next = NULL;
	if (front == NULL && rear == NULL)
	{
		front = temp;
		rear = temp;
		target = 1;  //用来避免指针同指首元素

	}
	else
	{
		rear->next = temp;
		rear = temp;
	}
}

void pop() //出队
{
	if (front == NULL && rear == NULL)
	{
		printf("error!");
		return;
	}
	struct Node* temp = front;
	front = front->next;
	free(temp);
}

//广度遍历(邻接表实现图)
void breadthsearch(struct Adjgarph* graph, int V, int* visit)
{
	visit[V] = 1;
	push(V); //将顶点数据传入队
	while (front !=	NULL || target == 1) //队列不为空 (front和rear都指向首元素)
	{
		printf("%d ", graph->List[front->data].data); //打印顶点数据
		target = 0; //赋零
		struct edgeNode* p = graph->List[front->data].firstnext; 
		while (p) //边结点不为空
		{
			if (!visit[p->local]) //该结点未被遍历过
				push(p->local); //结点入队
			visit[p->local] = 1; 
			p = p->next; //向下移动
		}
		pop(); //首元素出队
	}
}

主函数变化:

	//广度遍历
	int vin;
	int* visit = (int*)malloc(sizeof(int) * ((graph->Nv) + 1));
	for (int t = 0; t <= graph->Nv; t++)
		visit[t] = 0;
	printf("请输入初始点:");
	scanf_s("%d/n", &vin);
	breadthsearch(graph, vin, visit);


 文章来源地址https://www.toymoban.com/news/detail-769356.html

到了这里,关于数据结构之图(C语言)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++数据结构之图的遍历——DFS和BFS(带有gif演示)

    图的遍历 指的是从某一个顶点开始,访问图中的其余顶点,使得每个顶点被且仅被访问一次。 本文着重介绍DFS和BFS的区别和过程,因此采用的是最简单的邻接矩阵来储存无向图并实现两种算法。 下面是一个我在b站看到的一个较浅显易懂的图遍历视频,大家可以用作参考:

    2024年02月07日
    浏览(41)
  • 【数据结构】C语言结构体详解

    目录 前言 一、结构体的定义 二、定义结构体变量 三、结构体变量的初始化 四、使用typedef声明新数据类型名 五、指向结构体变量的指针 总结 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CSDN📚。 📣如需转

    2024年02月04日
    浏览(44)
  • 『初阶数据结构 • C语言』① - 数据结构为何重要

    本文内容借鉴一本我非常喜欢的书——《数据结构与算法图解》。学习之余,我决定把这本书精彩的部分摘录出来与大家分享。 数组是计算机科学中最基本的数据结构之一。如果你用过数组,那么应该知道它就是一个含有 数据的列表。它有多种用途,适用于各种场景,下面

    2024年02月16日
    浏览(42)
  • R语言数据结构(三)数据框

    数据结构是指在计算机中存储和组织数据的方式,不同的数据结构有不同的特点和适用场景。R语言中的常用数据结构,包括向量、矩阵、数组、列表和数据框。关于数据结构的使用,我们将分四篇文章分别介绍每种数据结构的操作方法和代码示例。 为方便大家理解记忆,对

    2024年01月15日
    浏览(32)
  • 【C语言】【数据结构】自定义类型:结构体

    这是一篇对结构体的详细介绍,这篇文章对结构体声明、结构体的自引用、结构体的初始化、结构体的内存分布和对齐规则、库函数offsetof、以及进行内存对齐的原因、如何修改默认对齐数、结构体传参进行介绍和说明。                  ✨  猪巴戒 :个人主页✨      

    2024年02月05日
    浏览(29)
  • 数据结构初阶(用C语言实现简单数据结构)--栈和队列

    ✨✨欢迎来到T_X_Parallel的博客!!       🛰️博客主页:T_X_Parallel       🛰️专栏 : 数据结构初阶       🛰️欢迎关注:👍点赞🙌收藏✍️留言 这小猫真好看 言归正传,通过上篇有关顺序表和链表的博客,可以了解到线性表的一些大致特征,这篇博

    2024年02月08日
    浏览(37)
  • 数据结构——二叉树基础结构篇(C语言)

    现在是北京时间2023年6月13日9点11分。从决定要开始减脂之后,饥饿总是伴随着我。一觉起来肚子咕咕叫,我还是想先把文章发了再吃第一餐。燕麦加蛋白粉几乎伴随了我大学的第一年早饭。昨天练了一个小时背,练背后还做了45分钟有氧。空腹训练没有影响我的训练状态。这

    2024年02月08日
    浏览(35)
  • C语言数据结构--链表

    顺序表的问题及思考 问题: 1. 中间/头部的插入删除,时间复杂度为O(N) 2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插入了5个数据,后面没有

    2024年02月05日
    浏览(39)
  • C语言数据结构——链表

    目录 前言 一、什么是链表 1.1链表的结构和概念 1.2 链表的分类 二、无头单向非循环链表 2.1 创建结构体 2.2 动态申请一个节点 2.3 单链表打印 2.4 单链表尾插/尾删 2.4.1 单链表尾插  2.4.2 单链表尾删 2.5 单链表头插/头删 2.5.1 头插 2.5.2 头删 2.6 单链表查找 2.7 单链表中间插入/中

    2024年02月16日
    浏览(49)
  • Python语言的数据结构

        🎂在我们的Python语言当中所谓的数据结构其实就是一些存储数据的类型。就比如说我们C语言当中的数组和结构体一样。我们Python语言当中的数据类型包括列表,元组,字典,集合,字符串五种形式。我们使用这四种不同的数据类型可以对数据进行不同的处理操作。接下

    2024年02月06日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包