JavaScript实现图结构

一、图论

1.1.图的简介

什么是图?

  • 图结构是一种与树结构有些相似的数据结构;
  • 图论是数学的一个分支,并且,在数学中,树是图的一种;
  • 图论以图为研究对象,研究顶点组成的图形的数学理论和方法;
  • 主要的研究目的为:事物之间的联系顶点代表事物代表两个事物间的关系

图的特点:

  • 一组顶点:通常用 V (Vertex)表示顶点的集合;
  • 一组边:通常用 E (Edge)表示边的集合;
    • 边是顶点和顶点之间的连线;
    • 边可以是有向的,也可以是无向的。比如A----B表示无向,A ---> B 表示有向;

图的常用术语:

  • 顶点:表示图中的一个节点

  • 边:表示顶点和顶点给之间的连线

  • 相邻顶点:由一条边连接在一起的顶点称为相邻顶点

  • 度:一个顶点的相邻顶点的数量

  • 路径:

    • 简单路径:简单路径要求不包含重复的顶点;
    • 回路:第一个顶点和最后一个顶点相同的路径称为回路;
  • 无向图:图中的所有边都是没有方向的;

  • 有向图:图中的所有边都是方向的;

  • 无权图:无权图中的边没有任何权重意义;

  • 带权图:带权图中的边有一定的权重含义;

1.2.图的表示

邻接矩阵

表示图的常用方式为:邻接矩阵

  • 可以使用二维数组来表示邻接矩阵;

  • 邻接矩阵让每个节点和一个整数相关联,该整数作为数组的下标值

  • 使用一个二维数组来表示顶点之间的连接

如上图所示:

  • 二维数组中的0表示没有连线,1表示有连线;
  • 如:A[ 0 ] [ 3 ] = 1,表示 A 和 C 之间有连接;
  • 邻接矩阵的对角线上的值都为0,表示A - A ,B - B,等自回路都没有连接(自己与自己之间没有连接);
  • 若为无向图,则邻接矩阵应为对角线上元素全为0的对称矩阵;

邻接矩阵的问题:

  • 如果图是一个稀疏图,那么邻接矩阵中将存在大量的 0,造成存储空间的浪费;
邻接表

另外一种表示图的常用方式为:邻接表

  • 邻接表由图中每个顶点以及和顶点相邻的顶点列表组成;
  • 这个列表可用多种方式存储,比如:数组/链表/字典(哈希表)等都可以;

如上图所示:

  • 图中可清楚看到A与B、C、D相邻,假如要表示这些与A顶点相邻的顶点(边),可以通过将它们作为A的值(value)存入到对应的数组/链表/字典中。
  • 之后,通过键(key)A可以十分方便地取出对应的数据;

邻接表的问题:

  • 邻接表可以简单地得出出度,即某一顶点指向其他顶点的个数;
  • 但是,邻接表计算入度(指向某一顶点的其他顶点的个数称为该顶点的入度)十分困难。此时需要构造逆邻接表才能有效计算入度;

二、封装图结构

在实现过程中采用邻接表的方式来表示边,使用字典类来存储邻接表。

2.1.添加字典类和队列类

