zoukankan      html  css  js  c++  java
  • 图论_迪杰斯特拉算法(Dijkstra)

    Dijkstra算法

        求解一系列点中任意两点间最短距离的算法 -- 图论最短距离

            简介:

                1. 案例:存在一系列的数据点(如9个点),我们将其编号为'A'~'I',这9个点的位置由坐标决定

                    在这11个顶点中,两两顶点之间或存在连线(即可通过其中一点直接达到另一点),

                    或不存在连线(即不可通过其中一点直接到达另一点), 现需要找到一条从0点到8点的最短距离的路径

                2. 倘若存在一条路径(如'A' - 'B' - 'H' - 'I' - 'C' - 'F' - 'E')为最短路径

                    则存在以下定理:

                        该路径上任一点如'I'到起点'A'的距离也是起点到该点(或该点到终点)路径的最短距离。

                            证明:反证法

                                比如:路径中的7这个点, 'A' - 'B' - 'H' - 'I'这条路径一定是从 'A' 到 'I' 这个路径上最短的一条路径

                                    如果不是(如 'A' 到 'I' 的最短路径为 'A' - 'C' - 'D' - 'I'),

                                    则最初选择的从'A' 到 'E'的最短路径应为:'A' - 'C' - 'D' - 'I' - 'C' - 'F' - 'E'

                                    得证

        Dijkstra算法中存储信息的3个列表

            1. 访问列表

                记录该节点是否已经被访问过,未被访问为false,已被访问为true

                visited = {

                    'A': false, 

                    'B': false, 

                    'C': false, 

                    'D': false, 

                    'E': false, 

                    'F': false, 

                    'G': false, 

                    'H': false, 

                    'I': false

                }

            2. 距离列表

                用来记录从起点到每个节点的总距离信息,初始时,距离均为inf

                距离列表可以用对象记录

                dist = {

                    'A': inf,

                    'B': inf,

                    'C': inf,

                    'D': inf,

                    'E': inf,

                    'F': inf,

                    'G': inf,

                    'H': inf,

                    'I': inf

                }

            3. 父节点列表

                用于存储每个被访问的节点的父节点是谁, 未被访问的节点父节点默认为-1

                parent = {

                    'A': -1,

                    'B': -1,

                    'C': -1,

                    'D': -1,

                    'E': -1,

                    'F': -1,

                    'G': -1,

                    'H': -1,

                    'I': -1

                }

    function Dijkstra(Grapg, first, last){
        // 1. 获取输入图结构的信息
        // 1.1 获取节点列表
        var vertexs = Grapg.vertexs;
        // 1.2 获取连线和权值信息
        var edges = Grapg.Edges;
    
        /*
            用于记录信息的三个列表:
                访问状态列表
                    visited
                距离列表
                    dist
                父节点列表
                    parent
        */
        // 2 列表初始化
        var visited = {};
        var dist = {};
        var parent = {};
        for(var i = 0; i < vertexs.length; i++){
            visited[vertexs[i]] = false;
            dist[vertexs[i]] = Infinity;
            parent[vertexs[i]] = -1;
        }
    
        // 3 首次更新三个列表
        visited[first] = true;
        dist[first] = 0;
        
        // 4. 依次访问节点,并在每次访问后更新三个列表,直到所有节点均被访问
        var cur = first;
        while(Object.values(visited).some(item => item === false)){
            /* 当还有节点未被访问时 */
    
            // 4.1 更新dist表和parent表
            // 获取当前节点cur的所连接的节点
            var linkedVertex = Object.keys(edges[cur]);
            // 遍历这些节点,更新dist距离表和parent表
            for(var k = 0; k < linkedVertex.length; k++){
                // 只有未被访问过的节点才需要更新
                if(visited[linkedVertex[k]] === false){
                    // 如果当前距离值小于起点直接到达该点距离值,则更新该点距离值
                    if(dist[cur] + edges[cur][linkedVertex[k]] < dist[linkedVertex[k]]){
                        // 更新dist距离表
                        dist[linkedVertex[k]] = dist[cur] + edges[cur][linkedVertex[k]];
                        // 更新parent表
                        parent[linkedVertex[k]] = cur;
                    }
                }
            }
    
            // 4.2 更新visited表
            // 找出未被访问的节点集合及对应的值
            var uKeys = [];
            var uValues = [];
            for(var i = 0; i < vertexs.length; i++){
                if(visited[vertexs[i]] === false){
                    // 数组ukeys中保存未被访问过的节点名
                    uKeys.push(vertexs[i]);
                    // 数组uValues中保存未被访问过的节点到起点的距离
                    uValues.push(dist[vertexs[i]]);
                }
            }
            // 查找当前未被访问过的点的距离最小值
            var minDist = Math.min(...uValues);
            // 查找当前未被访问过的点的距离最小值对应的那个节点名
            var minVertex = uKeys[uValues.indexOf(minDist)];
            
            // 更新当前点
            cur = minVertex;
            visited[cur] = true;
        }
    
        // 4.4 根据parent表,总结出最佳路径
        // 输出最佳路径
        var path = [last];
        while(path[0] != first){
            var p = parent[path[0]];
            path.unshift(p);
        }
    
        // 结果输出
        var res = {
            visited,
            dist,
            parent,
            path
        };
        return res;
    }
    View Code

    图结构封装:

      // Graph_unDirected.js

    // 1. 定义一个GraphData类,用来进行初始化数据添加
    function Graph_unDirected(){
        // 属性
        // 1. 所有的节点存入到列表中
        this.vertexs = [];
        // 2. 所有连线信息存入邻接表
        this.Edges = {};
        // 方法
        // 1. 存入节点(每次存入一个)
        Graph_unDirected.prototype.addVertex = function(v){
            if(this.vertexs.indexOf(v) == -1){
                this.vertexs.push(v);
                this.Edges[v] = {};
            }else{
                alert("插入的节点为重复节点, 请检查!");
                return false;
            }
        }
        // 2. 存入节点(每次存入多个)
        Graph_unDirected.prototype.addVertexs = function(v){
            // 传入的数据v应为一个列表[...]
            for(var i = 0; i < v.length; i++){
                // 若节点不存在,则插入
                if(this.vertexs.indexOf(v[i]) == -1){
                    this.vertexs.push(v[i]);
                    this.Edges[v[i]] = {};
                }else{
                    alert("插入的节点 " + v[i] + " 为重复节点,请检查");
                    return false;
                }
            }
        }
        // 3. 存入距离信息(每次存入一条)
        Graph_unDirected.prototype.addEdge = function(v, v1, dist){
            // 若原对象为空,则直接加入
            if(!this.Edges[v]){
                this.Edges[v] = {};
                this.Edges[v1] = {};
                this.Edges[v][v1] = dist;
                this.Edges[v1][v] = dist;
            }
            // 若该连线不存在,则添加
            if(!this.Edges[v].hasOwnProperty(v1)){
                this.Edges[v][v1] = dist;
                this.Edges[v1][v] = dist;
                console.log(this.Edges);
            }else{
                alert("插入的连线为重复线, 请检查!");
                return false;
            }
            return true;     
        }
        // 4. 存入距离信息(每次存入多条)
        Graph_unDirected.prototype.addEdges = function(v, list){
            // 这里输入的v为节点, list应为数组,数组元素为对象
            // 如v = 8, list = {2: 2, 6: 6, 7: 1}, 表示节点8到节点2的距离为2,节点8到节点6的距离为6, 节点8到节点7的距离为1
            // 取出邻接表中节点v对应的那个记录距离信息的对象, 如{2: 2, 6: 6, 7: 1}
            var [keys, values] = [Object.keys(list), Object.values(list)];
            // 若v对应的距离列表为空
            if(!this.Edges[v]){
                for(var s = 0; s < keys.length; s++){
                    this.Edges[v][keys[s]] = values[s];
                    this.Edges[keys[s]][v] = values[s];
                }
                return true;
            }
            // 若v对应的距离列表不为空
            for(var i = 0; i < keys.length; i++){
                var [kk, vv] = [keys[i], values[i]];
                if(Object.keys(this.Edges[v]).indexOf(kk) == -1){
                    this.Edges[v][kk] = vv;
                    if(!this.Edges[kk]){
                        this.Edges[kk] = {};
                    }
                    this.Edges[kk][v] = vv;
                }
            }
            return true;
        }
        // 5. 打印邻接表
        Graph_unDirected.prototype.toString = function(){
            // 获取邻接表中所有的对象和值
            var [ver, val] = [Object.keys(this.Edges), Object.values(this.Edges)];
            var res = "";
            for(var j = 0; j < ver.length; j++){
                var [k1, v1] = [Object.keys(val[j]), Object.values(val[j])];
                res += ver[j] + "  -->  ";
                for(var k = 0; k < k1.length; k++){
                    // 2: {1: 8, 3: 7, 8: 4, 8: 2},
                    res += k1[k] + ": " + v1[k] + "  ";
                }
                res += "
    ";
            }
            return res;
        }
    }
    View Code

    案例测试:

      求解如图所示图结构中,从节点0到节点4的最短距离

     // 测试代码

    <script src="./Graph_unDirected.js"></script>
    <script src="./Dijkstra.js"></script>
    <script>
        var g = new Graph_unDirected();
        // 2. 添加节点
        g.addVertexs(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']);
        // 3. 添加连线和权值
        g.addEdges('A', {'B': 4, 'H': 8});
        g.addEdges('B', {'A': 4, 'C': 8, 'H': 3});
        g.addEdges('C', {'B': 8, 'D': 7, 'F': 6, 'I': 2});
        g.addEdges('D', {'C': 7, 'E': 9, 'F': 14});
        g.addEdges('E', {'D': 9, 'F': 10});
        g.addEdges('F', {'C': 6, 'D': 14, 'E': 10, 'G': 2});
        g.addEdges('G', {'F': 2, 'H': 6, 'I': 6});
        g.addEdges('H', {'A': 8, 'B': 3, 'G': 6, 'I': 1});
        g.addEdges('I', {'C': 2, 'G': 6, 'H': 6});
    
        // 4. 输出图内容
        console.log(g.toString());
    
    
        var res = Dijkstra(g, 'A', 'E');
        console.log(res);
    </script>
    View Code
  • 相关阅读:
    轮播闪白效果
    轮播图效果
    打字游戏简约版
    js购物时的放大镜效果
    单例模式
    docker
    【spring】注解梳理
    【spring源码-1】BeanFactory + XMLBeanFactory
    【设计模式】
    【大数据Spark】PC单机Spark开发环境搭建
  • 原文地址:https://www.cnblogs.com/carreyBlog/p/13657193.html
Copyright © 2011-2022 走看看