算法训练day10 | php | 二叉树递归遍历, 迭代遍历,统一迭代

 先发二叉树的前、中、后序遍历的三个题目,归纳的方法在下面。

1、力扣题144. 二叉树的前序遍历

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

示例 1:

输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2: 

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

输入:root = [1,2]
输出:[1,2]

示例 5:

输入:root = [1,null,2]
输出:[1,2]

提示:

  • 树中节点数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

2、力扣题94. 二叉树的中序遍历

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

示例 1:

输入:root = [1,null,2,3]
输出:[1,3,2]

3、力扣题145. 二叉树的后序遍历

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

示例 1:

输入:root = [1,null,2,3]
输出:[3,2,1]

一、递归遍历

        1、前序遍历

        二叉树的前序遍历是中左右,在递归的处理逻辑中就是先处理输入的节点,再递归该节点的左节点和右节点。因为该题是返回存放遍历的值的数组,所以需设置一个全局数组用以存放二叉树的值。

        递归参数是二叉树的节点,单层递归逻辑是先将传入的二叉树节点的值放入数组,再递归左右节点,终止条件是传入的节点为null。

class Solution {
    private $result = [];

    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function preorderTraversal($root) {
        if($root == null) 
            return [];
        $this->result[] = $root->val;
        $this->preorderTraversal($root->left);
        $this->preorderTraversal($root->right);
        return $this->result;
    }
}

        2、后序遍历

        同上逻辑类似,只是递归左右节点和处理中节点的顺序不同。

class Solution {
    private $result = [];
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function postorderTraversal($root) {
        if(!$root) {
            return [];
        }
        $this->postorderTraversal($root->left);
        $this->postorderTraversal($root->right);
        $this->result[] = $root->val;
        return $this->result;
    }
}

        3、中序遍历

class Solution {
    private $result = [];
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function inorderTraversal($root) {
        if($root == null) 
            return [];
        $this->inorderTraversal($root->left);
        $this->result[] = $root->val;
        $this->inorderTraversal($root->right);
        return $this->result;
    }
}

二、迭代遍历

       1、前序遍历

        这里的迭代遍历是使用栈来进行遍历,先将root节点放入栈中,再将root节点取出放入数组,将该root节点的右左节点放入栈中,因为是前序遍历:中左右,故放入的时候是按照右、左的顺序。然后再不断重复此过程,每从栈中取出一个节点,如果该节点的右左节点存在,都要把该节点的右左节点放入。

        这里是利用了前序遍历的特性,因为是中左右的顺序,每次在把中节点的值放入数组时,就可以顺便通过中节点来获取其左右节点了。后序遍历(左右中)也可以用与这个类似的方法。

        但是中序遍历就不可以,因为中序遍历中节点是在中间的,不好处理,无法判断哪个是中节点。

class Solution {
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function preorderTraversal($root) {
        $stack = new splStack();
        $result = [];
        $stack->push($root);
        
        while(!$stack->isEmpty()) {
            $cur = $stack->pop();
            $result[] = $cur->val;
            if($cur->right) {
                $stack->push($cur->right);
            }
            if($cur->left) {
                $stack->push($cur->left);
            }
        }

        return $result;
    }
}
控制台

      2、后序遍历

        后序遍历的逻辑和上面前序的类似,前序是按中右左的顺序进行的,后序就要按中左右的顺序进行,最后得到的数组就是按中右左的顺序遍历的,把数组反转一下就得到了左右中也就是后序遍历了。

