图论_迪杰斯特拉算法(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 += "\n";
        }
        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

 

posted @ 2020-02-13 10:27  CarreyB  阅读(308)  评论(0编辑  收藏  举报