9.3-线索化二叉树

本文介绍了线索化二叉树的概念,旨在减少空指针带来的空间浪费。通过在二叉树的空指针域指向前驱或后续结点,实现了线索化。文章详细展示了中序、先序和后序线索化二叉树的算法,并提供了相应的遍历方法。通过中序遍历,找到第一个没有左子树的结点作为起点,按顺序输出结点。先序遍历则从根结点开始,输出没有前驱结点的结点。后序遍历则先找到第一个叶子结点,然后输出并遍历其后继结点。
摘要由CSDN通过智能技术生成

9.3-线索化二叉树

  • 二叉树中,我们会发现一个问题,就是会存在空指针域,因为不是每个结点都有左右结点,所以这个时候会造成空间浪费,为了减少这种不必要的浪费,我们将这些空指针域,指向某一遍历次序下,当前结点的前驱和后续结点,那么我们就将一个二叉树转化成了线索化二叉树
  • 二叉树有n个结点,则有n+1个空指针域
package com.hejiale.dataStructures.tree.threadBinaryTree;

public class ThreadBinaryTree {
    private HeroNode root;//根结点
    //为了实现线索化,需要创建一个指向当前结点前驱结点的一个指针
    private HeroNode pre = null;

    public ThreadBinaryTree(HeroNode root) {
        this.root = root;
    }

    /*
        -   中序线索化二叉树,其实归根到底,是对结点进行线索化
        -   node 就是当前需要线索化的结点
        -   在递归进行线索化的时候,pre总是指向当前结点的前一个结点
        -   我们每次递归,是解决当前结点的前驱结点pre,解决pre结点的后继结点node,而不是一步到位,一次性解决当前结点的前驱和后继
            1.  如果node结点为空,则无法线索化该结点,退出
            2.  先线索化左子树
            3.  线索化当前结点
                3.1 先处理当前结点的前驱结点
                3.2 处理当前结点的后继结点
            4.  线索化右子树
    */
    public void infixThreadNodes(HeroNode node) {
        if (node == null) {
            return;
        }
        //线索化左子树
        infixThreadNodes(node.getLeft());
        //线索化当前结点
        if (node.getLeft() == null) {
            //让当前结点的左指针指向前驱结点
            node.setLeft(pre);
            //修改当前结点的左指针类型
            node.setLeftType(1);//表明该指针指向的是前驱结点
        }
        //处理后继结点
        if (pre != null && pre.getRight() == null) {
            pre.setRight(node);
            pre.setRightType(1);
        }
        //每处理完一个结点后,让当前结点是下一个结点
        pre = node;
        //线索化右子树
        infixThreadNodes(node.getRight());

    }

    public void preThreadNodes(HeroNode node) {
        if (node == null) {
            return;
        }
        //线索化当前结点
        if (node.getLeft() == null) {
            //让当前结点的左指针指向前驱结点
            node.setLeft(pre);
            //修改当前结点的左指针类型
            node.setLeftType(1);//表明该指针指向的是前驱结点
        }
        //处理后继结点
        if (pre != null && pre.getRight() == null) {
            pre.setRight(node);
            pre.setRightType(1);
        }
        //每处理完一个结点后,让当前结点是下一个结点
        pre = node;
        //线索化左子树
        if (node.getLeftType() != 1) {
            preThreadNodes(node.getLeft());
        }
        //再线索化右子树
        if (node.getRightType() != 1) {
            preThreadNodes(node.getRight());
        }

    }

    //后序线索二叉树
    public void postThreadNodes(HeroNode node) {
        if (node == null) {
            return;
        }
        //线索化左子树
        postThreadNodes(node.getLeft());
        postThreadNodes(node.getRight());
        //线索化当前结点
        if (node.getLeft() == null) {
            //让当前结点的左指针指向前驱结点
            node.setLeft(pre);
            //修改当前结点的左指针类型
            node.setLeftType(1);//表明该指针指向的是前驱结点
        }
        //处理后继结点
        if (pre != null && pre.getRight() == null) {
            pre.setRight(node);
            pre.setRightType(1);
        }
        //每处理完一个结点后,让当前结点是下一个结点
        pre = node;
        //线索化右子树
    }

