链式前向星建图

news2024/11/13 12:49:36
回顾邻接局矩阵和邻接表建图:

​ 在之前的图论基础中,我们提到了两种建图方式:邻接矩阵、邻接表。

邻接矩阵实现:

int N; //所有节点个数
int Graph[N][N];
for(int i : Numbers){  //Numbers表示所有节点
    for(int j : Neighbors) {// Neighbors表示节点i的所有邻接节点
        Graph[i][j] = 1;  // 无权图:Graph[i][j] = 1代表存在边(i,j)
        Graph[i][j] = weight // 有权图:Graph[i][j] = weight 代表存在边(i,j)并且此边权重为weight
    }
}

邻接表实现:

int N;
vector<vector<int>> Graph(N); //无权图
vector<vector<pair<int,int>>> Graph(N); //有权图
for(int i : Numbers){
    for(int j : Neighbors){
        Graph[i].push_back(j);	//无权图: Graph[i]中存在j,则说明存在边(i,j)
        Graph[i].push_back(make_pair(j,weight))/有权图:Graph[i]中存在{j,weight},说明存在边(i,j)并且权重为weight
    }
}

这两种表示图的方式在比赛中效率有所欠佳,我们现在来学习一个在 A C M ACM ACM比赛中常用的表示图的方式:链式前向星。【空间要求严苛情况下】

链式前向星建图

链式前向星建图要用到三个数组和一个int型变量 c n t cnt cnt(记一个图中节点数为 N N N,边数为 M M M):

  1. head[N+1]head[]下标为节点编号【节点编号从1开始,所以head[0]我们舍弃不用】,head[i]存储的是编号 i i i节点的头部边编号(如果为0,说明节点 i i i无出向边)。
  2. next[M+1]next[]下标为边编号【边编号也是从1开始】,next[i]存储的是编号 i i i边对应的下一条边的编号(如果为0,说明无对应的下一条边)。
  3. to[M+1]to[]下标为边编号【边编号从1开始】,to[i]存储的是编号 i i i边指向的节点的编号。(有边就会有对应的指向节点,所以to[i] 0 0 0说明无编号为 i i i的边)。
  4. int cnt:记录建图过程中已经建好的边数,如果cnt r r r,说明已经建好了 r r r条边,最终cnt m m m,说明此图中有m条边。
示例:

现在我们来看一个链式前向星建图的详细操作示例:

图中节点和边如下图:

在这里插入图片描述

【注意】链式前向星建图是需要提前知道边数的,根据边来建图。

​ 现在我来建立 1 1 1号边 ( 1 , 3 ) (1,3) (1,3),此时cnt ++cnt变为1, 1 1 1号节点是边的出射点,所以要更新 1 1 1号节点的头部边, 1 1 1节点原来并没有头部边(没有出现过 1 1 1号节点座位出射点的边),其head[1] = 0,所以要执行head[1] = 1操作,但是在此之前,我们需要更新next[1]next[1]表示 1 1 1号边对应的下一条边,也就是 1 1 1节点原来的头部边。最近记录 1 1 1号边的指向点to[1] = 3。 总的来说,我们需要执行一下操作(有顺序):

cnt++; // 0 -> 1
next[1] = head[1];
head[1] = 1;
to[1] = 3;

​ 现在建立 2 2 2号边 ( 4 , 3 ) (4,3) (4,3)cnt++cnt变为2, 4 4 4号节点是边的出射点,所以要更新 4 4 4号节点的头部边,在此之前更新 2 2 2号边对应的下一条边,next[2]表示 2 2 2号边对应的下一条边,也就是 4 4 4节点原来的头部边。最近记录 2 2 2号边的指向点to[2] = 3。同样执行一下操作:

cnt++;//1 -> 2
next[2] = head[4];
head[4] = 2;
to[2] = 3;

​ 现在建立 3 3 3号边 ( 2 , 4 ) (2,4) (2,4)cnt++cnt变为3, 2 2 2号节点是边的出射点,所以要更新 2 2 2号节点的头部边,在此之前更新 3 3 3号边对应的下一条边,next[3]表示 3 3 3号边对应的下一条边,也就是 2 2 2节点原来的头部边。最近记录 3 3 3号边的指向点to[3] = 4。同样执行一下操作:

