【图的存储】

news2024/11/30 0:49:39

更好的阅读体验 \color{red}{更好的阅读体验} 更好的阅读体验


文章目录

    • 1. 邻接矩阵
    • 2. 边集数组
    • 3. 邻接表
    • 4. 链式邻接表
    • 5. 链式前向星
    • 总结

1. 邻接矩阵


思想

  • 利用二维数组 g[N][N] 存储所有的点到点的权值。
  • 其中 N 为点的数量,g[i][j] 表示点 i 到点 j 的权值。

时间复杂度 O ( n 2 ) \mathcal{O}(n^2) O(n2)

空间复杂度 O ( n 2 ) \mathcal{O}(n^2) O(n2)

应用

  • 只在点数不多的稠密图使用。
  • 大部分情况下点的数量 n = 1 0 3 n = 10^3 n=103,边的数量 m = 1 0 6 m = 10^6 m=106

示例

  • 现有 n 个点共 m 条边,以及每条边的起始点和终点及权值。
  • 这些点和边共同构成一个有向图。
  • 存储这些信息并输出。

在这里插入图片描述

输入

4 5
1 2 20
1 4 40
2 3 50
2 4 60
3 2 30

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;  //图的大小

int n, m;
int g[N][N];
bool vis[N];  //标记是否走过

void dfs(int u){  //深度优先遍历
    vis[u] = 1;  //标记当前点已经遍历过
    for(int i = 1; i <= n; i ++){  //遍历n个点
        if(g[u][i] != 0){
            cout << u << ' ' << i << ' ' << g[u][i] << endl;
            if(vis[i]) continue;  //当前的边的终点已经走过则跳过
            dfs(i);
        }
    }
}

void solve(){

    cin >> n >> m;
    for(int i = 0; i < m; i ++){
        int a, b, c; cin >> a >> b >> c;
        g[a][b] = c;
        // g[b][a] = c;  //如果是无向图加一条边
    }

    dfs(1);  //从1号点开始遍历

}

int main(){
    
    solve();
    
    return 0;
}

输出

1 2 20
2 3 50
3 2 30
2 4 60
1 4 40

2. 边集数组


思想

  • 利用结构体数组 e[N] 存储边的信息。
  • 其中 e[i] 包含第 i 条边的 {起始点u, 终点v, 边权w}

时间复杂度 O ( n m ) \mathcal{O}(nm) O(nm)

空间复杂度 O ( m ) \mathcal{O}(m) O(m)

应用

  • Kruskal 算法中,需要将边按照边权排序,直接存边。

示例

  • 现有 n 个点共 m 条边,以及每条边的起始点和终点及权值。
  • 这些点和边共同构成一个有向图。
  • 存储这些信息并输出。

在这里插入图片描述

输入

7 6
4 3 90
1 4 30
5 7 80
5 6 60
1 5 20
5 2 70

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;  //图的大小

int n, m;

struct edge{
    int u, v, w;
}e[N];

bool vis[N];  //标记是否走过

void dfs(int u){  //深度优先遍历
    vis[u] = 1;  //标记当前点已经遍历过
    for(int i = 1; i <= m; i ++){  //遍历m条边
        if(u == e[i].u){  //找到当前的边的起始点
            cout << e[i].u << ' ' << e[i].v << ' ' << e[i].w << endl;
            if(vis[e[i].v]) continue;  //当前的边的终点已经走过则跳过
            dfs(e[i].v);
        }
    }
}

void solve(){

    cin >> n >> m;
    for(int i = 1; i <= m; i ++){
        int a, b, c; cin >> a >> b >> c;
        e[i] = {a, b, c};
        // e[i] = {b, a, c};  //如果是无向图加一条边
    }

    dfs(1);  //从1号点开始遍历

}

int main(){
    
    solve();
    
    return 0;
}

输出

1 4 30
4 3 90
1 5 20
5 7 80
5 6 60
5 2 70 

3. 邻接表


思想

  • 利用出边数组 e[N][N] 存储边的信息。
  • 其中 e[u][i] 表示点 u 的所有出边,其出边包含 {终点v, 边权w}

时间复杂度 O ( n + m ) \mathcal{O}(n+m) O(n+m)

空间复杂度 O ( n + m ) \mathcal{O}(n+m) O(n+m)

应用

  • 可以应用于各种图,但是不能处理反向的边(网络流)。

