数据结构(c语言实现)——图(邻接矩阵存储)及图的遍历

概念

  1. 图:由点以及点之间的连接关系所描述的对象
    图是由点集和边集组成
    图分为有向图和无向图
    在这里插入图片描述
    图(a)就是有向图,图(b)就是无向图

完全图:从图的任意一个顶点可以直接到达其余各个顶点的图
连通图:从图中任意一个顶点可以到达其余各个顶点

图的存储

  1. 邻接矩阵存储
    在这里插入图片描述
    邻接矩阵是用一个一维数组存储点信息,用二维数组储存连接关系
#define MaxVertaxNum 50 //顶点最大数目
typedef char VertaxType;//顶点信息类型,用字符来表示顶点
typedef bool EdgeType;//不带权值的图,只存储点的连接信息
typedef struct Mgrap1 {
	VertaxType Vertax[MaxVertaxNum];
	EdgeType Edge[MaxVertaxNum][MaxVertaxNum];
	int vexNum, edgeNum;//点的数量、边的数量
}Mgrap1;
  1. 邻接链表存储
    在这里插入图片描述
    邻接链表是用线性表存储点信息,线性表中每个点信息节点尾头节点用链式存储方式存储与其有连接关系的节点
#define MaxVertaxNum 50 //顶点最大数目
//与顶点连接的信息
typedef struct ArcNode {
	int adjVex;
	struct ArcNode* next;//指向下一个依附顶点的指针(与表头存储结点相连接的节点)
}ArcNode;
//顶点表节点
typedef struct VNode {
	VertaxType vertax;//顶点信息
	ArcNode* first;//指向第一个依附的顶点(与vextax相连接的第一个节点)
}VNode , adjList[MaxVertaxNum];
//
typedef struct ALGraph {
	adjList vertices;//邻接表
	int vex, arc;//定点数、路径数
}ALGraph;

邻接链表存储图的遍历

  1. 深度优先遍历
    在这里插入图片描述
    在这里插入图片描述

void DFirst(Mgraph mg, VertaxType ver, visited& visited, Stack& stack)//深度优先遍历
{

	visit(ver);
	if (!visited.visited[ver])
	{
		visited.visited[ver] = true;//顶点访问,修改标记
		printf("%d", mg.Vertax[ver]);
	}

	push(stack, ver);//定点入栈

	VertaxType temVer = ver;
	while (!stackEmpty(stack))
	{
		if (vexIsTrue(mg, temVer, visited))//与该顶点相连接的所有顶点都已经访问,顶点出栈
		{
			pop(stack);
			if (!stackEmpty(stack))
			{
				temVer = getPop(stack);//读取顶点出栈后栈顶元素
			}
			else temVer = -1;

		}//if
		else
			break;
	}
	if (temVer != -1)
	{
		for (int j = VertaxNum - 1; j >= 0; j--)
		{
			if (mg.Edge[ver][j] != 0 && visited.visited[j] != true)
			{
				push(stack, mg.Vertax[j]);//将与顶点相连且未被访问的顶点入栈
			}

		}//for
		if (!stackEmpty(stack))
		{
			temVer = getPop(stack);
			DFirst(mg, temVer, visited, stack);
		}//if
	}//if	

}//DFirst

  1. 广度优先遍历
void WFirst(Mgraph mg, VertaxType ver, visited& visited, Queue& queue)//广度优先遍历
{
	visit(ver);
	
	visited.visited[ver] = true;
	printf("%d\n", ver);
	VertaxType temVer = ver;
	if (!vexIsTrue(mg, temVer, visited))//存在与节点连接但未被访问的节点
	{
		for (int j = VertaxNum - 1; j >= 0; j--)
		{
			if (mg.Edge[ver][j] != 0 && visited.visited[j] != true)
			{
				enQueue(queue, mg.Vertax[j]);//将与顶点相连且未被访问的顶点入栈
			}

		}//for
	}
	if (!QueueEmpty(queue))
	{
		deQueue(queue , temVer);
		WFirst(mg, temVer, visited, queue);
	}

}
  1. 完整程序
