二叉树遍历

        

二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法是采用队列。

深度优先遍历:
对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。
要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历、中序遍历、后序遍历。具体说明如下:

先序遍历:根、左、有
中序遍历:左、根、右
后序遍历:左、右、根

广度优先遍历:
又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。

深度优先搜素算法:
不全部保留结点,占用空间少;有回溯操作(即有入栈、出栈操作),运行速度慢。
通常深度优先搜索法不全部保留结点,扩展完的结点从数据库中弹出删去,这样,一般在数据库中存储的结点数就是深度值,因此它占用空间较少。所以,当搜索树的结点较多,用其它方法易产生内存溢出时,深度优先搜索不失为一种有效的求解方法。

广度优先搜索算法:
保留全部结点,占用空间大; 无回溯操作(即无入栈、出栈操作),运行速度快。
广度优先搜索算法,一般需存储产生的所有结点,占用的存储空间要比深度优先搜索大得多,因此,程序设计中,必须考虑溢出和节省内存空间的问题。但广度优先搜索法一般无回溯操作,即入栈和出栈的操作,所以运行速度比深度优先搜索要快些。
 

144. 二叉树的前序遍历https://leetcode-cn.com/problems/binary-tree-preorder-traversal/

94. 二叉树的中序遍历https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ 145. 二叉树的后序遍历https://leetcode-cn.com/problems/binary-tree-postorder-traversal/

二叉树的前中后序遍历 

二叉树节点的定义

class TreeNode{
    int val;
    TreeNode left;
    TreeNode right;

    public TreeNode() {
    }
    public TreeNode(int val) {
        this.val = val;
    }
    public TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

1、递归

前序

 //递归实现
    public List<Integer> preorderTraversal(TreeNode root){
        //创建result 用来保存输出数据
        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);

    }

中序

//递归实现
    public List<Integer> inorderTraversal(TreeNode root){
        List<Integer> result = new ArrayList<>();
        inOrder(root,result);
        return result;
    }
    public void inOrder(TreeNode root,List<Integer> result){
        if(root==null){
            return;
        }
        //左
        inOrder(root.left,result);
        //根
        result.add(root.val);
        //右
        inOrder(root.right,result);
    }

后序

 //递归实现
    public List<Integer> postorderTraversal(TreeNode root){
        List<Integer> result = new ArrayList<>();
        postOrder(root,result);
        return result;
    }
    public void postOrder(TreeNode root,List<Integer> result){
        if(root==null){
            return;
        }
        //左
        postOrder(root.left,result);
        //右
        postOrder(root.right,result);
        //根
        result.add(root.val);
    }

2、迭代

前序   

                显式的实现栈

//迭代
    public List<Integer> preorderTraversal(TreeNode root){
        List<Integer> res=new ArrayList<>();
        if(root==null){
            return res;
        }
        Deque<TreeNode> stack=new LinkedList<>();
        TreeNode node=root;
        while (!stack.isEmpty()||node!=null){
            while (node!=null){
                res.add(node.val);
                stack.push(node);
                node=node.left;
            }
            node=stack.pop();
            node=node.right;
        }
        return res;
    }

中序   

分析一下为什么刚刚写的前序遍历的代码,不能和中序遍历通用呢,因为前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,所以刚刚才能写出相对简洁的代码,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点。

那么再看看中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的

中序的迭代法较为复杂,因为

前序遍历中访问节点(遍历节点)和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!

 //迭代  难理解
        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){
                    //一直将左侧节点入栈
                    //直到cur==null
                    stack.push(cur);
                    cur=cur.left;
                }else {
                    cur=stack.pop();
                    result.add(cur.val);
                    cur=cur.right;
                }
            }
            return result;

        }

后序

再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:

//迭代
    //先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,
    // 就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了
    // 中左右(前序遍历)--调整-->中右左---->翻转---->左右中
    public List<Integer> postorderTraversal(TreeNode root){
        List<Integer> result = new ArrayList<>();
        if(root==null){
            return result;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        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;

    }

102. 二叉树的层序遍历https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

层次遍历使用队列

迭代法

//广度优先遍历应该使用队列进行模拟    迭代法
    public List<List<Integer>> levelOrder(TreeNode root){
        //定义返回值
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        //如果为空 直接返回
        if(root==null){
            return res;
        }
        //队列构建
        Queue<TreeNode> queue = new LinkedList<>();
        //第一层 根节点入队
        queue.offer(root);
        while (!queue.isEmpty()){
            //保存每一层结果
            List<Integer> level = new ArrayList<>();
            //保存该层节点个数
            int size = queue.size();
            //遍历该层节点
            while (size>0){
                //出队
                TreeNode node = queue.poll();
                //保存层级结果
                level.add(node.val);
                //追加左子节点
                if(node.left!=null){
                    queue.offer(node.left);
                }
                //追加右子节点
                if(node.right!=null){
                    queue.offer(node.right);
                }
                size--;
            }
            //追加本层结果到输出
            res.add(level);
        }
        return res;
    }

递归法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值