树的图示

根(root) 树的主干
image.png

要点
1.dom
2.选择器
3.json
4.虚拟dom
5.文本查找和输入提示

image.png

树的遍历

image.png

image.png

树的查找

image.png

树的路径

image.png

基于class 和 tag 的简单css选择器

image.png

  1. // 树的创建和递归
  2. // 如果一个 二叉树,他的左边总比 右边小,那么他就是一个有序树列
  3. class Tree {
  4. constructor(v, children) {
  5. this.v = v
  6. this.children = children || null
  7. }
  8. }
  9. const tree = new Tree(10, [new Tree(5), new Tree(3, [new Tree(7), new Tree(11)]), new Tree(2)])
  10. // 树的遍历 (先序)
  11. function tree_transverse (tree) {
  12. // console.log(tree.v)
  13. tree.children && tree.children.forEach(tree_transverse)
  14. }
  15. tree_transverse(tree) // 10 5 3 7 11 2 这里是深度优先原则
  16. // 树的遍历 (后序)
  17. function tree_transverse_l (tree) {
  18. tree.children && tree.children.forEach(tree_transverse_l)
  19. // console.log(tree.v) // 每次都迭代到叶子节点才可以做一次打印
  20. }
  21. tree_transverse_l(tree) // 5 7 11 3 2 10
  22. // 树的遍历 (中序)
  23. function tree_transverse_m (tree, ord = 0) {
  24. let transversed = false
  25. if (!tree.children) {
  26. console.log(tree.v)
  27. return
  28. }
  29. tree.children.forEach((child, i) => {
  30. if (i === ord) {
  31. transversed = true
  32. console.log(tree.v);
  33. }
  34. tree_transverse_m(child, ord)
  35. })
  36. !transversed && console.log(tree.v) // 每次都迭代到叶子节点才可以做一次打印
  37. }
  38. // tree_transverse_m(tree, 0) // 10 5 3 7 11 2
  39. // tree_transverse_m(tree, 1) // 5 10 7 3 11 2
  40. // tree_transverse_m(tree, 2) // 5 7 11 3 10 2
  41. // tree_transverse_m(tree, 3) // 5 7 11 3 2 10
  42. // 树的遍历 (回调)
  43. function tree_transverse_back (tree, ord = 0, callback) {
  44. let transversed = false
  45. if (!tree.children) {
  46. callback(tree.v)
  47. return
  48. }
  49. tree.children.forEach((child, i) => {
  50. if (i === ord) {
  51. transversed = true
  52. callback(tree.v);
  53. }
  54. tree_transverse_back(child, ord)
  55. })
  56. !transversed && callback(tree.v) // 每次都迭代到叶子节点才可以做一次打印
  57. }

全排列

[1, 2, 3, 4] 的所有排列组合
image.png

  1. // [1,2,3,4]的所有排列组合
  2. // 怎么先取出1,在取出 234 ,然后取出2 在取出 134那
  3. const A = [1, 2, 3, 4]
  4. // A.slice(0, 1)// 取出第0项
  5. // A.slice(2)// 取值第2项及以后
  6. // A.slice(0, 1).concat(A.slice(2)) // 然后可以继续向上相加
  7. function perm (A) {
  8. if (A.length === 1) { return [A] }
  9. return [].concat(...A.map((a, i) => {
  10. // [2,3,4]
  11. return perm(A.slice(0, i).concat(A.slice(i + 1))).map(p => [a].concat(p))
  12. }
  13. ))
  14. }
  15. console.log(perm(A))
  16. // 基于交换,因为上面的虽然出结果了,但是slice 和 concat 需要把数组重复拷贝
  17. // 所以优化就把一个数组里,[1,2,3,4]->[4,1,2,3]->[1,2,3,4]->[1,4,3,2]
  18. // 就是一个数组的内部交换 ,然后在还原在交换

手写一个节点选择器
image.png

function* transverse (node) {
  yield node
  if (node.children) {
    const children = [...node.children]
    for (let i = 0; i < children.length; i++) {
      yield* transverse(children[i])
    }
  }
}

function get_range (x) {
  const m = x.match(/\[(.*)\]/)
  if (m) {
    return (arr) => arr.slice(...m[1].split(':').filter(x => !!x))
  }
}

// .box img 
// [{className:'.box'},{tagName:'img'}]
function parse_selection_expr (expr) {
  return expr.split(' ').map(x => {
    const range = get_range(x)
    if (x[0] === '.') {
      return { className: x.substr(1), range }
    } else {
      return { tagName: x, range }
    }
  })
}
function is_ancestor (node1, node2) {
  let p = node2
  while (p) {
    if (p === node1) { return true }
    p = p.parentNode
  }
  return false
}

function selector (node, path) {
  if (path.length === 0) {return [node]}
  const p = path.shift()
  let nodes = []
  if (p.className) {
    nodes = [...document.getElementsByClassName(p.className)].filter(v => is_ancestor(node, v))
  } else if (p.tagName) {
    nodes = [...transverse(node)].filter(n => n.tagName === p.tagName)
  }
  p.range && (nodes = p.range(nodes))
  return [].concat(...nodes.map(node => selector(node, [...path])))
}

深度遍历

1.访问根节点。
2.对根节点的children挨个进行深度优先遍历。

const tree = {
  val: 'a',
  children: [{
    val: 'b',
    children: [
      {
        val: 'd',
        children: []
      },
      {
        val: 'e',
        children: []
      }
    ]
  },
  {
    val: 'c',
    children: [
      {
        val: 'f',
        children: []
      },
      {
        val: 'g',
        children: []
      }
    ]
  }]
}

/**
 * 
  算法口诀:  
  1.访问根节点。
  2.对根节点的children挨个进行深度优先遍历。
*/
const dfs = (root) => {
  console.log(root.val);
  if(!root.children) return
  root.children.forEach(dfs);
}

dfs(tree)

广度遍历

1.新建一个队列,把根节点入队。
2.把队头出队并访问。
3.把队头的children挨个入队。
4.重复第二、三步,直到队列为空。

const tree = {
  val: 'a',
  children: [{
    val: 'b',
    children: [
      {
        val: 'd',
        children: []
      },
      {
        val: 'e',
        children: []
      }
    ]
  },
  {
    val: 'c',
    children: [
      {
        val: 'f',
        children: []
      },
      {
        val: 'g',
        children: []
      }
    ]
  }]
}

/**
 * 
 * 算法口诀:
1.新建一个队列,把根节点入队。
2.把队头出队并访问。
3.把队头的children挨个入队。
4.重复第二、三步,直到队列为空。 
 */
const bfs = (root) => {
  const q = [root];
  let all = [];
  while (q.length > 0) {
    const n = q.shift();
    n.children.forEach((child) => {
      q.push(child);
    });
  }
  console.log(all)
}

bfs(tree)

二叉树每层的最大值


二叉树每次最左边的值