图论基础: 邻接矩阵与邻接表(c++实现)

news2024/11/16 19:44:28

文章目录

  • 邻接矩阵
  • 邻接表

邻接矩阵

邻接矩阵(Adjacency Matrix)是表示顶点之间相邻关系的矩阵。

设G=(顶点,边):G=(V,E)是一个图。其中V={v1,v2,…,vn} [1] 。G的邻接矩阵是一个具有下列性质的n阶方阵:

  1. 无向图的邻接矩阵一定是成对角线对称的,是一个对称矩阵,有向图不一定是对称的。
  2. 有向图当把它的行i固定时,遍历每一列j,得到的是顶点i的出度;当把列j固定时,遍历每一行,得到的是顶点i的入度。
    在这里插入图片描述

在这里插入图片描述

  1. 对于n个顶点和e条边的邻接矩阵存储时占用的空间是O(n^2),与边数e无关,邻接矩阵适合用于存储稠密图,任何图的邻接矩阵的表示都是唯一,图采用邻接矩阵来描述i,j之间的边很容易。

临界矩阵的结构分为两部分:V和E集合,其中,V是顶点,E是边。

因此,用一个一维数组存放图中所有顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵

邻接矩阵又分为有向图邻接矩阵和无向图邻接矩阵

#define INF 0x3F3F3F3F
constexpr auto MAXN = 100;
//存储图中所有顶点的数据:一维数组
struct Vertex
{
	int Vid;		//顶点编号
	string VInfo;	//顶点信息
};
//存储图中的顶点与边关系:二维数组
struct Graph
{
	int e, n;	//e: 实际边数 n:实际定点数
	Vertex vers[MAXN];		//顶点集合
	int edge[MAXN][MAXN];	//边的集合
}A;

图的邻接矩阵的表示格式:

邻接矩阵
1. 不带权的图的邻接矩阵的表示形式:
			{ 1 :对于无向图(i,j)(j,i),对于有向图<i,j>
	A[i][j]=  0 :i == j
			  0 :其他情况 
			}
2. 带权的图的邻接矩阵的表示形式:
			{ w :对于无向图(i,j)(j,i),对于有向图<i,j>
	A[i][j]=  0	:i == j
			  INF :其他情况
			}
#include <iostream>
#include <vector>
using namespace std;

#define INF 0x3F3F3F3F
//1.创建邻接矩阵
constexpr auto MAXN = 100;
struct Vertex
{
	int Vid;		//顶点编号
	string VInfo;	//顶点信息
};
struct Graph
{
	int e, n;	//e: 实际边数 n:实际定点数
	Vertex vers[MAXN];		//顶点集合
	int edge[MAXN][MAXN];	//边的集合
};
class adjacent_matrix
{
public:
	adjacent_matrix() {}
	~adjacent_matrix() {}
	//1. 创建图的邻接矩阵
	void CreateGraph(vector<vector<int>>& A, int n, int e)
	{
		g.e = e;
		g.n = n;
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				g.edge[i][j] = A[i][j];
			}
		}
	}
	//2.输出图
	friend ostream& operator<<(ostream& os, adjacent_matrix lhs)
	{
		for (int i = 0; i < lhs.g.n; i++)
		{
			for (int j = 0; j < lhs.g.n; j++)
			{
				if (lhs.g.edge[i][j] < INF)
				{
					os << lhs.g.edge[i][j] << " ";
				}
				else
				{
					os << "* ";
				}
			}
			os << endl;
		}
		return os;
	}
	//3. 求顶点度的运算
	//	3.1无向图的顶点度
	int Degree1(int v)
	{
		int d = 0;
		if (v < 0 || v >= g.n)
		{
			return -1;
		}
		for (int i = 0; i < g.n; i++)
		{
			if (g.edge[v][i] > 0 && g.edge[v][i] < INF)
			{
				d++;
			}
		}
		return d;
	}
	//	3.2有向图的定点度
	int Degree2(int v)
	{
		int d = 0;
		if (v < 0 || v >= g.n)
		{
			return -1;
		}
		for (int i = 0; i < g.n; i++)
		{
			if (g.edge[v][i] > 0 && g.edge[v][i] < INF)
			{
				//行:求出度
				d++;
			}
		}
		for (int i = 0; i < g.n; i++)
		{
			if (g.edge[i][v] > 0 && g.edge[i][v] < INF)
			{
				//列:求入度
				d++;
			}
		}
		return d;
	}
