DFS和BFS在前端的应用

678 阅读1分钟
作者:CQF

📔DFS概念:深度优先遍历(Depth First Search,简称 DFS)

图解.gif 图论中两种非常重要的算法,生产上广泛用于拓扑排序、寻路(走迷宫)、搜索引擎、爬虫等。

1.  DFS案例1——具体树

  • 查找父节点 点击节点时,带出父级以上节点名称 image.png

看看主流组件库怎么实现?

element-ui el-menu组件实现(el-tree没有实现)

// https://github.com/ElemeFE/element/blob/dev/packages/menu/src/menu-mixin.js indexPath() {   
    const path = [this.index];   
    let parent = this.$parent;   
    while (parent.$options.componentName !== 'ElMenu') {  
        if (parent.index) {      
        // 关键代码,父元素记录了index    
            path.unshift(parent.index);    
        }    
        parent = parent.$parent;  
    }  
    return path; 
}

ant-design实现了react-component的rc-menu组件实现

const getSubPathKeys = useCallback((key: string): Set<string> => {
  const connectedPath = `${key2pathRef.current.get(key)}${PATH_SPLIT}`;
  const pathKeys = new Set<string>();

  [...path2keyRef.current.keys()].forEach(pathKey => {
    if (pathKey.startsWith(connectedPath)) {
      pathKeys.add(path2keyRef.current.get(pathKey));
    }
  });
  return pathKeys;
}, []);

数据结构实现

function findParentLabel({ label }) {
  this.visitNodeList = []
  const dfs = (list, target, queue = []) => {
      for (let i = 0; i < list.length; i++) {
          let node = list[i]
          this.visitNodeList.push(node.label)
          if (node.label === target) {
              return [...queue, node.label]
          }
          if (node.children && node.children.length) {
              let result = dfs(node.children, target, [...queue, node.label])
              if (result && result.length) {
                  return result
              }
          }
      }
  }
}

2. DFS案例2——抽象树:排列组合

  •  全排列
  • [1,2,3]
  • [1,2,3] [1,3,2] [2,1,3] [2,3,1] [3,1,2] [3,2,1]
  • 组合 n选m
  • [1,2,3]
  • [1,2] [1,3] [2,3] 全排列图解如下

image.png 全排列图解操作如下 image.png

排列解题代码模板,即数字可重复的模板

function DFS (nums, stack) {
  if (满足添加) {
    // 处理逻辑
  }
  for (let i = 0; i < nums.length; i++) {
    cur = 出队()
    入栈(cur)
    DFS(队列, 栈)
    出栈()
    归队(cur)
  }
}

image.png

  • 📒 案例: 给定无序、不重复的数组data,取出 n 个数,使其相加和为sum
  • [9, 5, 4, 2, 6, 7, 1]
  • 3
  • 16

组合解题代码模板,即数字不可重复的模板

function DFS (nums, start, stack) {
  if (满足添加) {
    // 处理逻辑
  }
  for (let i = start; i < nums.length; i++) {
    入栈(cur)
    DFS(队列,下标,栈)
    出栈(cur)
  }
}

💪🏻 万能模板

funcion DFS () {
  if (满足条件) {
    // 处理逻辑
  }
  if(剪枝条件) {
    // 剪枝
  }
  for (选择 in 选择列表) {
    做选择
    DFS()
    撤销选择
  }
}

📔BFS概念:广度优先遍历(Breath First Search,简称 BFS)

图解.gif

Note: 能干嘛 最短路径 最少步数最近节点

解题模板

ffunction BFS (root) {
  入队(root)
  while (队列.length > 0) {
    len = 记录队列长度
    for (遍历队列) {
      cur = 出队()
      
      if (cur.子节点) {
        入队(cur.子节点)
      }
    }
  }
}

 📒 综合练习案例

  • 数字n代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
  • 分别用DFS和BFS实现
  • n = 3
  • ["((()))", "(()())", "(())()", "()(())", "()()()"]
function dfs (n) {
  let result = []
  // left表示剩余左括号的数量
  function dfs(left, right, str = '') {
    if (left === 0 && right === 0) {
      result.push(str)
      return
    }
    if (left > right) {
      return
    }
    left > 0 && dfs(left - 1, right, str + "(")
    right > 0 && dfs(left, right - 1, str + ')')
  }
  dfs(n, n)
  return result
}
function bfs (n) {
  let queue = [{
    str: '(',
    left: 1,
    right: 0
  }]
  let result = []
  while (queue.length) {
    let len = queue.length
    for (let i = 0; i < len; i++) {
      let cur = queue.shift()
      if (cur.left === n && cur.right === n) {
        result.push(cur.str)
        continue
      } 
      
      if (cur.left >= cur.right && cur.left < n) {
        queue.push({
          str: cur.str + '(',
          left: cur.left + 1,
          right: cur.right
        })
      }
      if (cur.left > cur.right) {
        queue.push({
          str: cur.str + ')',
          left: cur.left,
          right: cur.right + 1
        })
      }
    }
  }
  // console.log(result);