    //遍历中序线索化二叉树
    /*
        首先,中序遍历二叉树,第一个结点应该是什么样子的呢?我们可以分类讨论
            1.  只有左子树-->不成立,因为有左子树,那么一定会先遍历左子树的结点,所以有左子树的一定不是中序遍历的第一个结点
            2.  只有右子树-->可以,右子树的所有结点,按照左中右的遍历顺序,一定不会出现再该节点的前面
            3.  有左右子树-->只有左子树的情况不成立,那么该情况肯定也不成立
            4.  没有子树-->成立
        那么以上四种情况分析完后,我们可以得出,中序遍历的第一个结点,一定是没有左子树的,所以leftType一定是1,并且,由于是
        中序遍历,按照左中右的次序,所以第一个结点,应该出现再树的最左下方,是纯粹的左方,所以我们可以while循环遍历,如果
        leftType!=1,我们就令node = node.getLeft(),那么跳出循环时候的node,就是我们要找的中序遍历第一个结点
    */
    public void threadInfixList() {
        //定义一个变量,临时存储当前遍历的结点
        HeroNode node = root;
        while (node != null) {//不等于空,就可以遍历
            //循环找到leftType = 1的结点,第一个找到的就是
            while (node.getLeftType() == 0) {
                node = node.getLeft();
            }//跳出循环的node就是我们要找的中序遍历的第一个结点
            //打印该结点
            System.out.println(node);
            //如果当前结点的右指针指向的是后继结点,就一直输出
            while (node.getRightType() == 1) {
                //获取到当前结点的后继jiedian
                node = node.getRight();
                System.out.println(node);
            }//跳出循环,说明遇到了RightType != 1的结点,那么这个时候
            /*
                这个时候,不知道应该替换哪一个结点,我们同样可以分类讨论
                首先,遇到了RightType == 0的结点,说明当前结点是有右子树的,当前结点已经遍历了,那么这个时候无非就
                两种情况,要么遍历左子树结点,要么遍历右子树结点

                但是我们是中序遍历,左子树的结点肯定已经遍历了,如果我们遍历左子树的结点就会违背中序遍历的顺序
                所以我们应该遍历的是右子树结点
            */
            node = node.getRight();
            /*
                右子树结点进行下一次while循环,同样遵循先访问最左下结点的规律
            */
        }
    }

    /*
        遍历先序线索二叉树
        先序线索二叉树的遍历是最简单的,思路在代码中体现
    */
    public void threadPreList() {
        HeroNode node = root;
        while (node != null) {
            while (node.getLeftType() == 0) {//说明该结点没有前驱结点,则输出
                System.out.println(node);
                node = node.getLeft();
            }//当结点没有左子树的时候
            System.out.println(node);//输出该结点
            /*
                更新要遍历的结点,因为该结点没有左子树,那么接下来要遍历的结点分为两种情况
                    1.  该结点没有左子树,但是有右子树,则node = node.getRight()
                    2.  该结点是叶子结点,那么接下来应该访问该结点的后继结点,node = node.getRight()
                所以不论是哪种情况,都是node = node.getRight()
            */
            node = node.getRight();
        }

    }

    public void threadPostList() {
        HeroNode node = root;
        while (node != null) {
            //找到第一个叶子结点
            while (node.getRightType() != 1 && node.getLeftType() != 1) {
                if (node.getLeftType() == 0) {
                    node = node.getLeft();
                } else {
                    node = node.getRight();
                }
            }//跳出循环的结点就是第一个叶子结点
            System.out.println(node);//输出当前结点
            while (node.getRightType() == 1) {//如果当前结点右指针指向的是后继结点,则继续输出
                node = node.getRight();
                System.out.println(node);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值