数据结构标准入门-图(邻接矩阵)

297 阅读4分钟

提前声明,本篇文章只讲述个人理解,如果有地方和你想到不符合或者你觉得有误。欢迎评论区讨论

前置知识--字典(Dictionary)

字典是一种以 键值对 形式存数数据的数据结构,javaScript 中的 Object 类就是以字典的形式设置的,所以使用 Object 类本身的特性实现字典 Dictionary 类。

简单字典的代码实现:

class Dictionary{  constructor(){    this.dataStore={}  }  add(key,value){    this.dataStore[key]=value  }  find(key){    if(this.dataStore[key]===undefined){      this.add(key,[])      return []    }    return this.dataStore[key]  }  remove(key){    delete this.dataStore[key]  }  getKeys(){    return Object.keys(this.dataStore)  }  getKeysSort(){    return this.getKeys.sort()  }  count(){    return this.getKeys.length  }  showAll(){    let res=''    this.getKeys.forEach(key=>{      res+=`,{key:'${key}',value:'${this.dataStore[key]}'}`    })    return res.slice(1)  }  showAllSort(){    let res=''    this.getKeys().forEach(key=>{      delete this.dataStore[key]    })    this.dataStore={}  }}

在这篇文章,我们将学习另一种非线性的数据结构----图,这是数据结构最后的一个boss,也是传统数据结构的最后一种。

图的相关术语

图是网络结构的抽象模型,图是由一组由边链接的结点、或者顶点。学习图是很重要的,因为任何二元关系都可以用图来表示。

任何社交网络,列如facebook,Twitter和Google plus都可以用图来表示。

我们还可以用图来表示道路。航班以及通信状态。

下面让我们来学习图在数学以及技术上的概念。

一个图G=(V,E)由以下元素构成。

  • V:一组顶点
  • E:一组边,链接V中的顶点

下面表示一个图:

在着手实现算法之前,我们先了解一下图的基本术语。

由一条边链接在一起的顶点称为响铃顶点,比如A和B是响铃的,A和D是响铃的,A和C是响铃的,A和E是不响铃的。

一个顶点的度是其响铃顶点的数量,比如A和其他三个顶点链接,因此,A的度为3,E和其他两个顶点响铃,因此E的度是2

路径是顶点v1,v2,v3....vn的一个连续数列。举个例子,ADG是一条简单的路径。除去最后一个顶点(因为它和第一个顶点是同一个顶点)。环也是一个简单路径。比如ADCA(最后一个顶点重新回到A)

简单路径要求不包含重复的顶点。举个例子,ADG是一条简单的路径。除去最后一个顶点(因为他和第一个顶点是同一个顶点),环也是一个简单的路径,比如ADCA(最后一个顶点都可以回到A)。

如果图中不存在环,则称该图是无环的。如果图中每两个顶点之间都存在路径。则该图是联通的。

有向图和无向图

图可以是无向的(边没有方向)或者是有向的。如下图所示,有向图的边都有一个方向。

如果说图中每两个顶点都在双向上存在路径,则该图是强联通的,列如,C和D是强联通的,而A和B不是强联通的。

图还可以是未加权的,或者是加权的。如下图所示,加权图的边被赋予了权值:

我们可以使用图来解决计算机科学世界中很多问题,比如搜索图中的一个特定的结点或搜索一条特定的边,寻找图中的一条路径(从一个顶点到另外一个顶点),寻找两条图中的最短路径,以及环监测。

图的表现形式

  • 领结表

  • 我们可以使用领结表的动态数据结构来表示图。领结表由图中每个顶点的相邻点列表所组成。存在好几种方式来表现这种数据结构。我们可以直接使用列表(数组)、链表、甚至是散列表和字典来表示响铃的数据结构。下面的示意图展示了玲姐表的数据结构。

创建图类

我们这里声明类的骨架:

function Graph(){  let vertices=[];  let adjList=new Dictionary()}

我们使用一个数组来存储图中所有顶点的名字,以及一个字典来存储临街表,字典将会使用顶点的名字作为键。领结列表作为值。vertices数组和adjList字典两者都是我们Graph类的私有属性。

然后我们写入往图中添加顶点的方法

// 该方法向图中添加一个新的顶点  this.addVertex=function(v){    vertices.push(v)    adjList.add(v,[])  }

我们写入往图中添加边的方法,这边我们是双边关联的,所以我们在V点添加w的链接后也需要在w点添加v点的链接

// 该方法用来向图中添加边 this.addEdge=function(v,w){   adjList.find(v).push(w)   adjList.find(w).push(v) }

我们写入打印图的方法

this.toString=function(){    let s=''    for(let i=0;i<vertices.length;i++){      s+=vertices[i]+'->';      let nerghbors=adjList.find(vertices[i])      for(let j=0;j<nerghbors.length;j++){        s+=nerghbors[j]+''      }      s+='\n'    }    return s  }

现在我们执行如下代码进行图的打印操作:

let graph=new Graph()let myVertices=['A','V','C','E','G','B','R','Y']for(let i=0;i<myVertices.length;i++){  graph.addVertex(myVertices[i])}graph.addEdge('A','V')graph.addEdge('A','C')graph.addEdge('A','E')graph.addEdge('C','E')graph.addEdge('C','G')graph.addEdge('A','E')graph.addEdge('E','I')console.log(graph.toString())

输出结果如下:

A->VCEE
V->A   
C->AEG 
E->ACAI
G->C   
B->    
R->    
Y->

视频讲解地址: www.bilibili.com/video/BV1i3…