JS使用dijkstra算法实现查找最短路径

前言

最近有一个需求需要用到寻路,于是我查阅了一些资料,最后觉得dijkstra算法最适合我的场景,我需要从一个起始点S导航到终点D,了解了原理之后我使用HTML写了一个小的demo,下文会附上原理和Code部分。

原理

dijkstra算法主要是使用贪心算法的思想,每进行一个步骤都会找到当前最利于结果的方案;dijkstra算法也不适用于存在负方向的weight,这里我们只关注距离distance,不存在这种情况;

1.首先我们得到一个数据集nodes,这些nodes需要具备唯一的name(id),以及与nodes连接的其他node的关系(links),并且还需要距离distance(专业的术语应该是weight: 权重),有了这些之后,我们就可以准备开始啦

2.准备一个数组用来记录node的name,node的距离起点S的distance,以及可追溯的上一个node的name;下表所示

NodeNamemindistance of StartlastNodeName
AInfinitynull
BInfinitynull
CInfinitynull
DInfinitynull
EInfinitynull
FInfinitynull
GInfinitynull

我们假设所有Node距离起点都是无限大的距离,假设我使用A作为起点,F作为终点,那么可以得到A距离起点的distance为0,lastNodeName为null,这时我们就可以将A标记为已检查的Node,因为其他的Node不可能在距离上小于A,后面的计算中我们也不会再回到A;这是第一步

第二步,我们通过A的数据集中的links找到A的连接点B C D,如图(PS:B 到 G 距离为 6)

根据贪心算法的宗旨,只找当前最好的方案,那么无疑D就是距离最短的那个,但我们也同时在表格中更新B和C,如下

NodeNamemindistance of StartlastNodeNameisChecked
A0nulltrue
B12Afalse
C8Afalse
D7Afalse
EInfinitynullfalse
FInfinitynullfalse
GInfinitynullfalse

同理我们需要将最优解D打上已检查的标记,之后在通过D的links去找到最优解,D关联了G, E, C, A,由于A是上一个Node 我们不需要计算,那么其他的3个Node中加上之前的7,分别是G:17,E:11,C:12,其中C我们之前计算的距离是8,这里的C是12,所以我们不用更新表格,E是最优解,更新表格如下

NodeNamemindistance of StartlastNodeNameisChecked
A0nulltrue
B12Afalse
C8Afalse
D7Atrue
E11Dfalse
FInfinitynullfalse
G17Dfalse

此时我们发现没有被检查的Node中距离最短的是C,我们再重复刚刚从操作,最后会得到这样的表格

NodeNamemindistance of StartlastNodeNameisChecked
A0nulltrue
B12Atrue
C8Atrue
D7Atrue
E11Dtrue
F13Etrue
G16Dtrue

最后我们通过回溯的方式,从F一直向上回溯到A,就是 F->E->D->A,之后可以reverse一下,就是最短路径啦!

Code

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dijkstra Demo</title>
</head>

<body>
    <label for="local">LOCAL</label>
    <input type="text" id="local">

    <label for="dest">DEST</label>
    <input type="text" id="dest">

    <div id="resultUI"></div>
    <script>
        // Dijkstra 算法
        class Queue {
            constructor() {
                this.items = [];
            }
            // 入列
            enqueue(element) {
                this.items.push(element);
            }
            // 判断队列是否为空
            isEmpty() {
                return this.items.length === 0;
            }
        }
        // node
        const nodes = [
            {
                name: 'A',
                links: [
                    {
                        name: 'B',
                        weight: 12
                    },
                    {
                        name: 'C',
                        weight: 8
                    },
                    {
                        name: 'D',
                        weight: 7
                    }
                ],
            },
            {
                name: 'B',
                links: [
                    {
                        name: 'A',
                        weight: 12
                    },
                    {
                        name: 'G',
                        weight: 6
                    },
                ],
            },
            {
                name: 'C',
                links: [
                    {
                        name: 'A',
                        weight: 8
                    },
                    {
                        name: 'D',
                        weight: 5
                    },
                    {
                        name: 'E',
                        weight: 9
                    },
                ],
            },
            {
                name: 'D',
                links: [
                    {
                        name: 'A',
                        weight: 7
                    },
                    {
                        name: 'C',
                        weight: 5
                    },
                    {
                        name: 'E',
                        weight: 4
                    },
                    {
                        name: 'G',
                        weight: 10
                    },
                ],
            },
            {
                name: 'E',
                links: [
                    {
                        name: 'D',
                        weight: 4
                    },
                    {
                        name: 'C',
                        weight: 9
                    },
                    {
                        name: 'F',
                        weight: 2
                    },
                    {
                        name: 'G',
                        weight: 5
                    },
                ],
            },
            {
                name: 'F',
                links: [
                    {
                        name: 'E',
                        weight: 2
                    },
                    {
                        name: 'G',
                        weight: 9
                    },
                ],
            },
            {
                name: 'G',
                links: [
                    {
                        name: 'B',
                        weight: 6
                    },
                    {
                        name: 'D',
                        weight: 10
                    },
                    {
                        name: 'E',
                        weight: 5
                    },
                    {
                        name: 'F',
                        weight: 9
                    },
                ],
            },
        ]
        
        class Dijkstra {
            constructor(nodes, start, end) {
                this.nodes = nodes; // 节点
                this.start = start; // 起点
                this.end = end; // 终点
                this.flow = []; // 最优路径
                this.queue = new Queue(); // 队列
                this.minDistance = Infinity;
            }

            // 计算最优路径
            calcuate() {
                this.flow = []
                // 初始化队列
                for (const node of this.nodes) {
                    this.queue.enqueue({
                        name: node.name,
                        distance: this.start === node.name ? 0 : Infinity,
                        isChecked: false,
                        lastNode: null,
                        links: node.links
                    })
                }
                // 计算距离start地点的distance
                while (this.queue.items.some(item => !item.isChecked)) {
                    const tempList = this.queue.items.filter(item => !item.isChecked).sort((a, b) => a.distance - b.distance)
                    tempList[0].isChecked = true
                    tempList[0].links.forEach(link => {
                        if (!this.queue.items.filter(item => item.isChecked).find(item => item.name === link.name)) {
                            const index = this.queue.items.findIndex(value => link.name === value.name)
                            const _distance = link.weight + tempList[0].distance
                            if (this.queue.items[index].distance > _distance) {
                                this.queue.items[index].distance = link.weight + tempList[0].distance
                                this.queue.items[index].lastNode = tempList[0].name
                            }
                        }
                    })
                }

                this.flow.unshift(this.end)
                // 找到目的地后追溯至起点
                while (this.flow[0]) {
                    const lastNode = this.queue.items.find(item => this.flow[0] === item.name)
                    if (this.flow.length === 1) this.minDistance = lastNode.distance
                    this.flow.unshift(lastNode ? lastNode.lastNode : null)
                }
                this.flow.shift()
                return this.flow.join('->')
            }
        }

        const destDOM = document.querySelector('#dest')
        const localDOM = document.querySelector('#local')
        const resultDOM = document.querySelector('#resultUI')
        const DOMArr = [destDOM, localDOM]
        const dijkstraObject = new Dijkstra(nodes, localDOM.value, destDOM.value)
        DOMArr.forEach(DOM => {
            DOM.addEventListener('input', e => {
                const names = nodes.map(node => node.name)
                if (!names.includes(localDOM.value) || !names.includes(destDOM.value)) return
                dijkstraObject.start = localDOM.value
                dijkstraObject.end = destDOM.value
                const minPath = dijkstraObject.calcuate()
                resultDOM.innerHTML = `
                最短的路径: ${minPath} <br />
                长度为: ${dijkstraObject.minDistance}
                `
            })
        })
    </script>
</body>

</html>

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值