JavaScript数据结构与算法(5)图论

37 阅读3分钟

数据结构

图论

  • 图是网络结构的抽象模型, 是一组由边连接的节点
  • 顶点:顶点是图中的节点,
  • 边:边是连接顶点之间的连线
  • 相邻顶点:由一条边相连的两个顶点
  • 度:一个顶点的度是相邻顶点的个数
  • 路径:路径是一个顶点到另一个顶点的连续序列
  • 简单路径:要求路径中不包含重复顶点
  • 回路:第一个顶点和最后一个顶点相同的路径称为回路
  • 无向图:图中所有的边都是没有方向的
  • 有向图:图中的边是有方向的
  • 无权图:边没有权重,所有的边权重都一样
  • 带权图:边有一定的权重,比如统计路径的距离等

无权图

image.png

带权图

image.png

邻接矩阵

  • 1、邻接矩阵让每个节点和一个整数相关联,该整数作为数组的下标值
  • 2、我们用一个二维数组来表示顶点之间的连接
  • 3、二维数组[0][2] -> A ->C, 1代表有连线,0代表无连线。自己到自己通常用0表示A->A

缺点:稀疏图,若顶点多,边少,则矩阵中存在大量的0,浪费计算机储存空间

image.png

邻接表 1、邻接表由图中每个顶点以及和定点相邻的顶点组成** 2、这个列表有很多种结构来存储:数组/链表/字典**

缺点:

  • 1、邻接表计算“出度”是比较简单的(出度:指向别人的数量,入度:指向自己的数量)
  • 2、邻接表如果需要计算有向图的“入度”,那么是一件非常麻烦的事情
  • 3、它必须构造一个“逆邻接表”,才能有效计算“入度”,但是开发中“入度”相对用的较少

image.png

图的封装

//封装图结构
function Graph(){
    //属性:顶点(数组)/边(字典)
    this.vertexes = [] //顶点
    this.edges = new Dictionay() //

    //方法
    //添加方法
    // 1、添加顶点的方法
    Graph.prototype.addVertex = function(v){
        this.vertexes.push(v)
        this.edges.set(v,[])
    }

    // 2、添加边的方法
    Graph.prototype.addEdge = function(v1,v2){
        this.edges.get(v1).push(v2)
        this.edges.get(v2).push(v1)
    }

    //实现toString方法
    Graph.prototype.toString = function(){
        // 1、定义字符串,保存最终结果
        let resultString = ''

        //遍历所有的顶点,以及顶点对应的边
        for(let i=0 ;i<this.vertexes.length; i++){
            resultString += this.vertexes[i] + '->'
            let vEdges = this.edges.get(this.vertexes[i])
            for(let j=0; j<vEdges.length; j++){
                resultString += vEdges[j] + ' '
            }
            resultString += '\n'
        }
        return resultString
    }
    
     //初始化状态颜色
    Graph.prototype.initializeColor = function(){
        let colors = []
        for(let i=0; i<this.vertexes.length; i++){
            colors[this.vertexes[i]] = 'white'
        }
        return colors
    }
    
     //实现广度优先搜索(BFS)
    Graph.prototype.bfs = function(initV,handler){
        //1、初始化颜色
        let colors = this.initializeColor()

        // 2、创建队列 引入之前封装的队列
        let queue = new Queue()

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

        //4、循环从队列中取出元素
        while(!queue.isEmpty()){
            //4.1从队列取出衣蛾顶点
            let v = queue.dequeuq()

            //4.2获取和顶点项链的另外顶点
            let vList = this.edges.get(v)

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

            //4.4遍历所有的顶点,并且加入到队列中
            for(let i=0; i<vList.length; i++){
                let e = vList[i]
                if(colors[e] == 'white'){
                    colors[e] = 'gray'
                    queue.enqueue(e)
                }
            }

            //4.5访问顶点
            handler(v)

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

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

        //2、从某个顶点进行依次递归访问
        this.dfsVisit(initV,colors,handler)

    }

    Graph.prototype.dfsVisit = function(v, colors, handler){
        //1、将颜色设置为灰色
        colors[v] = 'gary'

        //2处理v顶点
        handle(v)

        //3访问顶点项链的另外顶点
        let vList = this.edges.get(v)
        for(let i=0; i<vList.length; i++){
            let e = vList[i]
            if(colors[e] == 'white'){
                this.dfsVisit(e,colors,handler)
            }
        }

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

图的遍历思想(都需要指定第一个被访问的顶点)

  • 1、广度优先算法(Breadth-First Search,简称BFS)

image.png

  • 2、深度优先算法(Depth-First Search,简称DFS)

image.png