首先需要引入之前实现的,之后会用到的字典类和队列类:

  1. //封装字典类
  2. function Dictionary(){
  3. //字典属性
  4. this.items = {}
  5. //字典操作方法
  6. //一.在字典中添加键值对
  7. Dictionary.prototype.set = function(key, value){
  8. this.items[key] = value
  9. }
  10. //二.判断字典中是否有某个key
  11. Dictionary.prototype.has = function(key){
  12. return this.items.hasOwnProperty(key)
  13. }
  14. //三.从字典中移除元素
  15. Dictionary.prototype.remove = function(key){
  16. //1.判断字典中是否有这个key
  17. if(!this.has(key)) return false
  18. //2.从字典中删除key
  19. delete this.items[key]
  20. return true
  21. }
  22. //四.根据key获取value
  23. Dictionary.prototype.get = function(key){
  24. return this.has(key) ? this.items[key] : undefined
  25. }
  26. //五.获取所有keys
  27. Dictionary.prototype.keys = function(){
  28. return Object.keys(this.items)
  29. }
  30. //六.size方法
  31. Dictionary.prototype.keys = function(){
  32. return this.keys().length
  33. }
  34. //七.clear方法
  35. Dictionary.prototype.clear = function(){
  36. this.items = {}
  37. }
  38. }
  39. // 基于数组封装队列类
  40. function Queue() {
  41. // 属性
  42. this.items = []
  43. // 方法
  44. // 1.将元素加入到队列中
  45. Queue.prototype.enqueue = element => {
  46. this.items.push(element)
  47. }
  48. // 2.从队列中删除前端元素
  49. Queue.prototype.dequeue = () => {
  50. return this.items.shift()
  51. }
  52. // 3.查看前端的元素
  53. Queue.prototype.front = () => {
  54. return this.items[0]
  55. }
  56. // 4.查看队列是否为空
  57. Queue.prototype.isEmpty = () => {
  58. return this.items.length == 0;
  59. }
  60. // 5.查看队列中元素的个数
  61. Queue.prototype.size = () => {
  62. return this.items.length
  63. }
  64. // 6.toString方法
  65. Queue.prototype.toString = () => {
  66. let resultString = ''
  67. for (let i of this.items){
  68. resultString += i + ' '
  69. }
  70. return resultString
  71. }
  72. }

2.2.创建图类

先创建图类Graph,并添加基本属性,再实现图类的常用方法:

  1. //封装图类
  2. function Graph (){
  3. //属性:顶点(数组)/边(字典)
  4. this.vertexes = [] //顶点
  5. this.edges = new Dictionary() //边
  6. }

2.3.添加顶点与边

如图所示:

创建一个数组对象vertexes存储图的顶点;创建一个字典对象edges存储图的边,其中key为顶点,value为存储key顶点相邻顶点的数组。

代码实现:

  1. //添加方法
  2. //一.添加顶点
  3. Graph.prototype.addVertex = function(v){
  4. this.vertexes.push(v)
  5. this.edges.set(v, []) //将边添加到字典中,新增的顶点作为键,对应的值为一个存储边的空数组
  6. }
  7. //二.添加边
  8. Graph.prototype.addEdge = function(v1, v2){//传入两个顶点为它们添加边
  9. this.edges.get(v1).push(v2)//取出字典对象edges中存储边的数组,并添加关联顶点
  10. this.edges.get(v2).push(v1)//表示的是无向表,故要添加互相指向的两条边
  11. }

2.4.转换为字符串输出

为图类Graph添加toString方法,实现以邻接表的形式输出图中各顶点。

代码实现:

  1. //三.实现toString方法:转换为邻接表形式
  2. Graph.prototype.toString = function (){
  3. //1.定义字符串,保存最终结果
  4. let resultString = ""
  5. //2.遍历所有的顶点以及顶点对应的边
  6. for (let i = 0; i < this.vertexes.length; i++) {//遍历所有顶点
  7. resultString += this.vertexes[i] + '-->'
  8. let vEdges = this.edges.get(this.vertexes[i])
  9. for (let j = 0; j < vEdges.length; j++) {//遍历字典中每个顶点对应的数组
  10. resultString += vEdges[j] + ' ';
  11. }
  12. resultString += '\n'
  13. }
  14. return resultString
  15. }

测试代码:

  1. //测试代码
  2. //1.创建图结构
  3. let graph = new Graph()
  4. //2.添加顶点
  5. let myVertexes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
  6. for (let i = 0; i < myVertexes.length; i++) {
  7. graph.addVertex(myVertexes[i])
  8. }
  9. //3.添加边
  10. graph.addEdge('A', 'B')
  11. graph.addEdge('A', 'C')
  12. graph.addEdge('A', 'D')
  13. graph.addEdge('C', 'D')
  14. graph.addEdge('C', 'G')
  15. graph.addEdge('D', 'G')
  16. graph.addEdge('D', 'H')
  17. graph.addEdge('B', 'E')
  18. graph.addEdge('B', 'F')
  19. graph.addEdge('E', 'I')
  20. //4.输出结果
  21. console.log(graph.toString());