示例

  • 现有 n 个点共 m 条边,以及每条边的起始点和终点及权值。
  • 这些点和边共同构成一个有向图。
  • 存储这些信息并输出。

在这里插入图片描述

输入

7 6
4 3 90
1 4 30
5 7 80
5 6 60
1 5 20
5 2 70

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;  //图的大小

int n, m;

struct edge{
    int v, w;
};

vector<edge> e;  //边集

void dfs(int u, int fa){  //深度优先遍历, fa记录当前点的父节点
    for(auto p : e[u]){  //遍历所有的出边
        if(fa == p.v) continue;  //若该出边的终点是父节点,说明已经走过
        cout << u << ' ' << p.v << ' ' << p.w << endl;
        dfs(p.v, u);
    }
}

void solve(){

    cin >> n >> m;
    for(int i = 1; i <= m; i ++){
        int a, b, c; cin >> a >> b >> c;
        e[a].push_back({b, c});
        // e[b].push_back({a, c});  //如果是无向图加一条边
    }

    dfs(1, 0);  //从1号点开始遍历

}

int main(){
    
    solve();
    
    return 0;
}

输出

1 4 30
4 3 90
1 5 20
5 7 80
5 6 60
5 2 70 

4. 链式邻接表


思想

  • 利用边集数组 e[N] 存储所有的边的信息,表头数组 h[N][N] 存储点的所有出边的编号。
  • 其中 e[j] 存储第 j 条边的 {起始u, 终点v, 边权w}h[u][i] 存储 u 点的第 i 条边的编号。

时间复杂度 O ( n + m ) \mathcal{O}(n+m) O(n+m)

空间复杂度 O ( n + m ) \mathcal{O}(n+m) O(n+m)

应用

  • 可以应用于各种图,也能处理反向的边。

示例

  • 现有 n 个点共 m 条边,以及每条边的起始点和终点及权值。
  • 这些点和边共同构成一个无向图。
  • 存储这些信息并输出。

输入

6 5
4 3 90
1 4 30
5 6 60
1 5 20
5 2 70

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;  //图的大小

int n, m;

struct edge{
    int u, v, w;
};

vector<edge> e;  //边集
vector<int> h[N];  //点的所有出边

void add(int a, int b, int c){
    e.push_back({a, b, c});
    h[a].push_back(e.size() - 1);
}

void dfs(int u, int fa){  //深度优先遍历, fa记录当前点的父节点
    for(int i = 0; i < h[u].size(); i ++){
        int j = h[u][i];
        if(fa == e[j].v) continue;
        cout << u << ' ' << e[j].v << ' ' << e[j].w << endl;
        dfs(e[j].v, u);
    }
}

void solve(){

    cin >> n >> m;
    for(int i = 1; i <= m; i ++){
        int a, b, c; cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);  //如果是无向图加一条边
    }

    dfs(1, 0);  //从1号点开始遍历

}

int main(){
    
    solve();
    
    return 0;
}

输出

1 4 30
4 3 90
1 5 20
5 6 60
5 2 70

5. 链式前向星


思想

  • 一个表头悬挂多个链表。
  • 利用边集数组 e[N] 存储所有的出边的信息,表头数组 h[N] 存储点的第一条出边的编号。
  • 其中 e[i] 存储第 i 条边的 {终点v, 边权w, 下一条边ne}h[u] 存储 u 点的第一条出边的编号。

时间复杂度 O ( n + m ) \mathcal{O}(n+m) O(n+m)

空间复杂度 O ( n + m ) \mathcal{O}(n+m) O(n+m)

应用

  • 可以应用于各种图,也能处理反向的边。

示例

  • 现有 n 个点共 m 条边,以及每条边的起始点和终点及权值。
  • 这些点和边共同构成一个无向图。
  • 存储这些信息并输出。

输入

6 5
4 3 90
1 4 30
5 6 60
1 5 20
5 2 70

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 3;  //图的大小

int n, m;

struct edge{
    int v, w, ne;
}e[N];  //边集

int idx, h[N];  //点的第一条出边,idx为下标编号

void add(int a, int b, int c){
    e[idx] = {b, c, h[a]};
    h[a] = idx ++;
}

void dfs(int u, int fa){  //深度优先遍历, fa记录当前点的父节点
    for(int i = h[u]; ~ i; i = e[i].ne){
        if(fa == e[i].v) continue;
        cout << u << ' ' << e[i].v << ' ' << e[i].w << endl;
        dfs(e[i].v, u);
    }
}