//测试程序
#include <stdio.h>
#include "MGraph.h"



int main()
{
	Mgraph mg;
	VertaxType ver[4] = { 0 , 1 , 2 , 3};
	EdgeType edge[4][4] = { {0 , 1 , 1 , 0} , {1 , 0 , 0 , 1} , {1 , 0 , 0 , 0} , {0 , 1 , 0 , 0} };
	initMgraph(mg, ver, edge);

	visited visited1;
	initVisited(visited1);
	Stack stack;
	initStack(stack);
	DFirst(mg, 0, visited1, stack);
	printf("*******************\n");
	visited visitedd;
	initVisited(visitedd);
	Queue queue; 
	initQueue(queue);
	WFirst(mg, 0, visitedd, queue);
	return 0;
}
#pragma once
//intQueue.h
//队列
#define max 51
//队列表最大
#define initSize 100 
typedef int Elem;
typedef struct 
{
	Elem elem[initSize];
	unsigned int front , rear;	//队头、队尾
	int flag;//空队列标记 flag = 0为空,flag = 1为满
}Queue;

void initQueue(Queue& que);//队列初始化
bool QueueEmpty(Queue que);//判断是否为空队列
bool QueueFull(Queue que);//判断队列是否已满
bool enQueue(Queue& que, Elem elem);//入队
bool deQueue(Queue& que ,Elem & elem);//出队
Elem getHead(Queue que);//读取对头元素


void initQueue(Queue& que)//队列初始化
{
	que.flag = 0;//标记为未满
	que.front = que.rear = 0;//对头队尾都指向对头,队列为空
}
bool QueueEmpty(Queue que)//判断是否为空队列
{
	if ( que.front == que.rear && que.flag == 0)
		return true;
	return false;
}
bool QueueFull(Queue que)//判断队列是否已满
{
	if (que.flag == 1)
		return true;
	return false;
}
bool enQueue(Queue& que, Elem elem)//入队
{
	if (QueueFull(que))//队列已满
		return false;
	que.elem[que.front % initSize] = elem;
	++que.front;
	if (que.front - que.rear == initSize - 1)
		que.flag = 1;
	else
		que.flag = 0;
	return true;
}
bool deQueue(Queue& que, Elem& elem)//出队
{
	elem = que.elem[que.rear % initSize];
	++que.rear;
	if (que.front - que.rear == initSize - 1)
		que.flag = 1;
	else
		que.flag = 0;
	return true;
}
Elem getHead(Queue que)//读取对头元素
{
	Elem elem = que.elem[que.front % initSize];
	return elem;
}
#pragma once
//intStack.h
#define initSize 10
typedef struct
{
	int number[initSize];
	int top;	//线性表当前位置
}Stack;

void initStack(Stack& stack);//初始化栈
bool stackEmpty(Stack stack);//判断是否为空栈
bool stackFull(Stack stack);//判断是否为满栈
bool push(Stack& stack, int number);//入栈
bool pop(Stack& stack);//出栈
int getPop(Stack stack);//读取栈顶元素

void initStack(Stack& stack)//初始化栈
{
	stack.top = 0;//指向栈的第一个空间,此时栈为空栈
}

bool stackEmpty(Stack stack)//判断是否为空栈
{
	if (stack.top == 0)
		return true;
	else
		return false;
}
bool stackFull(Stack stack)//判断是否为满栈
{
	if (stack.top >= initSize)
		return true;
	else
		return false;
}
bool push(Stack& stack, int number)//入栈
{
	//栈满
	if (stackFull(stack))
		return false;
	int top = stack.top;
	stack.number[top] = number;//元素入栈
	++stack.top;//栈顶向上移
	return true;
}
bool pop(Stack& stack)//出栈
{
	if (stackEmpty(stack))
		return false;
	else
	{
		--stack.top;//栈顶向下移	
	}

	return true;

}
int getPop(Stack stack)//读取栈顶元素
{
	if (!stackEmpty(stack))
	{
		int i = stack.top - 1;
		return stack.number[i];
	}

}
//MGraph.h
#pragma once
#include "intStack.h"
#include "intQueue.h"
#include <stdio.h>

