【数据结构 | 图论】如何用链式前向星存图(保姆级教程,详细图解+完整代码)

一、概述

链式前向星是一种用于存储图的数据结构,特别适合于存储稀疏图,它可以有效地存储图的边和节点信息,以及边的权重。

它的主要思想是将每个节点的所有出边存储在一起,通过数组的方式连接(类似静态数组实现链表)。这种方法的优点是存储空间小,查询速度快,尤其适合于处理大规模的图数据,在一些笔试或者竞赛的场景中经常使用

下面,我们用这张图来图解一下链式前向星的存储逻辑:

在这里插入图片描述

二、前置准备

注意看这里的设定,以及我加粗的提示。

  1. head数组:head[i]存储的是节点i的第一条边的编号。这样,我们可以通过head[i]快速找到从节点i出发的所有边。

  2. next数组:next[j]存储的是编号为j的边的下一条边的编号。这样,我们可以通过next[j]快速找到从同一个节点出发的下一条边。

  3. to数组:to[j]存储的是编号为j的边的终点节点编号。这样,我们可以通过to[j]快速找到边j的终点,也就是这条边要去往哪里。

  4. weight数组:weight[j]存储的是编号为j边的权重。这样,我们可以通过weight[j]快速找到边j的权重。

  5. cnt变量:cnt用于存储边的数量,也表示边的编号。每添加一条边,cnt就会增加1。这样,我们可以通过cnt快速知道当前图中边的数量,同时我们也认为cnt是新添加边的编号

三、初始化

public static void build(int n) {cnt = 1; // 边从1开始编号Arrays.fill(head, 1, n + 1, 0); // head[1 ... n] 全设为 0
}

在链式前向星中,我们使用cnt来作为边的编号,由于边的编号是从1开始的,所以初始化时我们将cnt设置为1。同时,将head数组的所有元素设置为0。因为head[i]存储的是节点i的第一条边的编号,所以,如果节点i没有出度(即没有从节点i出发的边),那么head[i]就应该为0。初始化时所有节点都没有出度,后续在添加边的时候,会更新对应的head[i]的值。

在这里插入图片描述

四、添加边(重点)

在链式前向星中添加边的操作是最核心的,它涉及到headnexttoweight数组的更新,以及边的编号cnt的自增。

在看代码之前,我们先回顾一下各个结构的下标以及值的含义:

  1. head数组:下标i表示节点编号,值head[i]表示从节点i出发的第一条边的编号。

  2. next数组:下标j表示边的编号,值next[j]表示编号为j的边的下一条边的编号。

  3. to数组:下标j表示边的编号,值to[j]表示编号为j的边的终点节点编号。

  4. weight数组:下标j表示边的编号,值weight[j]表示编号为j的边的权重。

结合上述含义,我们来看代码就很清晰了:

// (u, v, w): 有一条边,从u节点指向v节点,权重为w
// 在每一次添加边时,cnt都表示当前未分配的边的编号,添加边后cnt需++
public static void addEdge(int u, int v, int w) {next[cnt] = head[u];to[cnt] = v;weight[cnt] = w;head[u] = cnt;++cnt;
}

首先,我们需要更新next数组。next[cnt]存储的是编号为cnt的边的下一条边的编号。在添加新边时,我们将新边的next置为旧的头边号head[u],这样就可以通过next[cnt]快速找到从节点u出发的下一条边。

然后,我们需要更新to数组,将新边的终点设置为v,这样就可以通过to[cnt]快速找到边cnt的终点。

更新weight数组也很自然,就是将新边的权重设置为w,最后,我们将节点u的头边号修改为当前新边的编号,这样就可以通过head[u]快速找到从节点u出发的第一条边。

备注:记得每添加一条边,边的编号cnt就需要增加1

五、建图

建图分为有向图与无向图,输入的参数是一个二维数组edges作为输入,这个数组的每个元素都是一个长度为3的数组,代表一条边的两个端点和这条边的权重。

