图的定义是什么?JS如何表示图结构

图在JavaScript中常用邻接表表示,适合稀疏图和动态操作,邻接矩阵适用于顶点固定且边密集的场景,边列表则用于特定算法;实际应用如社交网络、导航和推荐系统均依赖图结构。

图的定义是什么?js如何表示图结构

图,简单来说,就是由一些“点”(我们称之为顶点或节点)和连接这些点的“线”(我们称之为边)构成的抽象结构。它最核心的作用是用来描述事物之间的关系。在JavaScript中,表示图结构最常见也最灵活的方式是使用邻接表(Adjacency List),当然,邻接矩阵(Adjacency Matrix)和边列表(Edge List)也是可选的方案,具体用哪种,得看你的实际需求和图的特性。

解决方案

在JavaScript中表示图结构,我个人最常用且推荐的是邻接表。它本质上是一个映射(Map或Object),每个键代表一个顶点,而对应的值通常是一个数组或Set,里面存储着与该顶点直接相连的所有邻居顶点。这种方式对于稀疏图(边相对较少)来说,在空间效率上表现出色。

1. 邻接表 (Adjacency List)

class Graph {    constructor() {        this.adjList = new Map(); // 使用Map来存储邻接表,键是顶点,值是Set(方便快速去重和查找)    }    addVertex(vertex) {        if (!this.adjList.has(vertex)) {            this.adjList.set(vertex, new Set()); // 新增顶点,并初始化一个空的邻居集合        }    }    addEdge(vertex1, vertex2, isDirected = false) {        // 确保两个顶点都存在        if (!this.adjList.has(vertex1)) {            this.addVertex(vertex1);        }        if (!this.adjList.has(vertex2)) {            this.addVertex(vertex2);        }        this.adjList.get(vertex1).add(vertex2); // 添加从vertex1到vertex2的边        if (!isDirected) { // 如果是无向图,需要双向添加            this.adjList.get(vertex2).add(vertex1);        }    }    removeVertex(vertex) {        if (!this.adjList.has(vertex)) return;        // 移除所有指向该顶点的边        for (let [v, neighbors] of this.adjList.entries()) {            neighbors.delete(vertex);        }        // 移除顶点自身        this.adjList.delete(vertex);    }    removeEdge(vertex1, vertex2, isDirected = false) {        if (this.adjList.has(vertex1) && this.adjList.get(vertex1).has(vertex2)) {            this.adjList.get(vertex1).delete(vertex2);        }        if (!isDirected && this.adjList.has(vertex2) && this.adjList.get(vertex2).has(vertex1)) {            this.adjList.get(vertex2).delete(vertex1);        }    }    getNeighbors(vertex) {        return this.adjList.get(vertex) || new Set();    }    printGraph() {        for (let [vertex, neighbors] of this.adjList.entries()) {            console.log(`${vertex} -> ${[...neighbors].join(', ')}`);        }    }}// 示例使用// const myGraph = new Graph();// myGraph.addVertex('A');// myGraph.addVertex('B');// myGraph.addEdge('A', 'B');// myGraph.addEdge('B', 'C');// myGraph.addEdge('A', 'C', true); // 有向边 A -> C// myGraph.printGraph();/*A -> B, CB -> A, CC -> B*/

2. 邻接矩阵 (Adjacency Matrix)

当图的顶点数量固定且边非常密集时,邻接矩阵可能是一个不错的选择。它使用一个二维数组

matrix[i][j]

来表示顶点

i

和顶点

j

之间是否存在边(通常用1表示有边,0表示无边,或者存储边的权重)。

// 假设顶点是0到n-1的数字class AdjacencyMatrixGraph {    constructor(numVertices) {        this.numVertices = numVertices;        this.matrix = Array(numVertices).fill(0).map(() => Array(numVertices).fill(0));    }    addEdge(v1, v2, weight = 1, isDirected = false) {        if (v1 = this.numVertices || v2 = this.numVertices) {            console.error("Invalid vertex index.");            return;        }        this.matrix[v1][v2] = weight;        if (!isDirected) {            this.matrix[v2][v1] = weight;        }    }    hasEdge(v1, v2) {        if (v1 = this.numVertices || v2 = this.numVertices) {            return false;        }        return this.matrix[v1][v2] !== 0;    }    getWeight(v1, v2) {        if (this.hasEdge(v1, v2)) {            return this.matrix[v1][v2];        }        return null;    }    // ... 其他操作,比如移除边、获取邻居等,都围绕这个二维数组展开}

3. 边列表 (Edge List)

最简单粗暴的方式,就是直接列出所有的边,每条边通常表示为一个包含两个顶点(和可能的权重)的数组。这种方式在图算法的某些特定阶段可能会用到,比如Kruskal算法(需要对边进行排序),但对于图的遍历和邻居查询则效率较低。

// const edgeList = [//     ['A', 'B'],//     ['B', 'C'],//     ['A', 'C', { weight: 5, type: 'friend' }] // 也可以存储额外信息// ];

图在现实世界中到底有什么用?为什么它这么重要?

说实话,我一直觉得图是计算机科学里最优雅也最实用的抽象之一,它把复杂的关系网变得一目了然。你可能每天都在和图打交道,只是没意识到。比如,我们用的各种社交网络,像微信朋友圈、微博关注,它们的关系链条就是典型的图结构,每个人是一个节点,关注或好友关系就是边。推荐系统也是图的天下,它会分析你和商品、你和朋友、朋友和商品之间的关系,然后给你推荐可能喜欢的东西。

再比如,导航系统,从A点到B点怎么走最快?这不就是找图上最短路径的问题吗?每个路口是节点,每段路是边,边的权重可以是距离或时间。甚至我们日常的项目管理,任务之间的依赖关系,哪个任务必须先完成,哪个可以并行,这都可以用图来清晰地表示和调度。还有软件工程里的依赖管理(比如npm包之间的依赖),编译器的AST(抽象语法树),数据库的关系模型,这些背后都有图论的影子。理解图,就像是掌握了一种描述世界底层逻辑的强大工具

邻接表和邻接矩阵,我到底该选哪个?

这是一个很实际的问题,我在做项目时也经常权衡。其实没有绝对的“最好”,只有“最适合”。

邻接表的优势在于:

空间效率高: 对于稀疏图(边数远小于顶点数的平方),它只存储实际存在的边,所以占用的内存更少。想一下,如果一个图有1000个节点,但每人只认识10个朋友,用邻接矩阵就需要1000×1000的二维数组,而邻接表只存储1000个节点和1000×10个边。动态性好: 添加或删除顶点、边都相对灵活,不需要像邻接矩阵那样频繁调整大小或重建。遍历邻居快: 获取一个顶点的所有邻居非常直接,直接访问对应的列表即可。

但它的缺点是:

判断两点间是否有边稍慢: 需要遍历一个顶点的邻居列表,最坏情况下是O(度)的时间复杂度。

邻接矩阵的优势在于:

查询效率高: 判断任意两点之间是否有边,是O(1)的操作,直接访问

matrix[i][j]

就行。实现简单: 对于固定数量的顶点,用二维数组表示直观且易于实现。

它的缺点是:

空间效率低: 对于稀疏图,它会存储大量的0,造成空间浪费。动态性差: 如果需要添加或删除顶点,通常需要重新构建整个矩阵,这在JS中尤为麻烦。

我的选择偏好: 通常情况下,我个人更倾向于使用邻接表。原因很简单,大多数实际应用中的图都是稀疏的,而且对动态性有要求。比如社交网络,你不可能预先知道会有多少用户,以及用户之间会有多少边。只有当你明确知道图的顶点数量是固定的,并且图非常密集(比如完全图),或者你需要频繁地执行“查询任意两点间是否有边”这种操作时,我才会考虑邻接矩阵。

实际操作中,如何用JS实现图的常见操作?

图的基本操作除了上面提到的添加/删除顶点和边,最核心的莫过于图的遍历了。理解遍历是掌握图算法的关键一步,因为很多复杂的图问题(比如最短路径、连通分量)都是在遍历的基础上进行的。

1. 广度优先搜索 (BFS – Breadth-First Search)

BFS就像在水面上扩散的波纹,它会一层一层地访问节点,先访问起始节点的所有邻居,再访问这些邻居的邻居,以此类推。它通常用于寻找最短路径(在无权图中)或者遍历所有可达节点。

// 基于前面定义的Graph类Graph.prototype.bfs = function(startVertex) {    const visited = new Set();    const queue = [startVertex]; // 使用数组模拟队列    visited.add(startVertex);    while (queue.length > 0) {        const currentVertex = queue.shift(); // 取出队列头部元素        console.log(`Visited: ${currentVertex}`); // 访问当前节点        for (const neighbor of this.getNeighbors(currentVertex)) {            if (!visited.has(neighbor)) {                visited.add(neighbor);                queue.push(neighbor); // 将未访问的邻居加入队列            }        }    }};// 示例:// const bfsGraph = new Graph();// bfsGraph.addEdge('A', 'B');// bfsGraph.addEdge('A', 'C');// bfsGraph.addEdge('B', 'D');// bfsGraph.addEdge('C', 'E');// bfsGraph.addEdge('D', 'F');// bfsGraph.bfs('A'); // 输出顺序可能是 A, B, C, D, E, F

2. 深度优先搜索 (DFS – Depth-First Search)

DFS则像是在迷宫里探险,它会尽可能深地探索一条路径,直到无路可走,然后回溯,再探索另一条路径。DFS通常用于检测环、拓扑排序、寻找连通分量等。

// 基于前面定义的Graph类Graph.prototype.dfs = function(startVertex) {    const visited = new Set();    const _dfs = (vertex) => {        visited.add(vertex);        console.log(`Visited: ${vertex}`); // 访问当前节点        for (const neighbor of this.getNeighbors(vertex)) {            if (!visited.has(neighbor)) {                _dfs(neighbor); // 递归访问未访问的邻居            }        }    };    _dfs(startVertex);};// 示例:// const dfsGraph = new Graph();// dfsGraph.addEdge('A', 'B');// dfsGraph.addEdge('A', 'C');// dfsGraph.addEdge('B', 'D');// dfsGraph.addEdge('C', 'E');// dfsGraph.addEdge('D', 'F');// dfsGraph.dfs('A'); // 输出顺序可能是 A, B, D, F, C, E (取决于邻居的迭代顺序)

实现这些操作时,通常会用到一个

visited

集合来跟踪已经访问过的节点,避免无限循环(尤其是在有环图中)。而队列(BFS)和递归调用栈(DFS)是实现这两种遍历的核心机制。实际项目里,你可能还会遇到带权图(边有权重)、有向图(边有方向)、多重图(两点间有多条边)等更复杂的场景,但核心的表示和遍历思想是相通的,只需在边的存储和遍历逻辑上做相应调整。

以上就是图的定义是什么?JS如何表示图结构的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1517724.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
JS如何实现拓扑图
上一篇 2025年12月20日 10:58:14
js怎么检查一个对象的原型
下一篇 2025年12月20日 10:58:28

相关推荐