cnt++;//2 -> 3
next[3] = head[2];
head[2] = 3; 
to[3] = 4;

​ 现在建立 4 4 4号边 ( 1 , 2 ) (1,2) (1,2)cnt++cnt变为4, 1 1 1号节点是边的出射点,所以要更新 1 1 1号节点的头部边,这里 1 1 1号节点的头部边不再为0,而是 1 1 1号边,所以next[4] = head[1] = 1,之后更新head[1] = 4。最近记录 4 4 4号边的指向点to[4] = 2

cnt++;//3->4
next[4] = head[1];
head[1] = 4;
to[4] = 2;

现在能发现链式前向星建图的规律以及通式了吧,其实就是下面这段代码:

// 加入边 (发射点i, 入射点j)
cnt++;
next[cnt] = head[i];
head[i] = cnt;
to[cnt] = j;
三种方式建图代码合集:
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;

const int MAXN = 10000;  // 节点个数最大值
const int MAXM = 1000;   // 边的条数最大值

// 邻接矩阵方式
int Graph1[MAXN + 1][MAXN + 1];  // 静态最大空间

// 邻接表方式  只能用动态结构去实现
vector<vector<pair<int, int>>> Graph2Direction(MAXN + 1);

// 链式前向星方式
vector<int> head(MAXN + 1, 0);
vector<int> nextEdge(MAXM + 1, 0);
vector<int> to(MAXM + 1, 0);
vector<int> weightEdge(MAXM + 1, 0);  // 需要权重
int cnt = 0;                          // 表示已经建立好了的边数

// 对图的三种实现方式分别初始化,以便之前的实例不用影响下一个实例
void build() {
  // 邻接矩阵初始化
  memset(Graph1, 0, sizeof(Graph1));

  // 邻接表初始化
  Graph2Direction.assign(MAXN + 1, vector<pair<int, int>>());

  // 链式前向星初始化
  cnt =
      0;  // 边数为0,说明建图重新开始,next[],to[]都会被覆盖,head[],weight[]需要重新置0
  fill(head.begin(), head.end(), 0);
  fill(weightEdge.begin(), weightEdge.end(), 0);
}

// 链式前向星加边
void addEdge(int u, int v, int weight) {
  cnt++;
  nextEdge[cnt] = head[u];
  to[cnt] = v;
  head[u] = cnt;
  weightEdge[cnt] = weight;
}

// 三种方式建立有向有权图
void directGraph(const vector<vector<int>>& edges) {  // m条边
  int m = edges.size();
  // 邻接矩阵建图
  for (int i = 0; i < m; i++) {
    int u = edges[i][0];
    int v = edges[i][1];
    int w = edges[i][2];
    Graph1[u][v] = w;
  }

  // 邻接表建图
  for (int i = 0; i < m; i++) {
    int u = edges[i][0];
    int v = edges[i][1];
    int w = edges[i][2];
    Graph2Direction[u].push_back(make_pair(v, w));
  }

  // 链式前向星建图
  for (int i = 0; i < m; i++) {
    int u = edges[i][0];
    int v = edges[i][1];
    int w = edges[i][2];
    addEdge(u, v, w);
  }
}

// 三种方式建立无向有权图
void undirectGraph(const vector<vector<int>>& edges) {
  int m = edges.size();
  // 邻接矩阵建图
  for (int i = 0; i < m; i++) {
    int u = edges[i][0];
    int v = edges[i][1];
    int w = edges[i][2];
    Graph1[u][v] = w;
    Graph1[v][u] = w;
  }

  // 邻接表建图
  for (int i = 0; i < m; i++) {
    int u = edges[i][0];
    int v = edges[i][1];
    int w = edges[i][2];
    Graph2Direction[u].push_back(make_pair(v, w));
    Graph2Direction[v].push_back(make_pair(u, w));
  }

  // 链式前向星建图
  for (int i = 0; i < m; i++) {
    int u = edges[i][0];
    int v = edges[i][1];
    int w = edges[i][2];
    addEdge(u, v, w);
    addEdge(v, u, w);
  }
}