void solve(){

    cin >> n >> m;
    memset(h, -1, sizeof h);  //初始化第一条出边为-1
    for(int i = 1; i <= m; i ++){
        int a, b, c; cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);  //如果是无向图加一条边
    }

    dfs(1, 0);  //从1号点开始遍历

}

int main(){
    
    solve();
    
    return 0;
}

输出

1 5 20
5 2 70
5 6 60
1 4 30
4 3 90

总结


  • 链式邻接表和链式前向星可以解决绝大部分的图论问题。
  • 推荐使用链式前向星,建图方式简便,空间压缩紧密,查找效率高。

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

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

相关文章

【C++】容器适配器

文章目录一. 什么是适配器?什么是容器适配器?二.理解容器适配器stack的模拟实现queue的模拟实现一. 什么是适配器?什么是容器适配器? 适配器是一种设计模式&#xff08;设计模式是一套被反复使用的&#xff0c;多数人知晓的&#xff0c;经过分类编目的&#xff0c;代码设计…

CAD未协调的新图层怎么处理?

在打开CAD图纸时&#xff0c;系统提示图形存在为协调的新图层是什么意思&#xff1f;所谓未协调图层&#xff0c;是指上次打印或者保存之类命令后新增的图层&#xff0c;大部分情况下增加新的外部参照时会把所有外部参照中的图层标记为未协调图层。CAD未协调的新图层怎么处理&a…

2023年底,我要通过这5点,实现博客访问量500W

说实话&#xff0c;这真的是一个非常高远的flag&#xff0c;因为我目前只有35W&#xff0c;但根据我2个月前还是12W的访问量&#xff0c;我觉得我还是可以拼一把的&#xff0c;在这里我想向大家分享一下我的计划&#xff0c;如何达成2023年底&#xff0c;博客访问量达到500W的K…

期刊会议排名、信息检索网站推荐、IEEE Latex模板下载(更新中...)

一.拿到一个期刊或论文&#xff0c;不知道他的影响因子、分区类型等等信息&#xff0c;可以使用以下几个网站搜索一下。二.一些会议期刊搜索1.国外The Latest Information Technology Conference and Journal List - Conference Partner&#xff08;信息技术最新国际会议和期刊…

Java程序员必知四种负载均衡算法

前言 一般来说&#xff0c;我们在设计系统的时候&#xff0c;为了系统的高扩展性&#xff0c;会尽可能的创建无状态的系统&#xff0c;这样我们就可以采用集群的方式部署&#xff0c;最终很方便的根据需要动态增减服务器数量。但是&#xff0c;要使系统具有更好的可扩展性&…

直观理解--马氏距离

首先我们很了解欧氏距离了&#xff0c;就是用来计算欧式空间&#xff08;就是我们常见的坐标系&#xff09;中两个点的距离的。 比如点 x(x1,…,xn)x (x_1,…,x_n)x(x1​,…,xn​) 和 y(y1,…,yn)y (y_1,…,y_n)y(y1​,…,yn​) 的欧氏距离为&#xff1a; d(x,y)(x1−y1)2(x2…

依靠小游戏带动产品增收,app运营这样做

大家都玩过小游戏吧&#xff1f;从小时候的4399游戏平台到现在的微信小游戏&#xff0c;尤其是风靡一时的“跳一跳”和“羊了个羊”都曾上榜热搜&#xff0c;让人印象深刻。在当下小游戏爆火社交平台的不断出现的背景下&#xff0c;小游戏的发展劲头更是强盛。 小游戏的吸引力在…

Memblaze发布大容量企业级SSD:支持32T最大容量,性能更强!

2023年1月10日 —— 今天&#xff0c;北京忆恒创源科技股份有限公司&#xff08;Memblaze&#xff09;正式发布 PBlaze6 6930 系列 PCIe 4.0 企业级 NVMe SSD。PBlaze6 6930 面向企业高性能业务应用开发&#xff0c;有着 1600K/680K IOPS 的 4K 随机读/写性能&#xff0c;具备更…

Mysql常见面试题