  • win8开机提示bootmgr is missing_win8开机BOOTMGR丢失错误修复

    首先使用Windows安装盘修复启动,若无效则通过命令提示符执行bootrec命令重建引导记录,最后检查BIOS中SATA模式设置是否正确。 如果您的Windows 8计算机在启动时显示“BOOTMGR is missing”错误,说明系统无法找到或加载引导管理器文件bootmgr,导致无法继续启动…

    2026年8月29日
    100
  • qq悟空浏览器怎么设置成后台下载视频

    悟空浏览器开启后台播放只需三步:1. 打开悟空浏览器进入视频频道并播放视频;2. 点击播放界面右上角“分享”图标;3. 选择“后台播放”选项即可在切换应用或锁屏后继续听音频,该功能不同于QQ浏览器的视频下载,仅支持在线播放时后台运行,若无法使用需检查视频来源、浏览器版本及系统权限。 QQ浏览器和悟空…

    2026年8月29日
    200
  • 百度浏览器功能全面解析 如何高效使用百度浏览器上网

    百度浏览器的核心功能值得深入挖掘的包括智能框、信息流推荐、内置截图与翻译工具、下载管理、广告拦截及安全防护等。智能框不仅能实现搜索联想与热点推荐,还可根据输入内容预判用户需求,直接提供百科、新闻或服务链接,减少二次搜索;信息流功能通过个性化设置兴趣偏好,可转化为高效的“定制化资讯平台”,提升碎片时间…

