概念

树的数据结构用来表示层级关系。
图的数据结构用来表示网络关系。

图的表示

我们需要研究的本质还是节点和节点间的关系,所以当一种表述方式能够说清楚这样的关系,它就是一种好的表示。常见的表示方法有:对象、邻接表、邻接矩阵(二维数组)。

  • 对象这种形式将节点抽象成node对象、边抽象成edge对象,这样的表示常见于可视化工具库中,比如AntV、D3.js:
  • 邻接表相较上面的对象的方法稍微抽象点,可以理解是用Map在对象和其关系之间做了一个映射。
  • 而最抽象的莫过于邻接矩阵了,在邻接矩阵中,没有体现具体节点,而节点的内涵隐式的包含在数组的下标之中。所以,这个矩阵(二维数组)展示的就是某节点和另一个节点是否存在边。这种表示方法虽然抽象,但是对于计算机而讲,就可以应用线性代数中矩阵的很多性质,相应的快速做出变换。

用最简单的图来举例说下我们的几种表示方法吧:
(如果我们现在有一个关系是这样的:节点有两个:1,2,其关系是:1->2)
用Object最直白的表示:

  1. const graph = {
  2. nodes:[
  3. {id: 1},
  4. {id: 2}
  5. ],
  6. edges:[
  7. {
  8. source: 1 ,
  9. target: 2
  10. }
  11. ]
  12. }

用邻接表来表示:

  1. const gTable = {
  2. 1: [2],
  3. 2: []
  4. }

用邻接矩阵来表示:

  1. const gMatrix = [
  2. [0, 1],
  3. [0, 0]
  4. ];

不过问题来了,看上去似乎object可以表述更多的信息,比如扩展下:

  1. const graph = {
  2. nodes:[
  3. {id: 1, name: 'lin7', gender: 'female'},
  4. {id: 2, name: 'cjj', gender: 'male'}
  5. ],
  6. edges:[
  7. {
  8. source: 1 ,
  9. target: 2,
  10. isInLove:true,
  11. when:'at frist sight'
  12. }
  13. ]
  14. }

这样我们表述了更多的信息,那么如果是这样的信息,邻接矩阵怎么做呢?

  1. const gMatrix = [
  2. [
  3. 0,
  4. {
  5. isInLove:true,
  6. when:'at frist sight'
  7. }
  8. ],
  9. [0, 0]
  10. ];

邻接矩阵并不关心节点,或者说节点信息在另外的地方维护,我们通过gMatrix就知道,存在第一个节点指向第二个节点的边,那么具体第一个节点和第二个节点是谁?那其实就是你说是谁就可以是谁。
那么邻接表这种方式其实是一个点边映射的Map,那么,当信息复杂需要做映射的时候,我们用一个WeakMap就能说的清楚。

遍历

遍历是最基本和最重要的方法了。
对于图,基本的遍历和树一样,也分为深度、广度优先(DFS & BFS)。
不过图有一点需要格外注意的是,在遍历过程中要防止路径成环,比如,可能出现这样的路径 A -> B -> C -> D -> B … 这种情况需要避免,否则程序奔溃,调用栈溢出了。怎么避免?用Set记录下遍历过的节点就好。
同样的DFS可以借助递归或者栈来实现。BFS需要借助队列实现。

题目

LeetCode 133:克隆图

133. 克隆图
给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
图中的每个节点都包含它的值 valint) 和其邻居的列表(list[Node])。
class Node {
public int val;
public List neighbors;
}
_
这道题是让我们深拷贝题目给出的数据结构。其实差不多是一道遍历题,但是按照题目的数据结构,我们的边关系也需要深拷贝,这点是这个题目的点。

  1. /**
  2. * // Definition for a Node.
  3. * function Node(val, neighbors) {
  4. * this.val = val === undefined ? 0 : val;
  5. * this.neighbors = neighbors === undefined ? [] : neighbors;
  6. * };
  7. */
  8. /**
  9. * @param {Node} node
  10. * @return {Node}
  11. */
  12. var cloneGraph = function(node) {
  13. if(!node) return;
  14. // 存放node和其备份的Map
  15. const nodeMapToCopy = new Map();
  16. const dfs = (n) => {
  17. const { val, neighbors } = n;
  18. const newNode = new Node(val);
  19. // 把克隆体放入map
  20. nodeMapToCopy.set(n, newNode);
  21. (neighbors || []).forEach(ngb => {
  22. if(!nodeMapToCopy.has(ngb)){
  23. dfs(ngb);
  24. }
  25. // 这时候一定存在nodeMapToCopy.get(ngb)
  26. newNode.neighbors.push(nodeMapToCopy.get(ngb));
  27. })
  28. }
  29. dfs(node);
  30. return nodeMapToCopy.get(node);
  31. };

LeetCode 65: 有效数字

65. 有效数字
验证给定的字符串是否可以解释为十进制数字。
例如:
“0” => true
“ 0.1 “ => true
“abc” => false
“1 a” => false
“2e10” => true
“ -90e3 “ => true
“ 1e” => false
“e3” => false
“ 6e-1” => true
“ 99e2.5 “ => false
“53.5e93” => true
“ —6 “ => false
“-+3” => false
“95a54e53” => false

这个题本来是个字符串题目,不用正则,一个个字符校验的话,用状态机图的思路解决,就和图扯上关系了。
下面是一副状态机图,这个状态机的节点表述当前状态,状态间的转化用边来表示。这个状态机的入口是0状态,成功的出口是:3、5、6状态。
每当新遇到一个字符,在当前状态的基础上根据边规定的条件,转化成下一种状态。如果最终能到成功状态,就返回成功。
image.png_

  1. /**
  2. * @param {string} s
  3. * @return {boolean}
  4. */
  5. var isNumber = function (s) {
  6. // 学习别人的状态机图解法
  7. // blank 表示空格
  8. // sign 表示正负号
  9. // digit 表示数字
  10. // . 就是小数点
  11. // e 就是 e
  12. // 邻接表表示状态机
  13. const graph = {
  14. 0: { "blank": 0, "sign": 1, ".": 2, "digit": 6 },
  15. 1: { "digit": 6, ".": 2 },
  16. 2: { "digit": 3 },
  17. 3: { "digit": 3, "e": 4 },
  18. 4: { "digit": 5, "sign": 7 },
  19. 5: { "digit": 5 },
  20. 6: { "digit": 6, ".": 3, "e": 4 },
  21. 7: { "digit": 5 },
  22. };
  23. let state = 0;
  24. let char;
  25. s = s.trim();
  26. for (char of s) {
  27. if (char >= "0" && char <= "9") {
  28. char = "digit";
  29. } else if (char == " ") {
  30. char = "blank";
  31. } else if (char == "+" || char == "-") {
  32. char = "sign";
  33. }
  34. state = graph[state][char];
  35. if (state == undefined) {
  36. return false;
  37. }
  38. }
  39. // 3 是小数,5是科学计数法,6是整数
  40. return state == 3 || state == 5 || state == 6;
  41. };

上面的代码中,状态机图的表示用邻接表表示法。