public:
	Graph g;
};

int main()
{
	adjacent_matrix a;
	int n = 5, e = 7;
	vector<vector<int>> A = {
		{0,1,2,6,INF},
		{INF,0,INF,4,5},
		{INF,INF,0,INF,3},
		{INF,INF,INF,0,INF},
		{INF,INF,INF,7,0}
	};
	a.CreateGraph(A, n, e);
	cout << a;
	cout << "顶点及其度:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << i << ":--" << a.Degree2(i) << endl;
	}
	return 0;
}

在这里插入图片描述

邻接表

邻接表,是一种链式存储结构,存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。

在这里插入图片描述

在邻接表中:

  1. 图中每个顶点建立一个带头节点的单链表,单链表的所有节点由与这个顶点所相邻的所有顶点构成的节点组成。
  2. 每个顶点构成一条边,称为边节点
  3. 所有的顶点的单链表构成一个数组,称为头节点数组
//边节点
struct Node
{
	int Neid;		//相邻点节点的序号
	int weight;		//节点权值
	Node* next;		//指向下一个节点的指针
};

//每个顶点的单链表
struct VexNode
{
	string VexInfo;	//第一个顶点的信息
	Node* head;		//单链表的头节点类型
};

//图
struct Graph
{
	int n, e;				//n:实际顶点数 e:实际边数
	VexNode graph[MAXN];	//单链表数组
};

完整代码:

#include "change.h"
/*
邻接表:以链式结构存储图的结构
*/
#define MAXN 100
#define INF 0x3F3F3F3F
//边节点
struct Node
{
	int Neid;		//相邻点节点的序号
	int weight;		//节点权值
	Node* next;		//指向下一个节点的指针
};

//每个顶点的单链表
struct VexNode
{
	string VexInfo;	//第一个顶点的信息
	Node* head;		//单链表的头节点类型
};

//图
struct Graph
{
	int n, e;				//n:实际顶点数 e:实际边数
	VexNode graph[MAXN];	//单链表数组
};

//邻接表
class AdjGraph
{
public:
	AdjGraph()
	{
		G = nullptr;
	}
	~AdjGraph() {}
	//创建邻接表
	void CreateGraph(vector<vector<int>> A, int n, int e)
	{
		G = new Graph;
		G->n = n; G->e = e;
		//首先初始化图中全部顶点的单链表为nullptr
		for (int i = 0; i < G->n; i++)
		{
			G->graph[i].head = nullptr;
		}
		for (int i = 0; i < G->n; i++)
		{
			//单链表连接的时候从小到大
			for (int j = G->n - 1; j >= 0; j--)
			{
				if (A[i][j] > 0 && A[i][j] < INF)
				{
					Node* p = new Node;
					p->Neid = j;
					p->weight = A[i][j];
					//单链表头插
					p->next = G->graph[i].head;
					G->graph[i].head = p;
				}
			}
		}
	}
	//销毁邻接表
	void destroyGraph()
	{
		for (int i = 0; i < G->n; i++)
		{
			//遍历图中每一个顶点单链表
			Node* temp = nullptr;
			Node* cur = G->graph[i].head;
			while (cur)
			{
				temp = cur->next;
				memset(cur, NULL, sizeof(cur));
				delete cur;
				cur = nullptr;
				cur = temp;
			}
		}
	}
	//输出图
	void display()
	{
		for (int i = 0; i < G->n; i++)
		{
			//遍历每个顶点单链表
			cout << i << ": ";
			Node* head = G->graph[i].head;
			while (head)
			{
				cout << "->";
				cout << head->Neid << "(" << head->weight << ")";
				head = head->next;
			}
			cout << endl;
		}
	}
	//求顶点的度:无向图的单链表的节点个数就是该节点的度
	int Degree1(int v)
	{
		int d = 0;
		auto head =  G->graph[v].head;
		while (head)
		{
			d++;
			head = head->next;
		}
		return d;
	}
	//有向图的顶点的度:出度+入度
	int Degree2(int v)
	{
		int d = 0;
		Node* outhead = G->graph[v].head;
		//求出度:顶点单链表的节点个数
		while (outhead)
		{
			d++;
			outhead = outhead->next;
		}
		//求入度:每个单链表中是否存在此顶点节点
		for (int i=0;i<G->n;i++)
		{
			Node* cur = G->graph[i].head;
			while (cur)
			{
				if (cur->Neid == v)
				{
					d++;
					break;
				}
				cur = cur->next;
			}
		}
		return d;
	}
public:
	Graph* G;
};