测试结果:

2.5.图的遍历

图的遍历思想:

  • 图的遍历思想与树的遍历思想一样,意味着需要将图中所有的顶点都访问一遍,并且不能有重复的访问(上面的toString方法会重复访问);

遍历图的两种算法:

  • 广度优先搜索(Breadth - First Search,简称BFS);
  • 深度优先搜索(Depth - First Search,简称DFS);
  • 两种遍历算法都需要指定第一个被访问的顶点

为了记录顶点是否被访问过,使用三种颜色来表示它们的状态

  • 白色:表示该顶点还没有被访问过;
  • 灰色:表示该顶点被访问过,但其相邻顶点并未完全被访问过;
  • 黑色:表示该顶点被访问过,且其所有相邻顶点都被访问过;

首先封装initializeColor方法将图中的所有顶点初始化为白色,代码实现如下:

  1. //四.初始化状态颜色
  2. Graph.prototype.initializeColor = function(){
  3. let colors = []
  4. for (let i = 0; i < this.vertexes.length; i++) {
  5. colors[this.vertexes[i]] = 'white';
  6. }
  7. return colors
  8. }
广度优先搜索

广度优先搜索算法的思路:

  • 广度优先搜索算法会从指定的第一个顶点开始遍历图,先访问其所有的相邻顶点,就像一次访问图的一层;
  • 也可以说是先宽后深地遍历图中的各个顶点;

实现思路:

基于队列可以简单地实现广度优先搜索算法:

  • 首先创建一个队列Q(尾部进,首部出);
  • 调用封装的initializeColor方法将所有顶点初始化为白色;
  • 指定第一个顶点A,将A标注为灰色(被访问过的节点),并将A放入队列Q中;
  • 循环遍历队列中的元素,只要队列Q非空,就执行以下操作:
    • 先将灰色的A从Q的首部取出;
    • 取出A后,将A的所有未被访问过(白色)的相邻顶点依次从队列Q的尾部加入队列,并变为灰色。以此保证,灰色的相邻顶点不重复加入队列;
    • A的全部相邻节点加入Q后,A变为黑色,在下一次循环中被移除Q外;

代码实现:

  1. //五.实现广度搜索(BFS)
  2. //传入指定的第一个顶点和处理结果的函数
  3. Graph.prototype.bfs = function(initV, handler){
  4. //1.初始化颜色
  5. let colors = this.initializeColor()
  6. //2.创建队列
  7. let que = new Queue()
  8. //3.将顶点加入到队列中
  9. que.enqueue(initV)
  10. //4.循环从队列中取出元素,队列为空才停止
  11. while(!que.isEmpty()){
  12. //4.1.从队列首部取出一个顶点
  13. let v = que.dequeue()
  14. //4.2.从字典对象edges中获取和该顶点相邻的其他顶点组成的数组
  15. let vNeighbours = this.edges.get(v)
  16. //4.3.将v的颜色变为灰色
  17. colors[v] = 'gray'
  18. //4.4.遍历v所有相邻的顶点vNeighbours,并且加入队列中
  19. for (let i = 0; i < vNeighbours.length; i++) {
  20. const a = vNeighbours[i];
  21. //判断相邻顶点是否被探测过,被探测过则不加入队列中;并且加入队列后变为灰色,表示被探测过
  22. if (colors[a] == 'white') {
  23. colors[a] = 'gray'
  24. que.enqueue(a)
  25. }
  26. }
  27. //4.5.处理顶点v
  28. handler(v)
  29. //4.6.顶点v所有白色的相邻顶点都加入队列后,将顶点v设置为黑色。此时黑色顶点v位于队列最前面,进入下一次while循环时会被取出
  30. colors[v] = 'black'
  31. }
  32. }

过程详解:

下为指定的第一个顶点为A时的遍历过程:

  • 如 a 图所示,将在字典edges中取出的与A相邻的且未被访问过的白色顶点B、C、D放入队列que中并变为灰色,随后将A变为黑色并移出队列;
  • 接着,如图 b 所示,将在字典edges中取出的与B相邻的且未被访问过的白色顶点E、F放入队列que中并变为灰色,随后将B变为黑色并移出队列;

  • 如 c 图所示,将在字典edges中取出的与C相邻的且未被访问过的白色顶点G(A,D也相邻不过已变为灰色,所以不加入队列)放入队列que中并变为灰色,随后将C变为黑色并移出队列;
  • 接着,如图 d 所示,将在字典edges中取出的与D相邻的且未被访问过的白色顶点H放入队列que中并变为灰色,随后将D变为黑色并移出队列。

如此循环直到队列中元素为0,即所有顶点都变黑并移出队列后才停止,此时图中顶点已被全部遍历。

测试代码:

  1. //测试代码
  2. //1.创建图结构
  3. let graph = new Graph()
  4. //2.添加顶点
  5. let myVertexes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
  6. for (let i = 0; i < myVertexes.length; i++) {
  7. graph.addVertex(myVertexes[i])
  8. }
  9. //3.添加边
  10. graph.addEdge('A', 'B')
  11. graph.addEdge('A', 'C')
  12. graph.addEdge('A', 'D')
  13. graph.addEdge('C', 'D')
  14. graph.addEdge('C', 'G')
  15. graph.addEdge('D', 'G')
  16. graph.addEdge('D', 'H')
  17. graph.addEdge('B', 'E')
  18. graph.addEdge('B', 'F')
  19. graph.addEdge('E', 'I')
  20. //4.测试bfs遍历方法
  21. let result = ""
  22. graph.bfs(graph.vertexes[0], function(v){
  23. result += v + "-"
  24. })
  25. console.log(result);

测试结果:

可见,安装了广度优先搜索的顺序不重复地遍历了所有顶点。

深度优先搜索

广度优先算法的思路:

  • 深度优先搜索算法将会从指定的第一个顶点开始遍历图,沿着一条路径遍历直到该路径的最后一个顶点都被访问过为止;
  • 接着沿原来路径回退并探索下一条路径,即先深后宽地遍历图中的各个顶点;

实现思路:

  • 可以使用结构来实现深度优先搜索算法;
  • 深度优先搜索算法的遍历顺序与二叉搜索树中的先序遍历较为相似,同样可以使用递归来实现(递归的本质就是函数栈的调用)。

基于递归实现深度优先搜索算法:定义dfs方法用于调用递归方法dfsVisit,定义dfsVisit方法用于递归访问图中的各个顶点。

在dfs方法中:

  • 首先,调用initializeColor方法将所有顶点初始化为白色;
  • 然后,调用dfsVisit方法遍历图的顶点;

在dfsVisit方法中:

  • 首先,将传入的指定节点v标注为灰色
  • 接着,处理顶点V;
  • 然后,访问V的相邻顶点;
  • 最后,将顶点v标注为黑色;

代码实现:

  1. //六.实现深度搜索(DFS)
  2. Graph.prototype.dfs = function(initV, handler){
  3. //1.初始化顶点颜色
  4. let colors = this.initializeColor()
  5. //2.从某个顶点开始依次递归访问
  6. this.dfsVisit(initV, colors, handler)
  7. }
  8. //为了方便递归调用,封装访问顶点的函数,传入三个参数分别表示:指定的第一个顶点、颜色、处理函数
  9. Graph.prototype.dfsVisit = function(v, colors, handler){
  10. //1.将颜色设置为灰色
  11. colors[v] = 'gray'
  12. //2.处理v顶点
  13. handler(v)
  14. //3.访问V的相邻顶点
  15. let vNeighbours = this.edges.get(v)
  16. for (let i = 0; i < vNeighbours.length; i++) {
  17. let a = vNeighbours[i];
  18. //判断相邻顶点是否为白色,若为白色,递归调用函数继续访问
  19. if (colors[a] == 'white') {
  20. this.dfsVisit(a, colors, handler)
  21. }
  22. }
  23. //4.将v设置为黑色
  24. colors[v] = 'black'
  25. }

过程详解:

这里主要解释一下代码中的第3步操作:访问指定顶点的相邻顶点。

  • 以指定顶点A为例,先从储存顶点及其对应相邻顶点的字典对象edges中取出由顶点A的相邻顶点组成的数组:

  • 第一步:A顶点变为灰色,随后进入第一个for循环,遍历A白色的相邻顶点:B、C、D;在该for循环的第1次循环中(执行B),B顶点满足:colors == "white",触发递归,重新调用该方法;
  • 第二步:B顶点变为灰色,随后进入第二个for循环,遍历B白色的相邻顶点:E、F;在该for循环的第1次循环中(执行E),E顶点满足:colors == "white",触发递归,重新调用该方法;
  • 第三步:E顶点变为灰色,随后进入第三个for循环,遍历E白色的相邻顶点:I;在该for循环的第1次循环中(执行I),I顶点满足:colors == "white",触发递归,重新调用该方法;
  • 第四步:I顶点变为灰色,随后进入第四个for循环,由于顶点I的相邻顶点E不满足:colors == "white",停止递归调用。过程如下图所示:

  • 第五步:递归结束后一路向上返回,首先回到第三个for循环中继续执行其中的第2、3...次循环,每次循环的执行过程与上面的同理,直到递归再次结束后,再返回到第二个for循环中继续执行其中的第2、3...次循环....以此类推直到将图的所有顶点访问完为止。

下图为遍历图中各顶点的完整过程:

  • 发现表示访问了该顶点,状态变为灰色
  • 探索表示既访问了该顶点,也访问了该顶点的全部相邻顶点,状态变为黑色
  • 由于在顶点变为灰色后就调用了处理函数handler,所以handler方法的输出顺序为发现顶点的顺序即:A、B、E、I、F、C、D、G、H 。

测试代码:

  1. //测试代码
  2. //1.创建图结构
  3. let graph = new Graph()
  4. //2.添加顶点
  5. let myVertexes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
  6. for (let i = 0; i < myVertexes.length; i++) {
  7. graph.addVertex(myVertexes[i])
  8. }
  9. //3.添加边
  10. graph.addEdge('A', 'B')
  11. graph.addEdge('A', 'C')
  12. graph.addEdge('A', 'D')
  13. graph.addEdge('C', 'D')
  14. graph.addEdge('C', 'G')
  15. graph.addEdge('D', 'G')
  16. graph.addEdge('D', 'H')
  17. graph.addEdge('B', 'E')
  18. graph.addEdge('B', 'F')
  19. graph.addEdge('E', 'I')
  20. //4.测试dfs遍历顶点
  21. let result = ""
  22. graph.dfs(graph.vertexes[0], function(v){
  23. result += v + "-"
  24. })
  25. console.log(result);

测试结果:

