目录
2.1 BFS( Breadth First Search )广度搜索算法
1.迪杰斯特拉算算法
简称迪算法。
功能:求单点到其他定点的最短距离,同时也能构造最短路径。注意,虽然最短距离只有一个,但是最短路径可有多条,该算法只能找到其中的一条。
实现代码:
/**
* 找最短路径和最短距离
* 还可以写成广度搜索+优先级队列来优化
*
* @param graph 路径网
* @param start 开始节点
* @param pathMatrix 从start出发,到每个节点的最短路径
* @param shortestDistance 从start到每个节点的最短距离
*/
public void shortestPath(int[][] graph, int start, boolean[][] pathMatrix, int[] shortestDistance) {
boolean[] finished = new boolean[graph.length];
for (int i = 0; i < graph.length; i++) {
finished[i] = false;
shortestDistance[i] = graph[start][i];
pathMatrix[i] = new boolean[graph.length];
if (shortestDistance[i] < MAX_VALUE) {
pathMatrix[i][start] = true;
pathMatrix[i][i] = true;
}
}
finished[start] = true; //以计算的最短路径的集合
shortestDistance[start] = 0;
for (int i = 1; i < graph.length; i++) {
//先找到这一次待加入的节点;
// 可以用优先级队列来优化
int minIndex = 0;
int min = MAX_VALUE;
for (int j = 0; j < shortestDistance.length; j++) {
if (!finished[j] && shortestDistance[j] < min) {
min = shortestDistance[j];
minIndex = j;
}
}
//找到后,把它标志位为已经入节点
finished[minIndex] = true;
for (int j = 0; j < graph.length; j++) {
if (!finished[j]) {
int newDistance = graph[minIndex][j] + min; //如果用Integer.MAX_VALUE表示没有边-无穷大,则这里newDistance会移出为负值,而导致
//下面的判断成立而进入if语句。
if (newDistance < shortestDistance[j]) {
shortestDistance[j] = newDistance;
pathMatrix[j] = Arrays.copyOf(pathMatrix[minIndex], pathMatrix[j].length);
pathMatrix[minIndex][j] = true; //加上这最后一条(minIndex,j)
}
}
}
}
}
2.BFS算法
2.1 BFS( Breadth First Search )广度搜索算法
和树中的层次优先遍历是一样的。
frontier = Queue() //前沿队列
frontier.put(start) //start节点入队
reached = set()
reached.add(start) //start节点为已访问
while not frontier.empty():
current = frontier.get()
for next in graph.neighbors(current): //current节点的临近节点,即下一层
if next not in reached:
frontier.put(next)
reached.add(next)
2.2 回看迪杰特斯拉算法
很明显,迪算法也是可以改成这种广度搜索,但是迪算法不但找到了路径,还完成了最短距离。迪算法在改成广度搜索的时候,队列中的元素的距离是不一样的,预期遍历队列来求最小值,不如直接用优先级队列,该O(n)算法为O(logn),算是一种优化。而最初我就是冲着这个优化来学习这个算法的。
/**
* 迪杰特斯拉算法的优先级优化算法
*
* @param graph 节点图
* @param start 开始节点
* @return shortestDistance 输出最短路径
*/
public int[] shortestPathWithPriorityQueue(int[][] graph, int start) {
int[] shortestDistance = new int[graph.length];
PriorityQueue<NodeAndWeight> frontierPriorityQueue = new PriorityQueue<>();
frontierPriorityQueue.add(new NodeAndWeight(start, graph[start][start]));
boolean[] reached = new boolean[graph.length];
reached[start] = true;
// 初始化最短距离数组
for (int i = 0; i < shortestDistance.length; i++) {
shortestDistance[i] = MAX_VALUE;
}
shortestDistance[start] = 0;
while (!frontierPriorityQueue.isEmpty()) {
// currentNode 当前离start节点最近的为访问到的节点
NodeAndWeight currentNode = frontierPriorityQueue.poll();
reached[currentNode.node] = true;
for (int i = 0; i < graph[currentNode.node].length; i++) {
int latestDistance = shortestDistance[currentNode.node] + graph[currentNode.node][i];
if (!reached[i] && latestDistance < shortestDistance[i]) {
shortestDistance[i] = latestDistance;
frontierPriorityQueue.add(new NodeAndWeight(i, latestDistance));
}
}
}
return shortestDistance;
}
这个优先级队列优化了什么呢?
- 优化了寻找下一个要到达的点是的逻辑——从顺序查找变成了堆查找;
- 没有其他优化了。
3.A-star算法
在2.1的BFS算法中,如果队列是优先级队列,则遍历就会沿着最优指执行的趋势进行。而这个优先级如果是到起点的距离,则能找到起点到所有节点的最短路径,就变成了迪杰特斯拉算法了。
如果是一个start点和一个end点呢?找一条这两个节点的通路。
- BFS肯定可行;如果队列排序且是距起点的距离,则可以找到最短路径。不过只是起止两点的最短路径。
- 权值如果是 f(n) = g(n) + h(n) ,其中 g(n)表示从起点到定点n的路径代价,而
h(n)表示节点n到目标节点的估值。
A-star算法主要用于地图上两个节点的路径。参考文章: Introduction to A*
个人感觉:
- A-star算法更多的用在坐标系中,而不是图的算法中,而且g(n)和h(n)的计算都比较直观。
- 想改变bfs的查找路径方向,可以从‘邻接点’规则和‘f(n)定义两方面下手。很显然,在参考文章中是指对f(n)进行了讨论的。
- 推荐文章: Introduction to the A* Algorithm