    2026年8月29日
    200
  • Listen1怎么修复播放错误_Listen1修复播放错误的解决方法

    首先检查网络连接并确保通畅,接着清除Listen1缓存数据以排除文件损坏影响;然后进入插件管理页面更换音源插件组合,提升资源匹配成功率;若问题仍存,更新Listen1至最新版本以修复兼容性问题;最后可尝试手动修改Hosts文件,绕过域名解析限制,恢复音乐播放功能。 如果您在使用Listen1播放音乐…

    2026年8月29日
    100
  • win8应用商店打不开怎么办 Win8应用商店无法打开修复指南

    首先清除应用商店缓存,若无效则检查系统更新、修复系统文件、启用TLS 1.2协议,最后重注册应用商店组件以恢复功能。 如果您尝试打开Windows 8系统中的应用商店,但应用无法加载或直接闪退,则可能是由于网络连接异常、缓存数据损坏或系统组件故障所致。以下是解决此问题的步骤: 本文运行环境:Dell…

    2026年8月29日
    200
  • CRM无法使用谷歌地图原因_问题诊断与解决策略

    首先检查谷歌地图API密钥配置是否正确,确认权限设置是否允许访问相关服务,若无误则排查CRM系统兼容性问题并尝试更新或联系技术支持。 CRM无法使用谷歌地图,通常是因为API密钥配置错误、权限问题、或者CRM系统与谷歌地图API之间的兼容性问题。解决办法包括检查API密钥、确认权限设置、更新CRM系…

