JavaScript数据结构与算法总结四——图(深度优先、广度优先、最短路径、最小生成树)

图由边的集合及顶点的集合组成。

边由顶点 对 (v1,v2) 定义,v1 和 v2 分别是图中的两个顶点。顶点也有权重,也称为成本。如果一个 图的顶点对是有序的,则可以称之为有向图。在对有向图中的顶点对排序后,便可以在两 个顶点之间绘制一个箭头。有向图表明了顶点的流向。

图的表示

图最常见的实现是邻接矩阵。每个节点都和一个整数相关联,该整数将作为数组的索引。用一个二维数组来表示顶点之间的连接。
在这里插入图片描述

也可以使用一种叫作邻接表的动态数据结构来表示图。邻接表由图中每个顶点的相邻顶点列表所组成。存在好几种方式来表示这种数据结构。我们可以用列表(数组)、链表,甚至是散列表或是字典来表示相邻顶点列表。

在这里插入图片描述

        //构建图,输入顶点数量
        function Graph(v) {
            this.vertices = v;
            this.edges = 0; 
            this.adj = [];
            for (var i = 0; i < this.vertices; ++i) {
                this.adj[i] = []; this.adj[i].push("");
            }
            this.addEdge = addEdge;
            this.showGraph = showGraph;
        }

        //添加边
        function addEdge(v, w) {
            this.adj[v].push(w);
            this.adj[w].push(v);
            this.edges++;
        }

        //输出所有顶点及其相邻顶点
        function showGraph() {
            for (var i = 0; i < this.vertices; ++i) {
                document.write(i ," -> ");
                for (var j = 0; j < this.vertices; ++j) {
                    if (this.adj[i][j] != undefined) {
                        document.write(this.adj[i][j] ,' ');
                    }
                }
                document.write("<br>");
            }
        }

        graphA = new Graph(5);
        graphA.addEdge(0, 1);
        graphA.addEdge(0, 2);
        graphA.addEdge(1, 3);
        graphA.addEdge(2, 4);
        graphA.showGraph();

图的遍历

深度优先搜索

本质是回溯算法

深度优先搜索包括从一条路径的起始顶点开始追溯,直到到达最后一个顶点,然后回溯, 继续追溯下一条路径,直到到达最后的顶点,如此往复,直到没有路径为止。这不是在搜 索特定的路径,而是通过搜索来查看在图中有哪些路径可以选择。

  • 遍历所有节点,如果没有被搜索则调用DFS方法
  • DFS方法,递归遍历当前节点的下一个节点

广度优先搜索

广度优先搜索算法会从指定的第一个顶点开始遍历图,先访问其所有的邻点(相邻顶点),再访问邻点的邻点。

编码

		//无向图
        //构建图,输入顶点数量
        function Graph(v) {
            this.vertices = v;//顶点数
            this.edges = 0;//边数

            this.adj = [];
            //通过 for 循环为数组中的每个元素添加一个子数组来存储所有的相邻顶点,并 将所有元素初始化为空字符串。
            for (var i = 0; i < this.vertices; ++i) {
                this.adj[i] = []; this.adj[i].push("");
            }

            //为 Graph 类添加一个数组,用来存储已访问过的顶点,将它所有元 素的值全部初始化为 false。
            this.marked = [];
            for (var i = 0; i < this.vertices; ++i) {
                this.marked[i] = false;
            }
            this.addEdge = addEdge;
            this.showGraph = showGraph;

            this.DFS = DFS;
            this.DFSTraverse = DFSTraverse;
            this.BFS = BFS;
            this.BFSTraverse = BFSTraverse;

        }

        //添加边
        function addEdge(v, w) {
            this.adj[v].push(w);
            this.adj[w].push(v);
            this.edges++;
        }

        //输出所有顶点及其相邻顶点
        function showGraph() {
            for (var i in this.adj) {
                document.write(i, " -> ");
                for (var j = 0; j < this.vertices; ++j) {
                    if (this.adj[i][j] != undefined) {
                        document.write(this.adj[i][j], ' ');
                    }
                }
                document.write("<br>");
            }
        }

        //深度优先
        function DFSTraverse() {
            let v = this.vertices;
            for (let i = 0; i < v; ++i) {
                if (!this.marked[i]) {
                    this.DFS(i);
                }
            }
        }
        function DFS(v) {
            this.marked[v] = true;
            if (this.adj[v] != undefined) {
                document.write("Visited vertex: ", v, "<br>");
            }
            for (var w in this.adj[v]) {
                let k = this.adj[v][w];
                if (!this.marked[k]) {
                    this.DFS(k);
                }
            }
        }

        //广度优先
        function BFSTraverse() {
            for (let i = 0; i < this.vertices; ++i) {
                if (!this.marked[i]) {
                    this.BFS(i);
                }
            }
        }
        function BFS(v) {
            this.marked[v] = true;
            if (this.adj[v] != undefined) {
                document.write("Visited vertex: ", v, "<br>");
            }
            for (let w in this.adj[v]) {
                let k = this.adj[v][w];
                if (!this.marked[k]) {
                    this.marked[k] = true;
                    if (this.adj[k] != undefined) {
                        document.write("Visited vertex: ", k, "<br>");
                    }
                }
            }
        }

        graphA = new Graph(5);
        graphA.addEdge(0, 1);
        graphA.addEdge(0, 2);
        graphA.addEdge(1, 3);
        graphA.addEdge(2, 4);
        document.write("Show all vertices and their neighbors:<br>");
        graphA.showGraph();
        // document.write("Depth first traversal:<br>");
        // graphA.DFSTraverse();
        document.write("Breadth first traversal:<br>");
        graphA.BFSTraverse();

