【JS】数据结构之图结构

news2025/1/17 22:10:56

文章目录

    • 基本概念
    • 图的实现

基本概念

什么是图?

  • 图是一种数据结构,与树结构有些相似(在数学的概念上,树是图的一种)
  • 树结构是一对多的关系,而图结构是多对多的关系
  • 比如导航的最优路径:可以看成多个地点可以连接成好多条边,这是我们要找出耗时最少,路程最短的路径。
  • 在图中,最基本的单元是顶点(相当于树中的节点),顶点之间的关系被称之为边。

在这里插入图片描述

  • 按照连接的两个顶点的边的不同,可以把图分为以下几种:
无向图: 边没有方向的图,边的作用仅仅连接两个顶点,没有其他含义
有向图: 边不仅连接两个顶点,并且具有方向性,可以是单向也可以是双向的
带权图: 边可以指定权重

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 图的术语:
相邻顶点: 当两个顶点通过一条边相连时,我们称这两个顶点是相邻的,并且是依附这两个顶点的。
: 某个顶点的度是依附于这个顶点的边的个数
子图: 一幅图中,所有的子集组成的图,包含这些边的依附的顶点。
路径: 是由边顺序连接的一系列的顶点组成
: 是一条至少含有一条边且终点和起点相同的路径。
连通图: 如果图中,任意一个顶点度存在一条路径到达另外一个顶点,那这幅图我们就称之为连通图。
连通子图: 一个非联通图由若干个连通的部分组成,每一个连通的部分就可以称为该图的连通子图。

在这里插入图片描述

  • 自环:即一条连接一个顶点和自身的边
  • 平行边:连接同一对顶点的两条边(有可能是多条边)

在这里插入图片描述

图的实现

欧拉开创了新的学科:图论(数学的一个分支)

  • 它以图为研究对象,研究顶点 和 边 组成的图形的数学理论和方法
  • 主要研究的目的是事物之间的关系,顶点代表事物,边代表两个事物间的关系。

在这里插入图片描述
图是由顶点和边构成,所以图里面要存储的图形结构信息,无非是存储图的顶点和图的边。
顶点可以之间通过数组去存储,那边该通过什么存储呢?以该图为例

在这里插入图片描述

  1. 邻接矩阵
矩阵是一个按照长方阵列排列的负数或实数集合。
N * M 数据的集合(九宫格)
去除表格线的九宫格就是矩阵的样式,矩阵是由行和列组成的一组数表。
邻接矩阵让每一个节点和整数相关联
用 1 表示顶点与顶点由直接的关系,用 0 表示没有连接。

在这里插入图片描述

  1. 邻接表

在这里插入图片描述

  • 图遍历思路:
1. 每一个顶点有三种状态
- 未发现(没有发现这个顶点)
- 已经发现(发现其他的顶点连接到这里,但是没有去查找这个顶点的全部连接的顶点)
- 已经探索(已经发现这个顶点连接的全部顶点)
2. 记录顶点是否被访问过,使用三种颜色来反映他们的状态
- 白色(未发现)
- 灰色(已经发现)
- 黑色(已经探索)
3. 广度优先遍历的过程
- 发现未发现顶点后,存放队列中,等待查找,并且并将这些顶点标记未发现
- 在队列中拿出已经发现的顶点,开始探索全部顶点,并且跳过已经探索的顶点
- 遍历完这个顶点以后,将这个顶点标志为已经探索
- 循环在队列中探索下一个顶点
4. 深度优先遍历的过程
- 从某一顶点开始查找,并将这个顶边标记为已经发现(灰色)
- 从这个顶点开始探索其他的全部的顶点,并跳过已经发现的顶点
- 变量为这个顶点以后,将这个顶点标记为已探索(黑色)
- 递归返回,继续探索下一个路径的最深顶点
  • 最短路径
1. 路径:由边顺序连接的顶点组成的
- 寻找最短路径,所谓路径指的是:
- 从图中某一个顶点(起点)到达另外一个顶点(终点)的路径
- 我们要找到一条路径使得沿这个路径边上的权值总和(路径长度)达到最小
2. 回溯点 
- 回溯点是离上一个顶点最近的点。A的回溯点是nullB的回溯点是A,E的回溯点是B
- 回溯路径(所有回溯点组成回溯路径)
const prev = {
	A: null,
	B: A,
	E: B,
	...
}
prev[E] = B	// E的回溯点是B
3. 常见的最短路径算法
- 迪杰斯特拉算法:是贪心算法的一个应用,用来解决单源点到其余顶点的最短路径的问题
- Floyd 算法: 经典的动态规划算法。
  • JavaScript实现一个图结构:
// 队列
class Queue {
	constructor() {
		this.items = [];
	}
	enqueue(ele){
		this.items.push(ele);
	}
	dequeue() {
		return this.items.shift();
	}
	// 查看队列前端元素
	front(){
		return this.items[0];
	}
	// 查看队列后端元素
	rear(){
		return this.items[this.items.length - 1];
	}
	// 查看队列是否为空
	isEmpty(){
		return this.items.length === 0;
	}
	// 查看队列中元素个数
	size(){
		return this.items.length;
	}
	// 清空队列
	clear(){
		this.items = []
	}
}
// 栈
class Stack{
	constructor() {
		this.items = [];
	}
	// 入栈
	push(ele){
		this.items.push(ele);
	}
	// 出栈
	pop() {
		return this.items.pop();
	}
	// 获取栈顶元素
	peek(){
		return this.items[this.items.length - 1];
	}
	// 查看栈是否为空
	isEmpty(){
		return this.items.length === 0;
	}
	// 查看栈中元素个数
	size(){
		return this.items.length;
	}
	// 清空栈
	clear(){
		this.items = []
	}
}
// 存储顶点和边
class Graph {
	constructor() {
		// 顶点可以使用数组存储
		this.vertiecs = [];
		// 存储全部的边
		this.edgeList = {};
		/*
			A:['B','C','D']
		*/
	}
	addVerTex(v) {
		// 添加顶点
		this.vertiecs.push(v);
		// 存储边
		this.edgeList[v] = [];
	}
	// 添加边
	addEdge(a,b){
		// 无向图,实现:A与B相连接的同时,需要去实现B与A相连接
		this.edgeList[a].push(b);
		this.edgeList[b].push(a);
	}
	// 添加toString
	toString() {
		let rst = '';
		for(let i = 0; i<this.vertiecs.length; i++){
			// 顶点
			let vertex = this.vertiecs[i];
			rst += `${vertex}=>`;
			// 边
			let egde = this.edgeList[vertex];
			for(let j=0; j<egde.length; j++){
				rst += egde[j];
			}
			rst += '\n';
		}
		return rst;
	}
	// 初始化颜色
	/*
		- 白色(未发现)
		- 灰色(已经发现)
		- 黑色(已经探索)
	*/
	initColor(){
		let colors = {};
		for(let i=0; i<this.vertiecs.length; i++){
			// 所有的顶点颜色设置为白色
			colors[this.vertiecs[i]] = 'white';
		}
		return colors;
	}
	// 广度优先
	bfs(v,callback){
		// 将全部的顶点都设置为白色
		let color = this.initColor();
		// 创建队列
		let queue = new Queue();
		// 从 v 开始遍历
		queue.enqueue(v);
		// 所有的回溯点设置为null
		let prev = {};
		for(let i=0; i<this.vertiecs.length; i++){
			prev[this.vertiecs[i]] = null;
		}

		// 从队列中依次取出和放入数据
		while(!queue.isEmpty()){
			// A 出列
			const qVertex = queue.dequeue();
			// 获取 A 的所有边
			const edge = this.edgeList[qVertex];
			// 遍历所有的边
			for(let i=0; i<edge.length; i++){
				// 当前顶点
				const e = edge[i];
				if(color[e] === 'white'){
					// 未发现的顶点全部入列
					color[e] = 'gray';
					// 设置回溯点
					prev[e] = qVertex;
					// 顶点入列
					queue.enqueue(e)
				}
			}
			// A 已经探索,为黑色
			color[qVertex] = 'black';
			if(callback){
				callback(qVertex);
			}
		}
		return prev;
	}
	// 深度优先
	dfs(v,callback){
		const color = this.initColor();
		this.dfsVisit(v,color,callback);
	}
	// 递归实现深度优先
	dfsVisit(v,color,callback){
		// 修改颜色为灰色
		color[v] = 'gray';
		// 已经被访问到了
		if(callback){
			callback(v);
		}
		// 获取所有的边
		let edge = this.edgeList[v];
		// 遍历所有的边
		for(let i=0; i<edge.length; i++){
			// 当前边
			let e = edge[i];
			if(color[e] === 'white'){
				// 递归调用
				this.dfsVisit(e,color,callback);
			}
		}
		// 设置为黑色
		color[v] = 'black';
	}
}

