本文乃Siliphen原创,转载请注明出处:http://blog.csdn.net/stevenkylelee

  上一节《Cocos2d-x
地图行走的实现1:图论与Dijkstra算法

  http://blog.csdn.net/stevenkylelee/article/details/38408253

  下一节《Cocos2d-x
地图行走的实现3:A*算法

  http://blog.csdn.net/stevenkylelee/article/details/38456419

  本节实践还有一种求最短路径算法:SPFA

1.寻路算法实现上的优化

  上一节我们实现的Dijkstra用了一个哈希表来保存搜索到的路径树。假设能用直接的訪问的方式,就不要用哈希表,由于直接訪问的方式会比哈希表更快。我们改动一下图顶点的数据结构。例如以下:

/*
图顶点
*/
class Vertex
{
friend class Graph ; public: Vertex( const string& Name )
{
m_strId = Name ; m_pGraph = 0 ;
} ~Vertex( ) { }; public: // 附加数据
unordered_map< string , void*> UserData ; public : const unordered_map< string , Edge* >& GetEdgesOut( ) const { return m_EdgesOut ; } const unordered_map< string , Edge* >& GetEdgesIn( ) const { return m_EdgesIn ; } const string& GetId( ) const { return m_strId ; } const string& GetText( ) const { return m_Text ; }
void SetText( const string& Text ) { m_Text = Text ; } Graph * GetGraph( ) { return m_pGraph ; } protected: // 出边集合
unordered_map< string , Edge* > m_EdgesOut ; // 入边集合
unordered_map< string , Edge* > m_EdgesIn ; // 节点表示的字符串
string m_Text ; // 节点的ID
string m_strId ; // 所属的图
Graph * m_pGraph ; public : // 寻路算法须要的数据
struct Pathfinding
{
// 路径代价预计
int Cost ; // 标识符
int Flag ; // 顶点的前驱顶点。 Vertex * pParent ; Pathfinding( )
{
Cost = 0 ;
Flag = 0 ;
pParent = 0 ;
}
}
PathfindingData ; };

  改动的地方是:把int m_Cost成员变量删掉。末尾添加了一个Pathfinding类型的字段。这个结构体负责保存寻路算法所须要的一些变量。尽管我们能够像这样unordered_map< Vertex* , int > , unordered_map< Vertex* , Vertex*> 动态地为顶点添加一些“暂时属性”。但这样的做法执行起来比較慢。

Pathfinding的pParent字段表示寻路算法执行完后,该顶点到起始顶点的一条”反向路径“。一直查找pParent直到为空,可追溯到起始顶点。这就是一条路径。起始顶点的Pathfinding::pParent肯定为空,由于它就是路径树的根节点。假设非起始顶点的Pathfinding::pParent为空,表示起始顶点到该顶点没有通路。

  上一节我们实现的Dijkstra是依照Dijkstra算法的思想用最简单的方法直接做的。这样做是为了更简单地表达出算法的思想。Dijkstra的算法优化就是在于如何做”选出拥有最小路径预计的顶点“。

关于这个问题的优化,能够搜索下 