Mysql常见面试题汇总①⭐事务的基本特性和隔离级别⭐ACID靠什么保证⭐什么是MVCC⭐mysql的主从同步原理简述MyISAM和InnoDB的区别简述mysql中索引类型以及对数据库的性能影响⭐索引的基本原理Mysql聚簇索引和非聚簇索引的区别⭐B树和B树的区别&#xff0c;为什么Mysql使用B树My…

第04讲:Docker部署MySQL8

MySQL 的 Docker 镜像在 dockerhub 上的地址&#xff1a;https://hub.docker.com//mysql 当前&#xff08;2021-04-02&#xff09;的 latest 和 8.0.23 是同一个镜像 。另外&#xff0c;5.7 版本和 5.7.33 是同一个镜像 第1步&#xff1a;安装mysql 查询中央仓库 docker se…

Kestrel封装在WindowService中(.net5,.net6,.net7三个版本的介绍)

Kestrel封装在WindowServer中背景关于WindowsServer开发服务.NET5版本建项目添加Controller添加引用修改Startup.cs修改Program.cs配置Kestrel监听发布程序通过命令行创建服务关于SC命令启动服务查看效果测试效果.NET6错误1解决办法&#xff1a;错误2运行效果如下图.NET7版本&a…

千锋教育嵌入式物联网教程之系统编程篇学习-01

目录 课程视频链接 笔记目的 什么是系统编程 如何操作内核的系统调用 系统调用的分类​编辑 系统调用的返回值 系统调用IO函数 文件描述符 文件IO文件描述符与标准IO的文件指针对应关系 Man手册使用 open函数 函数调用失败打印错误 close函数 测试一个进程最多能产生多少个…

如何准备pmp考试?

一、考前五准备 1.知识准备&#xff1a;读透教材&#xff0c;仔细回顾知识点&#xff0c;明确知识框架&#xff0c;模考题考前正确率达到95%。 2.精神准备&#xff1a;明确目标&#xff0c;有信心&#xff0c;不要焦虑。 3.精力准备&#xff1a;科学安排考前的复习和休息时间…

【nvivo11plus教程】03_查询+自动编码

注&#xff1a;nvivo11也可以使用自动编码的。我是在第一次使用自动编码的时候&#xff0c;它弹出来一个界面让我下载安装包&#xff0c;但是网速特别慢&#xff0c;需要5、6个小时&#xff0c;然后我电脑开了一个晚上&#xff0c;第二天早上显示下载失败&#xff0c;但是可以使…

十一、路由

一、前端路由的概念与原理 1.1、什么是路由 路由&#xff08;英文&#xff1a;router&#xff09;就是对应关系。 1.2、SPA与前端路由 SPA 指的是一个web网站只有唯一的一个HTML页面&#xff0c;所有组件的展示与切换都在这唯一的一个页面内完成。此时&#xff0c;不同组件…

【 java 集合】Collections工具类的使用

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

python基础语法二

一、条件判断 1.1 if…else…语句 if…else… 字面意思就是 如果…否则… num 3 # 通过取模运算判断num是否能被2整除 if num % 2 0:print(str(num) "是一个偶数") else:print(str(num) "是一个奇数")注意两条print语句前面的缩进 1.2 elif 当你有三…

DP4809国产双通道耳机音频功率放大器芯片兼容替代LM4809

目录DP4809简介参考原理图DP4809芯片特性DP4809简介 DP4809是带关机功能的双通道耳机音频功率放大器芯片。在5V输入工作电压下&#xff0c;负载 16Ω时&#xff0c;平均输出功率为 140mW&#xff0c;总谐波失真 THDN 小于0.1%。当 SHUTDOWN 管脚电压为 GND 时&#xff0c;芯片…

小程序技术科普:运行机制安全机制

接触小程序有一段时间了&#xff0c;总得来说小程序开发门槛比较低&#xff0c;但其中基本的运行机制和原理还是要懂的。 了解小程序的由来 在小程序没有出来之前&#xff0c;最初微信WebView逐渐成为移动web重要入口&#xff0c;微信发布了一整套网页开发工具包&#xff0c;称…

Windows安装Pytorch+CUDA环境

文章目录前言创建Python虚拟环境安装CUDA安装Pytorch参考资料前言 本文将介绍使用 conda 创建Python虚拟环境&#xff0c;安装Pytorch和CUDA依赖。 创建Python虚拟环境 conda常用命令&#xff1a; conda create -n poppy_leo_tf python3.7 # 创建虚拟环境 conda activate p…