//练习1:邻接矩阵转换为邻接表
Graph* change1(matrix lhs)
{
	Graph* nG = new Graph;
	nG->n = lhs.g.n;
	nG->e = lhs.g.e;
	for (int i = 0; i < nG->n; i++)
	{
		nG->graph[i].head = nullptr;
	}
	for (int i = 0; i < lhs.g.n; i++)
	{
		//邻接矩阵:行出度,列入度
		for (int j = lhs.g.n - 1; j >= 0; j--)
		{
			//行:出度
			if (lhs.g.edge[i][j] > 0 && lhs.g.edge[i][j] < INF)
			{
				//<i,j>是一个路径
				Node* node = new Node;
				node->Neid = j;
				node->weight = lhs.g.edge[i][j];

				//头插
				node->next = nG->graph[i].head;
				nG->graph[i].head = node;
			}
		}
	}

	return nG;
}
//练习1:邻接表转换为邻接矩阵
void change2(matrixGraph &g,Graph* lhs)
{
	g.e = lhs->e;
	g.n = lhs->n;
	for (int i = 0; i < g.n; i++)
	{
		for (int j = 0; j < g.n; j++)
		{
			//对角线元素置零,其他元素置INF
			if (i == j) g.edge[i][j] = 0;
			else g.edge[i][j] = INF;
		}
	}
	for (int i = 0; i < lhs->n; i++)
	{
		//顶点出度:遍历每一个顶点单链表的节点
		Node* cur = lhs->graph[i].head;
		while (cur)
		{
			// i->j
			g.edge[i][cur->Neid] = cur->weight;
			cur = cur->next;
		}
	}
}
int main()
{
	AdjGraph Ga;
	int n = 5, e = 7;
	vector<vector<int>> A = {
		{0,1,2,6,INF},
		{INF,0,INF,4,5},
		{INF,INF,0,INF,3},
		{INF,INF,INF,0,INF},
		{INF,INF,INF,7,0}
	};
	Ga.CreateGraph(A, n, e);
	Ga.display();
	cout << "顶点及其度:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << i << ":--" << Ga.Degree2(i) << endl;
	}

	matrix ga;
	ga.CreateGraph(A, n, e);
	cout << ga;
	cout << "顶点及其度:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << i << ":--" << ga.Degree2(i) << endl;
	}
	//邻接矩阵转化为邻接表
	auto Gp =  change1(ga);
	//邻接表转换为邻接矩阵
	matrixGraph g;
	change2(g,Gp);

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/164521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手眼标定,9点标定过程及其运算

在工业领域常常会遇到将相机安装在机器手中&#xff0c;由相机快速引导机器手进行工作的方式。其中9点标定的作用是将图像的坐标转化为机器手的坐标。 9点标定的作用意义&#xff1a; 1.计算像素当量&#xff0c;通过9点标定后的计算&#xff0c;可以得出一个由像素值转化为机器…

