图的基本操作(无向图)

2023-11-18

图的定义

图(Graph)在是一种较线性表和树更为复杂的数据结构。
线性表中,数据元素之间是被串起来的,只有线性关系,每个数据元素只有一个直接前驱和一个直接后继。
在这里插入图片描述
树形结构中,数据元素之间有着很明显的层次关系,并且每一层的数据元素可能和下一层的多个元素相关,但是只能和上一层的一个元素相关。 这就和一对父母可以有多个孩子,但是一个孩子只能有一对亲生父母一样。
在这里插入图片描述
但是实际上,在现实生活中,很多事物和人际关系都是十分复杂的,并非简单的一对一、一对多的关系。这个时候就可以用这种数据结构来表示,在图的数据结构中,结点之间的关系可以是任意的,图中任意两个数据元素都是可能相关的。
在这里插入图片描述

各种图的定义

在图中的数据元素叫做顶点(Vertex),假定V是顶点的有穷非空集合;VR是两个顶点直接的关系的集合。若<v,w>∈VR,则<v,w>表示从v到w的一条(Arc),且称v为弧尾(Tail)或初始点,称w为弧头(Head)或终端点。

  • 此时的图为有向图,即顶点之间的是有方向的。

在这里插入图片描述

  • 如果<v,w>∈VR,必有<w,v>∈VR,即VR是对称的,则以无序对(v,w)来代替这两个有序对,表示v和w之间的一条(Edge),边是没有方向的,此时的图称为无向图
    在这里插入图片描述

  • 如果图的弧或边具有与它相关的数字,则称这种与图的边或弧相关的数叫做权值(Weight),这种带权值的图成为(Net)。带权值的有向图成为有向网
    在这里插入图片描述

  • 带权值的无向图称为无向网
    在这里插入图片描述

图的存储结构

邻接矩阵表示法(数组表示法)

一维数组存储图中顶点的信息(顶点表),用二维数组存储图中边或弧的信息(邻接矩阵)。

  • 无向图和有向图中,若两顶点之间有边或弧,则二维数组中点为1,否则为0。在这里插入图片描述

  • 无向网和有向网中,若两顶点之间有边或弧,则二维数组中的点为其权值,否则为∞。在这里插入图片描述

邻接表表示法

当边数相对顶点数较少的图时,邻接矩阵这种结构会极大的浪费存储空间。于是我们使用一种把数组和链表相结合的存储方式——邻接表,用一个一维数组来存放顶点信息(顶点表),在将图中的每个顶点所有邻接点构成一个线性表,用单链表存储。
在这里插入图片描述

其他存储结构

还有十字链表邻接多重表边集数组这三种存储结构,本文不着重说明。 本文重点讲解无向图的邻接矩阵基本操作

无向图的邻接矩阵基本操作

首先定义和声明一下之后会用到的宏

无向图的数据元素的声明和队列的数据元素的声明(队列在广度优先遍历中会使用到)

#include<iostream>
using namespace std;
typedef char VertexType;     
typedef int EdgeType; 
typedef int status;
typedef int QElemType;
#define MAXVEX 20     //最大顶点个数设置为20
#define MAXSIZE 20    //设置队列的最大容量为20
#define ERROR 0
#define OK 1
bool Visited[MAXVEX];   //设置一个布尔变量,用于在两种遍历的时候标志顶点是否被访问过

typedef struct MGraph       //无向图的数组表示法存储结构
{
	VertexType vexs[MAXVEX];     //顶点表
	EdgeType arc[MAXVEX][MAXVEX];     //邻接矩阵
	int vertexs_num;            //顶点数
}MGraph;

创建无向图

创建无向图所需要的信息是顶点信息边关系
因为无向图的邻接矩阵是对称的,所以G.arc[i][j]=G.arc[j][i]

void Create(MGraph& G)
{    //创建一个无向图
	int i, j;
	cout << endl;
	cout << "该无向图的顶点个数为:";
	cin >> G.vertexs_num;
	cout << endl;
	cout << "请依次输入顶点" << endl<<endl;
	for (i = 0; i < G.vertexs_num; ++i)
	{//输入顶点表,构成一维的顶点数组
		cout << "第" << i + 1 << "个顶点值为:";
		cin >> G.vexs[i];
	}
	for (i = 0; i < G.vertexs_num; ++i)
			G.arc[i][i] = 0;           //初始化邻接矩阵,即无向图中主对角线所有元素都为0

	for (i = 0; i < G.vertexs_num; i++)
		for (j = i + 1; j < G.vertexs_num; j++)
		{
			cout << "若元素"<< G.vexs[i] <<"和元素"<< G.vexs[j] <<"有边,则输入1,否则输入0:";
			cin >> G.arc[i][j];
			G.arc[j][i] = G.arc[i][j];
		}
}

