首页 > 其他分享 >图的概念、存储与遍历

图的概念、存储与遍历

时间:2024-05-03 20:34:24浏览次数:14  
标签:存储 遍历 int 结点 概念 vector MAXN 顶点

相关概念

图 (graph) 是一个二元组 \(G=(V(G),E(G))\)。其中 \(V(G)\) 是非空集,称为 点集 (vertex set),对于 \(V\) 中的每个元素,我们称其为 顶点 (vertex)节点 (node),简称 ;\(E(G)\) 为 \(V(G)\) 结点之间边的集合,称为 边集 (edge set)

​ —— OI-Wiki

与树类似,同样使用结点和边来组织一张图,图上的一条边表示两个结点之间有关联。

但与树不同的是,图虽然也由顶点和边构成,但是它没有父亲和儿子的关系,任意两点之间的路径也可能并不唯一。

图可大致分为两种图, 无向图 (undirected graph)有向图 (directed graph)混合图 (mixed graph) 可分为有部分双向边的有向图。

无向图中,如果有一条边连接 \(u\) 和 \(v\),则 \(u\) 可到 \(v\),\(v\) 也可到 \(u\)。

有向图中,如果有一条边表示从 \(u\) 到 \(v\),则 \(u\) 可到 \(v\),但 \(v\) 不可到 \(u\)。

图的概念很多,这里只写了较为重要的概念,详细的概念可见 图论相关概念 - OI Wiki

带权图

从一张图的边进行分类,边上有权值的图称为 带权图,没有则称为 无权图

简单图

如果一张图的任意两点之间,只含有最多一条边,即 重边,同时,不存在连接自己到自己的边,即 自环,那么这种图就叫做简单图。如果一张图允许存在自环和重边,那么这张图就不是简单图。

稠密图与稀疏图

对于无向图,我们知道 \(n\) 个顶点的无向图最多只有 \(\frac{n(n-1)}{2}\) 条边,对于有向图,最多有 \(n(n-1)\) 条边。对于一张简单图,它的边数几乎达到 \(O(n^2)\) 的图称为稠密图,边数差不多为 \(O(n)\) 的图称为稀疏图。

对于一张有向图中的一个结点 \(v\), 可直接到达 \(v\) 的结点数量称为 入度(in-degree),从 \(v\) 出发到达的结点数量称为 出度(out-degree)。如下图,\(1\) 的入度是 \(1\),出度是 \(2\),\(4\) 的入度是 \(2\),出度是 \(0\)。

而对于一张无向图中的一个结点 \(v\),则只有 度(degree) 一说,结点 \(v\) 的 度(degree) 就是与顶点 \(v\) 相关联的结点的数量。如下图,\(1\) 的度是 \(3\),\(4\) 的度是 \(2\)。

image

连通

如果两个结点可通过一条路径互相到达,我们称之为这两个结点连通。

对于无向图,任意 两个结点都可以互相到达,那么我们叫这张图为连通图,反之则称为非连通图。

对于有向图,两个结点它们之间可以互相到达,我们称这两个结点为强连通,如果一个有向图上任意两个结点都可以互相到达,我们叫这张图为强连通图。

子图

我们取出一张图其中的一些顶点,以及保留两端都在这些顶点之中的边,这样构成的子图称为 顶点导出子图

我们取出这张图的一些边,并保留这些边的顶点,这样构成的子图为边导出子图。


存储

邻接矩阵

邻接矩阵适用于稠密图,我们可以定义一个二维数组 \(g[][]\),对于 \(u\)、\(v\) 两个结点,我们可以用 \(g[u][v]\) 来表示这两个点有一条边,如果是带权图,则可以记录这条边的权值,构造的空间复杂度是 \(O(n^2)\),但查询两个结点的时间复杂度只有 \(O(1)\)。

\(\texttt{code}\)

int g[MAXN][MAXN];
......
for(int i=1;i<=m;i++){
	int u,v;
    cin>>u>>v;
	g[u][v]=1;//如果是无向图,这里应该是 g[u][v]=g[v][u]=1
}

邻接表

