深度广度优先算法、A*算法

  • 深度优先算法(DFS)
    • 实现步骤
    • 实现代码
  • 广度优先算法(BFS)
    • 实现步骤
    • 实现代码
  • A*算法
    • Dijkstra算法
    • A*算法介绍
    • 实现流程
    • 实现过程
    • 实现代码

深度优先算法(DFS)

深度优先搜索属于图算法的一种,是一个针对图和树的遍历算法,英文缩写为DFS即Depth First Search。深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。一般用堆数据结构来辅助实现DFS算法。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。

实现步骤

在这里我用一个数组来进行实现,1作为起点,44作为终点
指定上下左右的顺序,按照顺序的优先级来遍历,优先级高的能走则优先走

1.从起点开始,由于上超出数组不能走,则开始向下走,同时将起点标记为走过的状态;由于上下的优先级比较高,所以一直走到55;

2.当到达55的时候,上下左都不能走,则向右走一步,发现不是终点,同时上可以走,则先走上方向

3.按照这种顺序一直走,直到走到33的位置

4.这个时候发现在33的位置每个方向都走不通,那么我们就来后退一步,然后按照之前的顺序继续遍历

5.当我们走到终点,就输出路径

实现代码

Map.h

#pragma once
#include <iostream>
using namespace std;
enum
{START,END,WALL,OLD,
};
class Map
{public:int m_nMap[7][9];int m_nMap2[9][11];Map();~Map();
};

Map.cpp

#include "stdafx.h"
#include "Map.h"Map::Map()
{int n = 1;for (int i = 0; i < 7; i++){for (int j = 0; j < 9; j++){m_nMap[i][j] = n;n++;}}int Map[9][11] ={    { WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL },{ WALL, START, 0, 0, 0, 0, 0, WALL, 0, 0, WALL },{ WALL, 0, 0, WALL, WALL, 0, 0, WALL, 0, 0, WALL },{ WALL, 0, 0, WALL, 0, 0, WALL, WALL, 0, 0, WALL },{ WALL, 0, 0, 0, WALL, 0, 0, WALL, 0, 0, WALL },{ WALL, 0, 0, 0, WALL, 0, 0, 0, END, 0, WALL },{ WALL, 0, 0, 0, 0, 0, 0, 0, 0, 0, WALL },{ WALL, 0, 0, 0, WALL, 0, 0, 0, 0, 0, WALL },{ WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL },};for (int i = 0; i < 9; i++){for (int j = 0; j < 11; j++){m_nMap2[i][j] = Map[i][j];}}
}
Map::~Map()
{}

Breath.h

#pragma once
#include <vector>
using namespace std;
#include "Map.h"
struct Pos1
{int x;int y;int sign;
};
class Breadth
{vector<Pos1> PosVector;Map map;
public:bool BFS(int x, int y,int sign);void GetRoad();Breadth();~Breadth();
};

Breath.cpp