#define MaxVertaxNum 50 //顶点最大数目
#define VertaxNum 4 //节点数量
typedef int VertaxType;//顶点信息类型,用字符来表示顶点
typedef bool EdgeType;//不带权值的图,只存储点的连接信息
typedef struct Mgraph {
	VertaxType Vertax[MaxVertaxNum];
	EdgeType Edge[MaxVertaxNum][MaxVertaxNum];
	int vexNum, edgeNum;//点的数量、边的数量
}Mgraph;
typedef struct visited {
	bool visited[VertaxNum];//标记顶点是否被访问
}visited;

/* 假设图的顶点分别为 0 , 1 , 2 , 3
邻接矩阵   0  1  0  0
		   1  0  1  1
		   0  1  0  0
		   0  1  0  0
*/
void initMgraph(Mgraph& mg, VertaxType Vertax[VertaxNum], EdgeType Edge[VertaxNum][VertaxNum]);//初始化
void DFirst(Mgraph mg, VertaxType ver, visited& visited, Stack& stack);//深度优先遍历
void WFirst(Mgraph mg, VertaxType ver, visited& visited, Queue& queue);//广度优先遍历
void initVisited(visited& visited);//初始化定点标记数组
void visit(VertaxType ver);//访问节点
bool vexIsTrue(Mgraph mg, VertaxType ver, visited visited);//与ver相连接的节点是否都已访问


void initMgraph(Mgraph& mg, VertaxType Vertax[VertaxNum], EdgeType Edge[VertaxNum][VertaxNum])//初始化
{
	int edgeNum = 0;
	for (int i = 0; i < VertaxNum; i++)//修改图的顶点信息
		mg.Vertax[i] = Vertax[i];
	for (int i = 0; i < VertaxNum; i++)//修改图的路径信息
	{
		for (int j = 0; j < VertaxNum; j++)
		{
			mg.Edge[i][j] = Edge[i][j];
			if (Edge[i][j] != 0)
				edgeNum++;
		}

	}
	mg.vexNum = VertaxNum;
	mg.edgeNum = edgeNum / 2;

}

//初始化节点标记
void initVisited(visited& visited)
{
	for (int i = 0; i < VertaxNum; i++)//初始化节点标记,开始都为false
		visited.visited[i] = false;
}

void visit(VertaxType ver)//访问节点
{

}

bool vexIsTrue(Mgraph mg, VertaxType ver, visited visited)//与ver相连接的节点是否都已访问
{
	for (int i = 0; i < VertaxNum; i++)
	{
		if (mg.Edge[ver][i] != 0 && !visited.visited[i])//有相连接但是为访问顶点
		{
			return false;
		}
	}
	return true;//所有相连节点都访问
}
void DFirst(Mgraph mg, VertaxType ver, visited& visited, Stack& stack)//深度优先遍历
{

	visit(ver);
	if (!visited.visited[ver])
	{
		visited.visited[ver] = true;//顶点访问,修改标记
		printf("%d\n", mg.Vertax[ver]);
	}

	push(stack, ver);//定点入栈

	VertaxType temVer = ver;
	while (!stackEmpty(stack))
	{
		if (vexIsTrue(mg, temVer, visited))//与该顶点相连接的所有顶点都已经访问,顶点出栈
		{
			pop(stack);
			if (!stackEmpty(stack))
			{
				temVer = getPop(stack);//读取顶点出栈后栈顶元素
			}
			else temVer = -1;

		}//if
		else
			break;
	}
	if (temVer != -1)
	{
		for (int j = VertaxNum - 1; j >= 0; j--)
		{
			if (mg.Edge[ver][j] != 0 && visited.visited[j] != true)
			{
				push(stack, mg.Vertax[j]);//将与顶点相连且未被访问的顶点入栈
			}

		}//for
		if (!stackEmpty(stack))
		{
			temVer = getPop(stack);
			DFirst(mg, temVer, visited, stack);
		}//if
	}//if	

}//DFirst