邻接表主要适用于稀疏图,我们定义 \(n\) 个向量,用于存储结点 \(u\) 可以直接到达的所有结点,如果是带权图,可以开一个结构体 node,含有二维信息 tow,分别表示到达的顶点和这两个点之间的边权,当然也可以用 pair 来记录这两个值,空间复杂度 \(O(m)\),但是判断两个结点是否有连边,需要遍历整个 vector 查询,最坏时间复杂度 \(O(n)\)。

\(\texttt{code}\)

vector<int> g[MAXN];
......
for(int i=1;i<=m;i++){
    int u,v;
    cin>>u>>v;
    g[u].push_back(v);
   	g[v].push_back(u);//无向图
}
//1


struct node{
    int to,w;
}
vector<node> g[MAXN];
......
for(int i=1;i<=m;i++){
    int u,v,w;
    cin>>u>>v>>w;
    g[u].push_back((node){v,w});
   	g[v].push_back((node){u,w});
}
//2


vector<pair<int,int>> g[MAXN];
......
for(int i=1;i<=m;i++){
    int u,v,w;
    cin>>u>>v>>w;
    g[u].push_back(make_pair(v,w));
   	g[v].push_back(make_pair(u,w));
}
//3

存边

除了以上两种存点的方式,还可以用结构体存储一条边,结构体中的成员变量 frtow,分别表示这条边所连接的两个结点和这条边的边权,空间复杂度 \(O(m)\)。

struct Edge{
    int fr,to,w;
}edges[MAXN];

遍历

图的 bfs 遍历:

\(\texttt{code}\)

vector<int> g[MAXN];
void bfs(int s){
	queue<int> q;
	q.push(s);
	vis[s]=1;
	while(!q.empty()){
		int now=q.front();
		cout<<now<<" ";
		q.pop();
		for(auto x:g[now]){
			if(vis[x]==0){
				vis[x]=1;
				q.push(x);
			}
		}
	}
}

图的 dfs 遍历:

\(\texttt{code}\)

vector<int> g[MAXN];
void dfs(int now){
	cout<<now<<" ";
	vis[now]=1;
	for(auto x:g[now]){
		if(vis[x]==0){
			dfs(x);
		}
	}
}

以上两种写法的时间复杂度是 \(O(n+m)\)。

拓扑排序

对于一个有向无环图,有一个遍历顺序,是等一个结点的所有前驱结点都遍历完了,才遍历这个结点。这种顺序就叫做拓扑序。

对于下面这张图,它的拓扑序可以是 1 2 3 4 5,而 1 2 4 3 5 则不是,因为 \(4\) 的前驱还有 \(3\) 没有遍历,必须先遍历 \(3\) 才能遍历 \(4\)。

int in[MAXN],res[MAXN];
vector<int> g[MAXN];
int num;
int topo(){
    for(int i=1;i<=n;i++){
        for(auto x:g[i]){
        	in[x]++;
        }
    }
    queue<int> q;
    for(int i=1;i<=n;i++){
        if(in[i]==0){ 
            q.push(i);
        }
    }
    while(!q.empty()){
        int k=q.front();
        q.pop();
        res[++num]=k;
        for(auto x:g[k]){
        	in[x]--;
        	if(in[x]==0){
                q.push(x);
            }
        }
    }
    if(num!=n){
        return false;
    }else{ 
        return true;
    }
}

时间复杂度 \(O(n+m)\)。

欧拉路径问题

给你一个图,请你选择一个起点出发,经过所有的边,要求每条边只能经过一次,问能否达成目标,也称一笔画问题。

欧拉定理:如果一张连通图,只有 \(0\) 个或者 \(2\) 个结点,他的度为奇数,那么这张图可以一笔画。

证明:我们选择一个度数为奇数的顶点出发,走向一个度数为偶数的顶点,那么这个图变成了一个仍然满足上述性质但是结构更简单的图了。所以我们可以归纳证明上述定理。

对于这道题,我们可以使用 Hierholzer算法,它的思路是,对于欧拉回路,我相当于是将图拆成若干个环。所以我每次找图的正好一个环,如果没法找了,我就回溯地把这个环给存起来,存到栈中。

\(\texttt{code}\)

vector<pair<int,int>> g[MAXN];
stack<int> s;
int vis[maxm];//对边标记
void dfs(int now){
    for(auto x:g[now]){
        if(vis[x.second]==0){
        	vis[x.second]=1;
        	dfs(x.first);
        }
    }
    s.push(now);
}

时间复杂度 \(O(n+m)\)。