// 建有向图
public static void directGraph(int[][] edges) {for (int[] edge : edges) {addEdge(edge[0], edge[1], edge[2]); // 添加有向边}
}// 建无向图
public static void undirectGraph(int[][] edges) {for (int[] edge : edges) {addEdge(edge[0], edge[1], edge[2]); // 添加边addEdge(edge[1], edge[0], edge[2]); // 添加反向边}
}

六、图解

下面这个数组提供了图的边信息,基本上题目都会给定形式的信息,让你去建图:

有一条边(u, v, w),表示从u节点指向v节点,权重为w
[[1, 6, 2],[1, 3, 57],[1, 4, 61],[2, 3, 100],[3, 5, 34],[4, 5, 13],
]

这里 u,v,w 的含义以及顺序应根据具体题目具体分析,这里的设定是(u, v, w)表示一条边从u节点指向v节点,权重为w

// 添加边:
public static void addEdge(int u, int v, int w) {next[cnt] = head[u];to[cnt] = v;weight[cnt] = w;head[u] = cnt;++cnt;
}

下面我们图解一下,在链式前向星中,依次添加6条边到有向图中的逻辑。

在这里插入图片描述

如果看不懂,建议返回上面去看各个数组的下标以及值的含义。

添加边 {1, 6, 2}

  • head[1] = 1:节点1的第一条边的编号是1。
  • next[1] = 0:边1没有下一条边。
  • to[1] = 2:边1的终点是节点2。
  • weight[1] = 6:边1的权重是6。
  • cnt:2,表示当前边的数量是1,下一条边的编号是2。

在这里插入图片描述

添加边 {1, 3, 57}

  • head[1] = 2:节点1的第一条边的编号是2。
  • next[2] = 1:边2的下一条边是边1。
  • to[2] = 3:边2的终点是节点3。
  • weight[2] = 57:边2的权重是57。
  • cnt:3,表示当前边的数量是2,下一条边的编号是3。

在这里插入图片描述

添加边 {1, 4, 61}

  • head[1] = 3:节点1的第一条边的编号是3。
  • next[3] = 2:边3的下一条边是边2。
  • to[3] = 4:边3的终点是节点4。
  • weight[3] = 61:边3的权重是61。
  • cnt:4,表示当前边的数量是3,下一条边的编号是4。

在这里插入图片描述

添加边 {2, 3, 100}

  • head[2] = 4:节点2的第一条边的编号是4。
  • next[4] = 0:边4没有下一条边。
  • to[4] = 3:边4的终点是节点3。
  • weight[4] = 100:边4的权重是100。
  • cnt:5,表示当前边的数量是4,下一条边的编号是5。

在这里插入图片描述

添加边 {3, 5, 34}

  • head[3] = 5:节点3的第一条边的编号是5。
  • next[5] = 0:边5没有下一条边。
  • to[5] = 5:边5的终点是节点5。
  • weight[5] = 34:边5的权重是34。
  • cnt:6,表示当前边的数量是5,下一条边的编号是6。

在这里插入图片描述

添加边 {4, 5, 13}

  • head[4] = 6:节点4的第一条边的编号是6。
  • next[6] = 0:边6没有下一条边。
  • to[6] = 5:边6的终点是节点5。
  • weight[6] = 13:边6的权重是13。
  • cnt:7,表示当前边的数量是6,下一条边的编号是7。

在这里插入图片描述

七、遍历图

遍历图的逻辑也不难理解,就是对于每个节点,遍历其所有的邻居,根据next数组不断去拿到和每个节点连接的边的编号,直到没有邻居节点为止,一步步跳着找嘛。

步骤如下:

  • 对于每个节点,通过head数组找到该节点的第一条边。
  • 通过next数组找到下一条边,直到next数组的值为0,表示没有更多的边。
  • 在遍历过程中,可以通过toweight数组获取边的终点和权重。

我们用打印邻居节点的方式来验证遍历的结果:

public static void traversal(int n) {StringBuilder sb = new StringBuilder();sb.append("链式前向星遍历,u: (v, w)表示u有一条边前往v,权重为w\n");for (int i = 1; i <= n; i++) {sb.append("[").append(i).append("]: ");for (int ei = head[i]; ei > 0; ei = next[ei]) {sb.append("(").append(to[ei]).append(",").append(weight[ei]).append(") "); // 输出边的终点和权重}sb.append("\n");}System.out.println(sb.toString()); // 打印结果
}

八、完整代码

package cn.zhengyiyi;import java.util.Arrays;public class Main {public static int N = 11;public static int M = 21; /*** 编号为 i 的节点,其第一条边的编号为 head[i]* 备注:如果 head[i] 为0,说明没有一条边从节点 i 出发*/public static int[] head = new int[N];/*** 编号为 i 的边,它的下一条边是 next[i],*/public static int[] next = new int[M];/*** 编号为 i 的边,前往的节点是 to[i],也就是第 i 条边的终点是 to[i]*/public static int[] to = new int[M];/*** 编号为 i 的边,权重是 weight[i]*/public static int[] weight = new int[M];/***  记录边的数量,初始时值为 1*/public static int cnt;// 初始化链式前向星public static void build(int n) {cnt = 1; // 边从1开始编号Arrays.fill(head, 1, n + 1, 0); // head[1 ... n] 全设为 0}// 添加一条边:(u->v,权重为w)public static void addEdge(int u, int v, int w) {// 1. 更新next数组,将新边的next置为旧的头边号head[u],方便后续跳到旧的头边号next[cnt] = head[u];// 2. 更新to数组,设置当前新边的终点为vto[cnt] = v; // 3. 更新weight数组,设置当前边的权重wweight[cnt] = w;// 4. 更新head数组,将原先的头边号修改为当前新边head[u] = cnt;// 5. 最后边的编号要自增++cnt;}// 建立有向图public static void directGraph(int[][] edges) {for (int[] edge : edges) {addEdge(edge[0], edge[1], edge[2]); // 添加有向边}}// 建立无向图public static void undirectGraph(int[][] edges) {for (int[] edge : edges) {addEdge(edge[0], edge[1], edge[2]); // 添加边addEdge(edge[1], edge[0], edge[2]); // 无向图需要添加反向边}}// 遍历图public static void traversal(int n) {StringBuilder sb = new StringBuilder();sb.append("链式前向星遍历,u: (v, w)表示u有一条边前往v,权重为w\n");for (int i = 1; i <= n; i++) {sb.append("[").append(i).append("]: ");for (int ei = head[i]; ei > 0; ei = next[ei]) {sb.append("(").append(to[ei]).append(",").append(weight[ei]).append(") "); // 输出边的终点和权重}sb.append("\n");}System.out.println(sb.toString()); // 打印结果}public static void main(String[] args) {int n = 5; // 节点数build(n); // 初始化int[][] directEdges = { // 有向图的边{ 1, 6, 2 },{ 1, 3, 57 },{ 1, 4, 61 },{ 2, 3, 100 },{ 3, 5, 34 },{ 4, 5, 13 }};directGraph(directEdges); // 建立有向图traversal(n); // 遍历有向图}
}

运行结果:

链式前向星遍历,u: (v, w)表示u有一条边前往v,权重为w
[1]: (4,61) (3,57) (6,2) 
[2]: (3,100) 
[3]: (5,34) 
[4]: (5,13) 
[5]: 

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloudConfig 使用git搭建配置中心

一 SpringCloudConfig 配置搭建步骤 1.引入 依赖pom文件 引入 spring-cloud-config-server 是因为已经配置了注册中心 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</…

Elasticsearch从入门到精通-07ES底层原理学习

Elasticsearch从入门到精通-07ES底层原理和高级功能 &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是程序员行走的鱼 &#x1f4d6; 本篇主要介绍和大家一块学习一下ES底层原理包括集群原理、路由原理、分配控制、分配原理、文档分析原理、文档并发安全原理以及一些高…

SQL-CRUD-2数据库实验

目录 第一关任务描述 相关知识 插入完整内容的行 插入选定内容的行 编程要求 测试说明 第一关代码 第二关任务描述 相关知识 删除表中的指定行 删除表中的所有行 编程要求 测试说明 第二关代码 第三关任务描述 相关知识 更新表中的指定行 编程要求 测试说明…

【多线程系列】你先说说synchronized的实现原理

面试官&#xff1a;听说你精通多线程&#xff0c;那我就考考你吧 面试官&#xff1a;不用慌尽管说&#xff0c;错了也没关系&#x1f60a;。。。 以贴近现实的【面试官面试】形式来分享技术&#xff0c;本期是《多线程系列》&#xff0c;感兴趣就关注我吧❤️ 面试官&#xff1…

L2-047 锦标赛

这题没做出来&#xff0c;查了一些博客&#xff0c;下面是我比较能接受的理解和书写方式。 读完题可以发现这是一个满二叉树&#xff0c;并且可以得到每场比赛失败者的信息&#xff08;决赛是胜利者和失败者都可以得到&#xff09; 对于一场比赛&#xff0c;它的胜利者要么是左…

Typora结合PicGo + Github搭建个人图床

目录 一 、GitHub仓库设置 1、新建仓库 2、创建Token 并复制保存 二、PicGo客户端配置 1、下载 & 安装 2、配置图床 三、Typora配置 一 、GitHub仓库设置 1、新建仓库 点击主页右上角的 号创建 New repository 填写仓库信息 2、创建Token 并复制保存 点击右上角…

JAVA的NIO和BIO底层原理分析

文章目录 一、操作系统底层IO原理1. 简介2. 操作系统进行IO的流程 二、BIO底层原理1. 什么是Socket2. JDK原生编程的BIO 三、Java原生编程的NIO1. 简介2. NIO和BIO的主要区别3. Reactor模式4. NIO的三大核心组件5. NIO核心源码分析 一、操作系统底层IO原理 1. 简介 IO&#x…

Python工具箱系列(五十一)

九宫格与词云 对图片进行九宫格切割&#xff0c;并且放到微信朋友圈曾经风靡一时。对于python来说&#xff0c;这个也非常简单。 from PIL import Image import mathdef ninerectanglegrid(inputfilename):"""实现九宫格切割Args:inputfilename (string): 输入…

【Vue3进阶】- 第2学堂小商城实战课程前言

该教程为进阶教程&#xff0c;如果你还不了解Vue3的基础知识&#xff0c;可以先前往Vue3基础教程&#xff0c;从入门到实战。 学习时遇到的任何疑问都欢迎在相应课文页面下方的问答区进行提问哦 我能学到什么&#xff1f; 编程写法千千万&#xff0c;实现需求是第一。 教程中…

Python+Django+Yolov5路面墙体桥梁裂缝特征检测识别html网页前后端

程序示例精选 PythonDjangoYolov5路面墙体桥梁裂缝特征检测识别html网页前后端 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjangoYolov5路面墙体桥梁裂缝特征检测识别html网页前…

List操作add,clear,addall报错UnsupportedOperationException的解决办法

ArrayList和Arrays.ArrayList是两码事 ArrayList 支持 add&#xff0c;clear&#xff0c;addall Arrays.ArrayList不支持add&#xff0c;clear&#xff0c;addall 这个方法的使用时候&#xff0c;传递的数组必须是对象数组&#xff0c;而不是基本数据类型 JDK源码 /** *返回由…

Python-VBA编程500例-024(入门级)

字符串写入的行数(Line Count For String Writing)在实际应用中有着广泛的应用场景。常见的应用场景有&#xff1a; 1、文本编辑及处理&#xff1a;在编写或编辑文本文件时&#xff0c;如使用文本编辑器或文本处理器&#xff0c;经常需要处理字符串并确定其在文件中的行数。这…

Numpy 初体验

文章目录 第1关&#xff1a;Numpy 创建数组第2关&#xff1a;Numpy 数组的基本运算第3关&#xff1a;Numpy 数组的切片与索引第4关&#xff1a;Numpy 数组的堆叠第5关&#xff1a;Numpy 的拆分 第1关&#xff1a;Numpy 创建数组 编程要求 本关的任务是&#xff0c;补全右侧编辑…

深度学习 - PyTorch基本流程 (代码)

直接上代码 import torch import matplotlib.pyplot as plt from torch import nn# 创建data print("**** Create Data ****") weight 0.3 bias 0.9 X torch.arange(0,1,0.01).unsqueeze(dim 1) y weight * X bias print(f"Number of X samples: {len(…

小狐狸JSON-RPC:钱包连接,断开连接,监听地址改变

detect-metamask 创建连接&#xff0c;并监听钱包切换 一、连接钱包&#xff0c;切换地址&#xff08;监听地址切换&#xff09;&#xff0c;断开连接 使用npm安装 metamask/detect-provider在您的项目目录中&#xff1a; npm i metamask/detect-providerimport detectEthereu…

基于Java在线考试系统系统设计与实现(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

快速上手Spring Cloud 六:容器化与微服务化

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

Qt_day4:2024/3/25

作业1&#xff1a; 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和…

新能源充电桩站场AI视频智能分析烟火检测方案及技术特点分析

新能源汽车充电起火的原因多种多样&#xff0c;涉及技术、设备、操作等多个方面。从技术层面来看&#xff0c;新能源汽车的电池管理系统可能存在缺陷&#xff0c;导致电池在充电过程中出现过热、短路等问题&#xff0c;从而引发火灾。在设备方面&#xff0c;充电桩的设计和生产…

吴恩达深度学习笔记:浅层神经网络(Shallow neural networks)3.9-3.11

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第三周&#xff1a;浅层神经网络(Shallow neural networks)3.9 神 经 网 络 的 梯 度 下 降 &#xff08; Gradient descent for neural networks&#xff09; 第一门课&#xff1a;神经网络和…