fr=aladdin" style="font-family:'Microsoft YaHei'; font-size:18px">优先级队列二项堆斐波那契堆

  std有一个叫 priority_queue 的容器。就是优先级队列。是用priority_queue还是自己写一个优先级队列来优化,你们自己考虑吧。俗话说,师傅领进门,修行靠个人。(什么堆来堆去的数据结构。哥早已忘得一干二净了 

2.SPFA算法介绍


  SPFA是 Shortest Path Faster Algorithm 的缩写,中文直译过来就是:最短路径高速算法。

作用在稀疏图上通常比Dijkstra更快。是一种高效的求最短路径算法。

和Dijkstra一样,也是求某个顶点到其它全部顶点的最短路径的一种算法。用我自己理解的话来说,SPFA是这样:

  2.1.SPFA算法须要什么

  SPFA须要用到一个先进先出的队列Q。

  SPFA须要对图中的全部顶点做一个标示,标示其是否在队列Q中。能够用哈希表做映射。也能够为顶点添加一个字段。后者的实现效率更高。

  2.2.SPFA是如何运行的

  2.2.1 SPFA的初始化

  SPFA的初始化和Dijkstra类似。

  先把全部顶点的路径预计值初始化为代价最大值。比方:0x0FFFFFFF。

  全部顶点都标记为不在队列中。

  起始顶点放入队列Q中。

  起始顶点标记在队列中。

  起始顶点的最短路径预计值置为最小值,比方0。

  然后以下是一个循环

  2.2.2 SPFA循环

  循环结束的条件是队列Q为空。第一次进入循环的时候,仅仅有起始顶点一个元素。

  每次循环,弹出队列头部的一个顶点。

  对这个顶点的全部出边进行松弛。假设松弛成功,就是出边终点上相应的那个顶点的路径代价值被改变了。且这个被松弛的顶点不在队列Q中。就把这个被松弛的顶点入队Q。注意。这里顶点入队的条件有2:1.松弛成功。2.且不在队列Q中。

  当队列Q没有了元素。算法结束。

  2.3.SPFA伪代码

void Spfa( 图G,起始顶点VStart )
{
foreach( 对图G中的全部顶点进行遍历,迭代对象v表示遍历到的每个顶点对象)
{
设置顶点v的路径代价预计值为代价最大值。比如:0x0FFFFFFF
设置标示顶点v不在队列中
顶点v的前驱顶点都为空
}
起始顶点VStart路径代价预计值为最小值0
起始顶点VStart入队Q for( 假设队列Q不为空)
{
队列Q弹出一个队头元素v
记录v已经不在队列Q中了
for( 遍历从队列Q中弹出的队头顶点v的每个出边)
{
u = 边终点上的顶点
Relax( v , u,边上的权值)
if( Relax松弛成功了 && 顶点u不在队列Q中)
{
u入队Q
记录u在队列中了
}
}
}
}

  

  从以上伪代码来看。SPFA和BFS非常像:都用了队列,都是从队列弹出一个元素进行扩展子节点。

SPFA不同于BFS的扩展:SPFA的扩展子节点是有条件的。依据松弛的结果。

3.SPFA算法的实现


  Dijkstra不须要关心松弛的结果,所以之前的Dijkstra的Relax函数返回值为void。而SPFA是须要知道松弛是否成功的。它依据此结果决定松弛的顶点是否须要入队。

所以,我们实现的SPFA的Relax函数须要返回bool。

  下面。是我的SPFA实现代码

  Spfa.h

#pragma once

#include "Graph\GraphPathfinding.h"

class Spfa :
public GraphPathfinding
{
public:
Spfa( );
~Spfa( ); public : virtual void Execute( const Graph& Graph , const string& VetexId ) ; private: inline bool Relax( Vertex* pStartVertex , Vertex* pEndVertex , int Weight ) ; };

  Spfa.cpp

#include "Spfa.h"
#include <queue>
using namespace std ; Spfa::Spfa( )
{
} Spfa::~Spfa( )
{
} void Spfa::Execute( const Graph& Graph , const string& VetexId )
{
// 取得图的顶点集合
const auto& Vertexes = Graph.GetVertexes( ) ;
// 取得起始顶点对象
Vertex *pVStart = Vertexes.find( VetexId )->second ; // Spfa算法须要一个队列保存顶点
queue< Vertex* > Q ; // 初始化
for ( auto& it : Vertexes )
{
Vertex *pV = it.second ; pV->PathfindingData.Cost = 0x0FFFFFFF ;
//IsInQueue[ pV ] = false ;
pV->PathfindingData.Flag = false ;
pV->PathfindingData.pParent = 0 ; // 顶点的父路径都设置为空
}
pVStart->PathfindingData.Cost = 0 ; // 起始顶点的路径代价为0
pVStart->PathfindingData.Flag = true ; // 起始顶点在队列中
//m_Ret.PathTree[ pVStart ] = 0 ; // 起始顶点的父路径为空
Q.push( pVStart ) ; // 起始顶点先入队 // spfa算法
for ( ; Q.size( ) ; )
{
auto pStartVertex = Q.front( ) ; Q.pop( ) ; // 队列弹出一个顶点v
pStartVertex->PathfindingData.Flag = false ; // 松弛v的全部出边
const auto& Eo = pStartVertex->GetEdgesOut( ) ;
for ( auto& it : Eo )
{
auto pEdge = it.second ;
auto pEndVertex = pEdge->GetEndVertex( ) ;
bool bRelaxRet = Relax( pStartVertex , pEndVertex , pEdge->GetWeight( ) ) ;
if ( bRelaxRet )
{
// 假设对于出边松弛成功。且出边相应的终点顶点不在队列中的话。就插入队尾
if ( pEndVertex->PathfindingData.Flag == false )
{
Q.push( pEndVertex ) ;
pEndVertex->PathfindingData.Flag = false ;
} } }
// end for }
// end for } bool Spfa::Relax( Vertex* pStartVertex , Vertex* pEndVertex , int Weight )
{
int n = pStartVertex->PathfindingData.Cost + Weight ;
if ( n < pEndVertex->PathfindingData.Cost )
{
// 更新路径代价
pEndVertex->PathfindingData.Cost = n ;
// 更新路径
//m_Ret.PathTree[ pEndVertex ] = pStartVertex ;
pEndVertex->PathfindingData.pParent = pStartVertex ; return true ;
} return false ;
}

4.Dijkstra与SPFA在实际上的比較

  下图是构造了一个比較大的图。对于一次寻路同一时候用了Dijkstra和SPFA。图的左下角显示2个算法所用的时间。

  对于上图来说,SPFA的执行要快于Dijkstra。

当然,是和没实用不论什么优化的Dijkstra比較的结果。一般来说Dijkstra执行比較稳定,优化后也能够得到不错的性能。而SPFA的优势在于稀疏图,也就是边数较少的图。

原因非常明显,SPFA不须要像Dijkstra那样去选最小路径代价的顶点出来松弛。它仅仅是从队列里面弹出一个就可以。

假设边数越少,入队的顶点也就越少。

5.本文project源码下载

  上一节的project代码不小心弄成了8分。这次设置为0分啦。

  下载地址:http://download.csdn.net/detail/stevenkylelee/7731827

版权声明:本文博客原创文章,博客,未经同意,不得转载。

Cocos2d-x 2地图步行实现:SPFA算法的更多相关文章

  1. Cocos2d-x 地图步行实现1:图论Dijkstra算法

    下一节<Cocos2d-x 地图行走的实现2:SPFA算法>: http://blog.csdn.net/stevenkylelee/article/details/38440663 本文 ...

  2. [hihoCoder] #1093 : 最短路径·三:SPFA算法

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 万圣节的晚上,小Hi和小Ho在吃过晚饭之后,来到了一个巨大的鬼屋! 鬼屋中一共有N个地点,分别编号为1..N,这N个地点之 ...

  3. 最短路之SPFA算法

    部分来自:http://blog.csdn.net/juststeps/article/details/8772755 求最短路径的算法有许多种,除了排序外,恐怕是OI界中解决同一类问题算法最多的了. ...

  4. (最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法模板的整理与介绍

    这一篇博客以一些OJ上的题目为载体.整理一下最短路径算法.会陆续的更新... 一.多源最短路算法--floyd算法 floyd算法主要用于求随意两点间的最短路径.也成最短最短路径问题. 核心代码: / ...

  5. 最短路径----SPFA算法

    求最短路径的算法有许多种,除了排序外,恐怕是ACM界中解决同一类问题算法最多的了.最熟悉的无疑是Dijkstra,接着是Bellman-Ford,它们都可以求出由一个源点向其他各点的最短路径:如果我们 ...

  6. 最短路径问题的Dijkstra和SPFA算法总结

    Dijkstra算法: 解决带非负权重图的单元最短路径问题.时间复杂度为O(V*V+E) 算法精髓:维持一组节点集合S,从源节点到该集合中的点的最短路径已被找到,算法重复从剩余的节点集V-S中选择最短 ...

  7. [知识点]SPFA算法

    // 此博文为迁移而来,写于2015年4月9日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vx93.html 1.前言 ...

  8. SPFA算法

    SPFA算法 一.算法简介 SPFA(Shortest Path Faster Algorithm)算法是求单源最短路径的一种算法,它是Bellman-ford的队列优化,它是一种十分高效的最短路算法 ...

  9. SPFA算法学习笔记

    一.理论准备 为了学习网络流,先水一道spfa. SPFA算法是1994年西南交通大学段凡丁提出,只要最短路径存在,SPFA算法必定能求出最小值,SPFA对Bellman-Ford算法优化的关键之处在 ...

随机推荐

  1. 修改合同号的bapi

    这个例子是在合同号中新增项目号: 1.先要读取该合同号的信息用一个BAPI BAPI_CONTRACT_GETDETAIL 2.调用修改合同号的BAPI. 代码如下: REPORT ztest_cla ...

  2. RapidCRC : Home

    RapidCRC : Home   What is RapidCRC? RapidCRC is a tool for windows for checking and creating CRC32 a ...

  3. Android各代码层获取系统时间的方法

    1. 在java层,long now = SystemClock.uptimeMillis(); 2. 在native层,nsecs_t now = systemTime(SYSTEM_TIME_MO ...

  4. .Net 配置文件——继承ConfigurationSection实现自己定义处理类处理自己定义配置节点

    除了使用继承IConfigurationSectionHandler的方法定义处理自己定义节点的类.还能够通过继承ConfigurationSection类实现相同效果. 首先说下.Net配置文件里一 ...

  5. System.getProperty("catalina.home")

    System.getProperty("catalina.base"),服务器配置目录

  6. 【Demo 0002】Java基础-语句

    本章学习要点:        1.  掌握Java关健语句使用方法;          2.  理解与语句相关的关键字用法; 一.Java 关键语句        Java语句以及关联关键字与C完全相 ...

  7. 从源代码角度分析ViewStub 疑问与原理

    一.提出疑问     ViewStub比較简单.之前文章都提及到<Android 性能优化 三 布局优化ViewStub标签的使用>.可是在使用过程中有一个疑惑,究竟是ViewStub上设 ...

  8. 【从cocos2d-x学习设计模式】第一阶段:辛格尔顿

    设计模式,它总结了前辈在许多方案重用代码.它是一个想法. 因为我们爱cocos2d-x,然后我们从去cocos2d-x在设计模式中,右一起学习!本篇解释未来辛格尔顿. 提cocos2d-x中间Dire ...

  9. xp下Oracle数据库导入SQLServer数据库数据

    Oracle数据库利用ODBC数据源.PLSQL Developer导入SQLServer数据库数据 操作: 建立数据源:控制面板→管理工具→数据源 (ODBC) 打开,界面如下: 点击添加,界面如下 ...

  10. 14.3.2.4 Locking Reads 锁定读

    14.3.2.4 Locking Reads 锁定读 如果你的查询数据,然后插入或者更新相关的数据 在同一个事务, 普通的SELECT 语句不足以给予足够保护. 其他事务可以更新或者删除相同的你要查询 ...