线索化二叉树详解

Posted mx_info

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线索化二叉树详解相关的知识,希望对你有一定的参考价值。

线索化二叉树详解

说明

  1. 线索化二叉树,由字面意思,就是将二叉树的节点拿线索连接起来
  2. 实质上,也就是将二叉树的叶子节点左右指针域彼此连接一个节点
  3. 二叉树的非叶子节点的左右指针域都各自连接了一个节点,但是叶子节点的左右指针域是空的,因此考虑将叶子节点的左右指针域按照某种遍历次序连接起来
  4. 按照二叉树的遍历方式,有前序中序后续三种遍历方式,因此可以形成三种链式结构
  5. 每个叶子节点前一个节点称为前驱节点,后一个节点称为后继节点,如果当前节点没有前驱或者后继节点,则直接置为空
  6. 以中序线索化二叉树为例,编写中序线索化二叉树的方法
  7. 先判断当前节点是否为空,如果为空,则直接返回
  8. 否则先向左递归线索化二叉树的左子树
  9. 然后再线索化当前节点,定义属性pre保存当前节点的前一个节点,因此当前节点的前一个节点置为pre
  10. 注意当前节点的后一个节点,需要用pre保存当前节点,然后遍历到后一个节点,然后用pre指向
  11. 注意第一个节点和最后一个节点
  12. 中序线索化如下,前序和后续类似

源码及分析

节点类
//创建节点
class HeroNode{
    //编号
    private int no;
    //姓名
    private String name;
    //左子树
    private HeroNode left;
    //右子树
    private HeroNode right;

    //线索化的前驱节点类型,是节点还是树,假定 0 为树 , 1 为节点
    private int leftType;
    //线索化的后继节点类型
    private int rightType;

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    //构造器,左子树和右子树默认为空
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name=\'" + name + \'\\\'\' +
                \'}\';
    }
    //删除节点

    /**
     *
     * @param no 要删除的节点编号
     */
    public void delNode(int no){
        //判断当前节点的左子树是否为空,如果不为空,再判断是否为要删除的节点
        if (this.left != null && this.left.no == no){
            this.left = null;
        }
        //判断当前节点的右子树是否为空,如果不为空,再判断是否为要删除的节点
        if (this.right != null && this.right.no == no){
            this.right = null;
        }
        //否则向左向右递归
        if (this.left != null){
            this.left.delNode(no);
        }
        if (this.right != null){
            this.right.delNode(no);
        }
    }


    //前序中序后序遍历主要区别在于父节点的输出位置不同,
    /**
     * 前序遍历先输出父节点信息,然后判断左子树是否为空,如果不为空,则递归前序遍历
     * 然后再判断右子树是否为空,如果不为空,则递归遍历前序遍历
     */
    //前序遍历
    public void preOrder(){
        //先输入当前节点信息
        System.out.println(this);
        //然后判断当前节点的左子树是否为空
        if (this.left != null){
            this.left.preOrder();
        }
        //再判断当前节点的右子树是否为空
        if (this.right != null){
            this.right.preOrder();
        }

    }
    //中序遍历
    public void infixOrder(){
        //先判断当前节点的左子树是否为空
        if (this.left != null){
            this.left.infixOrder();
        }
        //再输出当前节点的信息
        System.out.println(this);
        //然后再判断当前节点的右子树是否为空
        if (this.right != null){
            this.right.infixOrder();
        }
    }
    //后序遍历
    public void postOrder(){
        //先判断当前节点的左子树是否为空
        if (this.left != null){
            this.left.postOrder();
        }
        //再判断当前节点的右子树是否为空
        if (this.right != null){
            this.right.postOrder();
        }
        //最后输出当前节点的信息
        System.out.println(this);
    }

    //前序查找

    /**
     * 前序遍历查找
     * @param no 要查找的节点编号
     * @return 返回查找的结果
     */
    public HeroNode preOrderSearch(int no){
        //先判断当前节点是不是要查找的节点
        if (this.no == no){
            return this;
        }
        //如果当前节点不是要查找的节点,则判断左子树是否为空,若不为空,则递归前序查找
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.preOrderSearch(no);
        }
        //如果在左子树找到,则直接返回
        if (resNode != null){
            return resNode;
        }
        //如果左子树也没有找到,则判断右子树是否为空,并递归
        if (this.right != null){
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }
    //中序查找

    /**
     * 中序遍历查找
     * @param no 要查找的节点编号
     * @return 返回查找的结果
     */
    public HeroNode infixOrderSearch(int no){
        //先判断当前节点左子树是否为空,如果不为空则递归中序查找
        //定义变量保存查找的结果
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.preOrderSearch(no);
        }
        //如果查找到,则直接返回
        if (resNode != null){
            return resNode;
        }
        //如果没有找到,判断当前节点是不是要查找的节点
        if (this.no == no){
            return this;
        }
        //如果还没有找到,则判断右子树是否为空,不为空则递归中序查找
        if (this.right != null){
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }
    //后序查找

    /**
     * 后续遍历查找
     * @param no 要查找的节点编号
     * @return 返回查找的结果
     */
    public HeroNode postOrderSearch(int no){
        //判断当前节点的左子树是否为空,如果不为空,则递归后续查找
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.postOrderSearch(no);
        }
        if (resNode != null){
            return resNode;
        }
        if (this.right != null){
            resNode = this.right.postOrderSearch(no);
        }
        if (resNode != null){
            return resNode;
        }
        if (this.no == no){
            return this;
        }
        return resNode;
    }
}