    2026年8月29日
    100
  • MME-CoT— 港中文等机构推出评估视觉推理能力的基准框架

    mme-cot:大型多模态模型链式思维推理能力评估基准 MME-CoT是由香港中文大学(深圳)、香港中文大学、字节跳动、南京大学、上海人工智能实验室、宾夕法尼亚大学和清华大学等机构联合研发的基准测试框架,用于评估大型多模态模型(LMMs)的链式思维(Chain-of-Thought, CoT)推理能…

    2026年8月29日
    100
  • win8开机黑屏只有鼠标 Win8开机后黑屏只显示鼠标指针解决方法

    如果您成功登录Windows 8系统,但桌面无法正常显示,仅能看到鼠标指针在屏幕上移动,这通常意味着Windows资源管理器(Explorer.exe)未能正确启动或已崩溃。以下是解决此问题的步骤: 本文运行环境:联想ThinkPad E14,Windows 8.1。 一、重启Windows资源管理…

    2026年8月29日
    100
  • 小红书比特指纹浏览器是什么 社交平台专用浏览器功能解析

    比特指纹浏览器通过为每个账号生成独立的数字指纹和IP地址,实现多账号环境隔离,有效规避小红书等平台的账号关联与封禁风险。它深度伪装浏览器指纹(如User-Agent、Canvas、WebGL、字体、时区、屏幕分辨率等),结合代理IP和数据隔离技术,使每个账号看似来自不同设备和用户,解决多账号运营中的…

    2026年8月29日
    500
  • B站客户端VIP会员开通渠道有哪些_哔哩哔哩会员多渠道开通介绍

    开通哔哩哔哩大会员可通过APP个人中心、网页端账户管理、手机话费代扣及兑换码四种方式完成,用户可根据习惯选择支付或参与活动获取权益。 AI解答入口:“☞☞☞☞点击夸克AI手把手教你操作☜☜☜☜☜直接使用”; 直接观看“☞☞☞☞☞点击哔哩哔哩B站官网直达☜☜☜☜☜”; 直接观看“☞☞☞☞☞点击免费观看…

    2026年8月29日
    100
  • win10怎么查看硬盘是固态还是机械_win10硬盘类型检测与分辨方法

    1、通过任务管理器可快速识别硬盘类型,显示“固态硬盘”为SSD,“硬盘驱动器”为HDD;2、使用优化驱动器工具查看“媒体类型”列判断;3、设备管理器中根据硬盘型号含“SSD”等关键词识别;4、PowerShell执行Get-PhysicalDisk命令,MediaType字段显示SSD或HDD。 如…

    2026年8月29日
    100
  • 经纬度轮廓缩放算法中NaN值是如何产生的以及如何解决?

    经纬度轮廓缩放算法及NaN值问题详解 本文分析基于经纬度坐标的轮廓缩放算法实现中出现的NaN值问题。该算法需根据给定的经纬度点集,计算缩放后的经纬度坐标。算法流程通常为:将经纬度坐标转换为墨卡托投影坐标;基于向量运算,根据预设缩放距离和角度调整向量;最后将调整后的墨卡托坐标转换回经纬度坐标。 用户使…

