图算法
图,包含定点和边,就是计算起始定点到目标定点的最短开销。(举例:在游戏中,常运用于计算 rpc 到英雄的最短距离。)
之前看了网上别人写的图算法,在这里,从原理到实现做一个详细解读
计算过程
迪特斯克拉算法:
此处举例的图,结构如下:
一共6个顶点,a1是起始顶点,a6是终点
注意:后面的讲解会用到<当前顶点>这个角色,可以想象为当前选中的顶点,对应的,还有<上一个顶点>,<下一个顶点>
- 先初始化所有的顶点开销为无穷大。初始化<当前顶点>为第一个顶点
- 计算<当前顶点>到<下一个顶点>的最短开销。
结果:<当前顶点>到a3的开销最小
3. <当前顶点>移动到<下一个顶点>位置(最短开销的顶点)。
- 比较 :第一个顶点直接到<下一个顶点>的距离 a,
第一个顶点到<当前顶点>+<当前顶点>到<下一个顶点>的最短距离 b。
如果 a<b:
路径:路径设定为a,也就是第一个顶点直接到下一个顶点,也就是a1–>a4
否则:路径设定为b,也就是从第一个顶点->当前顶点->下一个顶点
现在,a=60,b=30+25 ,所以路径暂时设定为b
6. 重复2-4过程,直到当前顶点移动到终点
编码
将图元素用代码定义出来
1,顶点
已经作过 “当前顶点“ 这个角色的点,为true,否则为null
temp = 【true,null,null,null,null,null】
顶点用数组的 index 定义。顶点1 就是 temp 的 index=0
2,边
边,就是开销值。如果没有连接,则开销为无穷大。
3,图
用邻接矩阵表示
Integer infinity = Integer.MAX_VALUE; //用 infinity 代表无穷大
Integer[][] paraGraph=new Integer[][]{
//从a1定点出发,到各定点的开销
{0, infinity,30,60, infinity, 300},
//从a2顶点出发,到各个顶点的开销
{infinity, 0, 3,infinity,infinity,infinity},
//从a3顶点出发,到各个顶点的开销
{infinity,infinity, 0, 25,infinity,infinity},
//从a4顶点出发,到各个顶点的开销
{infinity,infinity,infinity, 0,infinity,70},
//从a5顶点出发,到各个顶点的开销
{infinity,infinity,infinity, 15, 0,10},
//从a6顶点出发,到各个顶点的开销
{infinity,infinity,infinity,infinity,infinity,0}
};
4,第一个顶点到终点的距离开销。
// distance代表从第一个点到其他点的开销之和。例如 distance[2] 代表从第一个点,也就是 temp[0] 到 temp[2] 的开销的和(也是最小开销),其中,从第一个点到最后一个点,不经过的点的开销为无穷大
Integer[] distance=new Integer[6];
// 用第一个点到其他点的直接的开销,初始化distance。没有连接的用无穷大初始化
for(Integer i=0;i<6;i++)
distance[i]=paraGraph[0][i];
开始编码:
- 计算第一个顶点到当前顶点的最短开销。
Integer min=Integer.MAX_VALUE; //第一个顶点到当前顶点最小的开销
Integer index=-1; //当前顶点所在的位置
// 遍历第一个顶点和其他顶点直接的开销,选出最小开销,赋值给min
for(Integer i=0;i<distance.length;i++){
// 选一个点为当前顶点
// 忽略第一个顶点,忽略当过 “当前顶点” 的顶点
if(temp[i] == Boolean.TRUE)
continue;
else{
//获取第一个点到当前顶点的最短距离,以及当前顶点的index
if(distance[i]<min){
index=i;
min=distance[i];
}
}
}
- 标记此顶点已经当过 “当前顶点”这个角色
temp[index]=Boolean.TRUE;
比较 :第一个顶点直接到下一个顶点的距离 a,第一个顶点到当前顶点+当前顶点到下一个顶点的最短距离 b
for(Integer i=0;i<paraGraph.length;i++){
//如果当前顶点和另外一个顶点有连接
if(paraGraph[index][i]!=Integer.MAX_VALUE){
//比对第一个顶点到下一个节点的开销,是否小于(第一个顶点到当前顶点的最小开销+当前顶点到下一个顶点的开销)
if(distance[i] < min+paraGraph[index][i]){
distance[i] = distance[i]; // 如果小,则直接使用第一个点到下一个点的开销
}else{
distance[i] = min + paraGraph[index][i]; // 否则,使用第一个点到当前点,然后从当前点再到下一个点的开销
}
}
}
重复上述步骤……
最终代码如下:
public class minDistance {
public static void main(String[] args) {
//这里我们将无穷大定义为int类型最大值 2^32-1 2147483647
Integer infinity = Integer.MAX_VALUE;
//构建图,用邻接矩阵作为图的数据结构,该地图是边的数组
Integer[][] paraGraph=new Integer[][]{
//从a1定点出发,到各定点的开销 从a1开始
{0, infinity,30,60, infinity, 300},
//从a2顶点出发,到各个顶点的开销 从a1开始
{infinity, 0, 3,infinity,infinity,infinity},
//从a3顶点出发,到各个顶点的开销 从a1开始
{infinity,infinity, 0, 25,infinity,infinity},
//从a4顶点出发,到各个顶点的开销 从a1开始
{infinity,infinity,infinity, 0,infinity,70},
//从a5顶点出发,到各个顶点的开销 从a1开始
{infinity,infinity,infinity, 15, 0,10},
//从a6顶点出发,到各个顶点的开销 从a1开始
{infinity,infinity,infinity,infinity,infinity,0}
};
// distance代表从第一个点到其他点的开销之和。例如 distance[2] 代表从第一个点,也就是 temp[0] 到 temp[2] 的开销的和(也是最小开销),其中,从第一个点到最后一个点,不经过的点的开销为无穷大
Integer[] distance=new Integer[6];
for(Integer i=0;i<6;i++)
distance[i]=paraGraph[0][i];
Boolean[] temp=new Boolean[6];//temp集合
minDistance(paraGraph, distance, temp);
}
public static void minDistance(Integer[][] paraGraph,Integer[] distance,Boolean[] temp){
temp[0]=true; //如果对应位置的定点计算过,则temp[i]设定为true
while(true){//外层循环
Integer min=Integer.MAX_VALUE; //当前顶点最小的开销
Integer index=-1; //当前顶点所在的位置
//内层循环:比对每个顶点和其他顶点之间的距离,获取最小距离
for(Integer i=0;i<distance.length;i++){
//比对定点是否已经计算过,开始的顶点忽略,从第二个顶点开始
if(temp[i] == Boolean.TRUE)
continue;//不包括集合temp中的顶点 已经包括了就继续循环,不在加入范围
else{
System.out.println(i);
//获取到下一个顶点的最短距离,以及下一个顶点的index
if(distance[i]<min){
index=i;
min=distance[i];
}
}
}
System.out.println("index "+index);
minDistance.<Integer>printTostring(distance);
if(index==-1) break;//index移动到最后的顶点,跳出循环
// 标记此顶点已经当过 “当前顶点”这个角色
temp[index]=Boolean.TRUE;
for(Integer i=0;i<paraGraph.length;i++){
//如果当前顶点和另外一个顶点有链接
if(paraGraph[index][i]!=Integer.MAX_VALUE){
//比对第一个顶点到其他顶点的开销,是否小于(第一个顶点到当前顶点的最小开销+当前顶点到下一个顶点的开销)
if(distance[i] < min+paraGraph[index][i]){
distance[i] = distance[i]; // 如果小,则直接使用第一个点到下一个点的开销
}else{
distance[i] = min + paraGraph[index][i]; // 否则,使用第一个点到当前点,然后从当前点再到下一个点
}
}
}
}
}
// 用于打印数组的方法
public static <T> void printTostring(T[] parent){
for (T son:parent) {
System.out.print(son+" ");
}
System.out.println("");
}
}