Java实现二叉树的遍历

二叉树

二叉树的条件:

  • 本身是有序树
  • 树中各个节点的度不能超过2
    在这里插入图片描述

存储结构

顺序存储结构:二叉树的顺序存储,指的是使用顺序表(数组)存储二叉树。需要注意的是,顺序存储只适用于完全二叉树。换句话说,只有完全二叉树才可以使用顺序表存储。因此,如果我们想顺序存储普通二叉树,需要提前将普通二叉树转化为完全二叉树。
链式存储结构:从树的根节点开始,将各个节点及其左右孩子使用链表存储。一般二叉树采用链式存储。
代码如下

 //Definition for a binary tree node.
 public class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode(int x) { val = x; }  //构造函数
 }

前序遍历

二叉树的几种遍历方式都是以根节点遍历的先后顺序来命名的,先序遍历根节点最先遍历,中序遍历根节点在中间,后序遍历根节点在最后,三种遍历方式采用递归方式实质都是深度优先搜索,下面是运用递归方法求得遍历后的集合序列。

前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。
实例:[1,2,4,null,3,5] 前序遍历输出:[1,2,3,4,5]
在这里多少时间图片描述
代码

class Solution {
    List<Integer> res =new ArrayList<Integer>();
    public List<Integer> preorderTraversal(TreeNode root) {        
        preTraverse(root);
        return res;
    }
    public void preTraverse(TreeNode r){
        if(r == null)
            return;
        res.add(r.val);     //先将根节点加入res集合中
        preTraverse(r.left);
        preTraverse(r.right);        
    }
}

利用非递归方式,栈来实现,以[1,2,4,null,3,5]为例,各个步骤如下:

  • 首先 1 先入栈,栈s中就一个TreeNode节点,s = [1]
  • 声明一个node节点,用于存放弹出的节点,由于此时 1 出栈,并将其的值加入res结果集中, node节点的右孩子节点4不为空,入栈,再将左孩子节点2入栈,此时栈中有两个元素[4,2],进入下次while循环
  • 由于节点2后入栈,所以将节点2弹出放入node节点中,将节点2加入res中,此时栈中就一个元素4,由于node节点左节点为空,右节点不为空,所以将右节点3入栈,此时栈中元素为[4,3],进入下一层循环
  • 同上,此时3出栈,加入res中,节点node左右孩子节点都为空,栈中就一个元素[4],进入下次循环
  • 同上,此时4出栈,加入res中,node左孩子节点5入栈,右孩子节点为空,栈中一个元素[5],继续下一次循环
  • 节点5出栈,加入结果res中,左右孩子节点为空,栈s也为空,while循环结束,返回结果集res

第一次循环:s = [1] -> s.pop(1) -> res.add(1) -> s.push(4),s.push(2) -> s = [4,2]
第二次循环:s = [4,2] -> s.pop(2) -> res.add(2) -> s.push(3) -> s = [4,3]
第三次循环:s = [4,3] -> s.pop(3) -> res.add(3) -> s = [4]
第四次循环:s = [4] -> s.pop(4) -> res.add(4) -> s.push(5) -> s = [5]
第五次循环:s = [5] -> s.pop(5) -> res.add(5) -> s==empty,end while, res = [1,2,3,4,5]

/**************栈 先入后出*************/
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {   
        List<Integer> res =new ArrayList<Integer>();
        if(root == null) return res;
        Stack<TreeNode> s = new Stack<TreeNode>();
        s.push(root);
        while(!s.isEmpty()){
            TreeNode node = new TreeNode();
            node = s.pop();
            res.add(node.val); 
            if(node.right != null)    //由于栈是后入先出,所以要先将每层的右边节点入栈
                s.push(node.right);
            if(node.left != null) 
                s.push(node.left);
        }
        return res;
    }    
}

中序遍历

中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。
实例:[1,2,4,null,3,5] 中序遍历输出:[2,3,1,5,4]
代码

class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        midTraverse(root);
        return res;
    }
    public void midTraverse(TreeNode r){
        if(r == null)
            return;
        
        midTraverse(r.left);
        res.add(r.val);     //在中间将根节点加入res集合中
        midTraverse(r.right);
    }
}

后序遍历

后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。
实例:[1,2,4,null,3,5] 后序遍历输出:[3,2,5,4,1]
代码

class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> postorderTraversal(TreeNode root) {
        lastTraverse(root);
        return res;
    }
    public void lastTraverse(TreeNode r){
        if(r == null)
            return;
        lastTraverse(r.left);
        lastTraverse(r.right);
        res.add(r.val);//在最后将val加入集合res中,根节点最后加入,先左后右
    }
}

层序遍历(BFS和DFS)

层序遍历就是逐层遍历树结构。

一、BFS

广度优先搜索是一种广泛运用在树或图这类数据结构中,遍历或搜索的算法。 该算法从一个根节点开始,首先访问节点本身。 然后遍历它的相邻节点,其次遍历它的二级邻节点、三级邻节点,以此类推。

当我们在树中进行广度优先搜索时,我们访问的节点的顺序是按照层序遍历顺序的。通常我们使用一个叫做队列 FIFO的数据结构来帮助我们做广度优先搜索。

下图为结果演示:
在这里插入图片描述
实例:[1,2,4,null,3,5] ,层序遍历输出:[[1],[2,4],[3,5]]
代码

/**********   广度优先搜索 利用队列   **********/
class Solution {   
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root == null) return new ArrayList<List<Integer>>(); //极端情况[]处理
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        Queue<TreeNode> queue = new LinkedList<>(); //利用队列来处理,先进先出
        queue.offer(root);   //将跟节点入队列
        while(!queue.isEmpty()){
            List<Integer> list = new ArrayList<>();   //在每层中定义一个list集合用于存放本层的遍历结果
            int size = queue.size();
            for(int i = 0;i < size ;i++){  //通过每层的size将各个节点的val加入list中
                TreeNode node = queue.poll(); //每层根据size依次出队列,先入先出
                list.add(node.val);
                if(node.left!=null)
                    queue.offer(node.left);  //将下一层的节点入队列
                if(node.right != null)
                    queue.offer(node.right);
            }
            res.add(list);  //本层遍历完后,将本层的集合list放入结果集合中          
        }
        return res;
    }
}

二、DFS

也可以用深度优先搜索,利用递归来实现。下图清楚表示了递归调用的过程。
过程图
在这里插入图片描述

代码

/**********   深度优先搜索  **********/
class Solution{
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    public List<List<Integer>> levelOrder(TreeNode root){
        if(root == null) return new ArrayList<List<Integer>>(); //极端情况[]处理
        DFSTraverse(root,0);  
        return res;
    }

    //DFS深度优先搜素,index表示层数
    public void DFSTraverse(TreeNode r,int index){
        if(res.size() <= index) //这一点很重要,如果res的size小于等于index 的值,说明需要增加size
            res.add(new ArrayList<Integer>());  
        res.get(index).add(r.val);  //将r的val加入index对应的层数中
        if(r.left != null)
            DFSTraverse(r.left,index+1);  //递归非空的孩子节点
        if(r.right != null)
            DFSTraverse(r.right,index+1);
    }
}

复杂度分析

两种方法的复杂度相同
时间复杂度:O(N),因为每个节点恰好会被运算一次。
空间复杂度:O(N),保存输出结果的数组包含 N 个节点的值。

参考
Java Queue详解:http://tutorials.jenkov.com/java-collections/queue.html
https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/er-cha-shu-de-ceng-ci-bian-li-by-leetcode/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值