线索化二叉树类
//创建一颗线索化二叉树
class ThreaderBinaryTree{
    //二叉树必有根节点
    private HeroNode root;
    //定义变量指向前驱节点,默认为空
    private HeroNode pre = null;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //编写中序线索化二叉树的方法
    /**
     *
     * @param node node为当前要中序线索化的节点
     */
    public void infixThreadedBinaryTree(HeroNode node){
        //先判断当前节点是否为空
        if (node == null){
            return;
        }
        //如果不为空,先线索化左子树
        infixThreadedBinaryTree(node.getLeft());
        //再线索化当前节点
        //当前节点的前驱节点为pre,后继节点需要在下一个节点连通,因为是单向的

        //设置当前节点的前驱节点,并设置前驱节点类型
        if (node.getLeft() == null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        //设置当前节点的后继节点及其类型
        if (pre != null && pre.getRight() == null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        //让pre指向当前节点
        pre = node;
        //最后再线索化右子树
        infixThreadedBinaryTree(node.getRight());
    }
    //重载线索化的方法
    public void infixThreadedBinaryTree(){
        this.infixThreadedBinaryTree(root);
    }

    //删除节点
    /**
     *
     * @param no 要删除的节点编号
     */
    public void delNode(int no){
        //先判断二叉树是否为空
        if (this.root != null){
            //再判断当前root节点是不是要删除的节点
            if (this.root.getNo() == no){
                root = null;
            }else {
                this.root.delNode(no);
            }
        }else {
            System.out.println("二叉树为空,不能删除...");
        }

    }
    //前序遍历
    public void preOrder(){
        if (this.root != null){
            this.root.preOrder();
        }else {
            System.out.println("二叉树为空...");
        }
    }
    //中序遍历
    public void infixOrder(){
        if (this.root != null){
            this.root.infixOrder();
        }else {
            System.out.println("二叉树为空...");
        }
    }
    //后续遍历
    public void postOrder(){
        if (this.root != null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空...");
        }
    }
    //前序查找
    public HeroNode preOrderSearch(int no){
        if (this.root != null){
            return this.root.preOrderSearch(no);
        }else {
            return null;
        }
    }
    //中序查找
    public HeroNode infixOrderSearch(int no){
        if (this.root != null){
            return this.root.infixOrderSearch(no);
        }else {
            return null;
        }
    }
    //后续查找
    public HeroNode postOrderSearch(int no){
        if (this.root != null){
            return this.root.postOrderSearch(no);
        }else {
            return null;
        }
    }
}
测试类
public static void main(String[] args) {
        ThreaderBinaryTree threaderBinaryTree = new ThreaderBinaryTree();
        HeroNode root = new HeroNode(1,"tom");
        HeroNode node2 = new HeroNode(3,"jack");
        HeroNode node3 = new HeroNode(6,"smith");
        HeroNode node4 = new HeroNode(8,"king");
        HeroNode node5 = new HeroNode(10,"mary");
        HeroNode node6 = new HeroNode(14,"dop");
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        threaderBinaryTree.setRoot(root);

        //进行线索化
        threaderBinaryTree.infixThreadedBinaryTree();

        //测试线索化的结果
        System.out.println("node5的前一个节点 = " + node5.getLeft());
        System.out.println("node5的后一个节点 = " + node5.getRight());

    }

以上是关于线索化二叉树详解的主要内容,如果未能解决你的问题,请参考以下文章

java实现线索化二叉树的前序中序后续的遍历(完整代码)

C#数据结构-线索化二叉树

线索化二叉树

数据--第37课 - 线索化二叉树

数据结构与算法:树 线索化二叉树(中,前,后序)

数据结构与算法:树 线索化二叉树(中,前,后序)