#include "stdafx.h"
#include "Breadth.h"Breadth::Breadth()
{}
bool Breadth::BFS(int x, int y, int sign)
{Pos1 pos = { x, y, sign };//1.将起点添加到路径中   PosVector.push_back(pos);int i = 0;while (i < PosVector.size()){int z = PosVector.size() - 1;int Posx = PosVector[i].x + 1, Posy = PosVector[i].y + 1;if (map.m_nMap2[Posx][Posy] == END){return 1;}else{int m = PosVector[i].x;int n = PosVector[i].y;sign++;if (map.m_nMap2[Posx - 1][Posy] == 0 || map.m_nMap2[Posx - 1][Posy] == END){   map.m_nMap2[Posx][Posy] = OLD;if (map.m_nMap2[Posx - 1][Posy] == END){Pos1 end = { m - 1, n, sign };PosVector.push_back(end);return 1;}elsemap.m_nMap2[Posx - 1][Posy] = OLD;Pos1 temp = { m - 1,n, sign };PosVector.push_back(temp);}if (map.m_nMap2[Posx + 1][Posy] == 0 || map.m_nMap2[Posx + 1][Posy] == END){map.m_nMap2[Posx][Posy] = OLD;if (map.m_nMap2[Posx + 1][Posy] == END){Pos1 end = { m + 1, n, sign };PosVector.push_back(end);return 1;}elsemap.m_nMap2[Posx + 1][Posy] = OLD;Pos1 temp = { m + 1, n, sign };PosVector.push_back(temp);}if (map.m_nMap2[Posx][Posy + 1] == 0 || map.m_nMap2[Posx][Posy + 1] == END){map.m_nMap2[Posx][Posy] = OLD;if (map.m_nMap2[Posx][Posy + 1] == END){Pos1 end = { m, n + 1, sign };PosVector.push_back(end);return 1;}elsemap.m_nMap2[Posx][Posy + 1] = OLD;Pos1 temp = { m, n+1, sign };PosVector.push_back(temp);         }if (map.m_nMap2[Posx][Posy - 1] == 0 || map.m_nMap2[Posx][Posy - 1] == END){map.m_nMap2[Posx][Posy] = OLD;if (map.m_nMap2[Posx][Posy - 1] == END){Pos1 end = { m, n - 1, sign };PosVector.push_back(end);return 1;}elsemap.m_nMap2[Posx][Posy - 1] = OLD;Pos1 temp = { m, n-1, sign };PosVector.push_back(temp);         }}i++;        }PosVector.pop_back();return 0;
}
void Breadth::GetRoad()
{if (!PosVector.empty()){vector<Pos1>::iterator PosIt = PosVector.end();PosIt--;cout << map.m_nMap[PosIt->x][PosIt->y] << "<-";int sign = PosIt->sign;int n = 0;while (n != -1){if (PosVector[sign].sign == -1){cout << map.m_nMap[PosVector[sign].x][PosVector[sign].y];}else{cout << map.m_nMap[PosVector[sign].x][PosVector[sign].y] << "<-";}sign = PosVector[sign].sign;n = sign;}cout << endl;}
}
Breadth::~Breadth()
{}

广度优先算法(BFS)

广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历算法这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。基本过程,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。如果所有节点均被访问,则算法中止。一般用队列数据结构来辅助实现BFS算法。

实现步骤

1.将起点加入到顺序表,从顺序表中取出第一个也就是起点,遍历它周围四个点,判断是否是终点,如果不是则把这四个点加入到顺序表中,这时顺序表中存放的数据为{1,10,2}

2.接下来取出顺序表中的第二个数据也就是10,遍历周围的四个点,如果不是终点则存入顺序表,这时顺序表的数据为{1,10,2,19,11}

3.判断完10之后,我们按照顺序表存放的数据接着向下走,遍历2周围的四个点,并存入顺序表,这时数据为{1,10,2,19,11,3}
到这里大家应该发现了规律了,就是将顺序表中数据周围的四个点遍历,并且将遍历的四个点加入到顺序表末尾作为新数据,然后我们一个个取出顺序表中的数据,继续上述过程,知道找到终点的位置,并且从终点反推路径;在这里发现忘记跟大家说了,在顺序表中存放数据的同时,记得将它的上一个数据在顺序表中的位置也同时记下来,例如存放19的时候同时记录下他的上一个,也就是10在顺序表中的位置(下标)为1,存放28的时候记录19在顺序表中的位置为3,这里大家可以用结构体存放数据和位置,顺序表中存放结构体。

实现代码

Deep.h

#pragma once
#include <vector>
using namespace std;
#include "Map.h"
struct Pos
{int x;int y;
};
class Deep
{vector<Pos> PosVector;Map map;
public:bool DFS(int x, int y);void GetRoad();Deep();~Deep();
};

Deep.cpp

#include "stdafx.h"
#include "Deep.h"Deep::Deep()
{}
bool Deep::DFS(int x, int y)
{Pos pos = { x, y };PosVector.push_back(pos);int Posx = x + 1, Posy = y + 1;if (map.m_nMap2[Posx][Posy] == END){return 1;}if (map.m_nMap2[Posx - 1][Posy] == 0 || map.m_nMap2[Posx - 1][Posy] == END){map.m_nMap2[Posx][Posy] = OLD;if (DFS(x - 1, y) == 1){return 1;}}if (map.m_nMap2[Posx + 1][Posy] == 0 || map.m_nMap2[Posx + 1][Posy] == END){map.m_nMap2[Posx][Posy] = OLD;if (DFS(x + 1, y) == 1){return 1;}}if (map.m_nMap2[Posx][Posy + 1] == 0 || map.m_nMap2[Posx][Posy + 1] == END){map.m_nMap2[Posx][Posy] = OLD;if (DFS(x, y + 1) == 1){return 1;}}if (map.m_nMap2[Posx][Posy - 1] == 0 || map.m_nMap2[Posx][Posy - 1] == END){map.m_nMap2[Posx][Posy] = OLD;if (DFS(x, y - 1) == 1){return 1;}}PosVector.pop_back();return 0;
}
void Deep::GetRoad()
{if (!PosVector.empty()){       vector<Pos>::iterator PosIt = PosVector.begin();for (; PosIt != PosVector.end(); PosIt++){    if (map.m_nMap2[PosIt->x + 1][PosIt->y + 1] == END){cout << map.m_nMap[PosIt->x][PosIt->y];}else{cout << map.m_nMap[PosIt->x][PosIt->y] << "->";}}cout << endl;}
}
Deep::~Deep()
{PosVector.clear();
}

