深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法,生产上广泛用于拓扑排序,寻路(走迷宫),搜索引擎,爬虫等,也频繁出现在 leetcode,高频面试题中。

1.深度优先遍历

1-1.递归实现

主要思路是从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点(看该节点 是否还有除该子节点以外的节点,没有继续回退到父节点,有则遍历该子节点之外的其他节点),再从另一条路开始走到底…,不断递归重复此过程,直到所有的顶点都遍历完成,它的特点是不撞南墙不回头,先走完一条路,再换一条路继续走。
递归的表达性很好,也很容易理解,不过如果层级过深,很容易导致栈溢出。
以上的遍历是树的前序遍历,实际上不管是前序遍历,还是中序遍历,亦或是后序遍历,都属于深度优先遍历。

<div id="root">
     <ul>
          <li>
              <a href="">
                  <img src="" alt="">
              </a>
          </li>
          <li>
              <span></span>
          </li>
          <li>
          </li>
      </ul>
      <p></p>
      <button></button>
  </div>
// 1-1方法一:递归方式
     function deepFirstSearch(node, nodeList) {
         if (node) {
             nodeList.push(node);
             var children = node.children;
             for (var i = 0; i < children.length; i++)
                 //每次递归的时候将 需要遍历的节点 和 节点所存储的数组nodeList传下去
                 deepFirstSearch(children[i], nodeList);
         }
         return nodeList;
     }
     let root = document.getElementById('root')
     var nodeList = deepFirstSearch(root, [])
     console.log(nodeList);

1-2.非递归实现

对二叉树来说,由于是先序遍历(先遍历当前节点,再遍历左节点,再遍历右节点),所以我们有如下思路:
对于每个节点来说,先遍历当前节点(将当前节点弹栈),然后把右节点压栈,再压左节点(这样弹栈的时候会先拿到左节点遍历,符合深度优先遍历要求)

弹栈,拿到栈顶的节点,如果节点不为空,重复步骤 1, 如果为空,结束遍历。

// 1-2方法二:非递归方式
       function deepFirstSearch(node) {
           var nodeList = [];   //下方存放弹栈节点的容器
           if (node != null) {
               var stack = [];   //上方栈结构
               stack.push(node);
               while (stack.length != 0) {
                   var item = stack.pop(); // 出栈,先弹出左边的子节点
                   nodeList.push(item);
                   var children = item.children;
                   for (var i = children.length - 1; i >= 0; i--)
                       // 入栈顺序为:子节点从右向左入栈
                       stack.push(children[i]);
               }
           }
           /* 将最终遍历结果存放在nodeList数组中。*/
           return nodeList;
       }
       let root = document.getElementById('root')
       var nodeList = deepFirstSearch(root)
       console.log(nodeList);

不用担心递归那样层级过深导致的栈溢出问题

2.广度优先遍历

广度优先遍历,指的是从图的一个未遍历的节点出发,先遍历这个节点的相邻节点,再依次遍历每个相邻节点的相邻节点。
广度优先遍历也叫层序遍历
深度优先遍历用的是栈,栈对数组尾部进行操作;而广度优先遍历要用队列来实现,队列对数组首部进行操作。

<script>
	// 1-2方法二:非递归方式
       function breadthFirstSearch(node) {
           var nodeList = [];
           if (node != null) {
               var queue = [];
               queue.unshift(node);  //进入队列
               while (queue.length != 0) {
                   var item = queue.shift();  //离开队列
                   nodeList.push(item);
                   var children = item.children;
                   for (var i = 0; i < children.length; i++)
                       // 依次将该层的子元素放到队列中
                       queue.push(children[i]);
               }
           }
           /* 将最终遍历结果存放在nodeList数组中。*/
           return nodeList;
       }
       let root = document.getElementById('root')
       var nodeList = breadthFirstSearch(root)
       console.log(nodeList);
   </script>

https://developer.51cto.com/art/202004/614590.htm