栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java实现二叉树的相关问题(1)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java实现二叉树的相关问题(1)

学习内容:

数据结构——二叉树

V表示根结点,L表示左孩子(左子树),R表示右孩子(右子树)

持续更新内容之树结构算法,主要是分析与解决相关二叉树的问题,主要包括有
1.树的遍历(先序,中序,后序,层序(BFS))
2.树的深度计算
3.对称二叉树的判断
4.BST(二叉搜索树)的判断
5.二叉树的路径搜索


树的遍历:

树的遍历的名称是由根节点遍历的顺序来确定的,如下详解。

1.先序遍历

根节点先遍历,然后是左树,然后是右树,无论是从整体看,还是从局部的子树来看都是符合此顺序。

2.中序遍历

左|中|右的遍历方式,即先遍历左树,然后是中间的根结点。

3.后序遍历

左|右|中的遍历顺序,即先遍历左树,然后是右树,最后是根结点,无论是局部还是整体来看,均是如此

4.层序遍历

顾名思义,一层一层的遍历树。

代码实现与学习

二叉树与之前所学的内容有着一个很大不同的地方,便是局部与整体的结构都是相似的,非常适合使用递归实现相关问题的解决,当然只是掌握递归的方法是不够,还需要掌握使用遍历的方法解决相关问题,二叉树问题中使用迭代解决,一般需要引入两种数据结构,栈(stack),队列(queue),针对问题的不同选择不同特点的结构,栈——先进后出,队列——先进先出。
创建一棵树,用于实现以上算法:

TreeNode tr = new TreeNode(1);
tr.right = new TreeNode(2);
tr.left = new TreeNode(3);
tr.left.right = new TreeNode(4);
tr.right.left = new TreeNode(5);
tr.right.right = new TreeNode(6);
1.先序遍历Java实现

首先是递归实现如下:

    private static void preOrder(TreeNode root) {
        if (root == null)  //为空时说明迭代应该终止了
            return;

        System.out.print(root.val + " ");   //根结点输出
        preOrder(root.left);				//左树进入迭代
        preOrder(root.right);				//右树进入迭代
    }

//输出结果
1 3 4 2 5 6

分析: 迭代较为关键的一点是中止条件,也就是上面的为空时,返回void。先序遍历的方式是,中|左|右,也就是说中间的先遍历,故先输出root.val,然后进入左中遍历,先序遍历无论是局部还是整体,都是符合上述的遍历顺序,故当左树遍历结束后,会进入右树。

迭代实现:
若用迭代的方式实现,则需要引入栈结构,代码如下:

    private static void preOrder01(TreeNode root) {
        if (root ==  null)
            return;

        Stack stack = new Stack<>();
        stack.push(root);    //根节点入栈,使得判定条件能够触发

        while (!stack.isEmpty()){
            TreeNode cur = stack.pop();   //弹出栈顶,即当前结点
            System.out.print(cur.val + " ");
            if (cur.right != null)		//若右结点存在,右结点进入
                stack.push(cur.right);
            if (cur.left != null)		//若左结点存在,左结点进入
                stack.push(cur.left);
        }
    }
    
//输出结果
1 3 4 2 5 6

与上面递归不同的是,右结点先进入,然后是左结点进入,这是与栈结构的特点有关——先进后出,若右结点新进入,会被压栈底,输出的时候则会在最后输出,流程如下:

入栈判断是,看是否存在右左子树。图中只画了一个入栈过程,没有全部画出。

2.中序遍历Java实现

左|中|右遍历方式,先左树遍历完,然后根结点,最后进入右树,递归实现如下:

    private static void inOrder(TreeNode root){
        if (root == null)
            return;
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }
    
//运行结果
3 4 1 5 2 6

开始直接找到左树的叶结点,然后向上,遍历完左树,进入右树遍历。

迭代实现:
不妨考虑递归是如何实现的,将递归遍历的顺序改为利用迭代实现即可,递归首先找到左树的最左边的叶结点,然后根,右子结点。迭代实现的思想也是如此,首先将左结点全部入栈,然后输出,右结点入栈。

    private static void inOrder01(TreeNode root){
        if (root == null)
            return;
        Stack stack = new Stack<>();
        while (true){  //反复执行
            while (root != null ){  //从当前结点出发,逐批入栈
                stack.push(root);
                root = root.left;
            }

            if (stack.isEmpty())   //所有结点处理完毕,跳出循环
                break;

            root = stack.pop();   //root的左树为空,才会执行以下语句
            System.out.print(root.val + " ");
            root = root.right;    //转向右子树

        }
    }
    
//运行结果
3 4 1 5 2 6
3.后序遍历Java实现

左|右|中遍历方式,先将右树遍历完毕,然后遍历左树,最后遍历根结点,递归实现如下:

    private static void postOrder(TreeNode root){
        if (root == null)
            return;

        postOrder(root.right);
        postOrder(root.left);
        System.out.print(root.val + " ");
    }

//运行结果
6 5 2 4 3 1 

若使用迭代的写法,则可以观察到,先序遍历与后序遍历是相反,不妨再添加一个栈,存储先序栈的输出,最后将val输出,迭代实现如下:

    private static void postOrder01(TreeNode root){
        if (root == null)
            return;

        Stack stack = new Stack<>();
        Stack stack1 = new Stack<>();
        stack.push(root);

        while (!stack.isEmpty()){
            TreeNode cur = stack.pop();
            stack1.push(cur);     //将前面栈的输出全部存储下来
            if (cur.right != null)
                stack.push(cur.right);
            if (cur.left != null)
                stack.push(cur.left);
        }

        while (!stack1.isEmpty())
            System.out.print(stack1.pop().val + " ");
    }

//运行结果
6 5 2 4 3 1 
4.层序遍历Java实现

层序遍历,是一层一层的进行遍历,可以获得树的深度,以及每层的结点,层序遍历,需要引入队列结构,先入先出。使用迭代实现如下:

    private static void levelOrder(TreeNode root){
        if (root == null)
            return;

        Queue queue = new linkedList<>();   //队列可以看成链表
        queue.add(root);

        while (!queue.isEmpty()){
            TreeNode cur = queue.poll();
            System.out.print(cur.val + " ");
            if (cur.left != null)
                queue.add(cur.left);
            if (cur.right != null)
                queue.add(cur.right);
        }
    }
    
//运行结果
1 3 2 4 5 6 

根结点入队,左树入,右树入,如果想把遍历的结果存放到list中,需要知道每层的个数,即需要遍历queue的次数,代码实现如下:

private static List> levelOrder01(TreeNode root){
    if (root == null)
        return new ArrayList<>();
    List> res = new ArrayList<>();
    Queue queue = new linkedList<>();

    queue.add(root);

    while (!queue.isEmpty()){
        int levelNum = queue.size();  //获得队列的长度,即需要遍历的次数
        List temp = new ArrayList<>();  //建立列表存放每行的结点值

        for (int i = 0; i < levelNum; i++) {
            TreeNode cur = queue.poll();
            temp.add(cur.val);
            if (cur.left != null)
                queue.add(cur.left);
            if (cur.right != null)
                queue.add(cur.right);
        }
        res.add(temp);
    }

    return res;
}

//运行结果
[[1], [3, 2], [4, 5, 6]]
总结

树的遍历的问题就这些,本人感觉比较重要的是学会运用递归去解决问题,因为树的整体和局部非常相似,非常适合递归的使用,用好了递归,很多问题都能迎刃而解,当迭代的方法也是需要掌握的,两者之间是存在一定联系的,最好都掌握。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/666770.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号