输出无向图

先输出顶点表,再输出二维的邻接矩阵

void Print(MGraph G)
{
	int i, j;
	cout << endl;
	cout << "该无向图的信息如下:" << endl << endl;;
	cout << "顶点元素表:" << endl;
	for (i = 0; i < G.vertexs_num; i++)
		cout << G.vexs[i] << " ";
	cout << endl<<endl;
	cout << "邻接矩阵如下:" << endl;
	cout  << "  ";
	for (i = 0; i < G.vertexs_num; i++)
		cout << "  " << G.vexs[i];
	cout << endl;
	for (i = 0; i < G.vertexs_num; i++)
	{
		cout << G.vexs[i];
		cout << " ";
		for (j = 0; j < G.vertexs_num; j++)
			cout << "  " << G.arc[i][j];
		cout << endl;
	}
	cout << endl;
}

无向图的遍历

图的遍历是从某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次。
通常有两种遍历次序的方法:深度优先遍历广度优先遍历

深度优先遍历

深度优先遍历又叫深度优先搜索,简称为DFS,类似于树的先根遍历,其实是一个递归的过程
从某顶点v出发进行DFS的基本思想为:

  1. 访问顶点v
  2. 依次从v的未被访问的邻接点出发,进行DFS
    在这里插入图片描述
    要设置一个类型为bool变量的辅助数组Visited,来标记顶点是否被访问过
void DFS(MGraph G, int i)
{   
	int j; 
	Visited[i] = true;   //将该顶点设置为已访问过的
	cout << G.vexs[i] << " ";
	for (j = 0; j < G.vertexs_num; j++)
	{
		if (G.arc[i][j] == 1 && !Visited[j] )     //如果两点之间有边并且第j+1个顶点没有被访问过
			DFS(G, j);
	}
}

void DFSTraverse(MGraph G)
{   //深度优先遍历算法
	int i;
	cout << endl;
	cout << "深度优先搜索的序列为:    " ;
	for (i = 0; i < G.vertexs_num; i++)
		Visited[i] = false;    //将所有的顶点都设置为未访问
	for(i=0;i<G.vertexs_num;i++)
		if(!Visited[i]) 
			DFS(G,i);
	cout << endl << endl;;
}

广度优先遍历

广度优先遍历又称为广度优先搜索,简称BFS,类似于树的层次遍历。

从某顶点v出发进行BFS的基本思想为:

  1. 访问顶点v
  2. 访问顶点v的所有未被访问的邻接点
  3. 依次从这些邻接点出发,访问它们的所有未被访问的邻接点;
  4. 依次类推,直到所有顶点都被访问为之

在这里插入图片描述
如果说深度优先遍历是一次遍历一个,那么广度优先遍历就是一次遍历一层
故这里要运用到队列这种先进先出的输出方法
算法思想为:

  1. 访问出发点v,并将其标记为已访问过
  2. 顶点v入队
  3. 当队列不为空时
    (1)取出队首顶点i
    (2)依次搜索顶点i的所有邻接点,若某一邻接点 j未被访问,则访问该邻接点,并将其入队
    在这里插入图片描述
    首先要对队列的相关函数进行相关定义
typedef struct {         //队列的数据结构(队列在广度优先遍历里会使用到)
	QElemType* base;   //初始化时动态分配空间
	int  front;        //头指针,队列非空时,指向队头元素
	int  rear;         //尾指针,队列非空时,指向队尾元素的下一个位置
}SqQueue;

初始化空队列


status InitQueue(SqQueue &Q) 
{   //初始化空队列
	Q.base= (QElemType *)malloc(MAXSIZE *sizeof(QElemType));
	if (Q.base == NULL)
	{
		cout << "分配内存失败!" << endl;
		exit(0);
	}
	Q.front = 0;
	Q.rear = 0;
	return OK;
}

判断队是否为空

status QueueEmpty(SqQueue Q)
{   //判断队列是否为空
	if (Q.front == Q.rear)    //头尾相等则说明是空队列
		return ERROR;
	else
		return OK;
}

元素入队