画图网站推荐

标签:存储,遍历,int,结点,概念,vector,MAXN,顶点
From: https://www.cnblogs.com/shimingxin1007/p/18171570

相关文章

  • 高效遍历:C++中分隔字符串单词的3种方法详解与实例
     概述:在C++中,遍历由空格分隔的字符串的单词有多种方法,包括使用`std::istringstream`、手动遍历字符和正则表达式。其中,`std::istringstream`是简单高效的选择,通过流提取单词。手动遍历字符较为繁琐,正则表达式方法更灵活但可能有性能开销。根据实际需求选择方法,本文提供了清晰......
  • ef core加密存储数据,如身份证号
    一、新建项目,安装nuget<PackageReferenceInclude="V6.EntityFrameworkCore.DataEncryption"Version="5.0.0"/>二、本示例采用:AES+256bits(Canusea128bits,192bitsor256bitskey)CipherModemode=CipherMode.CBC,PaddingModepadding=Paddin......
  • 混入、插件、插槽、vuex、本地存储
    【混入】#mixin(混入)功能:可以把多个组件共用的配置提取成一个混入对象,不需要在每个组件中都写了使用步骤   。。。【插件】1#1写plugins/index.js2importVuefrom"vue";3importaxiosfrom"axios";4importhunrufrom"@/mixin";......
  • 长江存储PC411 512GB SSD实测:旗舰读写性能 温度表现逆天
    一、前言:搭载长江存储PC411512GBSSD的机械革命蛟龙16S不久前我们测试过某品牌的笔记本,其搭载的PCIe4.0SSD在高负载运行时温度轻松突破70度,导致性能下降了20%左右。对于笔记本而言,由于无法像台式电脑那样给SSD安装厚重的散热装甲,在搭载高性能PCIe4.0SSD时,很容易出现温度失......
  • 机械硬盘与固态硬盘:两类存储介质的三大对比
    在数字化时代,云计算的无处不在和人工智能用例的出现提高了海量数据的价值。数据存储无疑是支撑人工智能的基石。行业分析师预计,随着EB级数据的持续增长,存储着绝大多数全球数据集的企业和大规模云数据中心将成为主要受益者。迄今为止,全球绝大部分的EB级数据都存储在机械硬盘上,对数......
  • windows密码存储以及hashdump所得信息解析
    1.windows登录的明文密码,存储过程是怎么样的,密文存在哪个文件下,该文件是否可以打开,并且查看到密文在Windows中密码通常不会以明文形式存储。系统会通过保存密码的哈希值来确保安全性。这个过程涉及到NTLM或Kerberos身份认证协议,它们负责加密存储密码。以下是存储过程的简要说......
  • 计算机的硬件系统存储器
    如果说程序操作数据是计算机系统的主题,而程序指令本身也是数据,那么作为存放数据的存储体系是计算机系统不可或缺的重要组成。微机系统的存储体系,按照访问速度划分为寄存器、高速缓冲存储器、存储器(主存储器或内存)、外存储器(外存或辅存)。在CPU诞生之初,为了提高运算速度,也为了方便......
  • 类模板的简单应用(用于存储不同类型数据的类容器)
    类模板应用explicitexplicit是一个关键字,用于指定该构造函数是显式构造函数。在C++中,当一个类的构造函数只有一个参数时,它可以被用于隐式类型转换,这可能会导致意想不到的行为和潜在的错误。为了避免这种情况,可以使用explicit关键字来声明该构造函数,表示禁止隐式类型转换,只能......
  • 快速了解Django:核心概念解析与实践指南
    title:快速了解Django:核心概念解析与实践指南date:2024/5/120:31:41updated:2024/5/120:31:41categories:后端开发tags:Django核心路由系统视图系统ORM管理中间件Web框架登录装饰器第一章:Django简介背景和发展历程:Django是一个开放源代码的Web应用框架......
  • kafka核心概念Broker、Topic、Partition和Replication
    在Kafka中,Broker、Topic、Partition和Replication是四个核心概念,它们各自扮演了不同的角色并共同协作以确保数据的可靠性、可扩展性和高性能。以下是关于这四个概念的详细解释:Broker(代理)*Broker是Kafka集群中的一个节点,负责存储和转发消息。Kafka集群由多个Broker组成。*Brok......