void WFirst(Mgraph mg, VertaxType ver, visited& visited, Queue& queue)//广度优先遍历
{
	visit(ver);
	
	visited.visited[ver] = true;
	printf("%d\n", ver);
	VertaxType temVer = ver;
	if (!vexIsTrue(mg, temVer, visited))//存在与节点连接但未被访问的节点
	{
		for (int j = VertaxNum - 1; j >= 0; j--)
		{
			if (mg.Edge[ver][j] != 0 && visited.visited[j] != true)
			{
				enQueue(queue, mg.Vertax[j]);//将与顶点相连且未被访问的顶点入栈
			}

		}//for
	}
	if (!QueueEmpty(queue))
	{
		deQueue(queue , temVer);
		WFirst(mg, temVer, visited, queue);
	}

}

``

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我可以回答您的问题。 首先,是一种非线性数据结构,它由一组节点(顶点)和一组边组成。可以用多种方式存储,其中邻接矩阵是一种常见的存储方式。邻接矩阵是一个二维数组,数组中的元素表示中两个节点之间是否有边相连。如果节点i和节点j之间有边相连,则邻接矩阵中第i行第j列的元素为1,否则为0。 下面是用C语言实现邻接矩阵存储: ``` #define MAX_VERTEX_NUM 100 //最大顶点数 typedef struct { int vexs[MAX_VERTEX_NUM]; //顶点数组 int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //邻接矩阵 int vexnum, arcnum; //的顶点数和边数 } MGraph; ``` 其中,vexs数组用来存储的顶点,arcs数组用来存储邻接矩阵,vexnum表示的顶点数,arcnum表示的边数。 接下来是遍历遍历是指按照某种顺序访问中的每个节点。常用的两种遍历方式是深度优先遍历和广度优先遍历。 深度优先遍历(Depth-First-Search,DFS)是一种先访问深度较深的节点的遍历方式。具体实现可以用递归或栈来实现。 下面是用C语言实现的深度优先遍历: ``` void DFS(MGraph G, int v, int* visited) { visited[v] = 1; //标记节点v已经被访问 printf("%d ", G.vexs[v]); //输出节点v的值 for (int i = 0; i < G.vexnum; i++) { if (G.arcs[v][i] == 1 && visited[i] == 0) { //如果节点v和节点i之间有边相连且节点i未被访问过 DFS(G, i, visited); //递归访问节点i } } } void DFSTraverse(MGraph G) { int visited[MAX_VERTEX_NUM] = {0}; //标记数组,用来记录每个节点是否被访问过 for (int i = 0; i < G.vexnum; i++) { if (visited[i] == 0) { //如果节点i未被访问过 DFS(G, i, visited); //从节点i开始进行深度优先遍历 } } } ``` 其中,DFS函数是递归实现的深度优先遍历,DFSTraverse函数是遍历整个的入口函数。 广度优先遍历(Breadth-First-Search,BFS)是一种先访问深度较浅的节点的遍历方式。具体实现可以用队列来实现。 下面是用C语言实现的广度优先遍历: ``` void BFSTraverse(MGraph G) { int visited[MAX_VERTEX_NUM] = {0}; //标记数组,用来记录每个节点是否被访问过 int queue[MAX_VERTEX_NUM]; //队列 int front = -1, rear = -1; //队列的头和尾 for (int i = 0; i < G.vexnum; i++) { if (visited[i] == 0) { //如果节点i未被访问过 visited[i] = 1; //标记节点i已经被访问 printf("%d ", G.vexs[i]); //输出节点i的值 queue[++rear] = i; //将节点i入队 while (front != rear) { //当队列不为空时 int j = queue[++front]; //将队列头部元素出队 for (int k = 0; k < G.vexnum; k++) { if (G.arcs[j][k] == 1 && visited[k] == 0) { //如果节点j和节点k之间有边相连且节点k未被访问过 visited[k] = 1; //标记节点k已经被访问 printf("%d ", G.vexs[k]); //输出节点k的值 queue[++rear] = k; //将节点k入队 } } } } } } ``` 其中,BFSTraverse函数是遍历整个的入口函数,使用了队列来实现广度优先遍历。 希望我的回答能够解决您的问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

浅隐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值