最短路径

Dijkstra 算法

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。

Dijkstra 算法是一种计算从单个源到所有其他源的最短路径的贪心算法

        //Dijkstra算法
        //构建图,输入顶点数量
        function Graph(v) {
            this.vertices = v;//顶点数
            this.edges = 0;//边数
            this.adj = [];
            //通过 for 循环为矩阵的每一个元素赋值0。
            for (let i = 0; i < this.vertices; i++) {
                this.adj[i] = [];
                for (let j = 0; j < this.vertices; j++)
                    this.adj[i][j] = 10000;
            }
            //为 Graph 类添加一个数组,用来存储已访问过的顶点,将它所有元 素的值全部初始化为 false。
            this.marked = [];
            for (var i = 0; i < this.vertices; i++) {
                this.marked[i] = false;
            }
            //添加一个数组用于存储路径长度
            this.dist = [];
            this.dist[0]=0;
            for (var i = 1; i < this.vertices; i++) {
                this.dist[i] = 10000;//取正无穷大
            }

            //用于存储路径
            // this.path = [];
            // for (var i = 1; i < this.vertices; i++) {
            //     this.path[i] = [];
            // }

            this.addEdge = addEdge;
            this.showGraph = showGraph;

            this.Dijkstra = Dijkstra;
            this.djtPath = djtPath;
            this.showDijkstra = showDijkstra;
        }

        //添加边
        function addEdge(v, w, k) {
            this.adj[v][w] = k;
            this.edges++;
        }

        //输出所有顶点及其指向的顶点
        function showGraph() {
            for (let i = 0; i < this.vertices; i++) {
                document.write(i, " -> ");
                for (let j = 0; j < this.vertices; j++) {
                    if (this.adj[i][j]) {
                        document.write(j, ' ');
                    }
                }
                document.write("<br>");
            }

        }

        //最短路径
        function Dijkstra() {
            for (let i=0;i<this.vertices;i++) {
                this.djtPath(i);
            }
        }
        function djtPath(v) {
            let t = this.dist[v];
            for(let w in this.adj[v]){
                let k = t+this.adj[v][w];
                if(this.dist[w]>k){
                    this.dist[w]=k;
                }
            }
        }

        function showDijkstra() {
            for(let i =1 ;i<this.vertices;i++){
                document.write("0->",i,":",this.dist[i],"<br>");
            }
        }
        graphA = new Graph(6);
        graphA.addEdge(0, 1, 2);
        graphA.addEdge(0, 2, 4);
        graphA.addEdge(1, 2, 2);
        graphA.addEdge(1, 3, 4);
        graphA.addEdge(1, 4, 2);
        graphA.addEdge(2, 4, 3);
        graphA.addEdge(3, 5, 2);
        graphA.addEdge(4, 3, 3);
        graphA.addEdge(4, 5, 2);
        document.write("Show all vertices and their neighbors:<br>");
        graphA.showGraph();
        graphA.Dijkstra();
        document.write("The shortest distance is:<br>");
        graphA.showDijkstra()

为了方便,我直接遍历,而且没有记录路径,实际上可以缩小时间复杂度,并在path 数组中存储。