水平分表、分库和垂直分表、分库和公共表的代码实现和讲解

文章目录一、环境准备二、水平分表1.概念2.代码三、水平分库1.概念2.代码四、垂直分表1.概念2.代码五、垂直分库1.概念2.代码六、公共表1.概念2.代码一、环境准备 操作系统&#xff1a;Win10数据库&#xff1a;MySQL5.7JDK&#xff1a;64位 jdk1.8.0_202应用框架&#xff1a;s…

DOS和DDOS攻击和防御(ATTACK)

目录 一、DOS攻击和DDOS攻击的区别 第一、我们可以从他们两个的英文全称上来看初步的区别 第二、攻击方法不同 二、DOS和DDOS攻击的实现方式 1.DOS攻击 1、SYN Flood(是DOS和DDOS攻击方式之一) 2、UDP洪水攻击 3、Ping洪流攻击 4、teardrop攻击 5、Land攻击 6、Smurf攻击 7、Fr…

【 uniapp - 黑马优购 | 登录与支付 1】登录组件布局实现、用户信息布局与渲染

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生&#xff0c;讨厌编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;小新爱学习. &#x1f43c;个人WeChat&#xff1a;见文末 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;…

设计模式-原型模式

设计模式-原型模式一 官方定义二 案例演示解决方案一 - 一般实现方式实现过程案例分析解决方案二使用场景实现过程一实现过程 二案例分析三 浅拷贝和深拷贝浅拷贝问题演示实现过程案例分析解决方案-----深拷贝实现方式一&#xff1a;重写clone()方法扩展思考一 官方定义 原型模…

在VMware 虚拟机(Win7)中还原真机Ghost备份的Win10系统

要求&#xff1a; 将真机Ghost备份的Win10系统还原到VMware安装的虚拟机&#xff08;Win7&#xff09;上 真机&#xff08;物理机&#xff09;&#xff1a;win10pro_pure_20220709.GHO &#xff08;备份的GHO文件&#xff09;&#xff1b;安装模式&#xff1a;Win10UEFIGPT 虚…

HashMap源码学习:红黑树原理详解

前言 JDK1.8后的HashMap引入了红黑树&#xff0c;在学习HashMap源码之前&#xff0c;了解了红黑树原理&#xff0c;及其如何通过代码进行实现后&#xff0c;在整体的看HashMap的源码就会简单很多。 概述 红黑树的特性 根节点必须是黑色节点。节点是红色或黑色。所有叶子都是…

Redis原理

Redis内部使用的是文件事件处理器file event handler,它是单线程的,所以Redis叫做单线程模型。它采用IO多路复用机制同时监听多个socket,将产生事件的socket压入内存队列中,事件分派器根据socket上的事件类型来选择对应的事件处理器进行处理。文件事件处理器包含4个部分:多…

【Java寒假打卡】Java基础-线程池