// 现在对三种建图方式建成的图进行遍历
void travse(int N)  // N为图中节点个数
{
  printf("邻接矩阵:\n");
  for (int i = 1; i <= N; i++) {
    printf("%d (邻居,边权): ", i);
    for (int j = 1; j <= N; j++) {
      if (Graph1[i][j] != 0) {
        printf("(%d,%d) ", j, Graph1[i][j]);
      }
    }
    printf("\n");
  }

  printf("邻接表:\n");
  for (int i = 1; i <= N; i++) {
    printf("%d (邻居,边权): ", i);
    for (const auto& p : Graph2Direction[i]) {
      printf("(%d,%d) ", p.first, p.second);
    }
    printf("\n");
  }

  printf("链式前向星:\n");
  for (int i = 1; i <= N; i++) {
    printf("%d (邻居,边权): ", i);
    for (int ei = head[i]; ei > 0; ei = nextEdge[ei]) {
      printf("(%d,%d) ", to[ei], weightEdge[ei]);
    }
    printf("\n");
  }
}

int main() {
  int n1 = 4;
  vector<vector<int>> edges = {
      {1, 3, 6}, {2, 4, 2}, {4, 3, 4}, {1, 2, 8}, {4, 3, 3}};
  build();
  directGraph(edges);
  travse(n1);

  return 0;
}

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

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

相关文章

VC++以资源方式打开可执行文件

刚看一个资料说可以在VC中&#xff0c;以资源方式打开可执行文件&#xff0c;然后它如果包含对话框一些资源&#xff0c;会呈现出来&#xff0c;可以把其他程序界面上的控件直接拷贝到自己程序&#xff1b; 但是操作了一下没有成功&#xff0c; 先新建一个空对话框准备拷贝东…

【Linux】Linux的基本指令(1)

A clown is always a clown.&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;Linux的背景 •&#x1f330;1.Unix发展的历史 •&#x1f330;2.Linux发展历史 •&#x1f330;3.企业应用现状 •&#x1f330;4.发行版本 &…

【protobuf】ProtoBuf的学习与使用⸺C++

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 前言&#xff1a;之前我们学习了Linux与windows的protobuf安装&#xff0c;知道protobuf是做序列化操作的应用&#xff0c;今天我们来学习一下protobuf。 目录 ⼀、初识ProtoBuf 步骤1&#xff1a;创建.proto文件 步…

WLAN无线局域网

目录 概述 IEEE 802.11标准与WiFi的世代 ​编辑 无线控制器AC&#xff08;Access Controller&#xff09; 无线接入点AP&#xff08;Access Point&#xff09; PoE&#xff08;Power Over Ethernet&#xff09; PoE交换机 STA&#xff08;Station&#xff09; BSS&#x…

简单生活的快乐

小明经常会被问到一个问题&#xff1a;为什么他那么有钱却选择过一种简单、谦逊的生活。先从小明的早年经历说起吧&#xff0c;大概是他六到十三岁的时候&#xff0c;物质对他来说是非常重要的。他记得当妈妈给他买了一双昂贵的鞋子时&#xff0c;他特别兴奋&#xff0c;喜欢向…

GEE 案例:利用sentinel-2数据计算的NDVI指数对比植被退化情况

目录 简介 NDVI指数 数据 函数 ui.Chart.image.series(imageCollection, region, reducer, scale, xProperty) Arguments: Returns: ui.Chart 代码 结果 简介 利用sentinel-2数据计算的NDVI指数对比植被退化情况 NDVI指数 NDVI&#xff08;Normalized Difference Ve…

武器检测系统源码分享

武器检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

压力测试Monkey命令参数和报告分析!

adb的操作命令格式一般为&#xff1a;adb shell monkey 命令参数 PART 01 常用参数 ⏩ -p <测试的包名列表> 用于约束限制&#xff0c;用此参数指定一个或多个包。指定包之后&#xff0c;Monkey将只允许系统启动指定的APP。如果不指定包&#xff0c;Monkey将允许系统…

【JVM】垃圾回收机制|死亡对象的判断算法|垃圾回收算法

思维导图 目录 1.找到谁是垃圾 1&#xff09;引用计数&#xff08;不是JVM采取的方式&#xff0c;而是Python/PHP的方案&#xff09; 2&#xff09;可达性分析&#xff08;是JVM采用的方案&#xff09; 2.释放对应的内存的策略 1&#xff09;标记-清除&#xff08;并不实…

信息安全数学基础(18)模重复平方计算法