2.6.完整实现

  1. //封装图结构
  2. function Graph (){
  3. //属性:顶点(数组)/边(字典)
  4. this.vertexes = [] //顶点
  5. this.edges = new Dictionary() //边
  6. //方法
  7. //添加方法
  8. //一.添加顶点
  9. Graph.prototype.addVertex = function(v){
  10. this.vertexes.push(v)
  11. this.edges.set(v, []) //将边添加到字典中,新增的顶点作为键,对应的值为一个存储边的空数组
  12. }
  13. //二.添加边
  14. Graph.prototype.addEdge = function(v1, v2){//传入两个顶点为它们添加边
  15. this.edges.get(v1).push(v2)//取出字典对象edges中存储边的数组,并添加关联顶点
  16. this.edges.get(v2).push(v1)//表示的是无向表,故要添加互相指向的两条边
  17. }
  18. //三.实现toString方法:转换为邻接表形式
  19. Graph.prototype.toString = function (){
  20. //1.定义字符串,保存最终结果
  21. let resultString = ""
  22. //2.遍历所有的顶点以及顶点对应的边
  23. for (let i = 0; i < this.vertexes.length; i++) {//遍历所有顶点
  24. resultString += this.vertexes[i] + '-->'
  25. let vEdges = this.edges.get(this.vertexes[i])
  26. for (let j = 0; j < vEdges.length; j++) {//遍历字典中每个顶点对应的数组
  27. resultString += vEdges[j] + ' ';
  28. }
  29. resultString += '\n'
  30. }
  31. return resultString
  32. }
  33. //四.初始化状态颜色
  34. Graph.prototype.initializeColor = function(){
  35. let colors = []
  36. for (let i = 0; i < this.vertexes.length; i++) {
  37. colors[this.vertexes[i]] = 'white';
  38. }
  39. return colors
  40. }
  41. //五.实现广度搜索(BFS)
  42. //传入指定的第一个顶点和处理结果的函数
  43. Graph.prototype.bfs = function(initV, handler){
  44. //1.初始化颜色
  45. let colors = this.initializeColor()
  46. //2.创建队列
  47. let que = new Queue()
  48. //3.将顶点加入到队列中
  49. que.enqueue(initV)
  50. //4.循环从队列中取出元素
  51. while(!que.isEmpty()){
  52. //4.1.从队列中取出一个顶点
  53. let v = que.dequeue()
  54. //4.2.获取和顶点相相邻的其他顶点
  55. let vNeighbours = this.edges.get(v)
  56. //4.3.将v的颜色变为灰色
  57. colors[v] = 'gray'
  58. //4.4.遍历v所有相邻的顶点vNeighbours,并且加入队列中
  59. for (let i = 0; i < vNeighbours.length; i++) {
  60. const a = vNeighbours[i];
  61. //判断相邻顶点是否被探测过,被探测过则不加入队列中;并且加入队列后变为灰色,表示被探测过
  62. if (colors[a] == 'white') {
  63. colors[a] = 'gray'
  64. que.enqueue(a)
  65. }
  66. }
  67. //4.5.处理顶点v
  68. handler(v)
  69. //4.6.顶点v所有白色的相邻顶点都加入队列后,将顶点v设置为黑色。此时黑色顶点v位于队列最前面,进入下一次while循环时会被取出
  70. colors[v] = 'black'
  71. }
  72. }
  73. //六.实现深度搜索(DFS)
  74. Graph.prototype.dfs = function(initV, handler){
  75. //1.初始化顶点颜色
  76. let colors = this.initializeColor()
  77. //2.从某个顶点开始依次递归访问
  78. this.dfsVisit(initV, colors, handler)
  79. }
  80. //为了方便递归调用,封装访问顶点的函数,传入三个参数分别表示:指定的第一个顶点、颜色、处理函数
  81. Graph.prototype.dfsVisit = function(v, colors, handler){
  82. //1.将颜色设置为灰色
  83. colors[v] = 'gray'
  84. //2.处理v顶点
  85. handler(v)
  86. //3.访问v相连的其他顶点
  87. let vNeighbours = this.edges.get(v)
  88. for (let i = 0; i < vNeighbours.length; i++) {
  89. let a = vNeighbours[i];
  90. //判断相邻顶点是否为白色,若为白色,递归调用函数继续访问
  91. if (colors[a] == 'white') {
  92. this.dfsVisit(a, colors, handler)
  93. }
  94. }
  95. //4.将v设置为黑色
  96. colors[v] = 'black'
  97. }
  98. }

参考资料:JavaScript数据结构与算法

