Javascript 二叉树、N叉树的遍历

是一种经常用到的数据结构,用来模拟具有树状结构性质的数据集合。
树里的每一个节点有一个根植和一个包含所有子节点的列表。从图的观点来看,树也可视为一个拥有N 个节点N-1 条边的一个有向无环图。

1.二叉树遍历

二叉树是一种更为典型的树树状结构。如它名字所描述的那样,二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”,👇是一种二叉树节点的定义。

  function TreeNode(val) {
      this.val = val;
      this.left = this.right = null;
  }

一棵二叉树可以按照前序中序后序或者层序来进行遍历。所谓前、中、后,不过是根的顺序,也可以称为先根遍历、中根遍历、后根遍历。

  • 前序遍历 - 首先访问根节点,然后遍历左子树,最后遍历右子树:
    前序
  • 中序遍历 - 首先遍历左子树,然后访问根节点,最后遍历右子树;
    中序
  • 后序遍历 - 首先遍历左子树,然后遍历右子树,最后访问根节点;
    后序
  • 层序遍历 - 按照从左到右的顺序,逐层遍历各个节点;
    后序
    N叉树的中序遍历没有标准定义,中序遍历只有在二叉树中有明确的定义。
    二叉树的遍历示例:
    给定一个二叉树,返回它的前序遍历。
    示例:
    输入:[1,null,2,3,5,7,9] 二叉树示例
    输出: [1,2,3,7,9,5],此时输入的数组被格式化成以下的二叉树结构:
const root = {
  val: 1,
  left: null,
  right: {
    val: 2,
    left: {
      val: 3,
      left: {
        val: 7
      },
      right: {
        val: 9
      }
    },
    right: {
      val: 5
    }
  }
}

递归解法1,初始结题思路未想到创建闭包函数inner:

	/**
	 * @param {TreeNode} root
	 * @return {number[]}
	 */
	function preorderTraversal(root) {
	    if (!root) {
	        return []
	    }
	    const res = []
	    const inner = (node) => {
	        if (!node) return
	        const {val, left, right} = node
	        res.push(val) 
	    inner(left)
	    inner(right)
	    }
	    inner(root)
	    return res
	};

递归解法2,用到了扩展操作符,更简洁:

/**
 - @param {TreeNode} root
 - @return {number[]}
 */
var preorderTraversal = function(root) {
    if (root) {
        return [root.val, ...preorderTraversal(root.left), ...preorderTraversal(root.right)]
    } else {
        return []
    }
};

迭代解法1:
利用栈来记录遍历的过程,实际上,递归就使用了调用栈,所以这里我们可以使用栈来模拟递归的过程

  • 首先根入栈
  • 将根节点出栈,将根节点值放入结果数组中
  • 然后遍历左子树、右子树,因为栈是先入后出,所以,我们先右子树入栈,然后左子树入栈
  • 继续出栈(左子树被出栈)…….
    依次循环出栈遍历入栈,直到栈为空,遍历完成。
    Javascript中虽没有栈和队列的定义,但是push和pop组合使用可以模拟栈push和shift组合使用可以模拟队列
// 前序遍历
const preorderTraversal = (root) => {
    const list = [];
    const stack = [];
    
    // 当根节点不为空的时候,将根节点入栈
    if(root) stack.push(root)
    while(stack.length > 0) {
	    // pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组。
        const curNode = stack.pop()
        // 第一步的时候,先访问的是根节点
        // push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。
        list.push(curNode.val)
        
        // 我们先打印左子树,然后右子树
        // 所以先加入栈的是右子树,然后左子树
        if(curNode.right !== null) {
            stack.push(curNode.right)
        }
        if(curNode.left !== null) {
            stack.push(curNode.left)
        }
    }
    return list
}

迭代解法参考

2.N叉树遍历

如果想把二叉树遍历转换为N叉树遍历,我们只需把如下表述:

遍历左子树… 遍历右子树…

转变为:

对于每个子节点: 通过递归调用遍历函数来遍历以该子节点为根的子树

N叉树遍历示例(以四叉树为例):

A
B
C
D
E
F
G
H
J
K

1.前序遍历
前序遍历首先访问根节点,然后逐个遍历以其子节点为根的子树。
结果: A->B->C->D->E->F->G->J->K->H.

2.后序遍历
后序遍历首先逐个遍历以根节点(A)的子节点为根的子树,最后访问根节点。
结果: C->D->B->E->J->K->G->H->F->A.

3.层序遍历
N叉树的层序遍历与二叉树的一致。从根节点开始,按照从左至右顺序逐层遍历。
结果: A->B->E->F->C->D->G->H->J->K.
N叉树遍历示例:
三叉树遍历
递归解法:

/**
 * // Node定义
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children;
 * };
 */
/**
 * @param {Node} root
 * @return {number[]}
 */
var preorder = function(root) {
  if (!root) {
    return []
  }
  const res = []
  const inner = (node) => {
    if (!node) return
    const {val, children} = node
    res.push(val)
    // forEach方法与map方法很相似,也是对数组的所有成员依次执行参数函数。但是,forEach方法不返回值,只用来操作数据。
    // 这就是说,如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法。
    // forEach的用法与map方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组。
    children.forEach(child => {
      inner(child)
    })
  }
  inner(root)
  return res
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值