xiaobenchi

导航

二叉树的遍历

递归及树的遍历

递归

递归三要素:

  1. 确定递归函数的参数和返回值:确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数,并且还要明确每次递归的返回值是什么,进而确定递归函数的返回类型。
  2. 确定终止条件:写完了递归算法,运行的时候经常会遇到栈溢出的错误,如果没写终止条件或者终止条件写的不对,操作系统的内存栈必然就会溢出。
  3. 确定单层递归的逻辑:确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程

以前序遍历为例:

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

    void traversal(TreeNode cur,ArrayList value)
    
  2. 确定终止条件:在递归过程中,如何判断递归结束,就是当前遍历的节点为空,那么结束本层递归

    if(cur == null) return;
    
  3. 确定单层递归的逻辑:前序遍历为中左右的顺序

    value.add(cur.val);
    traversal(cur.left,value);
    traversal(cur.right,value);
    

二叉树的遍历

前序遍历:

  1. 递归实现

    public void preorderTraversal(TreeNode root){
        List<Integer> result = new ArrayList<>();
        preorder(root,result);
        return result;
    }
    public void preorder(TreeNode root,List<Integer> result){
        if(root == null) return;
        result.add(root.val);
        preorder(root.left,result);
        preorder(root.right,result);
    }
    
  2. 迭代实现

    前序遍历是中左右,每次先处理中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。这样保证出栈顺序为中左右。

    注意:空节点不入栈

    class Iterator1{
        public List<Integer> preorderTraversal(TreeNode root){
            List<Integer> result = new ArrayList<>();
            if(root == null){
                return result;
            }
            Stack<TreeNode> stack = new Stack<>();
            stack.push(root);
            while(!stack.isEmpty()){
                TreeNode node = stack.pop();
                result.add(node.val);
                if(node.right != null){
                    stack.push(node.right);
                }
                if(node.left != null){
                    stack.push(node.left);
                }
            }
            return result;
        }
    }
    

中序遍历:

  1. 递归实现

    public void inorderTraversal(TreeNode root){
        List<Integer> result = new ArrayList<>();
        preorder(root,result);
        return result;
    }
    public void inorder(TreeNode root,List<Integer> result){
        if(root == null) return;
        preorder(root.left,result);
        result.add(root.val);
        preorder(root.right,result);
    }
    
  2. 迭代实现

    中序遍历为左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(存放节点数值),这就是的处理顺序和访问顺序不一致。

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

后序遍历:

  1. 递归实现

    public void postorderTraversal(TreeNode root){
        List<Integer> result = new ArrayList<>();
        preorder(root,result);
        return result;
    }
    public void postorder(TreeNode root,List<Integer> result){
        if(root == null) return;
        preorder(root.left,result);
        preorder(root.right,result);
         result.add(root.val);
    }
    
    
  2. 迭代实现

    前序遍历是中左右,后序遍历是左右中,只需要调整前序遍历的顺序然后再翻转结果数组,输出的结果就是左右中了。

    class Iterator3{
        public List<Integer> preorderTraversal(TreeNode root){
            List<Integer> result = new ArrayList<>();
            if(root == null){
                return result;
            }
            Stack<TreeNode> stack = new Stack<>();
            stack.push(root);
            while(!stack.isEmpty()){
                TreeNode node = stack.pop();
                result.add(node.val);
                if(node.left != null){
                    stack.push(node.left);
                }
                if(node.right != null){
                    stack.push(node.right);
                }
            }
            Collections.reverse(result);
            return result;
        }
    }
    

层序遍历:

  1. 递归实现

    class Recursion{
        public List<List<Integer>> reList = new ArrayList<List<Integer>>();
        
        public List<List<Integer>> levelOrder(TreeNode root){
            checkFun01(root);
            return reList;
        }
        
        public void checkFun01(TreeNode node, Integer deep){
            if(node == null) return;
            deep++;
            
            if(reList.size() < deep){
                //当层级增加时,list的Item也增加,利用list的索引值进行层级界定
                List<Integer> item = new ArrayList<Integer>();
                reList.add(item);
            }
            resList.get(deep - 1).add(node.val);
            checkFun01(node.left,deep);
            checkFun02(node.right,deep);
        }
    }
    
  2. 迭代实现

    //借助队列
    class Recursion{
        public List<List<Integer>> reList = new ArrayList<List<Integer>>();
        
        public List<List<Integer>> levelOrder(TreeNode root){
            checkFun02(root);
            return reList;
        }
    public void checkFun02(TreeNode node){
        if(node == null) return;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(node);
        
        while(!que.isEmpty()){
            List<Integer> itemList = new ArrayList<Integer>();
            int len = que.size();
            
            while(len > 0){
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);
                
                if(tmpNode.left != null) que.offer(tmpNode.left);
                if(tmpNode.right != null) que.offer(tmpNode.right);
                len--;
            }
            reList.add(itemList);
        }
    }
    }
    

根据遍历序列构造二叉树

从中序与后序遍历序列构造二叉树

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return buildTree1(inorder, 0, inorder.length, postorder, 0, postorder.length);
    }
    public TreeNode buildTree1(int[] inorder, int inLeft, int inRight,
                               int[] postorder, int postLeft, int postRight) {
        // 没有元素了
        if (inRight - inLeft < 1) {
            return null;
        }
        // 只有一个元素了
        if (inRight - inLeft == 1) {
            return new TreeNode(inorder[inLeft]);
        }
        // 后序数组postorder里最后一个即为根结点
        int rootVal = postorder[postRight - 1];
        TreeNode root = new TreeNode(rootVal);
        int rootIndex = 0;
        // 根据根结点的值找到该值在中序数组inorder里的位置
        for (int i = inLeft; i < inRight; i++) {
            if (inorder[i] == rootVal) {
                rootIndex = i;
                break;
            }
        }
        // 根据rootIndex划分左右子树
        root.left = buildTree1(inorder, inLeft, rootIndex,
                postorder, postLeft, postLeft + (rootIndex - inLeft));
        root.right = buildTree1(inorder, rootIndex + 1, inRight,
                postorder, postLeft + (rootIndex - inLeft), postRight - 1);
        return root;
    }
}

从前序与中序遍历序列构造二叉树

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return helper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }

    public TreeNode helper(int[] preorder, int preLeft, int preRight,
                           int[] inorder, int inLeft, int inRight) {
        // 递归终止条件
        if (inLeft > inRight || preLeft > preRight) return null;

        // val 为前序遍历第一个的值,也即是根节点的值
        // idx 为根据根节点的值来找中序遍历的下标
        int idx = inLeft, val = preorder[preLeft];
        TreeNode root = new TreeNode(val);
        for (int i = inLeft; i <= inRight; i++) {
            if (inorder[i] == val) {
                idx = i;
                break;
            }
        }

        // 根据 idx 来递归找左右子树
        root.left = helper(preorder, preLeft + 1, preLeft + (idx - inLeft),
                         inorder, inLeft, idx - 1);
        root.right = helper(preorder, preLeft + (idx - inLeft) + 1, preRight,
                         inorder, idx + 1, inRight);
        return root;
    }
}

注意:前序和后序遍历序列无法唯一确定一棵二叉树,因为没有中序遍历无法确定左右部分,也就无法进行分割。

posted on 2022-05-04 15:19  小迟在努力  阅读(20)  评论(0)    收藏  举报