Day38 —— Dijkstra and Prim
1. Background
今天学习的是,Dijkstra算法和prim算法,这两个算法是数据结构里面最经典的算法。
Dijkstra算法是求一个图中一个点到其他所有点的最短路径的算法。
Prim算法是查找连通网中的最小生成树。
2. Description
2.1 Dijkstra
Dijkstra 算法是一个基于「贪心」、「广度优先搜索」、「动态规划」求一个图中一个点到其他所有点的最短路径的算法,时间复杂度 O(n2)。
每次从 「未求出最短路径的点」中 取出 距离距离起点 最小路径的点,以这个点为桥梁 刷新「未求出最短路径的点」的距离
我们以老师的测试数据为例
t
e
m
p
M
a
t
r
i
x
1
=
[
0
9
3
6
5
0
2
4
3
2
0
1
2
8
7
0
]
tempMatrix1 = \begin{bmatrix} 0 & 9 & 3 & 6 \\ 5 & 0 & 2 & 4 \\ 3 & 2 & 0 & 1 \\ 2 & 8 & 7 & 0 \\ \end{bmatrix}
tempMatrix1=⎣⎢⎢⎡0532902832076410⎦⎥⎥⎤
如果是从0节点开始,那么把tempVisitedArray[0]改为true,再找到它所有的出度所链接的节点。图中可以看到,有1,2,3。然后在其中找权值最小的一条线,也就是比较tempDistanceArrayde 值,并储存。在寻找期间需要不断的回溯,确保最后的结果是最短路径。后面的步骤就是重复之前的操作。
0 ----> 2 : 3
0 ----> 2 ---->3 : 3 + 1 = 4
0 ----> 2 ----> 1 : 3 + 2= 5
2.2 Prim
普里姆算法的实现思路就比较简单直接了:
- 将连通网中的所有顶点分为两类(假设为 A 类和 B 类)。初始状态下,所有顶点位于 B 类;
- 选择一个最佳的顶点,将其从 B 类移动到 A 类;
- 从 B 类的所有顶点出发,找出一条连接着 A 类中的某个顶点且权值最小的边,将此边连接着的 A 类中的顶点移动到 B 类;
- 重复执行第 3 步,直至 B 类中的所有顶点全部移动到 A 类,恰好可以找到 N-1 条边。
t e m p M a t r i x 1 = [ 0 7 10000 5 10000 7 0 8 9 7 10000 8 0 10000 5 5 9 10000 0 15 10000 7 5 15 0 ] tempMatrix1 = \begin{bmatrix} 0 & 7 & 10000 & 5 & 10000 \\ 7 & 0 & 8 & 9 & 7 \\ 10000 & 8 & 0 & 10000 & 5 \\ 5 & 9 & 10000 & 0 & 15 \\ 10000 & 7 & 5 & 15 & 0 \\ \end{bmatrix} tempMatrix1=⎣⎢⎢⎢⎢⎡071000051000070897100008010000559100000151000075150⎦⎥⎥⎥⎥⎤
首先找到这个最佳的节点,这里是0号节点。
然后将它看作B类,再找到权值最小的那个边,这里找的是0到3的这条边,然后顺着这条边,把3也连带着看作B类。将其看作一个整体。后面的以此类推就行。
3. Code
package datastructure.graph;
import java.util.Arrays;
import matrix.IntMatrix;
public class Net {
// 距离的最大值
public static final int MAX_DISTANCE = 10000;
// 节点数。
int numNodes;
// 权值矩阵。
IntMatrix weightMatrix;
/**
*********************
* The first constructor.
*
* @param paraNumNodes The number of nodes in the graph.
*********************
*/
public Net(int paraNumNodes) {
numNodes = paraNumNodes;
weightMatrix = new IntMatrix(numNodes, numNodes);
for (int i = 0; i < numNodes; i++) {
Arrays.fill(weightMatrix.getData()[i], MAX_DISTANCE);
} // Of for i
}// Of the first constructor
/**
*********************
* The second constructor.
*
* @param paraMatrix The data matrix.
*********************
*/
public Net(int[][] paraMatrix) {
weightMatrix = new IntMatrix(paraMatrix);
numNodes = weightMatrix.getRows();
}// Of the second constructor
// 一个简单的toString方法。
public String toString() {
String resultString = "This is the weight matrix of the graph.\r\n" + weightMatrix;
return resultString;
}// Of toString
/**
*********************
* The Dijkstra algorithm: shortest path from the source to all nodes.
*
* @param paraSource The source node.
* @return The distances to all nodes.
*********************
*/
public int[] dijkstra(int paraSource) {
// Step 1. Initialize.
int[] tempDistanceArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
tempDistanceArray[i] = weightMatrix.getValue(paraSource, i);
} // Of for i
int[] tempParentArray = new int[numNodes];
Arrays.fill(tempParentArray, paraSource);
// -1 for no parent.
tempParentArray[paraSource] = -1;
// 略过已处理过的节点。
boolean[] tempVisitedArray = new boolean[numNodes];
tempVisitedArray[paraSource] = true;
// Step 2. 迪杰斯特拉算法的主要内容。
int tempMinDistance;
int tempBestNode = -1;
for (int i = 0; i < numNodes - 1; i++) {
tempMinDistance = Integer.MAX_VALUE;
for (int j = 0; j < numNodes; j++) {
if (tempVisitedArray[j]) {
continue;
} // Of if
if (tempMinDistance > tempDistanceArray[j]) {
tempMinDistance = tempDistanceArray[j];
tempBestNode = j;
} // Of if
} // Of for j
tempVisitedArray[tempBestNode] = true;
// 为下一轮做准备。
for (int j = 0; j < numNodes; j++) {
if (tempVisitedArray[j]) {
continue;
} // Of if
if (weightMatrix.getValue(tempBestNode, j) >= MAX_DISTANCE) {
continue;
} // Of if
if (tempDistanceArray[j] > tempDistanceArray[tempBestNode] + weightMatrix.getValue(tempBestNode, j)) {
tempDistanceArray[j] = tempDistanceArray[tempBestNode] + weightMatrix.getValue(tempBestNode, j);
tempParentArray[j] = tempBestNode;
} // Of if
} // Of for j
// For test
System.out.println("The distance to each node: " + Arrays.toString(tempDistanceArray));
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
} // Of for i
// Step 3. 输出。
System.out.println("Finally");
System.out.println("The distance to each node: " + Arrays.toString(tempDistanceArray));
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
return tempDistanceArray;
}// Of dijkstra
/**
*********************
* The minimal spanning tree.
*
* @return The total cost of the tree.
*********************
*/
public int prim() {
int tempSource = 0;
int[] tempDistanceArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
tempDistanceArray[i] = weightMatrix.getValue(tempSource, i);
} // Of for i
int[] tempParentArray = new int[numNodes];
Arrays.fill(tempParentArray, tempSource);
tempParentArray[tempSource] = -1;
// 略过已访问的节点
boolean[] tempVisitedArray = new boolean[numNodes];
tempVisitedArray[tempSource] = true;
// prim算法的主要内容。
int tempMinDistance;
int tempBestNode = -1;
for (int i = 0; i < numNodes - 1; i++) {
// 找到那个最佳的节点
tempMinDistance = Integer.MAX_VALUE;
for (int j = 0; j < numNodes; j++) {
if (tempVisitedArray[j]) {
continue;
} // Of if
if (tempMinDistance > tempDistanceArray[j]) {
tempMinDistance = tempDistanceArray[j];
tempBestNode = j;
} // Of if
} // Of for j
tempVisitedArray[tempBestNode] = true;
// 为下一轮做准备。
for (int j = 0; j < numNodes; j++) {
if (tempVisitedArray[j]) {
continue;
} // Of if
// 无法找到的节点
if (weightMatrix.getValue(tempBestNode, j) >= MAX_DISTANCE) {
continue;
} // Of if
if (tempDistanceArray[j] > weightMatrix.getValue(tempBestNode, j)) {
// Change the distance.
tempDistanceArray[j] = weightMatrix.getValue(tempBestNode, j);
// Change the parent.
tempParentArray[j] = tempBestNode;
} // Of if
} // Of for j
// For test
System.out.println("The selected distance for each node: " + Arrays.toString(tempDistanceArray));
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
} // Of for i
int resultCost = 0;
for (int i = 0; i < numNodes; i++) {
resultCost += tempDistanceArray[i];
} // Of for i
// 输出。
System.out.println("Finally");
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
System.out.println("The total cost: " + resultCost);
return resultCost;
}// Of prim
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String args[]) {
Net tempNet0 = new Net(3);
System.out.println(tempNet0);
int[][] tempMatrix1 = { { 0, 9, 3, 6 }, { 5, 0, 2, 4 }, { 3, 2, 0, 1 }, { 2, 8, 7, 0 } };
Net tempNet1 = new Net(tempMatrix1);
System.out.println(tempNet1);
tempNet1.dijkstra(1);
System.out.println();
// 建立一个无向网。
int[][] tempMatrix2 = { { 0, 7, MAX_DISTANCE, 5, MAX_DISTANCE }, { 7, 0, 8, 9, 7 },
{ MAX_DISTANCE, 8, 0, MAX_DISTANCE, 5 }, { 5, 9, MAX_DISTANCE, 0, 15 },
{ MAX_DISTANCE, 7, 5, 15, 0 } };
Net tempNet2 = new Net(tempMatrix2);
tempNet2.prim();
}// Of main
}
运行结果: