【算法证明 七】深入理解深度优先搜索

news2024/11/20 15:15:30

深度优先搜索包含一个递归,对其进行分析要复杂一些。与上一篇文章一样,还是给节点定义几个状态,然后详细分析深度优先搜索算法有哪些性质。

算法描述

定义状态

  • v . c o l o r :初始状态为白色,被发现时改为灰色,其所有的邻接节点遍历完成后,变为黑色。 v.color:初始状态为白色,被发现时改为灰色,其所有的邻接节点遍历完成后,变为黑色。 v.color:初始状态为白色,被发现时改为灰色,其所有的邻接节点遍历完成后,变为黑色。
  • v . π : v 的前驱节点,也就是说是从哪个节点发现 v 的,初始化为 n i l v.\pi : v 的前驱节点,也就是说是从哪个节点发现 v 的,初始化为 n i l v.π:v的前驱节点,也就是说是从哪个节点发现v的,初始化为nil
  • v . d :时间戳,表示节点第一次被发现的时间 v.d:时间戳,表示节点第一次被发现的时间 v.d:时间戳,表示节点第一次被发现的时间
  • v . f :时间戳,表示完成对邻接节点扫描的时间 v.f :时间戳,表示完成对邻接节点扫描的时间 v.f:时间戳,表示完成对邻接节点扫描的时间
DFS(G)
	for v in G.V
		v.color=white
		v.Π=nil
	time=0
	for v in G.V
		if v.color == white
			DFS-VISIT(G, v)

DFS-VISIT(G, v)
	time += 1;
	v.d = time
	v.color = grary
	for u in G.Adj[v]:
		if u.color = white:
			u.Π=v
			DFS-VISIT(G, u)
	v.color = black
	time += 1;
	v.f = time;

该算法的时间复杂度分析与广搜的分析类似,使用聚合分析,发现每个节点访问一次,每条边访问一次,总复杂度为 O ( V + E ) O(V+E) O(V+E)

深度优先搜索的性质

深度优先搜索提供了关于图结构价值很高的信息。
性质1:生成的前驱子图 G . π G.\pi G.π 是由多颗树构成的森林。

  1. u = v . π u=v.\pi u=v.π,当且仅当DFS-VISIT(G, v) 在对 u 节点邻接表搜索时被调用。
  2. v 是节点 u的后代,当且仅当节点 v 在节点u 为灰色时被发现

性质2:括号化结构:对于某一个节点,u, 如果以’(u’表示节点u的发现,’u)'表示节点u的完成。则算法的运行过程会形成一个恰当嵌套的括号化结构。

定理1. 括号化定理:在对图G进行深度优先搜索时,任意两个节点v,u,下面三种情况只有一种成立

  • 区间 [ u . d , u . f ] [u.d, u.f] [u.d,u.f]和区间 [ v . d , v . f ] [v.d, v.f] [v.d,v.f]完全分离, v v v u u u之间没有后代关系。
  • 区间 [ u . d , u . f ] [u.d, u.f] [u.d,u.f] 在 区间 [ v . d , v . f ] [v.d, v.f] [v.d,v.f] 之内。在 深度优先搜索树 中, u u u v v v 的后代
  • 区间 [ v . d , v . f ] [v.d, v.f] [v.d,v.f] 在 区间 [ u . d , u . f ] [u.d, u.f] [u.d,u.f] 之内。在 深度优先搜索树 中, v v v u u u 的后代

证明:当 u . d < v . d u.d \lt v.d u.d<v.d 时,根据 u . f u.f u.f v . d v.d v.d的关系,可以分为两种情况
u . f < v . d u.f \lt v.d u.f<v.d 时,容易扩充得到不等式 u . d < u . f < v . d < v . f u.d \lt u.f \lt v.d \lt v.f u.d<u.f<v.d<v.f,此时两区间分离,且没有一个节点是在另一个节点是灰色时被发现的,一次没有任何一个节点是另一个节点的后代
u . f > v . d u.f \gt v.d u.f>v.d,说明节点 v v v在节点 u u u是灰色时被发现。意味着v 是u 的后代。此外,当算法返回来继续处理 u u u时, v v v节点已经处理完,因此区间 [ v . d , v . f ] [v.d, v.f] [v.d,v.f] 在 区间 [ u . d , u . f ] [u.d, u.f] [u.d,u.f] 之内。 证明完毕
推论:在深度优先树中, v v v u u u 的后代,当且仅当 u . d < v . d < v . f < u . f u.d \lt v.d \lt v.f \lt u.f u.d<v.d<v.f<u.f成立

