引子:
Dijkstra算法:某个顶点到其他顶点的最短路径。
以下面这个图为例:其中源点是A。关键点:维护一个二维数组,具体见下面:
1、首先,派一名调查员驻扎在A处,在A处,a调查员能够知道与A相连的B和D的路径长度,在笔记本上记着下面图片1的信息。接下来选择没有驻扎调查员、和A直接相连以及到A路径最短的点。只有B(50)和D(80)直接和A相连,所以选择B。
注意:如果在笔记本上标注了#号的地方,表示已经在此地驻扎了调查员,同时意味着这个就是此地到源点A的最短路径,不会再在笔记本上修改此项。
2、根据上一位调查员的结果,另外派一名调查员驻扎在B处,这样b调查员就知道了与B相连的地点的路径。也就是C和D,致电告诉a调查员,则a调查员在笔记本上记上下面图片2的信息。现在只有A和B才驻扎了调查员,则下面我们应该选择一个与A或B直接相连的、并且到源点A的路径最短的以及没有驻扎调查员的地点。因为A-B-C是110,A到D是80,A-B-D是140,所以另外派一名调查员直接从A到D。
3、d调查员到D地点之后,开始调查,发现了D到E、D到C的路径,致电给a调查员,a调查员做下面如图所示的笔记。
4、这时,只有A、B、D三个地方驻扎了调查员,而与A、B、D直接相连的并且没有驻扎调查员的地点有C、E,所以,接下来就在C、E里面选择他们俩谁离源点A最近,通过比较知道,AC(100)的路径比AE(150)近,所以,接下来另外派一名调查员驻扎到C处、、、、、、
package graph;
public class DijkstraGraphTest3 {
public static void main(String[] args) {
MyDijkstraGraph g = new MyDijkstraGraph(40);
//增加顶点
g.addVertex("A");
g.addVertex("B");
g.addVertex("C");
g.addVertex("D");
g.addVertex("E");
g.addVertex("F");
g.addVertex("G");
//增加边和边的权重
g.addEdge(0, 1, 50);
g.addEdge(0, 3, 80);
g.addEdge(1, 2, 60);
g.addEdge(1, 3, 90);
g.addEdge(2, 4, 40);
g.addEdge(3, 2, 20);
g.addEdge(3, 4, 70);
g.addEdge(4, 1, 50);
g.addEdge(2, 5, 60);
g.addEdge(4, 5, 10);
g.addEdge(5, 6, 40);
//执行dijkstra算法
g.doDijkstra();
}
}
class MyDijkstraGraph{
//最大的权值
private static final int INFINITE = 10000;
//顶点队列
private Vertex[] vertexList;
//邻接矩阵
private int[][] matrix;
//顶点的个数
private int size;
//辅助数组,记录某点的父顶点以及这个点到源点的距离
private DistanceParent[] distanceParents;
public MyDijkstraGraph(int maxSize){
this.vertexList = new Vertex[maxSize];
this.matrix = new int[maxSize][maxSize];
this.size = 0;
this.distanceParents = new DistanceParent[maxSize];
}
//增加顶点
public void addVertex(String label){
vertexList[size++] = new Vertex(label);
}
//增加边和权重
public void addEdge(int start, int end, int weigth){
this.matrix[start][end] = weigth;
}
//执行算法
public void doDijkstra(){
//当前顶点
int currentVertex = 0;
//加入树中的顶点数
int treeNum = 1;
//初始化:把所有的边的权重都为最大
for(int i = 0; i < size; i++){
for(int k = 0; k < size; k++){
int weigth = matrix[i][k];
if(weigth == 0){
matrix[i][k] = INFINITE;
}
}
}
//初始化:第一个顶点到其余顶点的权重,父顶点下标都为0
for(int i = 0; i < size; i++){
int weigth = matrix[currentVertex][i];
distanceParents[i] = new DistanceParent(weigth, 0);
}
//每一次循环,都可以确认某一个顶点到源点的最小距离
while(treeNum < size){
currentVertex = getMin();
//如果成立,说明剩余的顶点没有和当前顶点相连,可以退出了
if(distanceParents[currentVertex].distance == INFINITE){
System.err.println("full ......");
break;
}
vertexList[currentVertex].setInTree(true);
treeNum++;
//打印
print(currentVertex);
//调整辅助数组
adjust(currentVertex);
}
}
/**
* 获取距离最小的非父顶点的下标
* @return
*/
public int getMin(){
int temp = INFINITE;
int tempIndex = -1;
for(int i = 0; i < size; i++){
int temp2 = distanceParents[i].distance;
if(temp2 < temp && !vertexList[i].isInTree){
temp = temp2;
tempIndex = i;
}
}
return tempIndex;
}
//调整辅助数组
public void adjust(int currentVertex){
for(int i = 1; i < size; i++){
if(currentVertex == i)
continue;
//当前顶点到边缘顶点的路径长度(权重)
int currentToFringe = matrix[currentVertex][i];
//这个边缘顶点到源点的路径长度(权重)
int startToFringe = distanceParents[currentVertex].distance+currentToFringe;
if(vertexList[i].isInTree == false && distanceParents[i].distance > startToFringe){
distanceParents[i].distance = startToFringe;
distanceParents[i].parentVertex = currentVertex;
}
}
}
public void print(int currentVertex){
Vertex parentVertex = vertexList[distanceParents[currentVertex].parentVertex];
System.out.println(parentVertex.label+" --> "+vertexList[currentVertex].label+
": "+distanceParents[currentVertex].distance+"; ");
}
/**
* 顶点类
* @author admin
*
*/
class Vertex{
//标签
String label;
//是否已经加入树中(即是否已经确认这个顶点到源点的最短路径)
boolean isInTree;
public Vertex(String label){
this.label = label;
this.isInTree = false;
}
public void setInTree(boolean isInTree) {
this.isInTree = isInTree;
}
}
/**
* 记录某点的父顶点以及这个点到源点的距离
* @author LiangYH
*
*/
class DistanceParent{
//从源点到现在所在点的距离
int distance;
//现在所在点的父顶点
int parentVertex;
public DistanceParent(){
}
public DistanceParent(int distance, int parentVertex){
this.distance = distance;
this.parentVertex = parentVertex;
}
}
}
二、
下面的代码使用连接矩阵来表示顶点与顶点的边的关系的。具体一点讲就是,有几个顶点就有几行,每一行是一个链表,链表中的元素在实际图中表示为都与同一个元素相连。比如第一行有两个元素B和C,则表示B和C都和A相连。而上面的代码是用邻接矩阵表示的。在内存空间使用方面,本人觉得下面这种方式比较好。
package graph;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class DijkstraGTest {
public static void main(String[] args) {
MyDijkstraG g = new MyDijkstraG(40);
//增加顶点
g.addVertex("A");
g.addVertex("B");
g.addVertex("C");
g.addVertex("D");
g.addVertex("E");
g.addVertex("F");
g.addVertex("G");
//增加边和边的权重
g.addEdge(0, 1, 50);
g.addEdge(0, 3, 80);
g.addEdge(1, 2, 60);
g.addEdge(1, 3, 90);
g.addEdge(2, 4, 40);
g.addEdge(3, 2, 20);
g.addEdge(3, 4, 70);
g.addEdge(4, 1, 50);
g.addEdge(2, 5, 60);
g.addEdge(4, 5, 10);
g.addEdge(5, 6, 40);
//执行dijkstra算法
g.doDijkstra();
}
}
class MyDijkstraG{
//最大的权值
private static final int INFINITE = 10000;
//二维数组,连接矩阵
List<LinkedList<VertexWeigth>> matrix = null;
//顶点队列
private Vertex[] vertexList;
//顶点的个数
private int size;
//辅助数组,记录某点的父顶点以及这个点到源点的距离
private DistanceParent[] distanceParents;
public MyDijkstraG(int maxSize){
this.vertexList = new Vertex[maxSize];
this.matrix = new ArrayList<LinkedList<VertexWeigth>>();
this.size = 0;
this.distanceParents = new DistanceParent[maxSize];
for(int i = 0; i < maxSize; i++){
LinkedList<VertexWeigth> list = new LinkedList<>();
matrix.add(list);
}
}
//增加顶点
public void addVertex(String label){
vertexList[size++] = new Vertex(label);
}
//增加边和权重
public void addEdge(int start, int end, int weigth){
VertexWeigth vw = new VertexWeigth(end, weigth);
matrix.get(start).add(vw);
}
//执行算法
public void doDijkstra(){
//当前顶点
int currentVertex = 0;
//加入树中的顶点数
int treeNum = 1;
//初始化:第一个顶点到其余顶点的权重,父顶点下标都为0
Iterator<VertexWeigth> iter = matrix.get(0).iterator();
while(iter.hasNext()){
VertexWeigth vw = iter.next();
distanceParents[vw.vertexIndex] = new DistanceParent(vw.weigth, 0);
}
//初始化:把源点不可达的边的权重都为最大,同时防止空异常
for(int i = 0; i < size; i++){
if(distanceParents[i] == null){
distanceParents[i] = new DistanceParent(INFINITE, 0);
}
}
//每一次循环,都可以确认某一个顶点到源点的最小距离
while(treeNum < size){
currentVertex = getMin();
//如果成立,说明剩余的顶点没有和当前顶点相连,可以退出了
if(distanceParents[currentVertex].distance == INFINITE){
System.err.println("full ......");
break;
}
vertexList[currentVertex].setInTree(true);
treeNum++;
//打印
print(currentVertex);
//调整辅助数组
adjust(currentVertex);
}
}
/**
* 获取距离最小的非父顶点的下标
* @return
*/
public int getMin(){
int temp = INFINITE;
int tempIndex = -1;
for(int i = 0; i < size; i++){
int temp2 = distanceParents[i].distance;
if(temp2 < temp && !vertexList[i].isInTree){
temp = temp2;
tempIndex = i;
}
}
return tempIndex;
}
//调整辅助数组
public void adjust(int currentVertex){
Iterator<VertexWeigth> iter = matrix.get(currentVertex).iterator();
while(iter.hasNext()){
VertexWeigth vw = iter.next();
int currentToFringe = vw.weigth;
int startToFringe = distanceParents[currentVertex].distance+currentToFringe;
if(vertexList[vw.vertexIndex].isInTree == false && distanceParents[vw.vertexIndex].distance > startToFringe){
distanceParents[vw.vertexIndex].distance = startToFringe;
distanceParents[vw.vertexIndex].parentVertex = currentVertex;
}
}
}
public void print(int currentVertex){
Vertex parentVertex = vertexList[distanceParents[currentVertex].parentVertex];
System.out.println(parentVertex.label+" --> "+vertexList[currentVertex].label+
": "+distanceParents[currentVertex].distance+"; ");
}
/**
* 用于保存尾顶点的下标以及边的权重
* @author LiangYH
*
*/
class VertexWeigth{
int vertexIndex;
int weigth;
public VertexWeigth(){
}
public VertexWeigth(int vertexIndex, int weigth){
this.vertexIndex = vertexIndex;
this.weigth = weigth;
}
}
/**
* 顶点类
* @author admin
*
*/
class Vertex{
//标签
String label;
//是否已经加入树中(即是否已经确认这个顶点到源点的最短路径)
boolean isInTree;
public Vertex(String label){
this.label = label;
this.isInTree = false;
}
public void setInTree(boolean isInTree) {
this.isInTree = isInTree;
}
}
/**
* 记录某点的父顶点以及这个点到源点的距离
* @author LiangYH
*
*/
class DistanceParent{
//从源点到现在所在点的距离
int distance;
//现在所在点的父顶点
int parentVertex;
public DistanceParent(){
}
public DistanceParent(int distance, int parentVertex){
this.distance = distance;
this.parentVertex = parentVertex;
}
}
}
结果: