Dijkstra 寻路算法

Dijkstra 寻路算法

Dijkstra 是解决单源最短路径问题的算法,是贪婪算法的经典例子,是广度优先搜索算法,是一种发散式的搜索,计算源点(起点)到所有节点的最短路径,解决的是有权图中最短路径问题(注意:权值不能为负)。时间复杂度和空间复杂度都比较高。

优点:当目标不确定时,DijkStra算法是更好的选择。
示例:假如有N个目标,只需要找到一个路径最近的目标,DijkStra算法会从源点开始,一层一层不断扩大范围的搜索,则最先被搜索到的目标即为路径最近的最优选择。
如果使用 AStar,JPS算法等,则需要遍历搜索 N 个目标,最终在N个路径中比较查找最近路径。会存在大量重复计算,降低效率

下面是一个Dijkstra寻路的动画展示
在这里插入图片描述

下图为一个有向赋权图 G=(V, E)
在这里插入图片描述
图中各节点到其他节点的权值,未连接的节点之间距离认为无穷大 ∞

ABCDEF
A0352
B01
C012
D046
E0
F0

表格读取方式为从横向到数列节点的权值(代价)
A 横向到 A、B、C、D、E、F
A(0) A -> A 权值/代价为 0
B(3) A ->B 权值/代价为 3
C(5) A ->C 权值/代价为 5
D(2) A ->D 权值/代价为 2
E(∞) A ->E 权值/代价为 ∞, A 不能直接到达 E 所以初始时记 A 到 E 的代价无穷大
F(∞) A ->F 权值/代价为 ∞,A 不能直接到达 F 所以初始时记 A 到 F 的代价无穷大
起始时所有节点{A,B,C,D,E,F} 都为未知的 Known = false
以A为源点:所以A为已知的,设置 A.Known = true

逻辑图如下
在这里插入图片描述

初始化
S={}
U={A(cost=0, know = true), B(cost=∞, know = false), C(cost=∞, know = false), D(cost=∞, know = false), E(cost=∞, know = false), F(cost=∞, know = false)}
如上A(cost=0, know = true) 解读为 从起点到 A 的代价为 0,A 为已知的
B(cost=∞, know = false) 解读为 从起点到 B 的代价为 ∞,B为未知的

下面简写为 A(0, true) ,B(∞, false)
S={}
U={A(0,true), B(∞,false), C(∞, false), D(∞,false), E(∞, false), F(∞, false)}
注:S 是已经计算出最短路径的节点集合
U 是未计算出最短路径的节点集合

代码核心逻辑如下

    public class Node
    {
        // 节点名
        public string nodeName;
        // 节点标记:已知/未知
        public bool know = false;
        // 节点邻居
        public List<Node> neighbourList = new List<Node>();
        // 节点到邻居的代价
        public List<int> neighbourCostList = new List<int>();
        // 从源点到当前节点的代价(默认值为int.MaxValue)
        public int cost = int.MaxValue;
        public Node(string nodeName)
        {
            this.nodeName = nodeName;
        }
    }

    class Dijkstra
    {
        public Dijkstra()
        {
            // 配置节点
            Node nodeA = new Node("A");
            Node nodeB = new Node("B");
            Node nodeC = new Node("C");
            Node nodeD = new Node("D");
            Node nodeE = new Node("E");
            Node nodeF = new Node("F");

            // A 为起点,所以已知节点设置cost = 0
            nodeA.cost = 0;
            nodeA.know = true;

            // 将节点 B 加入到节点 A 的邻居中
            nodeA.neighbourList.Add(nodeB);
            // 添加节点 A 到 节点 B 的代价(3)
            nodeA.neighbourCostList.Add(3);

            // 将节点 C 加入到节点 A 的邻居中
            nodeA.neighbourList.Add(nodeC);
            // 添加节点 A 到 节点 C 的代价(5)
            nodeA.neighbourCostList.Add(5);

            // 将节点 D 加入到节点 A 的邻居中
            nodeA.neighbourList.Add(nodeD);
            // 添加节点 A 到 节点 D 的代价(2)
            nodeA.neighbourCostList.Add(2);

            nodeB.neighbourList.Add(nodeC);
            nodeB.neighbourCostList.Add(1);

            nodeC.neighbourList.Add(nodeE);
            nodeC.neighbourCostList.Add(1);

            nodeC.neighbourList.Add(nodeF);
            nodeC.neighbourCostList.Add(2);

            nodeD.neighbourList.Add(nodeE);
            nodeD.neighbourCostList.Add(4);

            nodeD.neighbourList.Add(nodeF);
            nodeD.neighbourCostList.Add(6);

            List<Node> nodeList = new List<Node>();
            nodeList.Add(nodeA);
            nodeList.Add(nodeB);
            nodeList.Add(nodeC);
            nodeList.Add(nodeD);
            nodeList.Add(nodeE);
            nodeList.Add(nodeF);

            UpdateGraph(nodeList);
        }

        public void UpdateGraph(List<Node> nodeList)
        {
            // 根据权值排序,这里可以将List换成小根堆
            nodeList.Sort((a, b) =>
            {
                return a.cost - b.cost;
            });

            List<Node> closedList = new List<Node>();
            while (nodeList.Count > 0)
            {
                Node node = null;
                // 获取所有已知节点(know=true)中权值最小的节点
                for (int i = 0; i < nodeList.Count; ++i)
                {
                    Node temp = nodeList[i];
                    if (!temp.know)
                    {
                        continue;
                    }
                    node = temp;
                    nodeList.RemoveAt(i);
                    break;
                }
                // 如果没有已知节点,则退出
                if (null == node)
                {
                    break;
                }

                // 将已知节点加入到 closedList
                closedList.Add(node);

                // 遍历节点 node 的所有邻居节点
                for (int i = 0; i < node.neighbourList.Count; ++i)
                {
                    Node neighbour = node.neighbourList[i];
                    int cost = node.cost + node.neighbourCostList[i];
                    // 更新邻居节点的权值
                    neighbour.cost = neighbour.cost < cost ? neighbour.cost : cost;
                    // 将邻居节点设置为已知节点(know=true)
                    neighbour.know = true;
                }

                // 重新根据权值排序,这里可以换成小根堆
                nodeList.Sort((a, b) =>
                {
                    return a.cost - b.cost;
                });
            }

            // 遍历closedList打印每个节点的权值
            for (int i = 0; i < closedList.Count; ++i)
            {
                Node node = closedList[i];
                Console.WriteLine(node.nodeName + "   " + node.cost);
            }
        }
    }

堆优化:
将上方的 openList 替换为 最小堆,下面简称 堆
逻辑实现:
1.将起点加入 堆,调整堆
2.获取堆顶节点N (即当前代价最小的节点),从堆中删除节点N,并对堆进行调整
3.遍历节点N 的相邻节点
(1) 如果该节点在堆里或已访问过,且当前计算距离 < 之前计算的距离,则更新距离,并调整堆
(2) 如果该节点不在堆里,加入堆,更新小根堆
4.如果节点 N 是终点,结束算法,否则重复步骤 2、3

Djikstra算法的代码实现,借助Unity展示

下面是实现的代码动画展示
在这里插入图片描述
蓝色小球位置为搜索过的节点

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值