JS 深度优先遍历(DFS)和广度优先遍历(BFS)

JavaScript08

JS 深度优先遍历(DFS)和广度优先遍历(BFS),第1张

深度优先遍历DFS

自定义:深度单线游走,从根走完最后一个节点,在游走兄弟节点,走完兄弟的所有子节点,循环之。

     递归算法:

function deepFirstSearch(node, nodeList = []) {

  if (node) {

    nodeList.push(node)

    var children = node.children

    for (var i = 0i <children.lengthi++)

      //每次递归的时候将 需要遍历的节点 和 节点所存储的数组传下去

      deepFirstSearch(children[i], nodeList)

  }

  return nodeList

}

非递归算法:

function deepFirstSearch(node) {

  var nodes = []

  if (node != null) {

    var stack = []

    stack.push(node)

    while (stack.length != 0) {

      var item = stack.pop()

      nodes.push(item)

      var children = item.children

      for (var i = children.length - 1i >= 0i--)

        stack.push(children[i])

    }

  }

  return nodes

}

广度优先遍历(BFS)

自定义:从根开始 层层推进 走完一层 走下一层 (犹如爬楼,走完一层的楼梯,继续下一层的楼梯)

递归算法:(容易栈溢出)

function breadthFirstSearch(node) {

  var nodes = []

  var i = 0

  if (!(node == null)) {

    nodes.push(node)

    breadthFirstSearch(node.nextElementSibling)

    node = nodes[i++]

    breadthFirstSearch(node.firstElementChild)

  }

  return nodes

}

非递归算法:(推荐)

function breadthFirstSearch(node) {

  var nodes = []

  if (node != null) {

    var queue = []

    queue.unshift(node)

    while (queue.length != 0) {

      var item = queue.shift()

      nodes.push(item)

      var children = item.children

      for (var i = 0i <children.lengthi++)

        queue.push(children[i])

    }

  }

  return nodes

}

一、树结构

定义一颗树,JS中常见的树形数据结构如下,children属性对应的是子树

let tree = [

{

id: '1',

name: '节点1',

children: [

{

id: '1-1',

name: '节点1-1'

}

]

},

{

id: '2',

name: '节点2',

children: [

{

id: '2-1',

name: '节点2-1'

},

{

id: '2-2',

name: '节点2-2',

children: [

{

id: '2-2-1',

name: '节点2-2-1'

}

]

}

]

},

{

id: '3',

name: '节点3'

}

]

二、深度优先遍历(DFS)

1、递归实现

function treeIterator(tree, func) {

tree.forEach((node) =>{

func(node)

node.children &&treeIterator(node.children, func)

})

}

实现逻辑简述:定义treeIterator函数,传入tree(树)和func(回调函数)两个参数,遍历tree数组,执行回调函数,如果当前节点存在children,则递归调用。

函数调用验证:调用treeIterator函数,传入上文定义好的树结构数组,打印出每个节点的name值。

treeIterator(tree, (node) =>{

console.log(node.name)

})

控制台打印结果如下:

2、循环实现

function treeIterator(tree, func) {

let node, curTree = [...tree]

while ((node = curTree.shift())) {

func(node)

node.children &&curTree.unshift(...node.children)

}

}

实现逻辑简述:

(1)定义node作为当前节点,curTree为传入的树(不影响原数组tree);

(2)执行while循环,curTree数组第一个元素从其中删除,并返回第一个元素赋值给node;

(3)①执行回调函数;②如果当前节点存在子树,则追加到curTree数组的开头,继续执行循环,直到curTree没有元素为止。

函数调用验证:参考上述递归实现验证,方式和结果一致。

三、广度优先遍历(BFS)

function treeIterator(tree, func) {

let node, curTree = [...tree]

while ((node = curTree.shift())) {

func(node)

node.children &&curTree.push(...node.children)

}

}

实现逻辑简述:和上述深度优先遍历的循环实现差不多。区别在于如果当前节点存在子树,则追加到list数组的末尾。

如果单纯的根据一个value取值的话,先indexof(),再substr(), 当然前提是这个value字符是唯一的。 如果是很复杂的嵌套比如:var obj={a:{a:[{a:{a:"b"}}]},b:{b:{b:[{b:{b:{b:"b"}}}]}}} 要遍历取值的话,以我的认知 也就只能用数组的这种方式了; 你如果还想用骚的东西搞的话,那就试试一些解析器吧,查了一下有个vue.js,你可以研究一下。哈哈...