class Solution {
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function postorderTraversal($root) {
        $stack = new splStack();
        $result = [];
        $stack->push($root);

        while(!$stack->isEmpty()) {
            $cur = $stack->pop();
            $result[] = $cur->val;
            if($cur->left) {
                $stack->push($cur->left);
            }
            if($cur->right) {
                $stack->push($cur->right);
            }
        }
        //反转数组
        $result = array_reverse($result);
        return $result;
    }
}
控制台

        3、中序遍历

        上面已经说过了中序不能用以上的方法。中序遍历:左中右 要从左边开始遍历起,但题目又是从根节点起步,所以需要一个指针,一层层到达最左的节点,从这个节点开始处理。

        但此时指针在最底层,又如何把指针重新指向父节点呢?那么就需要在指针遍历到最左这个节点的过程中,把遍历到的节点全都入栈。在指针终于遍历到最左的节点时,把该节点出栈、放入数组,如果该节点的右节点存在,就按相同的逻辑遍历它的右节点;如果右节点也不在,就要回溯到该节点的父节点进行遍历,此时栈的顶端元素正是该节点的父节点,所以将父节点出栈处理,再重复遍历右节点、遍历父节点的过程。

        里面的单层处理过程正是左中右的顺序:

        第一层:遍历到最左(此时左节点为空),处理这个最左节点(相当于处理中节点),处理该节点的右节点。

        第二层:处理完最左节点的右节点后,指针回溯指向的最左节点的父节点也是一个中节点(此时左节点相当于这个节点的左子树,已经在上一层中遍历处理过了),再处理该节点的右节点。

class Solution {
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function inorderTraversal($root) {
        $stack = new splStack();
        $result = [];
        $cur = $root;

        while(!$stack->isEmpty() || $cur) {
            if($cur) {
                $stack->push($cur);
                $cur = $cur->left;
            } else {
                $cur = $stack->pop();
                $result[] = $cur->val;
                $cur = $cur->right;
            }
        }
        return $result;
    }
}

三、统一遍历

        1、前序遍历

        上面的迭代遍历的方法,只适用于前后序遍历,而这个统一遍历方法是前中后序都适用的。依旧是使用栈来存放节点。每个节点会入栈两次,第一次用于获取该节点的左右节点,第二次用于将该节点的值存入数组。节点第二次入栈后,会将一个null入栈,将null放在该节点后面,用于标识。

        前中后序的区别在于,第一次把节点出栈时、再把该节点和其左右节点入栈的顺序。如果是前序遍历,就把中左右反着来,按右左中的顺序入栈(这里的中节点是第二次入栈,所以还需加一个null),其他同理。

class Solution {
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function preorderTraversal($root) {
        $stack = new splStack();
        $result = [];
        if($root) {
            $stack->push($root);
        }
        
        while(!$stack->isEmpty()) {
            $cur = $stack->top();

            if($cur != null) {
                $stack->pop();

                if($cur->right) $stack->push($cur->right);

                if($cur->left) $stack->push($cur->left);

                $stack->push($cur);
                $stack->push(null);
            } else {
                $stack->pop();
                $temp = $stack->pop();
                $result[] = $temp->val;
            }

        }

        
        return $result;
    }
}

        2、后序遍历

        与上面的前序遍历类似,只是换了下位置

class Solution {
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function postorderTraversal($root) {
        $stack = new splStack();
        $result = [];
        if($root) $stack->push($root);

        while(!$stack->isEmpty()) {
            $cur = $stack->top();
            if($cur != null) {
                $stack->push(null);

                if($cur->right) $stack->push($cur->right);

                if($cur->left) $stack->push($cur->left);
            } else {
                $stack->pop();
                $temp = $stack->pop();
                $result[] = $temp->val;
            }
        }
        
        return $result;
    }
}

        3、中序遍历

        同上

class Solution {
    /**
     * @param TreeNode $root
     * @return Integer[]
     */
    function inorderTraversal($root) {
        $stack = new splStack();
        $result = [];
        if($root) {
            $stack->push($root);
        }

        while(!$stack->isEmpty()) {
            $cur = $stack->top();
            if($cur != null) {
                $stack->pop();
                if($cur->right) $stack->push($cur->right);

                $stack->push($cur);
                $stack->push(null);

                if($cur->left) $stack->push($cur->left);
            } else {
                $stack->pop();
                $temp = $stack->pop();
                $result[] = $temp->val;
            }
        }
        return $result;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值