十-3, Java实现线索化二叉树

10.1 线索二叉树的定义

[基本概念]

线索, 线索二叉树:

  • 对于n个结点的二叉树, 在二叉链表存储结构中有n+1个空链域, 利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针, 这些指针称为线索, 加上了线索的二叉树称为线索二叉树.

线索化:

  • 一棵二叉树中所有结点的空指针按照某种遍历方法加线索(指定前驱和后继结点)的过程叫做线索化. 根据对二叉树遍历方式的不同, 线索二叉树可以分为前序线索二叉树,中序线索二叉树, 后序线索二叉树.

优缺点:

  • 优点:
    1. 利用线索二叉树进行中序遍历时, 不必采用堆栈处理, 速度比一般二叉树的遍历速度快, 并且节约存储空间;
    2. 有了线索, 任意一个结点都能找到它的前驱和后继结点;
  • 缺点:
    1. 结点的插入和删除麻烦, 且速度也比较慢
    2. 线索子树不能共用, 比如中序线索化的二叉树只能由中序遍历读取出来;

[存储结构]

  • 线索二叉树中的线索能够记录每个结点的前驱和后继信息. 为了区别于线索指针和孩子指针, 我们需要在每个节点中设置两个标志 leftNodeType 和 rightNodeTyepe;
  • 当 leftNodeType=0时, 表明这个结点的左指针指向的是左孩子结点;
  • 当 leftNodeType=1时, 表明这个结点的左指针指向的是这个结点的前驱结点;

10.2 二叉树线索化,以及遍历线索二叉树的具体实现

这里以中序线索化二叉树为例;
在这里插入图片描述

  1. 结点类
  • 结点实体类主要是管理结点的指针域, 数据域, 以及供结点类初始化赋值, 定义结点数据的输出等等;

  • 代码实现:

    public class BinaryTreeNode{
        //一般为了数据安全,我们通常封装结点类的一些对象,并对外提供get,set方法;
        //这里我偷个懒算了, 不装了,直接public
        BinaryTreeNode leftNode;
        BinaryTreeNode rightNode;
        int id;
        String name;
        int leftNodeType;
        int rightNodeType;

        //构造方法, 便于创建结点时就初始化数据
        public BinaryTreeNode(int id, String name){
            this.id = id;
            this.name = name;
        }
        //因为结点类是用户自定义类, 所以重写tostring()很有必要.
        public String toString(){
            return "id: "+id+", name: "+name;
        }
        //设置左右子结点
        public void setLeftNode(BinaryTreeNode node){
            this.leftNode = node;
        }
        public void setRightNode(BinaryTreeNode node){
            this.rightNode = node;
        }
        //
    }
  1. 中序线索二叉树:
  • 建立线索二叉树,或者说对二叉树线索化, 实际上就是遍历一棵二叉树. 在遍历过程中, 访问结点的操作是检查当前的左右指针域是否为空, 是的话就把它们改为前驱结点或后继结点的线索; 为了实现这一过程, 我们设置node指向当前正在访问的结点, 而pre指向的是node刚刚访问过的一个结点, pre和node指针亦步亦趋, 完成对二叉树的线索化过程;

  • 代码实现:

    //定义一个全局的pre指针, 指向node刚刚访问过的一个结点
    BinaryTreeNode pre;
    public void threadedBianryTree(BinaryTreeNode node){
        //因为我们要递归的遍历左子树和右子树,所以下面是递归出口
        if(node == null) return;
        //递归访问左子树
        threadedBinaryTree(node.leftNode);
        
        //======================>线索化开始
        //此时递归左子树的操作已经完成了, 此时node位于左子树的最左边的一个叶子节点上 
        //随着每一次递归的归来, 我们对结点进行线索化
            //设置结点的前驱结点
        if(node.leftNode == null){
            node.leftNode = pre;
            node.leftNodeType = 1;
        }
            //设置结点的后继结点
        if(pre != null && pre.rightNode == null){
            pre.rightNode = node;
            pre.leftNodeType = 1;
        }
            //因为node随着递归归来是逐渐往根节点遍历的
            //pre也要跟着node
        pre = node;
        //======================>线索化结束
       
        //递归访问右子树
        threadedBinaryTree(node.rightNode);
    }
  1. 中序遍历中序线索二叉树
    public void midOrderThread(){
        //指定根节点
        BinaryTreeNode node = root;

        while(node != null){
            //一口气遍历到左子树的最左边
            if(node.leftNodeType == 0)
                node = node.leftNode;

            //输出左边的这个结点
            System.out.println(node);

            //借助线索往上遍历,打印出所有线索为1结点指向的那个节点
            if(node.rightNode == 1){
                node = node.rightNode;
                System.out.prinln(node);
            }
            //线索不为1, 去看他的右子节点
            node = node.rightNode;
        }
    }

完整代码实例: Java实现中序线索化二叉树,以及对其的中序遍历

posted @ 2022-05-26 20:31  青松城  阅读(57)  评论(0编辑  收藏  举报