BFS、SPFA、Dijkstra算法中vis数组的用法对比

2024-01-05 22:44

本文主要是介绍BFS、SPFA、Dijkstra算法中vis数组的用法对比,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

先上代码:

BFS:

bool vis[MAXN];
int dis[MAXN];void bfs(int s)
{queue<int> Q;Q.push(s);vis[s] = true;while(!Q.empty()){int u = Q.front();Q.pop();for(int e = first[u]; e; e = nxt[e]){int v = go[e];if(vis[v]) continue;dis[v] = dis[u] + 1;vis[v] = true;Q.push(v);}}
}

SPFA:

int dis[MAXN];
bool vis[MAXN];void SPFA(int s)
{for(int i = 1; i <= n; ++i) dis[i] = inf;dis[s] = 0;queue<int> Q;Q.push(s);vis[s] = true;while(!Q.empty()){int u = Q.front();Q.pop();vis[u] = false;for(int e = first[u]; e; e = nxt[e]){int v = go[e];if(dis[v] > dis[u] + val[e]){dis[v] = dis[u] + val[e];if(!vis[v]){Q.push(v);vis[v] = true;}}}}
}

Dijkstra:

struct node
{int u, ds;friend bool operator<(const node& n1, const node& n2){return n1.ds > n2.ds;}
};bool vis[MAXN];
int dis[MAXN];void Dijkstra(int s)
{memset(dis, 0x3f, sizeof(dis));dis[s] = 0;priority_queue<node> Q;Q.push({s, 0});while(!Q.empty()){int u = Q.top().u;Q.pop();if(vis[u]) continue;vis[u] = true;for(int e = first[u]; e; e = nxt[e]){int v = go[e];if(dis[v] > dis[u] + val[e]){dis[v] = dis[u] + val[e];if(!vis[v]) Q.push({v, dis[v]});}}}
}

分析对比

在BFS算法中

当Q.push()调用时顺便设置vis数组。原因是,BFS只能应用于边权为1的有向无环图(Directed Acyclic Graph, DAG),节点v第一次被搜索到时的dis值就是s→v的最短路。因此,在后面又访问到v时,根本不需要干任何事情,既不需要加入队列,也不需要更新dis值。因此,vis数组确保每个点只入队一次、dis值只被更新一次。

在SPFA(队列优化的Bellman-Ford算法)中

vis数组标记了节点v是否在Q中。元素出队时vis设置为false,入队时设置为true。如果节点v已经在队列中,则不需要再次入队。SPFA可以看作时BFS的衍生算法,它可以用于任意图,包括多重图、含有自环的图。对于已经访问过的节点,如果形成环路,后面的节点可以更新前面节点的dis值。其实可以去掉vis数组,但是这样会导致某一时刻队列中包含多个同一元素,极大地降低了算法的效率。因为同一时刻队列中有多个同一元素和只有一个该元素的效果是一样的。

Dijkstra算法是一个较为复杂的算法。根据《算法导论》

Dijkstra算法在运行过程中维持的关键信息是一组结点集合S。从源结点s到该集合中每个结点之间的最短路径已经被找到。算法重复从结点集V-S中选择最短路径估计最小的结点u,将u加入到集合S,然后对所有从u出发的边进行松弛。(P383)
算法维持的不变式为Q=V-S。(P384)(注:V是所有结点的集合,Q是优先队列,S为已经求出最短路的结点集合)
Dijkstra的暴力算法会维持该循环不变式。但我们的算法并不会,因为只有被松驰过的结点才有可能加入队列。理由是,没有被松驰过的结点其dis值一定是inf。Dijkstra还满足一个定理
定理 队列Q中dis值最小的结点u,其dis值就是s→u的最短路。
因此,我们的vis数组的意义是标志结点u是否属于集合S,即结点u的最短路是否已经被计算出来。当u出队时,根据定理,其最短路一定已经被计算出来了,因此标记vis[u]=true。在这之前,我们写道:
if(vis[u]) continue;
不加这句话会TLE。原因何在?为什么同一个结点会出队两次?因为它可能入队两次。或者说,它出队之前被连续松弛了超过一次,导致它在队里有好几个。因此我们使用vis数组,保证不走冤枉路。(亲测,if(!vis[v]) Q.push({v, dis[v]})中if(!vis[v])可以去掉)
区别Dijkstra和SPFA的一个方法是:Dijkstra末有4个右花括号,SPFA有5个

这篇关于BFS、SPFA、Dijkstra算法中vis数组的用法对比的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/574393

相关文章

MySQL中between and的基本用法、范围查询示例详解

《MySQL中betweenand的基本用法、范围查询示例详解》BETWEENAND操作符在MySQL中用于选择在两个值之间的数据,包括边界值,它支持数值和日期类型,示例展示了如何使用BETWEEN... 目录一、between and语法二、使用示例2.1、betwphpeen and数值查询2.2、be

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u

Java多种文件复制方式以及效率对比分析

《Java多种文件复制方式以及效率对比分析》本文总结了Java复制文件的多种方式,包括传统的字节流、字符流、NIO系列、第三方包中的FileUtils等,并提供了不同方式的效率比较,同时,还介绍了遍历... 目录1 背景2 概述3 遍历3.1listFiles()3.2list()3.3org.codeha

CPython与PyPy解释器架构的性能测试结果对比

《CPython与PyPy解释器架构的性能测试结果对比》Python解释器的选择对应用程序性能有着决定性影响,CPython以其稳定性和丰富的生态系统著称;而PyPy作为基于JIT(即时编译)技术的替... 目录引言python解释器架构概述CPython架构解析PyPy架构解析架构对比可视化性能基准测试测

Java序列化之serialVersionUID的用法解读

《Java序列化之serialVersionUID的用法解读》Java序列化之serialVersionUID:本文介绍了Java对象的序列化和反序列化过程,强调了serialVersionUID的作... 目录JavChina编程a序列化之serialVersionUID什么是序列化为什么要序列化serialV

python3中正则表达式处理函数用法总结

《python3中正则表达式处理函数用法总结》Python中的正则表达式是一个强大的文本处理工具,用于匹配、查找、替换等操作,在Python中正则表达式的操作主要通过内置的re模块来实现,这篇文章主要... 目录前言re.match函数re.search方法re.match 与 re.search的区别检索

MySQL 中的 JSON_CONTAIN用法示例详解

《MySQL中的JSON_CONTAIN用法示例详解》JSON_CONTAINS函数用于检查一个JSON文档中是否包含另一个JSON文档,这篇文章给大家介绍JSON_CONTAINS的用法、语法、... 目录深入了解 mysql 中的 jsON_CONTAINS1. JSON_CONTAINS 函数的概述2