前言
A*算法的核心思想在于启发式搜索,解决问题的关键在于找到一种方式,能够使得遍历往更接近最终目标的方向上进行,这需要我们能够找到它的代价函数来辅助我们进行遍历。
图中有障碍物情况下的最优路径
问题描述
给定一个 n 行 m 列的矩阵 graph , 其中如果 graph[i][j] = 1 表示这里不能通过, 如果 graph[i][j] = 0 表示这里可以通过,从矩阵左上角开始遍历,你每次可以往上下左右这四个方向移动,请你返回到右下角的最短路径的节点顺序,返回其中一种即可,如果不能到达,返回空;
输入描述
1 <= m,n <= 10000;
graph[i][j] = 0 或 graph[i][j] = 1;
给出的代码
class Solution{
public:
vector<int> FindMinPath(vector<vector<int>> &graph){
//在这里书写你的代码
}
}
注:暂时未找到这道题目在哪有,所以没有测试,实现不保证完全通过所有测试用例,仅仅提供思路
思路与分析
图的遍历我们使用 open 表来记录未遍历的节点, 使用 close 表记录已经遍历的节点。
如果使用 A*算法 来求解这一道问题,我们需要明确它的实际代价(Actual Cost) g(i, j) 和启发式代价(Heuristic Cost) h(i, j)。
这里它的实际代价 g(i, j) 为从 (0,0) 开始到节点 (i, j) 的实际路径长度;启发式代价 h(i, j) 为节点 (i,j) 到 节点 (n-1, m-1) 的曼哈顿距离。
曼哈顿距离是指两个点之间沿着坐标轴方向的总距离,也叫城市街区距离,因为它类似于城市中按街道和大道的网格形式行驶的距离。
定义:在二维空间中,两个点 ( P(x_1, y_1) ) 和 ( Q(x_2, y_2) ) 之间的曼哈顿距离为:
[
d
(
P
,
Q
)
=
∣
x
2
−
x
1
∣
+
∣
y
2
−
y
1
∣
]
[ d(P, Q) = |x_2 - x_1| + |y_2 - y_1| ]
[d(P,Q)=∣x2−x1∣+∣y2−y1∣]
这种度量忽略了直接的直线距离,而是计算沿着坐标轴的距离。
代价函数:
f
(
n
)
=
g
(
n
)
+
h
(
n
)
f(n) = g(n) + h(n)
f(n)=g(n)+h(n)
首先对于一个正在访问的节点,它可以访问的下一个节点为 U (上面的节点), D (下面的节点),L (左面的节点),R (右面的节点),如果这个节点不是障碍物且这个节点合法,那么将这个节点加入到 open 表里。
每次选择 open 表中 f 最小的节点进行访问,这代表着我们有更大概率更快地找到目标节点。
当 open 表里为空或者找到了最后的节点,跳出循环。
最后通过 close 表来得到最后要求的路径。
代码实现
#include <iostream>
#include <vector>
#include <queue>
#include <utility>
#include <unordered_map>
#include <algorithm>
using namespace std;
class OpenNode{
public:
int indexX; //当前的节点号X
int indexY; //当前的节点号Y
int parX; //前驱节点X
int parY; //前驱节点Y
int g; //当前代价
int h; //未来代价
OpenNode(int indexX, int indexY, int parX, int parY, int g, int h):indexX(indexX),indexY(indexY),parX(parX),parY(parY),g(g), h(h){};
OpenNode(){}
bool operator > (const OpenNode& node) const{
int a = this->g + this->h;
int b = node.g + node.h;
return a==b?(this->h > node.h):a>b;
}
bool operator < (const OpenNode& node) const{
int a = this->g + this->h;
int b = node.g + node.h;
return a==b?(this->h < node.h):a<b;
}
friend std::ostream& operator<<(ostream &os, const OpenNode& node);
};
std::ostream& operator<<(ostream &os, const OpenNode& node){
os<<node.indexX<<" "<<node.indexY<<" "<<node.parX<<" "<<node.parY<<" "<<node.g<<" "<<node.h;
return os;
}
class Solution{
public:
vector<vector<int>> FindMinPath(vector<vector<int>> &graph){
if(graph.size()==0 && graph[0].size()==0) return {};
vector<vector<int>> ans; //存储最终的路径
priority_queue<OpenNode, vector<OpenNode>, std::greater<OpenNode>> openTable; //open表 存储未遍历的节点
unordered_map<int, OpenNode> closeTable; //close表 存储已经遍历的节点, key = (indexX<<16) + indexY;
vector<pair<int,int>> direction = {{-1,0},{0,1},{1,0},{0,-1}}; //存储遍历的方向
int n = graph.size(), m = graph[0].size();
OpenNode start(0,0,-1,-1,0,n+m-1); //左上角的节点
openTable.push(start);
while(!openTable.empty()){
OpenNode curNode = openTable.top(); //取出总代价最小的节点
openTable.pop();
if(closeTable.count((curNode.indexX<<16)+ curNode.indexY)) continue;
closeTable[((curNode.indexX)<<16) + curNode.indexY] = curNode; //将当前节点加入到Close表中
if(curNode.indexX == n-1 && curNode.indexY == m-1) break; //找到了最后的节点
for(const auto &dir: direction){
int i=curNode.indexX + dir.first;
int j=curNode.indexY + dir.second;
if(i<0 || j<0 || i>=n || j>=m) continue;
if(graph[i][j] == 1) continue; //如果遇到了障碍物,不扩展该节点
if(closeTable.count((i<<16) + j)==0){
OpenNode nextNode(i,j,curNode.indexX,curNode.indexY,curNode.g+1,m+n-i-j-1);
openTable.push(nextNode);
}
}
}
//根据close表反向得到路径
if(closeTable.count(((n-1)<<16) + m-1)==0) return {}; //未遍历到最后一个节点
OpenNode curNode = closeTable[((n-1)<<16)+ m-1];
while(curNode.indexX!=0 || curNode.indexY!=0){
vector<int> vec(2);
vec[0] = curNode.indexX;
vec[1] = curNode.indexY;
ans.emplace_back(vec);
curNode = closeTable[(curNode.parX<<16) + curNode.parY];
}
vector<int> startPos(2);
ans.emplace_back(startPos);
reverse(ans.begin(), ans.end());
return ans;
}
};
int main() {
vector<vector<int>> graph = {
{0,0,0,0,0,0},
{0,0,1,0,1,1},
{0,0,1,0,1,0},
{0,1,0,0,0,0},
{0,0,0,0,0,0}
};
int rs = 10;
Solution solution;
vector<vector<int>> path = solution.FindMinPath(graph);
for(auto node:path){
cout<<"("<<node[0]<<","<<node[1]<<")"<<"->";
}
cout<<"End Search"<<endl;
}
得到结果
其他题目链接
备注
该文仅提供一种写法思路,未证明其正确性,如有错误和疏漏,敬请指出。