【Java寒假打卡】Java基础-线程池概述基本使用Executors创建指定上限的线程对象线程池-ThreadPoolExecutorvolatile概述 基本使用 package com.hfut.edu.test12;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class test1 {publ…

java+springboot笔记2023003

java的版本发布&#xff1a; 编译型语言是指使用专门的编译器&#xff0c;针对特定平台&#xff08;操作系统&#xff09; 将某种高级语言源代码一次性“翻译”成可被该平台硬件执行的机器码&#xff08;包括机器指令和操作数&#xff09;&#xff0c;并包装成该平台所能识别的…

Linux下更新curl版本

一、前景 由于低版本的curl存在一定的漏洞&#xff0c;会对我们的服务器安全造成问题&#xff0c;所以&#xff0c;我们需要将curl由低版本安装到高版本。 二、步骤 1、首先检测服务器安装的curl版本 curl --version 2、查看服务器安装的curl的安装包 rpm -qa curl 3、卸载…

基于springboot+mybatis+mysql+jsp房屋租赁管理系统(含论文)

基于springbootmybatismysqljsp房屋租赁管理系统&#xff08;含论文&#xff09;一、系统介绍二、所用技术三、功能展示三、其它系统四、获取源码一、系统介绍 包括管理员、房东、租客三种角色&#xff0c;外加游客(未登录情况) 出租类型包含整租和合租 权限 游客 < 租客 …

适合编程初学者的开源项目:小游戏2048(鸿蒙ArkTS版)

目标 为编程初学者打造入门学习项目&#xff0c;使用各种主流编程语言来实现。 2048游戏规则 一共16个单元格&#xff0c;初始时由2或者4构成。 1、手指向一个方向滑动&#xff0c;所有格子会向那个方向运动。 2、相同数字的两个格子&#xff0c;相遇时数字会相加。 3、每次…

用 JavaScript 写一个新年倒计时

目录前言&#xff1a;主题&#xff1a;运行结果&#xff1a;对应素材&#xff1a;代码实现思路&#xff1a;运行代码&#xff1a;春节的由来&#xff1a;总结&#xff1a;前言&#xff1a; 在春节即将到来&#xff0c;钟声即将响起&#xff0c;焰火即将燃起的日子里&#xff0c…

Kubernetes_CRD自定义资源

系列文章目录 文章目录系列文章目录前言一、CRD操作命令1.1 定义一种资源并查看1.2 使用刚刚定义的资源二、CRD效果演示2.1 实践&#xff1a;定义一种资源并查看2.2 实践&#xff1a;使用刚刚定义的资源总结前言 CRD就是自定义资源&#xff0c;就是自定义 apiVersionKind 参考…

TreeMap 原理实现及常用方法

TreeMap概述 红黑树回顾 TreeMap构造 put方法 get 方法 remove方法 遍历 总结 一. TreeMap概述 TreeMap存储K-V键值对&#xff0c;通过红黑树&#xff08;R-B tree&#xff09;实现&#xff1b; TreeMap继承了NavigableMap接口&#xff0c;NavigableMap接口继承了Sort…

蓝桥杯STM32G431RBT6学习——LED

蓝桥杯STM32G431RBT6学习——LED 前言 LED为每年必考考点&#xff0c;也是入门的基础&#xff0c;国信长天的开发板LED硬件如下&#xff1a; 经典的锁存器控制&#xff0c;因为LED所用引脚与LCD重叠&#xff0c;因此通过锁存器进行控制其状态。当74HC573的LE引脚&#xff08…

C语言综合练习5:快译通下

1 词典文件介绍 前面建立的词典&#xff0c;只有两个单词&#xff0c;现在我们要建立一个上万个单词的词典&#xff0c;所有单词及其翻译都在一个名为dict.txt的文件&#xff08;词典文件&#xff09;中 每个单词有两行&#xff0c;其中一行是单词原文&#xff0c;下一行是对…

Redis中的事务和乐观锁实现

redis事务相关命令&#xff1a; 开启事务&#xff1a;multi 关闭事务&#xff1a;discard 提交事务&#xff1a;exec 正常执行事务情况&#xff1a; 127.0.0.1:6379> multi OK 127.0.0.1:6379> set name zhangsan QUEUED 127.0.0.1:6379> set age 20 QUEUED 127.0.0.1…

AJAX这一篇就够啦~

AJAX这一篇就够啦~AJAX1、AJAX概述1.1 AJAX简介1.2 XML简介1.3 AJAX的特点2、HTTP相关2.1 HTTP概述2.2 请求报文2.3 响应报文2.4 常见的响应状态码2.5 不同类型的请求及其作用2.6 一般http请求 与 ajax请求3、原生AJAX的使用3.1 准备工作3.2 核心对象3.3 GET请求3.4 POST请求3.…