前言 模重复平方计算法&#xff08;Modular Exponentiation by Squaring&#xff09;&#xff0c;也称为快速幂算法&#xff0c;是一种用于高效计算 abmodn 的算法&#xff0c;其中 a、b 和 n 是整数&#xff0c;且 b 可能非常大。这种算法通过减少乘法操作的次数来加速计算过程…

伦敦金的交易差价意味着什么?

在伦敦金投资市场上&#xff0c;点差是指交易平台的买入价&#xff08;买价&#xff09;和卖出价&#xff08;卖价&#xff09;之间的差额。对投资者来说&#xff0c;点差是交易成本的一部分&#xff0c;但它是经纪商的收入来源。点差代表伦敦金投资者在进入和退出交易时需要支…

Python 入门教程(4)数据类型 | 4.5、字符串类型

文章目录 一、字符串类型1、字符串的定义2、字符串索引3、字符串的基本操作4、字符串的编码5、字符串的不可变性6、总结 前言&#xff1a; 在Python中&#xff0c;字符串&#xff08;String&#xff09;是一种非常重要的数据类型&#xff0c;用于表示和存储文本信息。Python的字…

我的AI工具箱Tauri版-VideoIntroductionClipCut视频介绍混剪

本教程基于自研的AI工具箱Tauri版进行VideoIntroductionClipCut视频介绍混剪。 进入软件后可以直接搜索 VideoIntroductionClipCut 或者依次点击 Python音频技术/视频tools 进入该模块。 视频样片《Tara音乐介绍》 《我的AI工具箱Tauri版-VideoIntroductionClipCut视频介绍混…

excel VBA进行间比法设计

在品比试验大家多使用间比法试验设计&#xff0c;这里通过excel VBA实现间比法设计&#xff0c;代码如下&#xff1a; Sub 生成试验设计()Dim ws As Worksheet Dim rng As Range, rng2 As Range, rng3 As Range Dim cell As Range, lastcell As Range Dim rd As String, sn As…

SpringBootWeb增删改查入门案例

前言 为了快速入门一个SpringBootWeb项目&#xff0c;这里就将基础的增删改查的案例进行总结&#xff0c;作为对SpringBootMybatis的基础用法的一个巩固。 准备工作 需求说明 对员工表进行增删改查操作环境搭建 准备数据表 -- 员工管理(带约束) create table emp (id int …

论文阅读 | 基于流模型和可逆噪声层的鲁棒水印框架(AAAI 2023)

Flow-based Robust Watermarking with Invertible Noise Layer for Black-box DistortionsAAAI, 2023&#xff0c;新加坡国立大学&中国科学技术大学本论文提出一种基于流的鲁棒数字水印框架&#xff0c;该框架采用了可逆噪声层来抵御黑盒失真。 一、问题 基于深度神经网络…

spring boot admin集成,springboot2.x集成监控

服务端&#xff1a; 1. 新建monitor服务 pom依赖 <!-- 注意这些只是pom的核心东西&#xff0c;不是完整的pom.xml内容&#xff0c;不能直接使用&#xff0c;仅供参考使用 --><packaging>jar</packaging><dependencies><dependency><groupId&g…

STM32 芯片启动过程

目录 一、前言二、STM32 的启动模式三、STM32 启动文件分析1、栈 Stack2、堆 Heap3、中断向量表 Vectors3.1 中断响应流程 4、复位程序 Reset_Handler5、中断服务函数6、用户堆栈初始化 四、STM32 启动流程分析1、初始化 SP、PC 及中断向量表2、设置系统时钟3、初始化堆栈并进入…

【Linux】POSIX信号量与、基于环形队列实现的生产者消费者模型

目录 一、POSIX信号量概述 信号量的基本概念 信号量在临界区的作用 与互斥锁的比较 信号量的原理 信号量的优势 二、信号量的操作 1、初始化信号量&#xff1a;sem_init 2、信号量申请&#xff08;P操作&#xff09;&#xff1a;sem_wait 3、信号量的释放&#xff08…

树——数据结构

这次我来给大家讲解一下数据结构中的树 1. 树的概念 树是一种非线性的数据结构&#xff0c;它是由n(n>0&#xff09;个有限结点组成一个具有层次关系的集合。 叫做树的原因&#xff1a;看起来像一棵倒挂的树&#xff0c;根朝上&#xff0c;叶朝下。 特殊结点&#xff1a…