概述
Contraction Hierarchies (CH) 是一种用于加速最短路径搜索问题的高效算法。它在2008年由Robert Geisberger、Peter Sanders、Dominik Schultes和Daniel Delling提出。CH算法结合了预处理和查询两个阶段,通过引入层次结构和优化搜索空间,显著降低了计算最短路径的时间复杂度。本文将对 CH 算法的原理进行深入剖析,并探讨其在现实场景中的应用。

CH算法核心思想
CH算法的核心思想是利用图的层次结构将查询过程分解为两个独立的方向,从而缩小搜索空间。首先,CH算法对图的顶点进行排序,从而将顶点分为不同的层次。然后,在预处理阶段,算法会在图中添加新的边(称为快捷方式)以保证在每个层次之间的最短路径。最后,在查询阶段,算法在上述构建的层次结构图中执行双向 Dijkstra 搜索,从而迅速找到最短路径。
预处理阶段
预处理阶段的目标是构建一个层次结构,并在原始图中添加快捷方式以保留最短路径信息。这个过程分为以下几个步骤:
- 顶点排序:首先,根据特定的排序策略(例如边的数量、邻居数量等)为图中的所有顶点分配一个顺序标签。
- 顶点收缩:按照顺序依次收缩每个顶点,同时在图中添加快捷方式。顶点收缩的过程中,临时删除顶点并将其标记为收缩,然后为其相邻顶点之间添加最短路径快捷方式(如果不存在更短的路径)。
- 快捷方式添加:为了保持最短路径,只有在快捷方式确实比现有路径更短时,才会将其添加到图中。
预处理阶段的输出是一个新的带有快捷方式的层次结构图,为查询阶段提供了更优化的搜索空间。
查询阶段
在查询阶段,利用预处理阶段构建的层次结构图,通过双向 Dijkstra 搜索算法快速找到最短路径。以下是查询阶段的关键步骤:
- 初始化:创建两个优先队列,一个用于正向搜索,一个用于反向搜索。
- 双向搜索:分别从起点和终点开始双向 Dijkstra 搜索。正向搜索从起点开始,只沿着高层次到低层次的边进行搜索;反向搜索从终点开始,只沿着低层次到高层次的边进行搜索。
- 搜索终止条件:当正向搜索和反向搜索的最小距离值之和大于等于已找到的最短路径长度时,搜索终止。
- 最短路径重构:通过将正向搜索和反向搜索的部分路径拼接起来,重构完整的最短路径。
利用预处理阶段生成的层次结构图和双向搜索策略,CH算法显著提高了查询效率,缩短了最短路径计算的时间。
应用场景
Contraction Hierarchies 算法在许多实际场景中具有广泛的应用价值,例如:
- 交通导航系统:CH算法能够在大规模的道路网络中快速找到最短路径,为交通导航系统提供实时、准确的路线规划。
- 网络分析:在复杂的网络结构中,如社交网络、互联网路由等,CH算法可以快速找到不同节点之间的最短路径,为网络分析提供基础数据。
- 地理信息系统:CH算法在地理信息系统中也有广泛应用,如计算地形分析、水流路径规划等。
示例
在这个简单的示例中,我们将使用Java语言和开源的GraphHopper路线规划引擎实现Contraction Hierarchies算法。GraphHopper是一个快速、灵活且易于使用的路由引擎,它已经内置了Contraction Hierarchies算法的实现。
准备工作
- 首先,确保已经安装了Java JDK(建议使用JDK 8或更高版本)。
- 创建一个新的Maven项目,添加以下依赖到
pom.xml
文件:
1 2 3 4 5 6 7
| <dependencies> <dependency> <groupId>com.graphhopper</groupId> <artifactId>graphhopper-core</artifactId> <version>3.0</version> </dependency> </dependencies>
|
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| import com.graphhopper.GraphHopper; import com.graphhopper.config.CHProfile; import com.graphhopper.config.Profile; import com.graphhopper.reader.osm.GraphHopperOSM; import com.graphhopper.routing.Path; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.util.HintsMap; import com.graphhopper.util.Instruction; import com.graphhopper.util.InstructionList; import com.graphhopper.util.Parameters; import com.graphhopper.util.PointList;
public class CHExample { public static void main(String[] args) { String osmFilePath = "path/to/your/osm/file"; String graphFolderPath = "path/to/graph-folder";
GraphHopper hopper = new GraphHopperOSM().forServer(); hopper.setDataReaderFile(osmFilePath); hopper.setGraphHopperLocation(graphFolderPath); hopper.setEncodingManager(EncodingManager.create("car")); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("fastest"));
hopper.importOrLoad();
double fromLatitude = 52.5170365; double fromLongitude = 13.3888599; double toLatitude = 52.509699; double toLongitude = 13.429102;
FlagEncoder encoder = hopper.getEncodingManager().getEncoder("car"); HintsMap hintsMap = new HintsMap(); hintsMap.setWeighting("fastest"); hintsMap.setVehicle("car"); hintsMap.set(Parameters.CH.DISABLE, false);
Path path = hopper.route(hopper.createRequest(fromLatitude, fromLongitude, toLatitude, toLongitude, encoder, hintsMap)); QueryGraph queryGraph = new QueryGraph(hopper.getGraphHopperStorage()); queryGraph.lookup(path.getEdgeIterState());
InstructionList instructions = path.calcInstructions(queryGraph, hopper.getTraverseMode(), encoder);
System.out.println("距离: " + path.getDistance() + " 米"); System.out.println("时间: " + path.getTime() / 1000 + " 秒"); System.out.println("路径指令:"); for (Instruction instruction : instructions) { System.out.println(instruction.toString()); }
System.out.println("路径坐标:"); PointList points = path.calcPoints(); for (int i = 0; i < points.size(); i++) { System.out.println("[" + points.getLatitude(i) + ", " + points.getLongitude(i) + "]"); }
hopper.close(); }
|
此示例代码首先初始化GraphHopper引擎,然后从OpenStreetMap数据文件中导入路网数据。接下来,代码定义了起点和终点的经纬度坐标,创建HintsMap并执行Contraction Hierarchies算法。最后,代码输出最短路径的距离、时间、路径指令和坐标点。
将OSM文件的路线替换为你想要使用的实际路网文件,并将GraphHopper数据存储的路径设置为一个可写目录。然后运行示例代码,将计算从起点到终点的最短路径,并打印相关信息。
总结
Contraction Hierarchies 算法是一种高效的最短路径搜索算法,通过预处理和查询两个阶段以及引入层次结构和优化搜索空间,显著降低了计算最短路径的时间复杂度。CH算法在交通导航、网络分析和地理信息系统等实际场景中具有重要的应用价值。