二叉树的遍历

135 阅读5分钟

遍历问题如果看不懂代码的话,多用示例代入走一圈就会慢慢理解!

前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序遍历。

递归

示例

image.png

输入: root = [1,2,3,4,5,6,7,null,null,8,9,null,null,null,10]
输出: [1,2,4,5,8,9,3,6,7,10]

思路

熟悉递归的话其实非常简单,就是每个结点都保证按照根左右的顺序就行

  • 递归边界:叶子结点
  • 递归式: 打印当前结点(root)的值->左子树前序遍历->右子树前序遍历

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var preorderTraversal = function(root) {
    var res=new Array();
    function preorder(rootnode){
       if(!rootnode) return;
       res.push(rootnode.val);
       preorder(rootnode.left);
       preorder(rootnode.right);
    }
    preorder(root);
    return res;
};

迭代

示例

image.png

输入: root = [1,2,3,4,5,6,7,null,null,8,9,null,null,null,10]
输出: [1,2,4,5,8,9,3,6,7,10]

思路

前序遍历需要按照根->左->右的顺序遍历,可以利用栈结构来控制输出序列的顺序。 观察示例:

  • 根结点出栈后左右儿子才出栈
  • 左子树比右子树先出栈
  • 获取到根结点才能获取到左右儿子 结合栈后进先出的特点,我们应该
  • 顺序:根结点出栈->右子树入栈->左子树入栈
  • 每个结点出栈时,将其对应的值push到结果数组中

代码

var preorderTraversal = function(root) {
    if(root===null) return [];
    const res=[];
    const stack=[];
    stack.push(root);
    while(stack.length>0){
        const temp=stack.pop();
        res.push(temp.val);
        if(temp.right) stack.push(temp.right);
        if(temp.left) stack.push(temp.left);
    }
    return res;
};

后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历

递归

示例

image.png

输入: root = [1,2,3,4,5,6,7,null,null,8,9,null,null,null,10]
输出: [4,8,9,5,2,6,10,7,3,1]

思路

跟前序一样,只是顺序变为左右根。

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function(root) {
    const res=[];
    const postorder=function(root){
        if(!root) return ;
        postorder(root.left);
        postorder(root.right);
        res.push(root.val);
    }
    postorder(root);
    return res;
};

迭代

示例

image.png

输入: root = [1,2,3,4,5,6,7,null,null,8,9,null,null,null,10]
输出: [4,8,9,5,2,6,10,7,3,1]

思路

后序遍历需要按照左->右->根的顺序遍历,同样尝试用栈结构处理。 观察示例:

  • 结果数组中左右儿子在根结点的前面
  • 左子树比右子树先出栈
  • 获取到根结点才能获取到左右儿子->根结点先入出栈 第一点和第三点要使得先从栈中取出来的根最后放入结果中,换一个角度想就是结果数组中根结点在左右子树的右边,js里就可以使用unshift()嘛!(记得左右子树入栈的顺序也要反一反)

代码

var postorderTraversal = function(root) {
    const res=[];
    if(!root) return res;//边界处理
    const stack=[];
    stack.push(root);
    while(stack.length){
        const temp=stack.pop();
        if(temp.left) stack.push(temp.left);
        if(temp.right) stack.push(temp.right);
        res.unshift(temp.val);
    }
    return res;
};

中序遍历

给定一个二叉树的根节点 root ,返回它的 中序 遍历

递归

示例

image.png

输入: root = [1,2,3,4,5,6,7,null,null,8,9,null,null,null,10]
输出: [4,2,8,5,9,1,6,3,7,10]

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var inorderTraversal = function(root) {
    const res=[];
    const inorder=function(root){
        if(!root) return;
        inorder(root.left);
        res.push(root.val);
        inorder(root.right);
    }
    inorder(root);
    return res;
};

迭代

示例

image.png

输入: root = [1,2,3,4,5,6,7,null,null,8,9,null,null,null,10]
输出: [4,2,8,5,9,1,6,3,7,10]

思路

中序遍历的顺序是左->根->右,左右子树在根的不同方向,不能用前两种遍历的基本思路:获取根再获取左右子树来做。

  • 需要先定位到最左边的结点:④ 路径为①-②-④
  • 将上述路径记录下来,回溯时便可到达④的父节点 ②
  • 从父结点点②即可获取④的兄弟结点,从而完成左根右的遍历顺序。

代码

var inorderTraversal = function(root) {
    const res=[];//结果数组
    const stack=[];//辅助栈
    let curr=root;//游标
    while(stack.length||curr){
        while(curr){
            stack.push(curr);
            curr=curr.left;//找到最左的叶子结点,把途径的结点都记录下来
        }
        curr=stack.pop();
        res.push(curr.val);
        curr=curr.right;//查看当前cur结点的右孩子
    }
    return res;
};

层序遍历

从上到下

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。【力扣102】

示例

image.png

输入: root = [3,9,20,null,null,15,7]
输出: [[3],[9,20],[15,7]]

思路

层序遍历很显然是先进先出,用队列来完成。需要注意的是每次循环时队列的长度就是当前层次的结点数。

代码

var levelOrder = function(root) {
    if(!root) return [];
    const queue=[];
    const res=[];
    queue.push(root);
    while(queue.length){
        let levelLength=queue.length;//当前层的结点数
        let levelVal=[];//用于存放当前层的结点值
        for(let i=0;i<levelLength;i++){//遍历当前层
            let temp=queue[0];//队头 即当前遍历到的结点
            levelVal.push(temp.val);
            if(temp.left) queue.push(temp.left);//将结点的左右子结点入队
            if(temp.right) queue.push(temp.right);
            queue.shift();// 将已遍历结点出队
        }
        res.push(levelVal);//当前层遍历完后放入结果,继续遍历下一层
    }
    return res;
};

从下到上

给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)【力扣107】

示例

image.png

输入: root = [3,9,20,null,null,15,7]
输出: [[15,7],[9,20],[3]]

思路

参考后序遍历的思路就很简单!

代码

var levelOrderBottom = function(root) {
    const res=[];
    const queue=[];
    if(!root) return res;
    queue.push(root);
    while(queue.length){
        const len=queue.length;
        const levelRes=[];
        for(let i=0;i<len;i++){
            let temp=queue.shift();
            levelRes.push(temp.val);
            if(temp.left) queue.push(temp.left);
            if(temp.right) queue.push(temp.right);
        }
        res.unshift(levelRes);
    }
    return res;
};