zoukankan      html  css  js  c++  java
  • 深度优先遍历(DFS)和广度优先遍历(BFS)的实现与简单应用

    实现

    DFS(Depth First Search) 深度优先遍历

    深度优先遍历是指从某个顶点出发, 首先访问这个顶点, 然后找出刚访问这个节点的第一个未被访问的子节点,然后再以此子节点为顶点, 继续找它的下一个顶点进行访问。重复此步骤,直至所有节点都被访问完为止。

    例如有这样一棵节点树:

    const nodes = {
      value: '1',
      children: [
        {
          value: '2',
          children: [{ value: '5', children: [{ value: '9' }] }]
        },
        {
          value: '3',
          children: [
            { value: '6', children: [{ value: '10' }] },
            { value: '7' }
          ]
        },
        {
          value: '4',
          children: [{ value: '8' }]
        }
      ]
    }
    

    在深度优先的情况下应当输出:
    ['1', '2', '5', '9', '3', '6', '10', '7', '4', '8']

    递归实现

    对于这类结构化数据,使用递归是比较容易理解的,我们只需要把每次递归的返回值添加到结果数组即可

    // *类型定义,之后的代码不再设置
    type VNode<T> = {
      value: T,
      children?: VNode<T>[]
    }
    
    // *递归实现
    function dfsRecursion<T>(node: VNode<T>) {
      const values: T[] = [];
    
      if (node) {
        values.push(node.value);
        node.children?.forEach(item => {
          values.push(...dfsRecursion(item));
        })
      }
    
      return values;
    }
    

    非递归实现

    非递归实现需要引入栈结构用于保存当前遍历的子树。由于栈是先进后出的结构,同时深度优先遍历是优先遍历每棵子树的第一棵子树,所以我们在压栈的的时候需要先将children逆序。

    function dfsNonRecursion<T>(node: VNode<T>) {
      if (!node) return;
    
      const values: T[] = [];
      const stack = [node];
    
      while (stack.length !== 0) {
        const curNode = stack.pop();
        if (!curNode) break;
    
        values.push(curNode.value);
    
        curNode.children && stack.push(...curNode.children.slice().reverse());
      }
    
      return values;
    }
    

    BFS(Breath First Search) 广度优先遍历

    广度优先遍历是将树按照层级层层遍历,直到某一层全部遍历完毕再遍历下一层级,直到所有节点全部遍历完成。

    在广度优先的情况下,前文的树应当输出:

    [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ]

    非递归实现

    广度优先遍历如果用递归来实现会比较麻烦,这里就展示非递归的实现。

    深度优先遍历我们用的是来实现,而广度优先遍历需要用到的是队列。每当遍历到某棵子树,就将这颗子树的所有子树追加到队列中,以实现层层遍历。

    function bfsNonRecursion<T>(node: VNode<T>) {
      if (!node) return;
    
      const values: T[] = [];
      const queue = [node];
    
      while (queue.length !== 0) {
        const curNode = queue.shift();
        if (!curNode) break;
    
        values.push(curNode.value);
    
        if (curNode.children) queue.push(...curNode.children);
      }
    
      return values;
    }
    

    用深度优先遍历思想实现深拷贝

    我们的深拷贝接收一个item,并根据item的类型进行不同的处理,如果item是数组,则使用map方法生成新数组,并传入deepClone进行递归调用;如果item是对象,则遍历对象的每个成员,并对每个成员都递归调用deepClone,将处理程序交由下一次函数调用来处理;如果item是简单数据类型,则直接返回,不做处理。

    function deepClone(item: any): any {
      if (Array.isArray(item)) return item.map(deepClone);
    
      if (Object.prototype.toString.call(item) === '[object Object]') {
        const obj: any = {};
        for (const key in item) {
          obj[key] = deepClone(item[key]);
        }
        return obj;
      }
    
      return item;
    }
    

    当然,这只是简单的实现,对于对象的循环引用等问题还需要做进一步的处理。

  • 相关阅读:
    MySQL中的MRR和BKA
    MySQL中的BNL优化器
    MySQL中索引
    MySQL中的两种排序方式: index和filesort
    设计模式之观察者模式
    设计模式之策略模式
    设计模式之模板方法
    java和JavaScript的区别
    HTML 简介--from app (W3)
    与代码同行-五年计划
  • 原文地址:https://www.cnblogs.com/hycstar/p/14785219.html
Copyright © 2011-2022 走看看