死磕二叉树(一)

定义

二叉树是每个结点最多有两个子树的树结构。

                 3
                / 
               4   5
              / 
             1   2

因为二叉树的左右子树都具有类似的结构,二叉树的题目往往和递归是关联的。

从二叉树的遍历谈起

遍历二叉树的每一个点,通常有三种方式,中序,前序,后序的方式,对应上图的树来说,结果是:

3 4 1 2 5 // 前序
1 4 2 3 5 // 中序
1 2 4 5 3 // 后序

前、后、中都是相对于根节点的位置来定义的。

首先来看:

94. 二叉树的中序遍历

深度优先

通过递归的方式很容易构造,关键在于如何选出递归结束的条件,比如当前节点是空值时,需要return

方法一:

class Solution {
    List<Integer> list = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        helper(root);
        return list;
    }
    
    void helper(TreeNode root){
        if(root == null) return;
        else {
            if(root.left != null) helper(root.left);
            list.add(root.val);
            if(root.right != null) helper(root.right);
        }
    }
}

方法二:

除了递归的方式,还有迭代法,迭代中序遍历时,需要借助一个栈,栈的功能是存储上一个节点,好让处理完左子树的时候,可以回头访问根节点,根据这个思路,我们可以在访问开始前沿着左子树一路访问到null,然后再访问上一个节点(此时可以看成根节点),最后再访问右节点:

class Solution {
    public List<Integer> inorderTraversal2(TreeNode root){
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();

        while (root != null || !stack.isEmpty()){
            if(root != null){
                stack.add(root);
                root = root.left;
            } else {
                root = stack.pop();
                list.add(root.val);
                root = root.right;
            }
        }
        return list;
    }
}

144. 二叉树的前序遍历

有了中序遍历的基础,前序遍历的递归形式可以很容易的写出来:

方法一:

class Solution {
    List<Integer> list = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        helper(root);
        return list;
    }
    
    void helper(TreeNode root){
        if(root == null) return;
        else {
            list.add(root.val);
            if(root.left != null) helper(root.left);
            if(root.right != null) helper(root.right);
        }
    }
}

方法二:

使用迭代法依然时要借助一个栈来实现,这里栈的功能和中序遍历类似,都是要存储上一个节点,便于访问完左子树后再访问右子树。不同的是,访问下一个左节点之前,就需要把节点的值保存到结果中去:

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

        return res;
    }
}

145. 二叉树的后序遍历

前序遍历和后序遍历之间的的关系:

前序遍历顺序为:根 -> 左 -> 右

后序遍历顺序为:左 -> 右 -> 根

前序遍历的时候,每个节点的值都会插入到结果链表的尾部res.add(root.val)

如果每次都选择插入到结果链表的头部,那么遍历顺序会变为:右 -> 左 -> 根

此时转换一下左右子树的先后顺序,就得到我们想要的结果:左 -> 右 -> 根

迭代法:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> res = new LinkedList<Integer>();
        Stack<TreeNode> stack = new Stack<>();
        
        while (!stack.isEmpty() || root != null){
            if(root != null){
                stack.add(root);
                res.addFirst(root.val);
                root = root.right;
            }else{
                root = stack.pop();
                root = root.left;
            }
        }

        return res;
    }
}

原文地址:https://www.cnblogs.com/punnpkin/p/13574323.html