    2026年8月29日
    100
  • 从零开始学习UCOSII操作系统1–UCOSII的基础知识

    大家好,我们又见面了,我是你们的朋友全栈君。 从零开始学习UCOSII操作系统1–UCOSII的基础知识 前言: 首先,比较主流的操作系统包括UCOSII、FREERTOS和LINUX等,其中UCOSII的资料相对丰富得多。 更重要的是,我目前还没有能力深入研究Linux操作系统。因此,本次学习UC…

    2026年8月29日
    100
  • 微信怎么查看登录设备记录 微信账号登录设备管理与查询

    首先打开微信点击“我”进入设置,依次选择“账号与安全”中的“登录设备管理”,可查看所有已登录设备;发现陌生设备后点击移除并确认,系统将强制该设备退出;操作前需通过密码或短信验证码等方式完成身份验证,确保账号安全。 如果您发现微信账号存在异常登录情况,或希望管理已授权的设备以保障账户安全,可以通过以下…

    2026年8月29日
    100
  • 再见,微信公众号!

    村长第1088原创 不扯高大上 只讲真实干 感谢关注、评论和转发 各位村民好,我是村长 普通人就不要做公众号了,浪费时间又不赚钱。 最近这一两个月以来,明显感觉到微信公众号越来越难做了。 作为一个普通人,如果还想做公众号,建议你做好充分的思想准备。 尤其是想把公众号当成自己自媒体重要的发展路径的,更…

    2026年8月29日
    100
  • LINUX如何创建一个指定大小的文件_LINUX快速创建指定大小文件方法

    使用dd命令是Linux中创建指定大小文件最常用方法,如dd if=/dev/zero of=largefile bs=1M count=500可创建500MB文件;bs支持b、K、M、G等单位;若无需真实写入,可用truncate -s 1G创建稀疏文件或fallocate -l 500M预分配空…

    2026年8月29日
    100
  • GoogleBard现在叫什么_GoogleBard更名为Gemini详情介绍

    Google将Bard更名为Gemini,标志着其AI战略的全面升级。1. 品牌统一:以Gemini命名核心对话产品,消除用户对技术与产品名混淆的认知障碍;2. 技术整合:底层全面采用Gemini系列模型,从Gemini Nano、Pro到Ultra 1.0,构建覆盖全场景的AI生态;3. 多模态强…

    2026年8月29日
    200
  • 未到期就被断网,百万用户被方正宽带“坑”了?

    未到期就被断网,百万用户被方正宽带“坑”了?未到期就被断网,百万用户被方正宽带“坑”了?未到期就被断网,百万用户被方正宽带“坑”了?未到期就被断网,百万用户被方正宽带“坑”了?

    办理了五年的宽带,还未到期,网就被停了,企业却以破产重整为由,暂时无法提供退费服务,多位用户因此向媒体投诉了方正宽带。 1.百万方正宽带用户被“坑”了? 据媒体报道,北京多位方正宽带用户投诉,他们续了5年的方正宽带,服务还没到期,就接到了宽带公司破产的消息。与方正宽带交涉后,对方给出了两个解决办法,…

    2026年8月29日 用户投稿
    000
  • 微软 Xbox 用 AI 制作招聘广告现低级错误:代码出现在显示器背面

    7 月 15 日消息,在全面推动人工智能(ai)战略的进程中,微软再次因一则由 ai 制作的招聘广告引发公众争议。近日,微软 xbox 图形团队在 linkedin 上发布了一则招聘信息,本意是招募图形驱动开发与游戏视觉优化方面的工程师,但其中出现的一个低级错误——“电脑屏幕倒装”,却招致了外界广泛…

    2026年8月29日
    000
  • thinkpad think book主要区别是什么

    ThinkPad和ThinkBook虽同为兄弟笔记本,但定位不同。ThinkPad专注高端商务,稳定可靠,追求极致性能,价格高昂,如同深度优化的算法。ThinkBook主打性价比和时尚,功能强大,易于上手,价格亲民,类似封装良好的库。选择ThinkPad还是ThinkBook取决于您的需求和预算。 …

    用户投稿 2026年8月29日
    100

发表回复

登录后才能评论
关注微信