const graph = new Graph();
// 添加顶点
graph.addVerTex('A');
graph.addVerTex('B');
graph.addVerTex('C');
graph.addVerTex('D');
graph.addVerTex('E');
graph.addVerTex('F');
// 添加边
graph.addEdge('A', 'B');
graph.addEdge('B', 'E');
graph.addEdge('B', 'F');
graph.addEdge('A', 'C');
graph.addEdge('C', 'D');
graph.addEdge('A', 'D');

console.log('广度优先算法');
// 调用广度优先算法
graph.bfs('A',(e)=>{
	console.log(e);
})
console.log('深度优先算法');
// 调用深度优先算法
graph.dfs('A',(e)=>{
	console.log(e);
})


const prev = graph.bfs('A');
// 为了验证最短路径再添加一条边,图结构如下图所示,这条边的添加不会影响到上面算法打印的顺序
graph.addEdge('D','F');
// 测试最短路径
const shortPath = (from, to)=>{
	// 当前顶点
	let vertex = to;	// 设置当前顶点,找到当前顶点回溯点
	let stack = new Stack();
	while.(vertex!==from){
		stack.push(vertex);
		vertex = prev[vertex];	// 	寻找自己的回溯点
	}
	stack.push(vertex);
	let path = '';
	while(!stack.isEmpty()){
		path += `${stack.pop()}=>`;
	}
	path = path.slice(0,path.length-2);
	return path;
}
console.log("A 到 F 的最短路径",shortPath('A','F'))
console.log("A 到 D 的最短路径",shortPath('A','D'))
  • 最短路径图

在这里插入图片描述

  • 总运行效果:

在这里插入图片描述

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

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

相关文章

【Pandas数据处理100例】(一百):Pandas中使用filter()过滤器实现数据筛选

前言 大家好,我是阿光。 本专栏整理了《Pandas数据分析处理》,内包含了各种常见的数据处理,以及Pandas内置函数的使用方法,帮助我们快速便捷的处理表格数据。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmPandas版本:1.3.5N…

Oracle的学习心得和知识总结(八)|Oracle数据库PL/SQL语言GOTO语句技术详解

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Guid…

Java常见漏洞——整数溢出漏洞、硬编码密码漏洞、不安全的随机数生成器

目录 前言&#xff1a; &#xff08;一&#xff09;整数溢出漏洞 0x01 整数溢出漏洞介绍 1.1 上界溢出 1.2 下界溢出 0x02 整数溢出漏洞修复 &#xff08;二&#xff09;硬编码密码漏洞 修复案例&#xff1a; &#xff08;三&#xff09;不安全的随机数生成器 前言&a…

1、Shell 概述

文章目录1、Shell 概述1.1 Linux 提供的 Shell 解析器有1.2 bash 和 sh 的关系1.3 Centos 默认的解析器是 bash尚硅谷2022版Linux扩展篇Shell教程-讲师&#xff1a;武晟然 壁立千仞 无欲则刚 1、Shell 概述 硬件–>操作系统核心&#xff08;Linux内核&#xff09;–>解释…

ubuntu2004 有线与另一个Ubuntu系统通信

在Ubuntu2004&#xff08;从机&#xff09;打开一个终端&#xff0c;输入如下配置有线网络ip&#xff0c;其中eth0 为有线网络的名称,up使能有线网络eth0&#xff1a; ifconfig eth0 192.169.10.2 up 并在.bashrc文件中输入 export ROS_MASTER_URIhttp://192.169.10.1:11311 …

Java多线程之线程池

Java高并发应用开发过程中会频繁的创建和销毁线程&#xff0c;为了节约成本和提升性能&#xff0c;往往会使用线程池来统一管理线程&#xff0c;使用线程池主要有以下几点优势 降低资源消耗&#xff1a;重复利用已创建的线程降低线程创建和销毁造成的消耗 提高响应速度&#xf…

ImageNet classification with deep convolutional neural networks

使用深度卷积神经网络进行ImageNet图像分类 目录 1.引言 2.网络结构 2.1 小细节 2.2 代码部分 3. 创新点 3.1 非线性激活函数ReLU&#xff08;提速&#xff09; 3.2 多GPU训练&#xff08;提速&#xff09; 3.3局部响应归一化&#xff08;增强泛化能力&#xff0c;已不…

我国天宫空间站以及各个仓位介绍

一、天宫空间站 天宫空间站&#xff08;China Space Station&#xff09;是中国从2021年开始建设的一个模块化空间站系统&#xff0c;为人类自1986年的和平号空间站及1998年的国际空间站后所建造的第三座大型在轨空间实验平台&#xff0c;基本构型由天和核心舱、问天实验舱和梦…

