JavaScript数据结构与算法 -- 图

1.图的邻接矩阵表示法

  • 邻接矩阵让每一个节点和一个证书相关联,该整数作为数组的下标值。
  • 我们用一个二维数组来标识顶点之间的连接。
    在这里插入图片描述
  • 在二维数组中0表示没有连线,1表示有连线。
  • 通过二维数组,我们很快的找到一个顶点和那些顶点有连线(比如A顶点,只需要遍历第一行就可)
  • 另外A-A,B-B也就是顶点到自己的连线,通常使用0表示。

2.图的邻接表表示

  • 邻接表由图中每个顶点以及和顶点相邻的顶点列表组成.
  • 这个列表有很多中方式来存储: 数组/链表/字典(哈希表)都可以.
    在这里插入图片描述
  • 比如我们要表示和A顶点有关联的顶点(边), A和B/C/D有边, 那么我们可以通过A找到对应的数组/链表/字典, 再取出其中的内容就可以啦。
  • 邻接表计算"出度"是比较简单的(出度: 指向别人的数量, 入度: 指向自己的数量)邻接表如果需要计算有向图的"入度", 那么是一件非常麻烦的事情。
    3.我们封装一个字典结构
    /***
        *   封装一个字典结构
        */
        function Dictionay(){
            //字典属性
            this.items = {};

            //字典所有操作方法

            //1.在字典中添加键值对
            Dictionay.prototype.set = function(key, value){
                this.items[key] = value;
            }

            //2.判断字典中是否存在某个key
            Dictionay.prototype.has = function(key){
                return this.items.hasOwnProperty(key);
            }

            //3.从字典中移除元素
            Dictionay.prototype.remove = function(key){
                //1.判断字典中是否有这个元素
                if(!this.has(key))  return false;

                //2.从字典中删除这个元素
                delete this.items[key];
                return true;
            }

            //4.根据key获取value
            Dictionay.prototype.get = function(key){
                return this.has(key) ? this.items[key]: undefined;
            }

            //5.获取所有的keys
            Dictionay.prototype.keys = function(){
                return Object.keys(this.items);
            }

            //6.size方法
            Dictionay.prototype.size = function(){
                return this.keys().length;
            }

            //7.clear方法
            Dictionay.prototype.clear = function(){
                this.items = {};
            }
        }

        //使用字典
        var dl = new Dictionay();
    • 使用我们封装的字典
      在这里插入图片描述
  • 图结构的封装
    4.图结构的封装(顶点:数组/边:字典)

/****
        *   图结构的封装
        */

        function Graph(){
            //属性 : 顶点(数组)/边(字典)
            this.vertexes = []; //顶点
            this.edge = new Dictionay();  //边
        }
  • 添加顶点的方法
//添加顶点的方法
            Graph.prototype.addVertex = function (v1) {
                this.vertexes.push(v1);
                //刚开始只有顶点,没有边,所以存储的是空数组
                this.edges.set(v1, []);
            }

            //添加边的方法
            //插入两个顶点
            Graph.prototype.addEdge = function (v1, v2) {
                this.edges.get(v1).push(v2);
                this.edges.get(v2).push(v1);
            }
  • 添加toString方法
 //toString()方法,以邻接表的方法打印出来
            Graph.prototype.toString = function(){
                //1.定义字符串,保存最终的结果
                var resultString = '';

                //2.遍历所有的顶点,以及对应的边
                for(var i = 0 ; i < this.vertexes.length; i ++){
                    resultString += this.vertexes[i] + '->';

                    //拼接和这个节点相连的节点,通过对应的key获取value(另外一个数组)
                    var vEdges = this.edges.get(this.vertexes[i]);
                    for(var j = 0; j < vEdges.length; j ++){
                        resultString += vEdges[j] + ' ';
                    }
                    resultString += '\n';
                }

                //3.返回string
                return resultString;
            }
  • 测试上述方法
//测试添加方法
        //1.创建图结构
        var graph = new Graph();

        //2.添加顶点
        var myVertexes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
        for (var i = 0; i < myVertexes.length; i++) {
            graph.addVertex(myVertexes[i]);
        }

        //3.添加边
        // 添加边
        graph.addEdge('A', 'B');
        graph.addEdge('A', 'C');
        graph.addEdge('A', 'D');
        graph.addEdge('C', 'D');
        graph.addEdge('C', 'G');
        graph.addEdge('D', 'G');
        graph.addEdge('D', 'H');
        graph.addEdge('B', 'E');
        graph.addEdge('B', 'F');
        graph.addEdge('E', 'I');

        //4.测试结果
        alert(graph.toString());

结果如下:
在这里插入图片描述

5.图的遍历(重点)

图的遍历思想

