深度搜索二进制树
#javascript #算法 #数据结构 #interviewprep

在此previous blog post中,我们了解了二进制树是什么,如何表示它以及不同类型的二进制树,总而言之,我们说,与其他数据结构不同,二进制树可以浏览两种不同的方式呼吸优先搜索和深度优先搜索。

这篇文章将重点放在深度优先搜索上。

什么是深度优先的搜索树遍历

深度优先搜索是一种算法,使您可以穿越整棵树。逻辑是,您一直向前前进,直到到达叶子(一个没有左或右子的节点),然后根据您使用的方法回溯。

Image description

深度优先的搜索树可以以三种方式写入:预订,顺序和oder后遍历。可以递归和迭代地编写每种方式。您选择的方式会影响结果。让我向您展示区别:

预购

提示在名称,预订,当您编写预订遍历时,您可以访问父节点,然后访问左边的孩子。如果您使用预订方法遍历上面的树,则结果应为:

Image description

让我们用JavaScript编写递归和迭代实现

递归

function dfsPreOrderRecursive(node, arr = []){
  if(node){
    arr.push(node.val); 
    if(node.left) dfsPreOrderRecursive(node.left, arr);
    if(node.right) dfsPreOrderRecursive(node.right, arr);
  }
}

说明 ,由于您知道要访问节点的顺序,因此递归方法大多是简单的。在这里,我们必须访问父节点,然后访问其孩子。因此,我们将当前节点的值推入结果数组,然后检查其是否具有左或右子,如果是这样,我们通过给子节点和结果数组来调用该功能。

迭代

function dfsPreOrderIterative(root){
  const stack = [root]
  const result = []; 
  let current;

  while(stack.length){
    current = stack.pop(); 
    result.push(current.val);

    if(current.right) stack.push(current.right); 
    if(current.left) stack.push(current.left); 
  }

  return result; 

  }

解释 :如果没有任何约束,则预先遍历是直接的方法。由于我们不需要检查节点是否有任何孩子,因此我们将当前节点的值推到结果,然后检查其是否有孩子,如果这样,它们将被添加到堆栈中。由于我们使用的是堆栈,我们将右向左推右,并且遵循FIFO订单。

为了

对于订购遍历,您必须在回溯到父母和正确的孩子之前访问左孩子。当您当前的节点有一个左孩子时,您必须继续前进。如果您使用订购方法遍历上面的树,则结果应为:

Image description

让我们用JavaScript编写递归和迭代实现。

递归

function dfsInOrderRecursive(node, arr = []){ 
  if(node){
    if(node.left) dfsInOrderRecursive(node.left, arr);
    arr.push(node.val); 
    if(node.right) dfsInOrderRecursive(node.right, arr);
  }

  return arr; 

 }

说明 :由于您知道要访问节点的顺序,因此递归方法大多是简单的。在这里,我们必须在回溯到父母之前访问左孩子,然后访问右子女,因此我们检查当前的节点是否有左孩子,如果是,我们通过给孩子来调用该功能s值进入结果数组,最后我们检查是否存在合适的孩子,如果是这样,我们通过给合适的孩子来调用该功能。

迭代

function dfsInOrderIterative(root){
  const stack = []; 
  const result = [];
  let current = root; 

  while(stack.length || current){
    while(current){
      stack.push(current); 
      current = current.left; 
    }
    current = stack.pop();
    result.push(current.val); 
    current = current.right;
  }

  return result;
}

说明: 对于阶段遍历,我们必须推动左子女,父母,然后推动右子。这意味着我们必须从剩下的孩子开始前进。对于迭代实现,当前循环浏览所有左子女,然后如果没有左子女,则将当前节点的值推入结果。

邮寄订单

对于后订单遍历,您必须在回溯到父母之前访问孩子。您必须在当前节点有孩子的同时继续前进。如果您使用后订单方法遍历上面的树,则结果应为:

Image description

让我们用JavaScript编写递归和迭代实现。

递归

function dfsPostOrderRecursive(node, arr = []){
  if(node){
    if(node.left) dfsPostOrderRecursive(node.left, arr);
    if(node.right) dfsPostOrderRecursive(node.right, arr);
    arr.push(node.val);
  }

  return arr;
}

说明 :由于您知道要访问节点的顺序,因此递归方法大多是简单的。在这里,我们必须在父母面前访问孩子,因此我们检查当前节点是否有左或右子孩子,如果是,我们通过给孩子来调用该功能,然后将值推入结果数组中。

迭代

function dfsPostOrderIterative(root){
  const s1 = [root];
  const s2 = [];
  const result = []; 
  let current;

  while(s1.length){
    current = s1.pop();

    if(current.left) s1.push(current.left);
    if(current.right) s1.push(current.right);

    s2.push(current.val);
  }

  return s2.reverse();

}

复杂性分析

我们在这篇文章中看到的每种方法都具有相同的时间和空间复杂性o(n)甚至递归方法,因为每个递归功能都使用引擎盖下的堆栈,以跟踪您正在穿越的元素。您的选择将取决于您必须解决的问题。