JavaScript通过reduce+递归实现树的深度遍历

越是结构化的有规律的数据操作起来越简单,只是我们没有找到规律和工具。

首先贴代码

首先定义了一个树结构,需求是通过任意节点遍历出其所有的子节点。
根据需求的不同,就会有深度遍历和广度遍历两种,getAllChildrenDFSByReduce(),getAllChildrenDFSByStack()是深度遍历的两种实现,getAllChildrenBFSByQueue()是广度遍历的实现。

class Node {
  constructor(id, data, parent) {
    this.id = id
    this.data = data
    this.parent = parent || null
    this.children = []

    if (this.parent !== null) {
      this.parent.children.push(this)
    }
  }

  // DFS使用递归作为驱动力
  // 使用递归只能实现前序和后序遍历,取决于item放在什么位置
  getAllChildrenDFSByReduce() {
    return this.children.reduce((res, item) => {
       return [...res, item, ...item.getAllChildrenDFSByReduce()]
    }, [])
  }

  // DFS使用栈遍历作为驱动力
  // 对比递归的方式我们发现: 1. 使用递归(调用栈)代替了stack 2. 使用reduce代替了data
  getAllChildrenDFSByStack() {
    const stack = [this]
    const data = []
    while (stack.length !== 0) {
      // 以前序遍历为例
      const node = stack.pop()
      if (node !== this) data.push(node)
      stack.push(...node.children.reverse())
    }
    return data
  }

  // BFS使用队列遍历作为驱动力
  getAllChildrenBFSByQueue() {
    const queue = [this]
    const data = []
    while (queue.length !== 0) {
      const node = queue.shift()
      data.push(...node.children)
      queue.push(...node.children)
    }
    return data
  }
}

let id = 1
const root = new Node(id++, {}, null)
new Node(id++, {}, root)
new Node(id++, {}, root)
new Node(id++, {}, root.children[0])
new Node(id++, {}, root.children[0])
new Node(id++, {}, root.children[1])
new Node(id++, {}, root.children[1])
new Node(id++, {}, root.children[0].children[0])
new Node(id++, {}, root.children[0].children[0])
new Node(id++, {}, root.children[0].children[1])
new Node(id++, {}, root.children[0].children[1])
new Node(id++, {}, root.children[1].children[0])
new Node(id++, {}, root.children[1].children[0])
new Node(id++, {}, root.children[1].children[1])
new Node(id++, {}, root.children[1].children[1])

/*
                 1
           2            3
        4     5      6    7
       8  9 10 11 12 13 14 15
*/


// [2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
console.log(root.getAllChildrenDFSByReduce().map(item => item.id))
// [2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
console.log(root.getAllChildrenDFSByStack().map(item => item.id))
// [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
console.log(root.getAllChildrenBFSByQueue().map(item => item.id))

深度优先遍历和广度优先遍历

深度优先遍历(DFS)和广度优先遍历(BFS)是树和图搜索的两种策略,一种是纵深优先(子节点优先),一种是广度优先(父节点优先)

做最短路径搜索时,DFS耗性能,BFS耗内存

深度遍历依赖栈结构,通过不断的回溯(很像递归)将子节点优先摘取出来,二叉树里面根据根节点和左右子树节点的排列顺序分为前序(根左右),中序(左根右),后序(左右根)
广度遍历又叫层序遍历,依赖队列结构,通过队列将录入的节点依次摘出,不存在回溯过程。

递归的思想

递归的思想除了分治之外,关键在于学会抽象和隔离问题的复杂性。
比如:全世界的人口 = 我 + 我旁边的同事 + 我的家人 + 我的朋友 + ...,这就陷入了复杂问题的细节之中;
如果不考虑细节:全世界的人口 = 一个人 + 剩下来的人,就轻松很多;
如果恰好发现”剩下来的人 = 一个人 + 再次剩下来的人“,那就可以用递归了,因为问题是相似的。

递归,分治与第一性原理

第一性原理是告诉我们分析问题:首先直击本质,再具体解决各个子问题。
火星旅行票价降低100倍 = 飞船成本降低10倍 + 飞船载客量提高10倍,然后再考虑如果降低飞船成本,如何提高载客量,这是马斯克的逻辑。
这三个词的本质是一样的,告诉我们分析问题的策略:先宏观再微观,先抽象再具体。

posted @ 2020-06-24 17:16  Peterer~王勇  阅读(2287)  评论(0编辑  收藏  举报