Head First设计模式(阅读笔记)-07.适配器模式

火鸡冒充鸭子 现在缺少一个绿头鸭对象&#xff0c;需要用野生火鸡对象冒充一下&#xff0c;但是二者的接口都不一样该怎么去冒充呢&#xff1f; // 鸭子接口 public interface Duck{public void quack(); // 呱呱叫public void fly(); // 飞行 } // 火鸡接口 public interfac…

应力奇异,你是一个神奇的应力!

在用ANSYS进行压力容器应力分析计算的时候&#xff0c;总会出现一些应力集中的问题&#xff0c;而且&#xff0c;有些应力集中点竟然没办法采用倒圆角的办法消除&#xff0c;采用网格加密方法时&#xff0c;甚至应力值比之前更大。这个情况&#xff0c;大家通常称为应力奇异。 …

springboot-mybatisplus-redis二级缓存

前言 mybatis可以自己带有二级缓存的实现&#xff0c;这里加上redis是想把东西缓存到redis中&#xff0c;而不是mybaits自带的map中。这也就构成了我们看到的springboot mybatisplus redis实现二级缓存的题目。 具体步骤如下&#xff1a; 首先加入需要的依赖 <dependenc…

《InnoDB引擎六》InnoDB 1.0.x版本之前的Master Thread

Master Thread 工作方式 在后台线程中提到&#xff0c;Master Thread是核心的后台线程。InnoDB存储引擎的主要工作都是在一个单独线程中完成的。 InnoDB 1.0.x版本之前的Master Thread Master Thread具有最高的线程优先级别。内部由多个循环组成&#xff1a;主循环(loop)、后台…

[附源码]SSM计算机毕业设计医院仪器设备管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于萤火虫算法优化的BP神经网络预测模型(Matlab代码实现)

目录 1 概述 2 萤火虫算法 3 萤火虫算法优化BP神经网络的算法设计 3.1 基本思想 3.2 萤火虫算法优化BP神经网络算法 4 运行结果 5 参考文献 6 Matlab代码及文章 1 概述 现实的世界中混沌现象无处不在,大至宇宙,小到基本粒子,都受到混沌理论支配.如气候变化会出 现混沌…

(三)DepthAI-python相关接口:OAK Nodes

消息快播&#xff1a;OpenCV众筹了一款ROS2机器人rae&#xff0c;开源、功能强、上手简单。来瞅瞅~ 编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查…

[MySQL]-压力测试_TPCC-MySQL

[MySQL]-压力测试_TPCC-MySQL 森格 | 2022年10月 对数据库学习来说&#xff0c;压力测试也是十分必要的一环&#xff0c;本文章主要介绍了TPCC-MySQL这个工具的使用。 一、基本概念 1.1 基准测试VS压力测试 基准测试&#xff1a; 直接简单、易于比较&#xff0c;用于评估服务…

CAD必练图形

这次我们用CAD梦想画图软件绘制一个CAD新手必练图形&#xff0c;它用到的有CAD矩形、直线、圆弧、等分等命令结合起来完成绘制的&#xff0c;可以跟着一起操作一下。 目标图形 操作步骤 1.使用CAD矩形命令&#xff08;快捷键&#xff1a;REC&#xff09;绘制一个长80宽30的矩…

为什么macbook不能删除u盘里东西?苹果电脑如何删除u盘文件

为什么macbook不能删除u盘里东西&#xff1f;有时候&#xff0c;我们会发现U盘不能够在Mac上正常使用&#xff0c;只能够读取U盘上的文件数据&#xff0c;但是若想要对其进行删除或者是编辑操作&#xff0c;完全不能够实现&#xff0c;本文为大家详细介绍了不同情况的不同解决方…

云原生主题学习月|共同学习全球领先的亚马逊云科技云原生课程,组团共学拿奖励~

CSDN 已上线亚马逊云科技超过 60 门中文数字化培训课程&#xff0c;希望为学习者提供亚马逊云科技基础技能知识和最佳实践。 每门课程时长从十分钟到几小时不等&#xff0c;由亚马逊云科技专家打造&#xff0c;其中包括最受欢迎的《亚马逊云科技云从业者必修知识》&#xff0c…

vue3发送验证码倒计时 (防止连点、封装复用)

一、实现思路 倒计时 流程图二、实现一个简单的验证码倒计时 //倒计时初始变量 const codeNum ref(60); // 定时器id let clearId: number; // 发送验证码 const sendCode async () > { // 防止下次点击 如果倒计时的时间不是60 就不执行下面逻辑if (codeNum.value ! 60)…