定理2:白色路径定理。 v v v u u u 的后代,当且仅当 算法发现 u u u 时,存在一条从 u u u v v v 的全部由白色节点组成的路径。
证明: v v v u u u的后代时, v v v u u u 之后被发现。发现 u u u 时, u u u 的后代此时均未被发现为白色,当然包括 v v v。所以可以顺着后代路径,找到一条达到 v v v 的白色路径。
当发现 u u u 时,存在一条从 u u u v v v 的白色路径。意味着深度优先算法至少一定会 完成 v v v 的访问后,再回到 u u u。满足不等式, u . d < v . d < v . f < u . f u.d \lt v.d \lt v.f \lt u.f u.d<v.d<v.f<u.f。符合定理1的后两条之一。因此充分性和必要性均得证。

性质3:边的分类

根据深度优先搜索森林 G . π G.\pi G.π,可以定义 4 种边类型。

  • 树边: G . π G.\pi G.π 中的边
  • 后向边:当v是u的祖先时, ( u , v ) (u, v) (u,v) 称为后向边
  • 前向边:当v是u的祖先时, ( v , u ) (v, u) (v,u) 称为前向边
  • 横向边:其他的所有边。

深度优先搜索算法可以将图中的所有边进行分类:当探索边 ( v , u ) (v, u) (v,u)

  • u u u 是 白色, ( v , u ) (v,u) (v,u)是树边
  • u u u 是 灰色, ( v , u ) (v,u) (v,u)是后向边
  • u u u 是 黑色, ( v , u ) (v,u) (v,u)是前向边或横向边
    • u . d < v . d u.d\lt v.d u.d<v.d 时, ( v , u ) (v,u) (v,u)是前向边
    • u . d > v . d u.d\gt v.d u.d>v.d 时, ( v , u ) (v,u) (v,u)是横向边

无向图的边类型按照符合的第一顺位分类。

定理3:无向图的边,要么是树边,要么是后向边。
证明:设 ( u , v ) (u, v) (u,v) 时无向图的一条边。假设 u . d < v . d u.d < v.d u.d<v.d u u u 先被访问。 v v v u u u 的邻接节点链表里。但算法第一次访问边 ( u , v ) (u, v) (u,v) 时,仍然有两种可能:如果从 v v v 访问 u u u,此时 u u u 已经被发现, ( u , v ) (u, v) (u,v) 是一条后向边。如果从 u u u 访问 v v v v v v 一定是白色。因为如果是灰色或者黑色,那么 ( u , v ) (u, v) (u,v) 一定已经从 v v v 访问过了。因此 ( u , v ) (u, v) (u,v) 此时是树边。

强连通分量

这应该是图论中第一个简单(只用到了深搜)有用,但是难想,不直观的算法了。
首先定义强连通分量:图G的强连通分量是一个最大的节点集合 C ⊆ G . V C \subseteq G.V CG.V,该集合中的任意两个节点之间都可以相互到达。下图中圈起来的节点,就是强连通分量

强连通分量示意

为了实现强连通分量算法,先讨论一下分量图: G S C C = ( V S C C , E S C C ) G^{SCC} = (V^{SCC}, E^{SCC}) GSCC=(VSCC,ESCC)。定义如下:假如 G 由强连通分量 C 1 , C 2 , . . . , C k C_1, C_2,..., C_k C1,C2...,Ck,易知强连通分量之间并不相交。任意从分量中挑出代表节点 v 1 , v 2 , . . . , v k v_1, v_2, ..., v_k v1,v2,...,vk 作为 V S C C V^{SCC} VSCC。如果对于两个节点 x ∈ C x , y ∈ C y x\in C_x, y\in C_y xCx,yCy,存在边 ( x , y ) (x, y) (x,y),则边 ( v x , v y ) (v_x, v_y) (vx,vy) E S C C E^{SCC} ESCC中。上面的分量图可以通过缩点变成分量图如下:
请添加图片描述

定理4 :分量图是有向无环图:设 C C C C ′ C' C是两个不同的强连通分量,设 u , v ∈ C u,v\in C u,vC, u ′ , v ′ ∈ C ′ u',v'\in C' u,vC。如果存在一条边 ( u , u ′ ) (u, u') (u,u),则必存在边 ( v ′ , v ) (v',v) (v,v)
证明:如果存在边$(v’,v),那么 C , C ′ C, C' C,C 两个强连通分量里的节点便满足了强连通分量的定义, C , C ′ C, C' C,C 应该合并成 1 个,而不是两个,矛盾。