status push(SqQueue &Q, QElemType e)
{    //在队尾插入元素
	if ((Q.rear + 1) % MAXSIZE == Q.front)//队列已满
		return ERROR;
	Q.base[Q.rear] = e;//插入队尾
	Q.rear = (Q.rear + 1) % MAXSIZE;//尾部指针后移,如果到最后则转到头部
	return OK;
}

元素出队

status pop(SqQueue &Q, QElemType & e) 
{	//元素出队,并用e返回被删除的队头元素
	if (Q.front == Q.rear)//队列空
		return ERROR;
	e = Q.base[Q.front];//返回队头元素
	Q.front = (Q.front + 1) % MAXSIZE;//队头指针后移,如到最后转到头部
	return OK;
}

BFS

void BFSTraverse(MGraph  G)
{   //广度优先遍历算法
	SqQueue Q;
	int i,j;
	QElemType k;
	int mark;    //用于标记
	cout << endl;
	cout << "广度优先搜索的序列为:    ";
	InitQueue(Q);
	for (i = 0; i < G.vertexs_num; ++i)
		Visited[i] = false;
	for (i = 0; i < G.vertexs_num; ++i)
	{
		if (!Visited[i])
		{
			Visited[i] = true;
			push(Q,G.vexs[i]);    //该顶点入队
			cout << G.vexs[i] << " ";
			while (!QueueEmpty(Q)) 
			{
				pop(Q, k);        //该顶点出队,把出队的元素赋值给k
				cout << k << " ";
				for (j = 0; j < G.vertexs_num; j++)
				{
					if (G.vexs[j] == k) mark = k;    //将此时的队头元素标记
				}
				for (j = 0; j < G.vertexs_num; ++j)
				{
					if (G.arc[mark][j]==1&&!Visited[j])    //将表中标记行中未被访问的结点入队
					{
						Visited[j] = true;
						cout << G.vexs[j] << " ";
						push(Q,G.vexs[j]);
					}
				}
			}
		}
	}
	cout << endl << endl;
}

主函数和测试运行

主函数如下

int main()
{
	int a;
	SqQueue Q;
	InitQueue(Q);
	MGraph G;
	cout << "1.创建无向图   2.输出无向图的信息   3.用深度优先遍历(DFS)无向图     4.用广度优先遍历(BFS)无向图 " <<endl<< endl;
	while (1)
	{
		cout << "          请输入想要执行的操作:   ";
		cin >> a;
		switch (a)
		{
		case 1:	Create(G); break;
		case 2: Print(G); break;
		case 3:	DFSTraverse(G); break;
		case 4:	BFSTraverse(G); break;
		default: cout << "请重新输入正确的操作!" << endl;
		}
	}
	return 0;
}

对下图进行操作
在这里插入图片描述
在这里插入图片描述

别因为学会说话,尝到了甜头,就忘记了做人

  • 你必须逼着自己自信,这样生活才会被你的假装给欺骗,让好运在你身上接连发生。接下来你要做的就是强迫自己维持下去,直到真正的强大。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图的基本操作(无向图) 的相关文章

  • Java代码审计入门篇

    作者 i春秋核心白帽yanzmi 原文来自 https bbs ichunqiu com thread 42149 1 1 html 本期斗哥带来Java代码审计的一些环境和工具准备 Java这个语言相对于PHP来说还是比较复杂的 所以一开

