最优路径之Dijkstra算法(一)
#一、算法原理
-
先根据路径图初始化二维数组的距离(即权值),数组存放对应点到各个节点的距离。
例如:Metro[0]=[0, 2, 3, 6,2048,2048]
表示A到A距离为0,到B距离为2,到C距离为3……。
添加初始节点A到已确定点中,设置点A的状态为已确定。此时:已确定点数组 S={A}, 未确定点数组 U={B,C,D,E,F} 节点A到各个点的距离 prev={0, 2, 3, 6,∞,∞}
-
选出当前节点组中未被选中且距离最小的点,加入到已确定路径点中。
最小值为2,则添加点B到已确定点中。此时:*S={A,B}, U={C,D,E,F} prev={0, 2, 3, 6,∞,∞}*
-
由于点B已确定,这里明显点A 数据组prev也需要更新数据。这里分为几种情况(以到点P为例):
第一、点A不能到点B能到,最新的距离为A–>B–>P,距离需累加。
第二、点A,B均能到,则此时需要选出最佳距离,即比较A–>P与A–>B–>P的距离选出最佳路径。
第三、点A能到B不能到,最新的距离为A–>P,无需更新,则此种情况可不考虑。
第四、点A,B均不能到,故数据无需更新,则此种情况可不考虑。
则更新后的数据为:*prev={0, 2, 3,6,6,8}*
-
重复执行步骤2,将第三个点加入到已确定点中,通过比较未确定的点中点C距离最小,同时更新数据,则有以下数据:
S= {A,B,C} U= {D,E,F} prev= {0, 2, 3, 5,6,8}
继续重复执行步骤2,3,直至数组U中没有数据,则此时所有点的最优路径都已经计算完毕,最后得到的数组prev即点A到所有点的最优距离。
prev= {0, 2, 3, 5,6,8}
-
至此整个算法计算完成,但随后我发现最后我拿到最优距离之后却无法得到相应的路径,由此需要在计算最优距离过程中同时存储路径。这里我将各个节点中的上一个点存下来,依次上推可以拿到任意点的最优路径
public class PathNode { public int curIndex; public int lastIndex; public PathNode(int _curIndex, int _lastIndex) { curIndex = _curIndex; lastIndex = _lastIndex; } }
#三、实现代码
class Program:MonoBehaviour
{
int[,] Metro = new int[6, 6] {
{ 0, 2, 3, 6,2048,2048},
{ 2, 0, 2048,2048,4,6},
{ 3, 2048, 0, 2,2048,2048},
{6, 2048, 2,0,1, 3},
{2048,4,2048, 1,0, 2048},
{2048,6,2048, 3,2048, 0}};
static int row = 6;
List<int> S = new List<int>(row);//S储存确定最短路径的顶点的下标
List<int> U = new List<int>(row);//U中存储尚未确定路径的顶点的下标
private List<PathNode> pathNode = new List<PathNode>();//储存节点的逻辑顺序
int[] prev = new int[row];//用以存储前一个最近顶点的数据
bool[] Isfor = new bool[6] { false, false, false, false, false, false };
public void Start()
{
for (int i = 0; i < row; i++)
{
pathNode.Add(new PathNode(-1,-1));
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
MyDijkstra(5);
}
}
void MyDijkstra(int _index)
{
int pathValue = 0;
S.Add(_index);
Isfor[_index] = true;
for (int i = 0; i < row; i++)
{
U.Add(i);
}
U.Remove(_index);
//初始化当前节点数据
for (int i = 0; i < row; i++)
{
prev[i] = Metro[_index,i];
}
InitPathNode(pathNode, prev.ToList(), _index);
while (U.Count>0)
{
int m_temp = 2048;
int curIndex = _index;//当前最小值下标
//找到最小值,获取下标
for (int i = 0; i < row; i++)
{
if (prev[i] < m_temp && !Isfor[i])
{
m_temp = prev[i];
curIndex = i;
}
}
pathValue = m_temp;
Isfor[curIndex] = true;
S.Add(curIndex);
U.Remove(curIndex);
//更新当前节点数据信息
for (int i = 0; i < row; i++)
{
if (!Isfor[i])
{
if (prev[i] < 2048 && Metro[curIndex, i] < 2048)
{
if (Metro[curIndex, i] + pathValue < prev[i])
{
prev[i]=Metro[curIndex, i] + pathValue;
pathNode[i] = new PathNode(i, curIndex);
}
}
else if (Metro[curIndex, i] < 2048)
{
prev[i] = Metro[curIndex, i] + pathValue;
pathNode[i]=new PathNode(i,curIndex);
}
}
}
}
//打印节点的最短路径顺序
for (int i = 0; i < row; i++)
{
string path = "";
List<int> indexList=new List<int>();
indexList.Add(i);
GetPathforIndex(pathNode, indexList, i, _index);
for (int j = 0; j < indexList.Count; j++)
{
if (path=="")
{
path = $"{(indexList[j] + 1)}";
}
else
{
path += $"-->{indexList[j] + 1}";
}
}
Debug.LogError($"从点{_index}到点{i + 1}的路径为:{path},距离为:{prev[i]}");
}
}
/// <summary>
/// 初始化节点的连接关系
/// </summary>
/// <param name="_pathNodes"></param>
/// <param name="_list"></param>
/// <param name="_index"></param>
void InitPathNode(List<PathNode> _pathNodes,List<int> _list,int _index)
{
for (int i = 0; i < _list.Count; i++)
{
if (_list[i]<2048)
{
_pathNodes[i]=new PathNode(i,_index);
}
}
}
/// <summary>
/// 根据下标获取节点的路径
/// </summary>
/// <param name="_pathNodes"></param>
/// <param name="_path">存储路径节点</param>
/// <param name="_index"></param>
/// <param name="_startIndex">起点</param>
/// <returns></returns>
List<int> GetPathforIndex(List<PathNode> _pathNodes,List<int> _path ,int _index,int _startIndex)
{
_path.Add(_pathNodes[_index].lastIndex);
if (_pathNodes[_index].lastIndex== _startIndex)
{
return _path;
}
else
{
return GetPathforIndex(_pathNodes, _path, _pathNodes[_index].lastIndex, _startIndex);
}
}
}
public class PathNode
{
public int curIndex;
public int lastIndex;
public PathNode(int _curIndex, int _lastIndex)
{
curIndex = _curIndex;
lastIndex = _lastIndex;
}
}