高级搜索算法
高级搜索算法超越了基本的DFS和BFS,包括A*搜索、双向搜索和迭代加深等专门技术。这些算法对于高效解决复杂的路径查找和优化问题至关重要。
A*搜索算法
A*搜索使用启发式函数引导搜索朝向目标,结合了Dijkstra算法的优点和有信息搜索策略。
A*搜索实现
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <cmath>
using namespace std;
struct Point {
int x, y;
Point(int x = 0, int y = 0) : x(x), y(y) {}
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
// 曼哈顿距离启发式
int manhattanDistance(const Point& other) const {
return abs(x - other.x) + abs(y - other.y);
}
// 欧几里得距离启发式
double euclideanDistance(const Point& other) const {
return sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y));
}
};
// Point的哈希函数
struct PointHash {
size_t operator()(const Point& p) const {
return hash<int>()(p.x) ^ (hash<int>()(p.y) << 1);
}
};
struct AStarNode {
Point pos;
int gCost; // 从起点的代价
int hCost; // 到目标的启发式代价
Point parent;
AStarNode(Point p, int g, int h, Point par)
: pos(p), gCost(g), hCost(h), parent(par) {}
int fCost() const { return gCost + hCost; }
bool operator>(const AStarNode& other) const {
return fCost() > other.fCost();
}
};
class AStar {
private:
vector<vector<int>> grid;
int rows, cols;
vector<Point> directions = {{0,1}, {1,0}, {0,-1}, {-1,0},
{1,1}, {1,-1}, {-1,1}, {-1,-1}}; // 8方向
public:
AStar(vector<vector<int>>& g) : grid(g), rows(g.size()), cols(g[0].size()) {}
bool isValid(const Point& p) {
return p.x >= 0 && p.x < rows && p.y >= 0 && p.y < cols && grid[p.x][p.y] == 0;
}
int getMoveCost(const Point& from, const Point& to) {
// 对角线移动代价更高
if (abs(from.x - to.x) + abs(from.y - to.y) == 2) {
return 14; // 约为sqrt(2) * 10
}
return 10;
}
vector<Point> findPath(Point start, Point goal) {
priority_queue<AStarNode, vector<AStarNode>, greater<AStarNode>> openSet;
unordered_map<Point, int, PointHash> gScore;
unordered_map<Point, Point, PointHash> cameFrom;
openSet.push(AStarNode(start, 0, start.manhattanDistance(goal), Point(-1, -1)));
gScore[start] = 0;
while (!openSet.empty()) {
AStarNode current = openSet.top();
openSet.pop();
if (current.pos == goal) {
// 重构路径
vector<Point> path;
Point curr = goal;
while (!(curr.x == -1 && curr.y == -1)) {
path.push_back(curr);
curr = cameFrom[curr];
}
reverse(path.begin(), path.end());
return path;
}
// 如果已经找到更好的路径则跳过
if (gScore.count(current.pos) && current.gCost > gScore[current.pos]) {
continue;
}
for (const Point& dir : directions) {
Point neighbor(current.pos.x + dir.x, current.pos.y + dir.y);
if (!isValid(neighbor)) continue;
int tentativeG = current.gCost + getMoveCost(current.pos, neighbor);
if (!gScore.count(neighbor) || tentativeG < gScore[neighbor]) {
gScore[neighbor] = tentativeG;
cameFrom[neighbor] = current.pos;
int hCost = neighbor.manhattanDistance(goal);
openSet.push(AStarNode(neighbor, tentativeG, hCost, current.pos));
}
}
}
return {}; // 未找到路径
}
void printPath(const vector<Point>& path) {
if (path.empty()) {
cout << "未找到路径!" << endl;
return;
}
vector<vector<char>> display(rows, vector<char>(cols, '.'));
// 标记障碍物
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == 1) display[i][j] = '#';
}
}
// 标记路径
for (int i = 0; i < path.size(); i++) {
if (i == 0) display[path[i].x][path[i].y] = 'S'; // 起点
else if (i == path.size() - 1) display[path[i].x][path[i].y] = 'G'; // 终点
else display[path[i].x][path[i].y] = '*';
}
// 打印网格
for (const auto& row : display) {
for (char c : row) {
cout << c << " ";
}
cout << endl;
}
cout << "路径长度: " << path.size() << endl;
}
};
int main() {
// 创建带障碍物的网格(1=障碍物,0=空闲)
vector<vector<int>> grid = {
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
AStar astar(grid);
Point start(0, 0);
Point goal(9, 9);
cout << "使用A*搜索从(0,0)到(9,9)找路径:" << endl;
cout << "图例: S=起点, G=终点, *=路径, #=障碍物, .=空闲" << endl << endl;
vector<Point> path = astar.findPath(start, goal);
astar.printPath(path);
return 0;
}A*搜索使用f(n) = g(n) + h(n),其中g(n)是从起点的实际代价,h(n)是到目标的启发式估计。当启发式函数是可接受的(从不高估)时,算法保证最优路径。
双向搜索
双向搜索从起点和终点同时运行两个搜索,在中间相遇以指数级减少搜索空间。
双向BFS实现
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_set>
#include <unordered_map>
using namespace std;
class BidirectionalBFS {
private:
vector<vector<int>> graph;
int n;
public:
BidirectionalBFS(vector<vector<int>>& g) : graph(g), n(g.size()) {}
vector<int> findPath(int start, int goal) {
if (start == goal) return {start};
// 正向和反向搜索的两个队列
queue<int> forwardQueue, backwardQueue;
unordered_set<int> forwardVisited, backwardVisited;
unordered_map<int, int> forwardParent, backwardParent;
forwardQueue.push(start);
backwardQueue.push(goal);
forwardVisited.insert(start);
backwardVisited.insert(goal);
forwardParent[start] = -1;
backwardParent[goal] = -1;
while (!forwardQueue.empty() || !backwardQueue.empty()) {
// 扩展正向搜索
if (!forwardQueue.empty()) {
int meetingPoint = expandLevel(forwardQueue, forwardVisited, backwardVisited,
forwardParent, true);
if (meetingPoint != -1) {
return constructPath(meetingPoint, forwardParent, backwardParent);
}
}
// 扩展反向搜索
if (!backwardQueue.empty()) {
int meetingPoint = expandLevel(backwardQueue, backwardVisited, forwardVisited,
backwardParent, false);
if (meetingPoint != -1) {
return constructPath(meetingPoint, forwardParent, backwardParent);
}
}
}
return {}; // 未找到路径
}
private:
int expandLevel(queue<int>& q, unordered_set<int>& visited,
unordered_set<int>& otherVisited, unordered_map<int, int>& parent,
bool isForward) {
int levelSize = q.size();
for (int i = 0; i < levelSize; i++) {
int current = q.front();
q.pop();
for (int neighbor : graph[current]) {
if (otherVisited.count(neighbor)) {
// 找到交集!
parent[neighbor] = current;
return neighbor;
}
if (!visited.count(neighbor)) {
visited.insert(neighbor);
parent[neighbor] = current;
q.push(neighbor);
}
}
}
return -1; // 在此层未找到交集
}
vector<int> constructPath(int meetingPoint, unordered_map<int, int>& forwardParent,
unordered_map<int, int>& backwardParent) {
vector<int> path;
// 构建从起点到交汇点的路径
vector<int> forwardPath;
int current = meetingPoint;
while (current != -1) {
forwardPath.push_back(current);
current = forwardParent[current];
}
reverse(forwardPath.begin(), forwardPath.end());
// 构建从交汇点到终点的路径
vector<int> backwardPath;
current = backwardParent[meetingPoint];
while (current != -1) {
backwardPath.push_back(current);
current = backwardParent[current];
}
// 合并路径
path = forwardPath;
path.insert(path.end(), backwardPath.begin(), backwardPath.end());
return path;
}
};
// 迭代加深DFS
class IterativeDeepeningDFS {
private:
vector<vector<int>> graph;
int n;
public:
IterativeDeepeningDFS(vector<vector<int>>& g) : graph(g), n(g.size()) {}
vector<int> findPath(int start, int goal, int maxDepth = 20) {
for (int depth = 0; depth <= maxDepth; depth++) {
vector<bool> visited(n, false);
vector<int> path;
if (dfsLimited(start, goal, depth, visited, path)) {
return path;
}
}
return {}; // 在深度限制内未找到路径
}
private:
bool dfsLimited(int current, int goal, int depth, vector<bool>& visited, vector<int>& path) {
path.push_back(current);
if (current == goal) {
return true;
}
if (depth == 0) {
path.pop_back();
return false;
}
visited[current] = true;
for (int neighbor : graph[current]) {
if (!visited[neighbor]) {
if (dfsLimited(neighbor, goal, depth - 1, visited, path)) {
return true;
}
}
}
visited[current] = false;
path.pop_back();
return false;
}
};
int main() {
// 创建示例图
vector<vector<int>> graph = {
{1, 2}, // 0 -> 1, 2
{0, 3, 4}, // 1 -> 0, 3, 4
{0, 5, 6}, // 2 -> 0, 5, 6
{1, 7}, // 3 -> 1, 7
{1, 7}, // 4 -> 1, 7
{2, 8}, // 5 -> 2, 8
{2, 8}, // 6 -> 2, 8
{3, 4, 9}, // 7 -> 3, 4, 9
{5, 6, 9}, // 8 -> 5, 6, 9
{7, 8} // 9 -> 7, 8
};
BidirectionalBFS bidiBFS(graph);
IterativeDeepeningDFS iddfs(graph);
int start = 0, goal = 9;
// 测试双向BFS
cout << "双向BFS从 " << start << " 到 " << goal << ": ";
vector<int> bidiPath = bidiBFS.findPath(start, goal);
if (!bidiPath.empty()) {
for (int i = 0; i < bidiPath.size(); i++) {
if (i > 0) cout << " -> ";
cout << bidiPath[i];
}
cout << " (长度: " << bidiPath.size() << ")" << endl;
} else {
cout << "未找到路径" << endl;
}
// 测试迭代加深DFS
cout << "迭代加深DFS从 " << start << " 到 " << goal << ": ";
vector<int> iddfsPath = iddfs.findPath(start, goal);
if (!iddfsPath.empty()) {
for (int i = 0; i < iddfsPath.size(); i++) {
if (i > 0) cout << " -> ";
cout << iddfsPath[i];
}
cout << " (长度: " << iddfsPath.size() << ")" << endl;
} else {
cout << "未找到路径" << endl;
}
return 0;
}双向搜索将时间复杂度从O(b^d)减少到O(b^(d/2)),其中b是分支因子,d是深度。迭代加深结合了DFS的空间效率和BFS的最优性。
关键要点
- A*搜索使用启发式函数高效引导搜索,在可接受启发式下保证最优路径
- 双向搜索通过在中间相遇减少指数级搜索空间
- 迭代加深结合了DFS的内存效率和BFS的完整性与最优性
- 根据问题约束选择搜索算法:内存、最优性要求和启发式可用性