最全的二叉树遍历总结

101 阅读2分钟

前言:二叉树对于算法来说可谓是重中之重,而二叉树的遍历是二叉树的基础,是学好二叉树的前提,之前一直是一知半解,这次我来好好总结下,如果对你有帮助希望可以点个赞,十分感谢!

一、递归版

前序遍历

function preorder(root,res = []) {
  if (!root) {
    return res;
  }
  res.push(root.val);
  root.left && preorder(root.left, res);
  root.right && preorder(root.right, res);
  return res;
}

中序遍历

function inorder(root,res = []) {
  if (!root) {
    return res;
  }
  root.left && inorder(root.left, res);
  res.push(root.val);
  root.right && inorder(root.right, res);
  return res;
}

后序遍历

function postorder(root,res = []) {
  if (!root) {
    return res;
  }
  root.left && postorder(root.left, res);
  root.right && postorder(root.right, res);
  res.push(root.val);
  return res;
}

二、迭代版-模拟出栈顺序

前序遍历

function preorder(root) {
  if (!root) {
    return [];
  }
  const stack = [root];
  const res = [];
  while(stack.length) {
    // 迭代法模拟递归过程,后进先出,所以node.left要放在后面
    let node = stack.pop();
    res.push(node.val);
    node.right && stack.push(node.right);
    node.left && stack.push(node.left);
  }
  return res;
}

中序遍历

function inorder(root) {
  const stack = [];
  const res = [];
  let cur = root;
  while(stack.length || cur) {
    // 通过指针的遍历访问到二叉树左侧的最底层
    while(cur) {
      stack.push(cur);
      cur = cur.left;
    }
    cur = stack.pop();
    res.push(cur.val);
    cur = cur.right;
  }
  return res;
}

后序遍历

function postorder(root) {
  if (!root) {
    return [];
  }
  const stack = [root];
  const res = [];
  while(stack.length) {
    let node = stack.pop();
    res.unshift(node.val);
    node.left && stack.push(node.left);
    node.right && stack.push(node.right);
  }
  return res;
}

三、迭代版统一写法(标记法)

通过标记法控制值出栈的顺序

前序遍历

function preorder(root) {
  if (!root) {
    return [];
  }
  const stack = [root];
  const res = [];
  while(stack.length) {
    let node = stack.pop();
    while(node === null) {
      res.push(stack.pop().val);
      continue;
    }
    node.right && stack.push(node.right);
    node.left && stack.push(node.left);
    stack.push(node);
    stack.push(null);
  }
  return res;
}

中序遍历

function inorder(root) {
  if (!root) {
    return [];
  }
  const stack = [root];
  const res = [];
  while(stack.length) {
    let node = stack.pop();
    while(node === null) {
      res.push(stack.pop().val);
      continue;
    }
    node.right && stack.push(node.right);
    stack.push(node);
    stack.push(null);
    node.left && stack.push(node.left);
  }
  return res;
}

后序遍历

function postorder(root) {
  if (!root) {
    return [];
  }
  const stack = [root];
  const res = [];
  while(stack.length) {
    let node = stack.pop();
    while(node === null) {
      res.push(stack.pop().val);
      continue;
    }
    stack.push(node);
    stack.push(null);
    node.right && stack.push(node.right);
    node.left && stack.push(node.left);
  }
  return res;
}

四、层序遍历

function levelOrder(root) {
  if (!root) {
    return [];
  }
  const stack = [root];
  const res = [];
  while (stack.length) {
    const length =  stack.length;
    const arr = [];
    for (let i = 0; i < length; i++){
      // 采用先进先出策略,防止后面插入的数据影响取值
      const node = stack.shift();
      arr.push(node.val);
      node.left && stack.push(node.left);
      node.right && stack.push(node.right);
    }
    res.push(arr);
  }
  return res;
}

总结:

不管是先中后序遍历还是层序遍历,左节点都会先输出

  1. 若采用后进先出策略(push,pop),则left在right下面执行,如前序遍历
  2. 若采用先进先出策略(push,shift),则left在right上面执行,如层序遍历
  3. 特殊情况:后序遍历中由于通过unshift改变了输出结果,所以即使采用了后进先出策略,left也在right上面执行