本文目录导读:

在PHP项目中实现最短路径算法,通常有以下几种常见方案,取决于你的具体需求:
核心算法实现
Dijkstra算法(单源最短路径)
适用于非负权值图,是最常用的算法:
class Dijkstra {
private $graph;
public function __construct($graph) {
$this->graph = $graph;
}
public function shortestPath($source, $target) {
$distances = [];
$previous = [];
$unvisited = [];
// 初始化
foreach ($this->graph as $vertex => $neighbors) {
$distances[$vertex] = INF;
$previous[$vertex] = null;
$unvisited[$vertex] = true;
}
$distances[$source] = 0;
while (!empty($unvisited)) {
// 找到未访问中距离最小的顶点
$minVertex = null;
foreach ($unvisited as $vertex => $value) {
if ($minVertex === null || $distances[$vertex] < $distances[$minVertex]) {
$minVertex = $vertex;
}
}
if ($minVertex === $target) {
break;
}
unset($unvisited[$minVertex]);
// 更新邻居距离
foreach ($this->graph[$minVertex] as $neighbor => $weight) {
$alternative = $distances[$minVertex] + $weight;
if ($alternative < $distances[$neighbor]) {
$distances[$neighbor] = $alternative;
$previous[$neighbor] = $minVertex;
}
}
}
// 重构路径
$path = [];
$current = $target;
while ($current !== null) {
array_unshift($path, $current);
$current = $previous[$current];
}
return [
'distance' => $distances[$target],
'path' => $path
];
}
}
// 使用示例
$graph = [
'A' => ['B' => 4, 'C' => 2],
'B' => ['D' => 5, 'C' => 1],
'C' => ['D' => 8, 'E' => 10],
'D' => ['E' => 2],
'E' => []
];
$dijkstra = new Dijkstra($graph);
$result = $dijkstra->shortestPath('A', 'E');
print_r($result);
Bellman-Ford算法(支持负权值)
class BellmanFord {
public function shortestPath($graph, $source, $target) {
$vertices = array_keys($graph);
$distances = [];
$previous = [];
// 初始化
foreach ($vertices as $vertex) {
$distances[$vertex] = INF;
$previous[$vertex] = null;
}
$distances[$source] = 0;
// 松弛所有边 |V|-1 次
for ($i = 0; $i < count($vertices) - 1; $i++) {
foreach ($graph as $u => $neighbors) {
foreach ($neighbors as $v => $weight) {
if ($distances[$u] + $weight < $distances[$v]) {
$distances[$v] = $distances[$u] + $weight;
$previous[$v] = $u;
}
}
}
}
// 检查负权环
foreach ($graph as $u => $neighbors) {
foreach ($neighbors as $v => $weight) {
if ($distances[$u] + $weight < $distances[$v]) {
return ['error' => '图中包含负权环'];
}
}
}
// 重构路径
$path = [];
$current = $target;
while ($current !== null) {
array_unshift($path, $current);
$current = $previous[$current];
}
return [
'distance' => $distances[$target],
'path' => $path
];
}
}
数据库存储方案
邻接表存储结构
CREATE TABLE nodes (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL
);
CREATE TABLE edges (
id INT PRIMARY KEY AUTO_INCREMENT,
from_node INT,
to_node INT,
weight DECIMAL(10,2),
FOREIGN KEY (from_node) REFERENCES nodes(id),
FOREIGN KEY (to_node) REFERENCES nodes(id),
INDEX idx_from (from_node),
INDEX idx_to (to_node)
);
PHP实现数据库查询
class GraphDatabase {
private $pdo;
public function __construct($pdo) {
$this->pdo = $pdo;
}
public function loadGraph() {
$stmt = $this->pdo->query("
SELECT n1.name as from_name, n2.name as to_name, e.weight
FROM edges e
JOIN nodes n1 ON e.from_node = n1.id
JOIN nodes n2 ON e.to_node = n2.id
");
$graph = [];
while ($row = $stmt->fetch()) {
$graph[$row['from_name']][$row['to_name']] = $row['weight'];
}
return $graph;
}
}
实用扩展方案
A*算法(带启发式搜索)
适用于大规模地图,比Dijkstra更高效:
class AStar {
private $graph;
public function shortestPath($start, $goal, $heuristicCallback) {
$openSet = [$start];
$cameFrom = [];
$gScore = [$start => 0];
$fScore = [$start => $heuristicCallback($start, $goal)];
while (!empty($openSet)) {
// 找到fScore最小的节点
$current = array_reduce($openSet, function($a, $b) use ($fScore) {
return $fScore[$a] < $fScore[$b] ? $a : $b;
});
if ($current === $goal) {
return $this->reconstructPath($cameFrom, $current);
}
$openSet = array_diff($openSet, [$current]);
foreach ($this->graph[$current] as $neighbor => $weight) {
$tentativeGScore = $gScore[$current] + $weight;
if (!isset($gScore[$neighbor]) || $tentativeGScore < $gScore[$neighbor]) {
$cameFrom[$neighbor] = $current;
$gScore[$neighbor] = $tentativeGScore;
$fScore[$neighbor] = $gScore[$neighbor] + $heuristicCallback($neighbor, $goal);
if (!in_array($neighbor, $openSet)) {
$openSet[] = $neighbor;
}
}
}
}
return null; // 无路径
}
private function reconstructPath($cameFrom, $current) {
$path = [$current];
while (isset($cameFrom[$current])) {
$current = $cameFrom[$current];
array_unshift($path, $current);
}
return $path;
}
}
性能优化建议
-
使用优先队列:用SplPriorityQueue替代数组查找
$queue = new SplPriorityQueue(); $queue->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
-
缓存中间结果:对于地图应用,缓存常用路径
$cache = new Redis(); $cacheKey = "path_{$from}_{$to}"; if ($cached = $cache->get($cacheKey)) { return unserialize($cached); } -
分治处理:大型图考虑分区计算
// 将地图分成多个区域,先计算区域间路径
实际应用场景
- 物流配送系统:使用Dijkstra + 实时交通数据
- 社交网络:使用BFS(无权图)
- 导航系统:A*算法 + 地理坐标启发函数
- 网络路由:Bellman-Ford或SPFA
选择哪种方案取决于你的具体需求:数据量大小、是否支持负权、是否需要实时计算、内存限制等,对于大多数PHP Web应用,Dijkstra算法配合数据库存储是最实用的选择。