提前声明,本篇文章只讲述个人理解,如果有地方和你想到不符合或者你觉得有误。欢迎评论区讨论
前置知识--字典(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…