Floyd-Warshall 算法

Floyd-Warshall 算法是一种计算图中所有最短路径的动态规划算法。通过该算法,我们可以找出从所有源到所有顶点的最短路径。

		//Floyd算法
        //使用邻接矩阵构建图,输入顶点数量
        function Graph(v) {
            this.vertices = v;//顶点数
            this.edges = 0;//边数
            this.adj = [];
            //通过 for 循环为矩阵的每一个元素赋值0。
            for (let i = 0; i < this.vertices; i++) {
                this.adj[i] = [];
                for (let j = 0; j < this.vertices; j++)
                    this.adj[i][j] = Infinity;
            }

            //添加一个数组用于存储路径长度
            this.dist = [];

            this.addEdge = addEdge;
            this.showGraph = showGraph;

            this.floyd=floyd;
            this.showFloyf=showFloyf;

        }

        //添加边
        function addEdge(v, w, k) {
            this.adj[v][w] = k;
            this.edges++;
        }

        //输出所有顶点及其指向的顶点
        function showGraph() {
            for (let i = 0; i < this.vertices; i++) {
                document.write(i, " -> ");
                for (let j = 0; j < this.vertices; j++) {
                    if (isFinite(this.adj[i][j])) {
                        document.write(j, ' ');
                    }
                }
                document.write("<br>");
            }

        }

        function floyd(){
            for (let i = 0; i < this.vertices; i++) {
                this.dist[i] = [];
                for(let j=0;j<this.vertices;j++){
                    if(i===j){
                        this.dist[i][j]=0;
                    }else if(!isFinite(this.adj[i][j])){
                        this.dist[i][j]=10000;
                    }else{
                        this.dist[i][j] = this.adj[i][j];
                    }
                }
            }
            for(let k = 0;k<this.vertices;k++){
                for(let i=0;i<this.vertices;i++){
                    for(let j=0;j<this.vertices;j++){
                        if((this.dist[i][k]+this.dist[k][j])<this.dist[i][j]){
                            this.dist[i][j]=this.dist[i][k]+this.dist[k][j];
                        }
                    }
                }
            }
        }

        function showFloyf(){
            for(let i=0;i<this.vertices;i++){
                for(let j=0;j<this.vertices;j++){
                    if(this.dist[i][j]<10000){
                        document.write(this.dist[i][j]," &nbsp;&nbsp;");
                    }else{
                        document.write("INF ");
                    }
                }
                document.write("<br>");
            }
        }

        //最短路径
        graphA = new Graph(6);
        graphA.addEdge(0, 1, 2);
        graphA.addEdge(0, 2, 4);
        graphA.addEdge(1, 2, 2);
        graphA.addEdge(1, 3, 4);
        graphA.addEdge(1, 4, 2);
        graphA.addEdge(2, 4, 3);
        graphA.addEdge(3, 5, 2);
        graphA.addEdge(4, 3, 3);
        graphA.addEdge(4, 5, 2);
        document.write("Show all vertices and their neighbors:<br>");
        graphA.showGraph();
        graphA.floyd();
        document.write("The shortest distance is:<br>");
        graphA.showFloyf();

最小生成树

Prim 算法

