天天看点

4.二叉树的先序、中序以及后序遍历的递归写法与非递归写法(LeetCode第94、144、145题)

一、递归法

这次我们要好好谈一谈递归,为什么很多同学看递归算法都是“一看就会,一写就废”。

主要是对递归不成体系,没有方法论,每次写递归算法 ,都是靠玄学来写代码,代码能不能编过都靠运气。

本篇将介绍前后中序的递归写法,一些同学可能会感觉很简单,其实不然,我们要通过简单题目把方法论确定下来,有了方法论,后面才能应付复杂的递归。

这里帮助大家确定下来递归算法的三个要素。每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!

1.确定递归函数的参数和返回值:

确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

2.确定终止条件:

写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

3.确定单层递归的逻辑:

确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

以下以前序遍历为例:

**确定递归函数的参数和返回值:**因为要打印出前序遍历节点的数值,所以参数里需要传入vector在放节点的数值,除了这一点就不需要在处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下:

void traversal(TreeNode cur, vector& vec)*

**确定终止条件:**在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:

if (cur == NULL) return;

**确定单层递归的逻辑:**前序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:

vec.push_back(cur->val); // 中

traversal(cur->left, vec); // 左

traversal(cur->right, vec); // 右

单层递归的逻辑就是按照中左右的顺序来处理的,这样二叉树的前序遍历,基本就写完了,在看一下完整代码:

二、先序遍历的递归写法

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
         
         ArrayList<Integer> list = new ArrayList<>();
         preorder(root,list);
         return list;
    }

    private void preorder(TreeNode root,List<Integer> list)
    {
        if(root == null)
        {
            return;
        }
        list.add(root.val);
        preorder(root.left,list);
        preorder(root.right,list);
    }
}
           

三、先序遍历的非递归写法

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
         
         List<Integer> list = new ArrayList<>();
         Stack<TreeNode> stack = new Stack<>();
         while(root != null || !stack.isEmpty())
         {
             while(root != null)
             {
                 list.add(root.val);
                 stack.push(root);
                 root = root.left;
             }
             TreeNode cur = stack.pop();
             root = cur.right;
         }
         return list;
    }
}
           

四、中序遍历的递归写法

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
         
         ArrayList<Integer> list = new ArrayList<>();
         inorder(root,list);
         return list;
    }

    public void inorder(TreeNode root,List<Integer> list)
    {
        if(root == null)
        {
            return;
        }
        inorder(root.left,list);
        list.add(root.val);
        inorder(root.right,list);
    }
}
           

五、中序遍历的非递归写法

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
         
         ArrayList<Integer> list = new ArrayList<>();
         Stack<TreeNode> stack = new Stack<>();
         while(root != null || !stack.isEmpty())
         {
             while(root != null)
             {
                 stack.push(root);
                 root = root.left;
             }
             root = stack.pop();
             list.add(root.val);
             root = root.right;
         }
         return list;
    }
}
           

六、后序遍历的递归写法

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
         
         ArrayList<Integer> list = new ArrayList<>();
         postorder(root,list);
         return list;
    }

    private void postorder(TreeNode root,List<Integer> list)
    {
        if(root == null)
        {
            return;
        }
        postorder(root.left,list);
        postorder(root.right,list);
        list.add(root.val);
    }
}
           

七、后序遍历的非递归写法

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
         
         ArrayList<Integer> list = new ArrayList<>();
         Stack<TreeNode> stack = new Stack<>();
         while(root != null || !stack.isEmpty())
         {
             while(root != null)
             {
                 list.add(root.val);
                 stack.push(root);
                 root = root.right;
             }
             TreeNode cur = stack.pop();
             root = cur.left;
         }
         Collections.reverse(list);
         return list;
    }
}