JavaScript实现图结构的更多相关文章

  1. JavaScript数据结构——图的实现

    在计算机科学中,图是一种网络结构的抽象模型,它是一组由边连接的顶点组成.一个图G = (V, E)由以下元素组成: V:一组顶点 E:一组边,连接V中的顶点 下图表示了一个图的结构: 在介绍如何用Ja ...

  2. JavaScript实现栈结构(Stack)

    JavaScript实现栈结构(Stack) 一.前言 1.1.什么是数据结构? 数据结构就是在计算机中,存储和组织数据的方式. 例如:图书管理,怎样摆放图书才能既能放很多书,也方便取? 主要需要考虑 ...

  3. Twproject Gantt – 开源的 JavaScript 甘特图组件

    Twproject Gantt 是一款基于 jQuery 开发的甘特图组件,也可以创建其它图表,例如任务树(Task Trees).内置编辑.缩放和 CSS 皮肤等功能.更重要的是,它是免费开源的. ...

  4. 图结构练习——判断给定图是否存在合法拓扑序列(dfs算法(第一个代码),邻接矩阵(前两个代码),邻接表(第三个代码))

    sdut 2140 图结构练习——判断给定图是否存在合法拓扑序列 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  给定一个有向图 ...

  5. 图结构练习——最短路径(floyd算法(弗洛伊德))

    图结构练习——最短路径 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  给定一个带权无向图,求节点1到节点n的最短路径.   输 ...

  6. 图结构练习——最短路径(dijkstra算法(迪杰斯拉特))

      图结构练习——最短路径 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  给定一个带权无向图,求节点1到节点n的最短路径.   ...

  7. 图结构练习——最小生成树(kruskal算法(克鲁斯卡尔))

    图结构练习——最小生成树 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  有n个城市,其中有些城市之间可以修建公路,修建不同的公 ...

  8. 图结构练习——最小生成树(prim算法(普里姆))

      图结构练习——最小生成树 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  有n个城市,其中有些城市之间可以修建公路,修建不同 ...

  9. C++图结构的图结构操作示例

    示例代码: /* By qianshou 2013/10/5明天就要开学了~哎~ */ #include<iostream> using namespace std; /********* ...

随机推荐

  1. JUC常用同步工具类——CountDownLatch,CyclicBarrier,Semaphore

    在 JUC 下包含了一些常用的同步工具类,今天就来详细介绍一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它们之间的区别. 一.CountDownLa ...

  2. CentOS下安装Anaconda和pycharm

    前情提要:Linux越来越受大家喜爱,而在Linux中有一个社区很活跃的系统:那就是CentOS:而Anaconda又是几乎就一劳永逸的,你装了它之后基本上很多类库就不用再装了.然后就是pycharm ...

  3. js 面向对象中,定义一个函数的过程

    定义一个函数做的两件事:1: 实例化一个Function对象:2: 实例化一个Object对象,并给该函数扩展prototype属性指向这个构造函数 大致过程如图所示: 每一种引用类型(函数,对象,数 ...

  4. leetcode 209 3 滑动窗口

    class Solution { public: int minSubArrayLen(int s, vector<int>& nums) { ,r=-; //由于数组是[]区间, ...

  5. shell编程中星号(asterisk "*")的坑

    今天分享一个有关shell编程中由通配符引起的问题. 1. 问题代码 cat test.logs 4567890 * ##*************************************## ...

  6. 基于JWT实现token验证

    JWT的介绍 Json Web Token(JWT)是目前比较流行的跨域认证解决方案,是一种基于JSON的开发标准,由于数据是可以经过签名加密的,比较安全可靠,一般用于前端和服务器之间传递信息,也可以 ...

  7. 基于osg的python三维程序开发(二)------向量

    上一篇文章展示了如何简单创建一个osg python 程序, 本篇展示了了一些基础数据结构的使用: from pyosg import * vec = osg.Vec3Array() #push ba ...

  8. Flink消费Kafka到HDFS实现及详解

    1.概述 最近有同学留言咨询,Flink消费Kafka的一些问题,今天笔者将用一个小案例来为大家介绍如何将Kafka中的数据,通过Flink任务来消费并存储到HDFS上. 2.内容 这里举个消费Kaf ...

  9. 【Java实用工具】——使用oshi获取主机信息

    最近在筹划做一个监控系统.其中就要获取主机信息,其中遇到一些问题.在此做个记录,以便以后查阅. 在该监控系统中,想要做到主机的CPU.内存.磁盘.网络.线程.JVM内存.JVM GC 等维度的监控,J ...

  10. 关于动态路由中路由之间的跳转(页面a跳转到页面b)

    由addRouters方法获取到后台的动态路由,要实现路由之间的跳转,不可直接用path: '***',而是将动态路由存储到vuex中,再从vuex中取得,如:this.$store.menu.nav ...