图的遍历思想和树的遍历思想是一样的。
图的遍历意味着需要将途中每个顶点访问以边,并且不能有重复。
图的两种遍历方式
广度优先搜索(BFS)
深度优先搜索(DFS)
两种遍历方式都需要第一个被访问的节点

5.1 BFS和DFS的思想

BFS:基于队列,入队列的顶点先被探索
DFS: 基于栈或者使用递归,通过将顶点存入栈中,顶点是沿着路径被探索的,存在新的相邻顶点就去访问。
为了记录顶点是否被访问过,我们使用三种颜色来反映他们的状态。

  • 白色:表示该顶点还没有被访问
  • 灰色:表示该顶点被访问过,但未被探索过
  • 黑色:表示该顶点被访问过且被完全探索过

初始化状态颜色代码:

//初始化状态颜色(使用颜色来记录状态,并且全部置为白色)
            Graph.prototype.initColor = function(){
                var colors = [];
                for(var i = 0; i < this.vertexes.length; i ++){
                    colors[this.vertexes[i]] = 'white';
                }
                return colors;
            }
5.2 BFS广度优先搜索

广度优先搜索的实现

创建一个队列Q
将v标注为被发现的(灰色),并将v放入队列Q
如果Q非空,执行下面的步骤
将v从q中取出队列
将v标注为被发现的灰色
将v所有的未被访问的零界点(白色),加入队列中
将v标注为黑色
在这里插入图片描述

  • 广度优先搜索的代码实现
//封装队列类
        function Queue() {
            //属性
            this.items = [];

            //方法
            //1.将元素加入到队列中
            Queue.prototype.enqueue = function (element) {
                this.items.push(element);
            }
            //2.从队列中删除前端元素
            Queue.prototype.dequeue = function () {
                return this.items.shift()
            }
            //3.查看前端的元素
            Queue.prototype.front = function () {
                return this.items[0];
            }
            //4.查看队列是否为空
            Queue.prototype.isEmpty = function () {
                return this.items.length == 0;
            }
            //5.查看队列中元素的个数
            Queue.prototype.size = function () {
                return this.items.length;
            }
            //6.toString方法
            Queue.prototype.toString = function () {
                let resultString = '';
                for (let i = 0; i < this.items.length; i++) {
                    resultString += this.items[i] + ',';
                }
                return resultString;
            }
        }


//实现广度优先搜索(BFS)
            Graph.prototype.bfs = function(initV,handler){
                // 1.初始化颜色
                var colors = this.initColor();

                //2.创建队列
                var queue = new Queue();

                //3.将顶点加入到队列中
                queue.enqueue(initV);

                //4.循环从队列中取出元素
                while(!queue.isEmpty()){
                    //4.1 从队列中取出顶点
                    var v = queue.dequeue();

                    //4.2 获取和顶点相连的其他顶点
                    var vList = this.edges.get(v);

                    //4.3 将v的颜色设置为灰色
                    colors[v] = 'gray';

                    //4.4 遍历所有的领点,并且加入到队列中
                    for(var i = 0 ; i < vList.length; i++){
                        var e = vList[i];
                        if(colors[e] == 'white'){
                            //表示从来没有探测过
                            colors[e] = 'gray';
                            queue.enqueue(e);
                        }
                    }

                    //4.5 v已经被探测,访问v顶点
                    handler(v);

                    //4.6 将顶点设置为黑色
                    colors[v] = 'black';
                }
            }


 //5.测试bfs
        var result = '';
        graph.bfs(graph.vertexes[0],function(v){
            result += v + ' ';
        })
        alert(result);

在这里插入图片描述

5.3 DFS深度优先搜索

深度优先搜索的实现

深度优先搜索算法会从第一个指定的顶点开始遍历图,沿着路径直到这条路径最后被访问了
接着按照园路回退并且探索吓一条路径
广度优先搜索可以使用队列,也可以使用栈,这里我们使用递归完成。
在这里插入图片描述

 //实现深度优先搜索(DFS)
        Graph.prototype.dfs = function(initV,handler){
            //1.初始化颜色
            var colors = this.initColor();

            //2.从某个顶点开始依次递归访问
            this.dfsVisit(initV,colors,handler);
        }

        //实现递归访问
        Graph.prototype.dfsVisit = function(v,colors,handler){
            //1.先将颜色设置为灰色
            colors[v] = 'gray';

            //2.处理v顶点
            handler(v);

            //3.访问v相连的其他顶点
            var vList = this.edges.get(v);
            for(var i = 0; i < vList.length; i++){
                var e = vList[i];
                if(colors[e] == 'white'){
                    //如果是白色就递归访问
                    this.dfsVisit(e, colors, handler);
                }
            }

            //4.将v设置为黑色
            colors[v] = 'black';
        }



 //6.测试dfs
        var result2 = '';
        graph.dfs(graph.vertexes[0],function(v){
            result2 += v + ' ';
            return result2;
        })
        alert(result2);

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值