二叉树知识之5线索化二叉树

线索化二叉树的引出

在这里插入图片描述
从上图中可以看出,叶子节点和某些节点如8,10,14,6的左右节点是没有连接的,并没有完全的用上,如果想充分的利用各个节点的左右指针,让各个节点指向自己的前后节点。故引入了线索二叉树的概念

线索二叉树的介绍

线索二叉树的规则:

  1. 根据上图,可以看出n个节点的二叉树中,会有n+1个空指针域。利用二叉树链表中的空指针域,存放指向节点在某种遍历次序下的前驱和后继节点的指针(这种指针的附加称为索引)以下以中序遍历为例。

在这里插入图片描述

规定;
2. 一个节点的前一个节点称为前驱节点
3. 一个节点的后一个节点称为后继节点

线索二叉树的解释

按照上图,数组中为树的中序遍历结果,
例如:
8号节点的下一个节点是3,故后继节点为3,
因为3号节点在树中已经有了左右两个子节点,故没有索引
10号节点,有两个多余的子节点,而且在数组中上一个数是10,故树中前驱节点为10,数组中下一个节点为1,故树中的后继索引为1.,
以此类推。。

代码实现线索化思路

故针对以上情况分析,实现代码的思路如下:
当线索化二叉树之后,节点的left属性和right属性有以下几种情况

  1. left指向左子树也可能指向前驱节点,比如1号节点left指向左子树,而10号节点的左指向了前驱节点
  2. right指向右子树也可能指向后继节点。如1号节点right指向右子树,而10号节点的右指向了后继节点
    故引入另外两个辅助标志,来判断是左右子树还是前后驱继节点。
  3. 如果leftType=0,则表示指向左子节点,leftType=1,表示指向后继节点
  4. 如果rightType=0,则表示指向左子节点,rightType=1,表示指向后继节点

代码实现线索化遍历的思路:

根据辅助标志来判断是子树还是前驱后继节点。

综合测试代码:

package com.njupt.binaryTree;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/07/24/20:25
 * @Description:
 */
public class ThreadTree {
    public static void main(String[] args) {
        NodePoint root = new NodePoint(1, "aa");
        NodePoint node2 = new NodePoint(3, "bb");
        NodePoint node3 = new NodePoint(6, "cc");
        NodePoint node4 = new NodePoint(8, "dd");
        NodePoint node5 = new NodePoint(10, "ee");
        NodePoint node6 = new NodePoint(14, "ff");

        root.left = node2;
        root.right = node3;
        node2.left = node4;
        node2.right = node5;
        node3.left = node6;

        //测试中序线索化
        ThreadBinaryTree threadBinaryTree = new ThreadBinaryTree();
        threadBinaryTree.root = root;
        threadBinaryTree.threadedNodes();
        //以10号节点测试
        NodePoint left = node5.left;
        NodePoint right = node5.right;
        System.out.println("前驱节点" + left);
        System.out.println("后驱节点" + right);

        System.out.println("使用线索化遍历二叉树中序遍历");
        threadBinaryTree.threadedList();
    }
}

//实现线索化功能的二叉树
class ThreadBinaryTree {
    public NodePoint root;
    //为了实现线索化,需要创建要给指向当前结点的前驱结点的指针
    //在递归进行线索化时,pre 总是保留前一个结点
    public NodePoint pre = null;

    //重载一把threadedNodes方法
    public void threadedNodes() {
        this.threadedNodes(root);
    }

    //遍历线索化二叉树的方法
    public void threadedList() {
        //定义一个变量,存储当前遍历的结点,从root开始
        NodePoint node = root;
        while (node != null) {
            //循环的找到leftType == 1的结点,第一个找到就是8结点
            //后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
            //处理后的有效结点
            while (node.leftType == 0) {
                node = node.left;
            }

            //打印当前这个结点
            System.out.println(node);
            //如果当前结点的右指针指向的是后继结点,就一直输出
            while (node.rightType == 1) {
                //获取到当前结点的后继结点
                node = node.right;
                System.out.println(node);
            }
            //替换这个遍历的结点
            node = node.right;

        }
    }

    //编写对二叉树进行中序线索化的方法

    /**
     * @param
     */
    public void threadedNodes(NodePoint node) {

        //如果node==null, 不能线索化
        if (node == null) {
            return;
        }
        //(一)先线索化左子树
        threadedNodes(node.left);
        //(二)线索化当前结点[有难度]

        //处理当前结点的前驱结点
        //以8结点来理解
        //8结点的.left = null , 8结点的.leftType = 1
        if (node.left == null) {
            //让当前结点的左指针指向前驱结点
            node.left = pre;
            //修改当前结点的左指针的类型,指向前驱结点
            node.leftType = 1;
        }

        //处理后继结点
        if (pre != null && pre.right == null) {
            //让前驱结点的右指针指向当前结点
            pre.right = node;
            //修改前驱结点的右指针类型
            pre.rightType = 1;
        }
        //!!! 每处理一个结点后,让当前结点是下一个结点的前驱结点
        pre = node;

        //(三)在线索化右子树
        threadedNodes(node.right);


    }

}

//节点类信息
class NodePoint {
    public int no;
    public String name;
    public NodePoint left;
    public NodePoint right;
    //规定:如果leftType=0,则表示指向左子节点,leftType=1,表示指向后继节点
    public int leftType;
    //同理。
    public int rightType;

    public NodePoint(int no, String name) {
        this.no = no;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Node1{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }

}

测试结果:

前驱节点Node1{no=3, name='bb'}
后驱节点Node1{no=1, name='aa'}
使用线索化遍历二叉树中序遍历
Node1{no=8, name='dd'}
Node1{no=3, name='bb'}
Node1{no=10, name='ee'}
Node1{no=1, name='aa'}
Node1{no=14, name='ff'}
Node1{no=6, name='cc'}

Process finished with exit code 0

和预期一致。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值