Prim 算法是一种求解加权无向连通图的 MST 问题的贪心算法。它能找出一个边的子集,使得其构成的树包含图中所有顶点,且边的权值之和最小。

        //Prim算法
        //构建图,输入顶点数量
        function Graph(v) {
            this.vertices = v;//顶点数
            this.edges = 0;//边数
            this.adj = [];
            //通过 for 循环为矩阵的每一个元素赋值0。
            for (let i = 0; i < this.vertices; i++) {
                this.adj[i] = [];
                for (let j = 0; j < this.vertices; j++)
                    this.adj[i][j] = 10000;
            }
            //为 Graph 类添加一个数组,用来存储已访问过的顶点,将它所有元 素的值全部初始化为 false。
            this.marked = [];
            for (var i = 0; i < this.vertices; i++) {
                this.marked[i] = false;
            }
            //添加一个数组用于存储路径长度
            this.dist = [];
            this.dist[0] = 0;
            for (var i = 1; i < this.vertices; i++) {
                this.dist[i] = 10000;//取正无穷大
            }

            //用于存储路径
             this.path = [];
             for (var i = 1; i < this.vertices; i++) {
                 this.path[i] = [];
             }

            this.addEdge = addEdge;
            this.showGraph = showGraph;

            this.Prim = Prim;
            this.djtPath = djtPath;
            this.showPrim = showPrim;
        }

        //添加边
        function addEdge(v, w, k) {
            this.adj[v][w] = k;//存储正向边
            this.adj[w][v] = k;//存储反向边,若是有向图则删去
            this.edges++;
        }

        //输出所有顶点及其指向的顶点
        function showGraph() {
            for (let i = 0; i < this.vertices; i++) {
                document.write(i, " -> ");
                for (let j = 0; j < this.vertices; j++) {
                    if (this.adj[i][j]<10000) {
                        document.write(j, ' ');
                    }
                }
                document.write("<br>");
            }

        }

        //最短路径
        function Prim() {
            for (let i = 0; i < this.vertices; i++) {
                if (!this.marked[i]) {
                    this.djtPath(i);
                }
            }
        }
        function djtPath(v) {
            this.marked[v]=true;
            let t = this.dist[v];
            for (let w in this.adj[v]) {
                let k = t + this.adj[v][w];//原点到当前节点的距离加当前节点到下一节点的距离
                if (this.dist[w] > k) {
                    this.dist[w] = k;
                    this.path[w]=[v,w];//记录相应的节点
                }
            }
        }
        function showPrim() {
            for (let i = 1; i < this.vertices; i++) {
                document.write(this.path[i][0],"->",this.path[i][1], ":", this.dist[i],"<br>");
            }
        }
        graphA = new Graph(6);
        graphA.addEdge(0, 1, 2);
        graphA.addEdge(0, 2, 4);
        graphA.addEdge(1, 2, 2);
        graphA.addEdge(1, 3, 4);
        graphA.addEdge(1, 4, 2);
        graphA.addEdge(2, 4, 3);
        graphA.addEdge(3, 5, 2);
        graphA.addEdge(4, 3, 3);
        graphA.addEdge(4, 5, 2);
        document.write("Show all vertices and their neighbors:<br>");
        graphA.showGraph();
        graphA.Prim();
        document.write("The shortest distance is:<br>");
        graphA.showPrim()

Kruskal 算法

        //Kruskal算法
        //构建图,输入顶点数量
        function Graph(v) {
            this.vertices = v;//顶点数
            this.edges = 0;//边数
            this.adj = [];//边集
            this.path = [];//用于存储路径
            
            this.addEdge = addEdge;
            this.showGraph = showGraph;

            this.Kruskal = Kruskal;
            this.find = find;
            this.showKruskal = showKruskal;
        }

        //添加边
        function addEdge(v, w, k) {
            this.adj.push([v, w, k]);
            this.edges++;
        }

        //输出所有顶点及其指向的顶点
        function showGraph() {
            for(let i in this.adj){
                document.write(this.adj[i][0],"-",this.adj[i][1],":",this.adj[i][2],"<br>")
            }
        }
        //最短路径
        function Kruskal() {
            let cost = this.adj;
            while (this.path.length < this.vertices-1) {//遍历边,得到的最小生成树的边数为顶点数-1
                let min = 10000, index = 0;
                for (let j in cost) {//找到当前最小边
                    if (cost[j][2] < min) {
                        index = j;
                        min = cost[j][2];
                    }
                }
                if (this.find(index, cost)) {
                    this.path.push(cost[index]);
                }
                cost.splice(index, 1);//删除找到的最小节点
            }
        }
        function find(v, cost) {
            for (let i in this.path) {//遍历存储的节点
                if (cost[v][1] === this.path[i][0]) {
                    return 0;
                }
            }
            return 1;
        }

        function showKruskal() {
            for(let i in this.path){
                document.write(this.path[i],"<br>");
            }
        }
        graphA = new Graph(6);
        graphA.addEdge(0, 1, 2);
        graphA.addEdge(0, 2, 4);
        graphA.addEdge(1, 2, 2);
        graphA.addEdge(1, 3, 4);
        graphA.addEdge(1, 4, 2);
        graphA.addEdge(2, 4, 3);
        graphA.addEdge(3, 5, 2);
        graphA.addEdge(4, 3, 3);
        graphA.addEdge(4, 5, 2);
        document.write("Show the map:<br>");
        graphA.showGraph();
        graphA.Kruskal();
        document.write("The minimum spanning tree:<br>");
        graphA.showKruskal()
posted @ 2021-05-17 15:14  Patrick-Rex  阅读(29)  评论(0)    收藏  举报  来源