随机推荐

  • SIM800C二次开发(EAT开发)------------(3)下载APP文件

    下载步骤有篇比较好的帖子可以更好的认识SIM800C的接口方式和步骤 帖子链接如下 https blog fish2bird com p 1274 SIM800C支持USB下载和UART下载两种方式 SIM800C有两个UART接口 下载使
  • 【GD32F427开发板试用】多路ADC规则组同时采样 DMA进行传输数据 顺带开启FPU浮点运算

    本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动 更多开发板试用活动请关注极术社区网站 作者 Hello eQN7e7 前言 开启浮点运算 加快浮点类型数据计算 使用GD32F427V START开发板的ADC1采样四路电
  • 二极管连接的MOS管

    二极管连接的MOS管 求输出电阻时可以电压 电流负反馈
  • STM32CubeMX安装、使用、配置

    1 在官网下载应用 https www st com 并安装java环境所需软件jre 8u271 windows x64 exe 2 使用cube新建项目 打开file gt new prj 3 Pinout Configuration配
  • 985高校副教授晒年薪,网友:公积金顶我一个月工资了......

    教师的工资一直具有争议性 大家的认知两极分化 有人说教师收入特别高 也有人说教师收入堪堪够生活 不存在谁说假话 而是因为各地区教师薪资水平差异较大 学校属于事业单位 薪资受当地经济水平影响 而教师群体中收入最高的是大学老师 曾经就有一份统计
  • ubuntu21.10搭建ebpf环境,BCC和bpftrace

    1 安装虚拟机 虽说centos是生产环境中的标准系统 但是从个人学习角度还是推荐ubuntu 各种软件安装包都能方便地找到 操作界面时也很漂亮 之前一直在centos7 6上折腾 自己升级内核版本 自己安装各种高版本依赖 有一段时间被折磨
  • JavaScript i++与++i、=+与+=的区别

    i i i 先赋值再自增 i 先自增再赋值 都是表达式 i i 1 题目 var a 10 b 20 c 30 a a e a b c a 结果 77 表达式 A B B转化为数字 赋值给A 表达式 A B A A B let x 2 y
  • 会计计算机二级考试试题,计算机二级考试MS-Office考试题库--excle--有答案.docx

    请在 答题 菜单下选择 进入考生文件夹 命令 并按照题目要求完成下面的操作 注意 以下的文件必须保存在考生文件夹下 小蒋是一位中学教师 在教务处负责初一年级学生的成绩管理 由于学校地处偏远地区 缺乏必要的教学设施 只有一台配置不太高的PC可
  • qt界面之toolTip

    一般需要在按钮中加入toolTip的提示 可以如下所示 后续继续更新
  • java并发编程

    并发编程 1 java线程 1 1 创建线程 1 1 1 Thread 匿名内部类实现Thread线程 new Thread t1 Override public void run start 1 1 2 Runnable new Thre
  • MobaXterm 终端永久设置字体大小

    刚接触 MobaXterm 没多久 想设置下界面字体大小 结果翻了翻网上 一些人都在瞎扯 没一个好用的 自己解决之后 特写出来 找到顶部的Settings 进去之后 找到font settings 调整为你想要的字体大小 一般12 14 就
  • 听老人一句劝,别去外包,干了四年,废了....

    我是一个普通二本大学机械专业毕业 目前做IT行业的软件测试已经有4年多了 18年通过校招进入湖南某软件公司 干了接近4年的功能测试 今年年初 感觉自己不能够在这样下去了 长时间呆在一个舒适的环境会让一个人堕落 而我已经在一个企业干了四年的功
  • QT笔记——信号与槽

    Qt信号与槽机制通过connect 关联信号 QT4 1 槽函数必须有slots关键字 2 SIGNAL SLOT 将函数转为字符串 不进行错误检查 3 槽函数和信号一致 参数 返回值 没有返回值 sender 发送信号的对象 signal
  • C++中在类中重载输出运算符时遇到error: declaration of ‘class T‘的问题的解决

    一 问题代码及报错提示 include
  • 5 神经网络(PRML)

    之前我们讨论的模型是对于分类的回归模型 包含了线性组合的多个基础函数 但是他的应用范围有一定的限制 另外一个方法在于事先限定基础函数的个数并且使得他可自适应的 也就是说使得他的参数值在训练当中是可以发生变化的 其中最成功的模型是前向神经网络
  • 微信小程序开发(六)WXML 模板

    WXML模板
  • AttributeError: module ‘distutils‘ has no attribute ‘version‘ 解决方案

    问题描述 今天在执行时出现了题述错误 查阅了半天才找到解决方案 特此记录 LooseVersion distutils version LooseVersion 解决方案 将以上代码改写成 from distutils version im
  • JDBC基础

    JDBC是什么 用java语言操作关系型数据库的一套api JDBC是用来干什么的 用java语言来操作数据库 JDBC怎么写 1 加载驱动类Driver全限定名 包 类名 2 获取连接 getConnection url username
  • maven.plugins.enforcer.BannedDependences 异常解决方案

    maven plugins enforcer BannedDependences 异常解决方案 简介 maven enforce plugin是一个规范maven构建环境的插件 例如 Maven版本 JDK版本和OS系列以及更多内置规则和用
  • 图的基本操作(无向图)

    图的定义 图 Graph 在是一种较线性表和树更为复杂的数据结构 在线性表中 数据元素之间是被串起来的 只有线性关系 每个数据元素只有一个直接前驱和一个直接后继 在树形结构中 数据元素之间有着很明显的层次关系 并且每一层的数据元素可能和下一