为了不产生歧义,对节点的描述 v . f v.f v.f 表示的都是 对 G G G 深度优先遍历的结果,而不是 G T G^T GT

定理5:(深度优先搜索的节点完成时间) :设 C C C C ′ C' C是图 G G G的两个不同的强连通分量, f ( C ) f(C) f(C)表示强连通分量 C C C 的节点 v . f v.f v.f 的最大值, d ( C ) d(C) d(C)表示强连通分量 C C C 的节点 v . d v.d v.d 的最小值。如果存在一条边 ( u , v ) ∈ G . E (u,v) \in G.E (u,v)G.E满足 u ∈ C u\in C uC, v ∈ C ′ v\in C' vC,那么 f ( C ) > f ( C ′ ) f(C) > f(C') f(C)>f(C).
证明:根据深度优先搜索中,最先发现的节点在 C 中还是 C’ 中进行讨论。

  • 如果 d ( C ) < d ( C ′ ) d(C) \lt d(C') d(C)<d(C),那么深度优先搜索算法一定会通过 边 ( u , v ) (u, v) (u,v) 遍历完 C ′ C' C 中的节点后,在回到 C C C 中。因此 f ( C ) > f ( C ′ ) f(C) > f(C') f(C)>f(C).
  • 如果 d ( C ) > d ( C ′ ) d(C) \gt d(C') d(C)>d(C),那么由于 分量图是无环图,将无法通过 C ′ C' C 到达 C C C中的任何一个节点。必然是算法返回 DFS 主循环后,再访问 C C C 中的节点,此时仍有 f ( C ) > f ( C ′ ) f(C) > f(C') f(C)>f(C) 证毕。

推论5.1:设 C C C C ′ C' C是图 G G G的两个不同的强连通分量,如果存在一条边 ( u , v ) ∈ G T . E (u,v) \in G^T.E (u,v)GT.E,满足 u , v ∈ C u,v\in C u,vC, u ′ , v ′ ∈ C ′ u',v'\in C' u,vC,那么 f ( C ) < f ( C ′ ) f(C) < f(C') f(C)<f(C).
根据定理5 以及下图,本推论有较为直观的理解,不再证明。
请添加图片描述

强连通分量算法

strongly-connected-components(G)
	DFS(G) // 计算出 v.f
	compute GT // 计算转置图,节点列表按照 v.f 降序排列
	DFS(GT)
	print GT 的深度优先搜索森林。

定理6 强连通分量算法正确

证明: 数学归纳法:归纳假设是 算法第三行运行时,生成的前 k 棵树是强连通分量。初始情况 k = 0,显然成立。
假设前 k k k 棵树是强连通分量,考虑第 ( k + 1 ) (k + 1) (k+1) 棵树。树跟节点为 u u u u u u 位于强连通分量 C C C 中。由于 u u u是根节点,对于除 C C C 外的未被访问的任意 强连通分量 C ′ C' C,有 u . f = f ( C ) > f ( C ′ ) u.f = f(C) \gt f(C') u.f=f(C)>f(C)。根据归纳假设 C C C 当前所有的节点都是白色。根据白色路径定理, C C C 中的所有其他节点都是 u u u 的后代。根据推论 5.1,任何从 C 出发的边,一定通向 f ( C b ) f(C^b) f(Cb) 更大连通分量 C b C^b Cb。因此根据我们的遍历顺序,除 C C C内的节点外,不存在节点能够成为 u u u 的后代。因此 k + 1 k+1 k+1 棵树刚好形成一个强连通分量。归纳 完毕。

可以从 G T G^T GT 的分量图角度来看待第二次深度优先遍历。就相当于逆着 G T G^T GT 的分量图的拓扑序来遍历,看上面的右图更直观。

给出求强连通分量的 C++ 代码做参考

int V;
vector<int> G[MAX_V];
vector<int> Gt[MAX_V];
vector<int> vs;
bool used[MAX_V];
int cmp[MAX_V]; // 表示 节点 v 所属强连通分量的拓扑序编号
void add_edge(int from, int to) {
	G[from].push_back(to);
	Gt[to].push_back(from);
}
void dfs(int v) {
	used[v] = true;
	for (int i = 0; i < G[v].size(); i++) {
		if (!used[G[v][i]) dfs(G[v][i]);
	}
	vs.push_back(v);
}
void rdfs(int v, int k) {
	used[v] = true;
	cmp[v] = k;
	for (int i = 0; i < Gt[v].size(); i++) {
		if (!used[v]) dfs(Gt[v][i])
	}
}

int scc() {
	memset(used, 0, sizeof(used));
	for (int v = 0; v < V; v++) {
		if(!used[v]) dfs(v);
	}
	memset(used, 0, sizeof(used));
	int k = 1;
	for (int v = V - 1; v >= 0; v--) {
		if(!used[vs[v]]) rdfs(vs[v], k++);
	}
	return k; // 表示有几组强连通分量
}

至此,应该可以说搞懂图的深度优先搜索了。

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

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

相关文章

Mysql的SQL性能分析【借助EXPLAIN分析】

性能分析 要说sql有问题&#xff0c;需要拿出证据&#xff0c;因此需要性能分析 Mysql查询优化器&#xff08;Mysql Query Optimizer&#xff09; Mysql中有专门负责优化SELECT语句的优化器模块&#xff0c;主要功能&#xff1a;通过计算分析系统中收集到的统计信息&#xf…

Xline v0.4.1: 一个用于元数据管理的分布式KV存储

Xline是什么&#xff1f;我们为什么要做Xline&#xff1f; Xline是一个基于Curp协议的&#xff0c;用于管理元数据的分布式KV存储。现有的分布式KV存储大多采用Raft共识协议&#xff0c;需要两次RTT才能完成一次请求。当部署在单个数据中心时&#xff0c;节点之间的延迟较低&a…

python机器学习——分类模型评估 分类算法(k近邻,朴素贝叶斯,决策树,随机森林,逻辑回归,svm)

目录 分类模型的评估模型优化与选择1.交叉验证2.网格搜索 【分类】K近邻算法【分类】朴素贝叶斯——文本分类实例&#xff1a;新闻数据分类 【分类】决策树和随机森林1.决策树2.决策树的算法3.代码实现实例&#xff1a;泰坦尼克号预测生死 【集成学习】随机森林1.集成学习2.随机…

LOMO:在受限资源上全参数微调

LOMO&#xff1a;Full Parameter Fine-Tuning for large language models with limited resources IntroductionMethodRethink the functionality of optimizerUsing SGD LOMO&#xff1a; LOw-Memory Optimization 实验参考 Introduction 在这篇文章中&#xff0c;作者的目的…

Go 语言进阶 - 工程进阶

前言&#xff1a; \textcolor{Green}{前言&#xff1a;} 前言&#xff1a; &#x1f49e;这个专栏就专门来记录一下寒假参加的第五期字节跳动训练营 &#x1f49e;从这个专栏里面可以迅速获得Go的知识 今天的内容包括以下两个内容。关于实践的内容我会在后续发布出来。 01.语言…

新零售破局丨2023年探索全新电商运维模式——永倍达模式深度解析

新零售破局丨2023年探索全新电商运维模式——永倍达模式深度解析 大家好&#xff01;我是微三云胡佳东&#xff0c;一家专业的电商软件开发公司的负责人。 近年来&#xff0c;随着电商的高速发展&#xff0c;不少电商平台成为了市场经济的优质榜样&#xff0c;互联网市场竞争也…

设计模型学习-UML图

1&#xff0c;简介 UML图有很多种类型&#xff0c;但掌握其中的类图、用例图和时序图就可以完成大部分的工作。其中最重要的便是「类图」&#xff0c;它是面向对象建模中最常用和最重要的图&#xff0c;是定义其他图的基础。 类图主要是用来显示系统中的类、接口以及它们之间的…

Ubuntu环境下读取罗技G29方向盘信息

本篇博客最早发布于实验室公共博客&#xff0c;但已无人维护&#xff0c;现迁移至个人博客 引言 实验室有这么酷的驾驶设备&#xff0c;来了一年还没有实际操作过&#xff0c;早就蠢蠢欲试了&#xff0c;哈哈哈不过之前负责的师兄还在就一直没敢用&#xff0c;现在他毕业了就可…

rust abc(2): 从 hello world 到整数、浮点类型

文章目录 1. 目的2. 搞懂 hello world2.1 代码2.2 fn 的含义2.3 main() 的含义2.4 println! 的含义2.5 行尾分号是必要的吗&#xff1f;2.6 左花括号可以放下一行吗&#xff1f; 3. 数据类型的例子3.1 代码3.2 rust 的注释3.3 编译运行结果3.4 基本数据类型 4. 整数类型的例子4…

SpringMVC系列-3 拦截器

背景 本文作为 SpringMVC系列 的第三篇&#xff0c;以SpringMVC系列-2 HTTP请求调用链为基础&#xff0c;介绍Spring MVC的拦截器。 1.拦截器 SpringMVC的核心实现是DispatcherServlet&#xff0c;本质是一个Servlet实现类&#xff0c;拦截器位于DispatcherServlet逻辑中&am…

MySQL进阶SQL语句2之表连接

目录 1.连接查询 1.1inner&#xff08;内连接&#xff09; 1.2left join&#xff08;左连接&#xff09; 1.3right join&#xff08;右连接&#xff09; 1.4直接查询两个表相同的字段值的数据 2. VIEW&#xff08;视图&#xff09; 2.1create view&#xff08;创建视图…

设计模式之迭代器模式笔记

设计模式之迭代器模式笔记 说明Iterator(迭代器)目录迭代器模式示例类图学生类抽象迭代器角色接口具体迭代器角色类抽象聚合角色接口具体聚合角色类测试类 说明 记录下学习设计模式-迭代器模式的写法。JDK使用版本为1.8版本。 Iterator(迭代器) 意图:提供一种方法顺序访问一…

Python2、3下载安装、环境配置和Python2、3版本共存配置

一、python 版本简介 python 包括 python2、python3 两个大版本&#xff0c;其中 python3 改进了 python2 的一些不足&#xff0c;但由于以前很多应用是用 python2 开发的&#xff0c;维护这些应用还需用到 python2&#xff0c;故 python2 尚未被完全淘汰。 北京时间 2020 年 4…

近期参与开源的心得体会

引言 最近随着Kepler项目加入CNCF sandbox&#xff0c;写一篇blog来记录下参与这个项目半年的发展的心得体会。 运营 项目的运营最好还是专注于项目自身的发展&#xff0c;围绕项目的特点&#xff0c;创新点入手&#xff0c;为大家提供价值&#xff0c;从而自然而然的扩大自…

【计算机网络】计算机网络期末自测题(一)答案

2019-2020 学年第 2 学期自测题答案及评分标准 (卷 1) 计算机网络 一、 填空题&#xff1a; 参考答案&#xff1a; 1 、 01000101 、11100111 3 、 100Mbps、双绞线、基带、全双工 [10Mbps 要求单位] 4 、 报文 5 、 ICMP 6 、 虚电路 7 、 距离矢量、链路状态 …

什么是网络安全?

文章目录 一、概述1.1 网络安全的指标1.2 网络安全的特征 二、网络安全威胁2.1 黑客能破坏的2.2 Internet安全手段2.2.1 端口扫描2.2.2 分组嗅探sniffing2.2.3 IP欺骗Spoofing 2.3 Internet安全威胁2.3.1 DOS拒绝服务 三、密码学3.1 对称加密算法3.1.1 传统加密3.1.2 现代加密技…

Redis(七):Redis基础入门

Redis基础入门 Redis用途Redis优缺点docker运行RedisRedis常用命令String命令Hash命令List命令Set命令ZSet命令全局命令 Redis事务Redis持久化机制RDBAOFRDBAOF&#xff08;默认&#xff09; Redis内存淘汰机制Redis对过期Key的处理 Redis用途 Redis是一种开源的NoSQL内存数据库…

【MySql】多版本并发控制MVCC前置知识——隐藏字段、undo日志与Read View

文章目录 3个记录隐藏列字段undo日志模拟 MVCCRead View 数据库并发的场景有三种&#xff1a; 读-读 &#xff1a;不存在任何问题&#xff0c;也不需要并发控制 读-写 &#xff1a;有线程安全问题&#xff0c;可能会造成事务隔离性问题&#xff0c;可能遇到脏读&#xff0c;幻读…

UOS系统下搭建qtcreator编译环境

文章目录 前言一、依赖包说明二、No valid kits found 问题现象三、No valid kits found 问题解决1.查找qt安装路径2.设置Qt Versions3.构建套件&#xff08;kit&#xff09;下选择Qt版本4.重新添加工程 前言 本文记录了在UOS系统下如何安装qtcreator以及涉及的依赖包安装&…

冷静期or跌落神坛:净水市场纠结,“易开得”们路在何方?

文丨琥珀消研社 作者丨余二 1986年11月1日&#xff0c;一场火灾拉开了世界三大水污染——莱茵河水污染的序幕。 是夜&#xff0c;位于瑞士巴塞尔市的桑多兹化学公司的一个化学品仓库发生火灾&#xff0c;装有约1250吨剧毒农药的钢罐爆炸&#xff0c;大火持续了4个多小时&…