A*算法

Dijkstra算法

迪杰斯特拉(Dijkstra)算法是典型的最短路径的算法,由荷兰计算机科学家迪杰斯特拉于1959年提出,用来求得从起始点到其他所有点最短路径。该算法采用了贪心的思想,每次都查找与该点距离最近的点,也因为这样,它不能用来解决存在负权边的图。解决的问题可描述为:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点vs到其余各点的最短路径。

1.Dijkstra算法的基本思想:

通过Dijkstra计算图G中的最短路径时,需要指定起点vs(即从顶点vs开始计算)。此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点vs的距离)。初始时,S中只有起点vs;U中是除vs之外的顶点,并且U中顶点的路径是"起点vs到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。重复该操作,直到遍历完所有顶点。
  
2.算法步骤:

a.初始时,S只包含源点,即S={vs},vs的距离为0。U包含除vs外的其他顶点,即U={其余顶点},若u不是vs的出边邻接点,则<u,vs>权值为∞;

b.从U中选取一个距离vs最小的顶点k,把k加入S中(该选定的距离就是vs到k的最短路径长度min);

c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点vs到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,即dist[u] = min( dist[u], min + w[k][u] );

d.重复步骤b和c直到所有顶点都包含在S中。

A*算法介绍

A*[1](A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是许多其他问题的常用启发式算法。A*算法作为Dijkstra算法的扩展,因其高效性而被广泛应用于寻路及图的遍历。

公式表示为: f(n)=g(n)+h(n),

其中,
f(n) 是从初始状态经由状态n到目标状态的代价估计,
g(n) 是在状态空间中从初始状态到状态n的实际代价,
h(n) 是从状态n到目标状态的最佳路径的估计代价(欧几里(斜边的长度)/曼哈顿距离(x的距离+y的距离差))。

(对于路径搜索问题,状态就是图中的节点,代价就是距离)

h(n)的选取

保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取(或者说h(n)的选取)。

我们以d(n)表达状态n到目标状态的距离,那么h(n)的选取大致有如下三种情况:

1.如果h(n)< d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。

2.如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。

3.如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。

实现流程

把起始点添加到开启列表
do
{寻找开启列表中F值最低的格子, 我们称它为当前格.把它切换到关闭列表,从开启列表移除;对当前格相邻的8格中的每一个if (它不可通过 || 已经在 "关闭列表" 中){什么也不做.}if (它不在开启列表中){把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的FGH值}else   //它已经在开启列表中{if (用 G 值为参考检查新的路径是否更好, 更低的G值意味着更好的路径){把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值.}}//查找结束点是否已经在开启列表中,如果在,这时候路径被找到。
} while(当开启列表为空时,跳出循环);
最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.

实现过程

搜索区域(The Search Area)
我们假设某人要从 A 点移动到 B 点,但是这两点之间被一堵墙隔开。如图 1 ,绿色是 A ,红色是 B ,中间蓝色是墙。

图 1

你应该注意到了,我们把要搜寻的区域划分成了正方形的格子。这是寻路的第一步,简化搜索区域,就像我们这里做的一样。这个特殊的方法把我们的搜索区域简化为了 2 维数组。数组的每一项代表一个格子,它的状态就是可走 (walkalbe) 和不可走 (unwalkable) 。通过计算出从 A 到 B需要走过哪些方格,就找到了路径。一旦路径找到了,人物便从一个方格的中心移动到另一个方格的中心,直至到达目的地。

方格的中心点我们成为“节点 (nodes) ”。如果你读过其他关于 A* 寻路算法的文章,你会发现人们常常都在讨论节点。为什么不直接描述为方格呢?因为我们有可能把搜索区域划为为其他多变形而不是正方形,例如可以是六边形,矩形,甚至可以是任意多变形。而节点可以放在任意多边形里面,可以放在多变形的中心,也可以放在多边形的边上。我们使用这个系统,因为它最简单。

开始搜索(Starting the Search)
一旦我们把搜寻区域简化为一组可以量化的节点后,就像上面做的一样,我们下一步要做的便是查找最短路径。在 A* 中,我们从起点开始,检查其相邻的方格,然后向四周扩展,直至找到目标。

我们这样开始我们的寻路旅途:

从起点 A 开始,并把它就加入到一个由方格组成的 open list( 开放列表 ) 中。这个 open list 有点像是一个购物单。当然现在 open list 里只有一项,它就是起点 A ,后面会慢慢加入更多的项。 Open list 里的格子是路径可能会是沿途经过的,也有可能不经过。基本上 open list 是一个待检查的方格列表。

查看与起点 A 相邻的方格 ( 忽略其中墙壁所占领的方格,河流所占领的方格及其他非法地形占领的方格 ) ,把其中可走的 (walkable) 或可到达的 (reachable) 方格也加入到 open list 中。把起点 A 设置为这些方格的父亲 (parent node 或 parent square) 。当我们在追踪路径时,这些父节点的内容是很重要的。稍后解释。

把 A 从 open list 中移除,加入到 close list( 封闭列表 ) 中, close list 中的每个方格都是现在不需要再关注的。

如下图所示,深绿色的方格为起点,它的外框是亮蓝色,表示该方格被加入到了 close list 。与它相邻的黑色方格是需要被检查的,他们的外框是亮绿色。每个黑方格都有一个灰色的指针指向他们的父节点,这里是起点 A 。

图 2 。

下一步,我们需要从 open list 中选一个与起点 A 相邻的方格,按下面描述的一样或多或少的重复前面的步骤。但是到底选择哪个方格好呢?具有最小 F 值的那个。

路径排序(Path Sorting)
计算出组成路径的方格的关键是下面这个等式:

F = G + H

这里,

G = 从起点 A 移动到指定方格的移动代价,沿着到达该方格而生成的路径。

H = 从指定的方格移动到终点 B 的估算成本。这个通常被称为试探法,有点让人混淆。为什么这么叫呢,因为这是个猜测。直到我们找到了路径我们才会知道真正的距离,因为途中有各种各样的东西 ( 比如墙壁,水等 ) 。本教程将教你一种计算 H 的方法,你也可以在网上找到其他方法。

我们的路径是这么产生的:反复遍历 open list ,选择 F 值最小的方格。这个过程稍后详细描述。我们还是先看看怎么去计算上面的等式。

如上所述, G 是从起点A移动到指定方格的移动代价。在本例中,横向和纵向的移动代价为 10 ,对角线的移动代价为 14 。之所以使用这些数据,是因为实际的对角移动距离是 2 的平方根,或者是近似的 1.414 倍的横向或纵向移动代价。使用 10 和 14 就是为了简单起见。比例是对的,我们避免了开放和小数的计算。这并不是我们没有这个能力或是不喜欢数学。使用这些数字也可以使计算机更快。稍后你便会发现,如果不使用这些技巧,寻路算法将很慢。

既然我们是沿着到达指定方格的路径来计算 G 值,那么计算出该方格的 G 值的方法就是找出其父亲的 G 值,然后按父亲是直线方向还是斜线方向加上 10 或 14 。随着我们离开起点而得到更多的方格,这个方法会变得更加明朗。

有很多方法可以估算 H 值。这里我们使用 Manhattan 方法,计算从当前方格横向或纵向移动到达目标所经过的方格数,忽略对角移动,然后把总数乘以 10 。之所以叫做 Manhattan 方法,是因为这很像统计从一个地点到另一个地点所穿过的街区数,而你不能斜向穿过街区。重要的是,计算 H 是,要忽略路径中的障碍物。这是对剩余距离的估算值,而不是实际值,因此才称为试探法。

把 G 和 H 相加便得到 F 。我们第一步的结果如下图所示。每个方格都标上了 F , G , H 的值,就像起点右边的方格那样,左上角是 F ,左下角是 G ,右下角是 H 。

图 3

好,现在让我们看看其中的一些方格。在标有字母的方格, G = 10 。这是因为水平方向从起点到那里只有一个方格的距离。与起点直接相邻的上方,下方,左方的方格的 G 值都是 10 ,对角线的方格 G 值都是 14 。

H 值通过估算起点于终点 ( 红色方格 ) 的 Manhattan 距离得到,仅作横向和纵向移动,并且忽略沿途的墙壁。使用这种方式,起点右边的方格到终点有 3 个方格的距离,因此 H = 30 。这个方格上方的方格到终点有 4 个方格的距离 ( 注意只计算横向和纵向距离 ) ,因此 H = 40 。对于其他的方格,你可以用同样的方法知道 H 值是如何得来的。

每个方格的 F 值,再说一次,直接把 G 值和 H 值相加就可以了。

继续搜索(Continuing the Search)
为了继续搜索,我们从 open list 中选择 F 值最小的 ( 方格 ) 节点,然后对所选择的方格作如下操作:

把它从 open list 里取出,放到 close list 中。

检查所有与它相邻的方格,忽略其中在 close list 中或是不可走 (unwalkable) 的方格 ( 比如墙,水,或是其他非法地形 ) ,如果方格不在open lsit 中,则把它们加入到 open list 中。

把我们选定的方格设置为这些新加入的方格的父亲。

如果某个相邻的方格已经在 open list 中,则检查这条路径是否更优,也就是说经由当前方格 ( 我们选中的方格 ) 到达那个方格是否具有更小的 G 值。如果没有,不做任何操作。

相反,如果 G 值更小,则把那个方格的父亲设为当前方格 ( 我们选中的方格 ) ,然后重新计算那个方格的 F 值和 G 值。如果你还是很混淆,请参考下图。

图 4

Ok ,让我们看看它是怎么工作的。在我们最初的 9 个方格中,还有 8 个在 open list 中,起点被放入了 close list 中。在这些方格中,起点右边的格子的 F 值 40 最小,因此我们选择这个方格作为下一个要处理的方格。它的外框用蓝线打亮。

首先,我们把它从 open list 移到 close list 中 ( 这就是为什么用蓝线打亮的原因了 ) 。然后我们检查与它相邻的方格。它右边的方格是墙壁,我们忽略。它左边的方格是起点,在 close list 中,我们也忽略。其他 4 个相邻的方格均在 open list 中,我们需要检查经由这个方格到达那里的路径是否更好,使用 G 值来判定。让我们看看上面的方格。它现在的 G 值为 14 。如果我们经由当前方格到达那里, G 值将会为 20(其中 10 为到达当前方格的 G 值,此外还要加上从当前方格纵向移动到上面方格的 G 值 10) 。显然 20 比 14 大,因此这不是最优的路径。如果你看图你就会明白。直接从起点沿对角线移动到那个方格比先横向移动再纵向移动要好。

当把 4 个已经在 open list 中的相邻方格都检查后,没有发现经由当前方格的更好路径,因此我们不做任何改变。现在我们已经检查了当前方格的所有相邻的方格,并也对他们作了处理,是时候选择下一个待处理的方格了。

因此再次遍历我们的 open list ,现在它只有 7 个方格了,我们需要选择 F 值最小的那个。有趣的是,这次有两个方格的 F 值都 54 ,选哪个呢?没什么关系。从速度上考虑,选择最后加入 open list 的方格更快。这导致了在寻路过程中,当靠近目标时,优先使用新找到的方格的偏好。但是这并不重要。 ( 对相同数据的不同对待,导致两中版本的 A* 找到等长的不同路径 ) 。

我们选择起点右下方的方格,如下图所示。

图 5

这次,当我们检查相邻的方格时,我们发现它右边的方格是墙,忽略之。上面的也一样。

我们把墙下面的一格也忽略掉。为什么?因为如果不穿越墙角的话,你不能直接从当前方格移动到那个方格。你需要先往下走,然后再移动到那个方格,这样来绕过墙角。 ( 注意:穿越墙角的规则是可选的,依赖于你的节点是怎么放置的 )

这样还剩下 5 个相邻的方格。当前方格下面的 2 个方格还没有加入 open list ,所以把它们加入,同时把当前方格设为他们的父亲。在剩下的3 个方格中,有 2 个已经在 close list 中 ( 一个是起点,一个是当前方格上面的方格,外框被加亮的 ) ,我们忽略它们。最后一个方格,也就是当前方格左边的方格,我们检查经由当前方格到达那里是否具有更小的 G 值。没有。因此我们准备从 open list 中选择下一个待处理的方格。

不断重复这个过程,直到把终点也加入到了 open list 中,此时如下图所示。

图 6

注意,在起点下面 2 格的方格的父亲已经与前面不同了。之前它的 G 值是 28 并且指向它右上方的方格。现在它的 G 值为 20 ,并且指向它正上方的方格。这在寻路过程中的某处发生,使用新路径时 G 值经过检查并且变得更低,因此父节点被重新设置, G 和 F 值被重新计算。尽管这一变化在本例中并不重要,但是在很多场合中,这种变化会导致寻路结果的巨大变化。

那么我们怎么样去确定实际路径呢?很简单,从终点开始,按着箭头向父节点移动,这样你就被带回到了起点,这就是你的路径。如下图所示。从起点 A 移动到终点 B 就是简单从路径上的一个方格的中心移动到另一个方格的中心,直至目标。就是这么简单!

图 7

实现代码

AStar.h

#include <vector>
#include <list>const int kCost1 = 10; //直移一格消耗
const int kCost2 = 14; //斜移一格消耗struct Point
{int x, y; //点坐标,x代表横排,y代表竖列int F, G, H; //F=G+HPoint* parent; //当前点的父节点,用于回溯查找路径Point(int x, int y):x(x),y(y),F(0),G(0),H(0),parent(NULL)  //带参构造{}
};
//A*算法
class Astar
{public://初始化A*算法使用的地图void InitAstar(std::vector<std::vector<int>> &_maze);//获取路径:1.开始点 2.结束点 3.斜边移动是否考虑四周有障碍物std::list<Point*> GetPath(Point &startPoint, Point &endPoint, bool isIgnoreCorner);
private://查找路径的方法:A*算法的核心逻辑Point *findPath(Point &startPoint, Point &endPoint, bool isIgnoreCorner);//获取周围八个点,如果能走或者不在关闭列表中,则放入vector并返回std::vector<Point *> getSurroundPoints(const Point *point, bool isIgnoreCorner) const;//判断某个点是否可走bool isCanreach(const Point *point, const Point *target, bool isIgnoreCorner) const;//判断开启/关闭列表中是否包含某点Point *isInList(const std::list<Point *> &list, const Point *point) const;//从开启列表中查找F值最小的节点Point *getLeastFpoint();//计算FGH值int calcG(Point *temp_start, Point *point);int calcH(Point *point, Point *end);int calcF(Point *point);
private:std::vector<std::vector<int>> maze;//地图(maze:迷宫)std::list<Point *> openList;  //开启列表std::list<Point *> closeList; //关闭列表};

AStar.cpp

#include <math.h>
#include "Astar.h"void Astar::InitAstar(std::vector<std::vector<int>> &_maze)
{maze = _maze;
}int Astar::calcG(Point *temp_start, Point *point)
{//如果是斜边,extraG为14,否则为10int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2;//如果是初始节点,则其父节点是空int parentG = point->parent == NULL ? 0 : point->parent->G;//当前点的G值 = 父节点的G值 + 额外的G值return parentG + extraG;
}int Astar::calcH(Point *point, Point *end)
{//欧几里距离:两点之间的直线距离//return sqrt((double)(end->x - point->x)*(double)(end->x - point->x) + (double)(end->y - point->y)*(double)(end->y - point->y))*kCost1;//曼哈顿距离:水平距离+垂直距离return (abs(end->x - point->x) + abs(end->y - point->y)) * kCost1;
}int Astar::calcF(Point *point)
{//F = G + Hreturn point->G + point->H;
}Point* Astar::getLeastFpoint()
{//将第一个点作为F值最小的点,进行比较,最终拿到F值最小的点if (!openList.empty()){auto resPoint = openList.front();for (auto &point : openList)if (point->F<resPoint->F)resPoint = point;return resPoint;}return NULL;
}Point* Astar::findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner)
{//把起始点添加到开启列表openList.push_back(new Point(startPoint.x, startPoint.y));do{auto curPoint = getLeastFpoint();//找到F值最小的点openList.remove(curPoint); //从开启列表中删除closeList.push_back(curPoint);//放到关闭列表//找到当前周围八个格中可以通过的格子(1.可走且没有在关闭列表)std::vector<Point*> surroundPoints = getSurroundPoints(curPoint, isIgnoreCorner);for (auto &target : surroundPoints){//对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G Hif (!isInList(openList, target)){target->parent = curPoint;target->G = calcG(curPoint, target);target->H = calcH(target, &endPoint);target->F = calcF(target);openList.push_back(target);//添加到开启列表中}//对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做,否则设置它的父节点为当前点,并更新G和Felse{int tempG = calcG(curPoint, target);if (tempG < target->G){target->parent = curPoint;target->G = tempG;target->F = calcF(target);}}//查找结束点是否已经在开启列表中,如果在,这时候路径被找到。Point *resPoint = isInList(openList, &endPoint);//如果返回不为空,表示找到终点,则通过终点的parent一直反推得出路径if (resPoint)return resPoint;}}while (!openList.empty());//当开启列表为空时,跳出循环return NULL;//找不到一条路径
}std::list<Point*> Astar::GetPath(Point &startPoint, Point &endPoint, bool isIgnoreCorner)
{Point *result = findPath(startPoint, endPoint, isIgnoreCorner);std::list<Point *> path;//返回路径,如果没找到路径,返回空链表while (result){path.push_front(result);result = result->parent;//通过parent回溯}//清空临时开闭列表,防止重复执行GetPath导致结果异常openList.clear();closeList.clear();return path;
}Point* Astar::isInList(const std::list<Point *> &list, const Point *point) const
{//判断某个节点是否在列表中,这里不能比较指针,因为每次加入列表是新开辟的节点,只能比较坐标for (auto p : list)if (p->x == point->x&&p->y == point->y)return p;return NULL;
}bool Astar::isCanreach(const Point *point, const Point *target, bool isIgnoreCorner) const
{//如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回falseif (target->x<0 || target->x>maze.size() - 1|| target->y<0 || target->y>maze[0].size() - 1|| maze[target->x][target->y] == 1|| (target->x == point->x&&target->y == point->y)|| isInList(closeList, target)){return false;}else{if (abs(point->x - target->x) + abs(point->y - target->y) == 1)//非斜角可以return true;else{//斜对角要判断是否绊住if (maze[point->x][target->y] == 0 && maze[target->x][point->y] == 0)return true;elsereturn isIgnoreCorner;}}
}std::vector<Point *> Astar::getSurroundPoints(const Point *point, bool isIgnoreCorner) const
{std::vector<Point *> surroundPoints;for (int x = point->x - 1; x <= point->x + 1; x++)for (int y = point->y - 1; y <= point->y + 1; y++)if (isCanreach(point, new Point(x, y), isIgnoreCorner))//判断是否能走,能走则加入到vector中返回surroundPoints.push_back(new Point(x, y));return surroundPoints;
}

main.cpp

#include <iostream>
#include "Astar.h"
using namespace std;
int main()
{//初始化地图,用二维矩阵代表地图,1表示障碍物,0表示可通vector<vector<int>> maze = {{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },{ 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1 },{ 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1 },{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1 },{ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1 },{ 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1 },{ 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 },{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }};Astar astar;astar.InitAstar(maze);//设置起始和结束点Point start(1, 1);Point end(6, 10);//A*算法找寻路径list<Point *> path = astar.GetPath(start, end, false);//打印路径点for (auto &p : path)cout << p->x << ',' << p->y << endl;return 0;}

深度广度优先算法、A*算法相关推荐

  1. 二叉树广度和深度遍历的全部算法

    二叉树广度和深度遍历的全部算法 对于二叉树的遍历,有广度遍历和深度遍历两大类,对于深度遍历又分为先序.中序和后序,这三种先中后序又可以用递归和非递归两种算法来写,下面就分别对这两大类算法做个总结,以后 ...

  2. 专访DeepID发明者孙祎:关于深度学习与人脸算法的深层思考

    专访DeepID发明者孙祎:关于深度学习与人脸算法的深层思考 发表于2015-11-18 09:51 作者周建丁 CNN卷积神经网络DeepID人脸算法深度学习孙祎Linkface 摘要:DeepID ...

  3. AI公开课:19.05.29 浣军-百度大数据实验室主任《AutoDL 自动化深度学习建模的算法和应用》课堂笔记以及个人感悟

    AI公开课:19.05.29 浣军 百度大数据实验室主任<AutoDL 自动化深度学习建模的算法和应用>课堂笔记以及个人感悟 导读        浣军博士,汉族,1975年出生于江苏苏州, ...

  4. 【百家稷学】从传统方法到深度学习,人脸算法和应用的演变(河南平顶山学院技术分享)...

    继续咱们百家稷学专题,本次聚焦在人脸方向.百家稷学专题的目标,是走进100所高校和企业进行学习与分享. 分享主题 本次分享是在河南平顶山学院,主题是<从传统方法到深度学习,人脸算法和应用的演变& ...

  5. 深度增强学习前沿算法思想

    作者: Flood Sung,CSDN博主,人工智能方向研究生,专注于深度学习,增强学习与机器人的研究.  责编:何永灿,欢迎人工智能领域技术投稿.约稿.给文章纠错,请发送邮件至heyc@csdn.n ...

  6. 基于深度学习的多目标跟踪算法——ReID与MOT的联系

    ©PaperWeekly 原创 · 作者|黄飘 学校|华中科技大学硕士 研究方向|多目标跟踪 最近基于深度学习的多目标跟踪算法越来越多,有用于特征提取的,有改进单目标跟踪器的,也有提升数据关联的.如果 ...

  7. 基于深度学习的多目标跟踪算法(上):端到端的数据关联

    ©PaperWeekly 原创 · 作者|黄飘 学校|华中科技大学硕士生 研究方向|多目标跟踪 最近基于深度学习的多目标跟踪算法越来越多,有用于特征提取的,有改进单目标跟踪器的,也有提升数据关联的.如 ...

  8. 深度剖析目标检测算法YOLOV4

    深度剖析目标检测算法YOLOV4 目录 简述 yolo 的发展历程 介绍 yolov3 算法原理 介绍 yolov4 算法原理(相比于 yolov3,有哪些改进点) YOLOV4 源代码日志解读 yo ...

  9. 深度强化学习-DDPG算法原理和实现

    全文共3077个字,8张图,预计阅读时间15分钟. 基于值的强化学习算法的基本思想是根据当前的状态,计算采取每个动作的价值,然后根据价值贪心的选择动作.如果我们省略中间的步骤,即直接根据当前的状态来选 ...

最新文章

  1. babel6 babel7_当您已经准备好Babel时设置Flow
  2. ADO.NET中连接池状态的跟踪
  3. Linux Java Web 服务器搭建之tomcat安装
  4. 不可错过的CMS学习笔记
  5. C++ 模板:template
  6. 2020数字中国创新大赛-智能算法赛-冠军方案
  7. Slack设置根据关键字自动提醒的小技巧
  8. 【CodeForces - 897D】Ithea Plays With Chtholly (交互题型,贪心,思维构造,题目信息)
  9. linux本地时间与utc不一致_辽宁无新增 | 北京新增本地确诊病例31例,中高考时间目前不做调整...
  10. 值得一看的50条从商之道
  11. android微信群视频,10. 搞定微信群聊的神器——录屏软件集合
  12. VB键盘输入一个数求阶乘的和
  13. Linux C++ socket编程实例
  14. 算法学习(五)—— 广度优先搜索
  15. 悼念博客专家雷霄骅七律诗一首
  16. python模拟鼠标拖动滑块_py+selenium拼图式拖动滑块的验证
  17. 新零售线上+线下的完美营销
  18. 使markdown文档中的图片居中
  19. 遗传算法的交叉变异操作杂记
  20. 浅谈SQL注入漏洞原理及利用方式

热门文章

  1. 一个线程池引发的悲剧
  2. 杭州保俶塔实验机器人_浙江省第三届中小学生智能机器人比赛获奖名单
  3. Quartus II 上手攻略
  4. Oracle undo保留时间的几个相关参数
  5. 字符串中的十六进制字符如何转换成十六进制数
  6. 新概念2 课文和单词(11)
  7. java使用inputStream_java InputStream使用
  8. 测试学习-114-使用jmeter工具对web项目进行性能测试与稳定性测试
  9. MATLAB|学生版本|正版打开安装包出现无法连接mathworks解决方案
  10. 纯电阻电路的分析方法——回路电流法