线索二叉树

简介: 线索二叉树在二叉树的结点上加上线索的二叉树称为线索二叉树。

java实现线索化二叉树


线索二叉树:

在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。


存储结构:

线索二叉树中的线索能记录每个结点前驱和后继信息。为了区别线索指针和孩子指针,在每个结点中设置两个标志leftType和rightType。
当leftType和rightType为0时,left和right分别是指向左孩子和右孩子的指针;否则,left是指向结点前驱的线索(pre),right是指向结点的后继线索(suc)。由于标志只占用一个二进位,每个结点所需要的存储空间节省很多。


现将二叉树的结点结构重新定义如下:

其中:leftType=0 时left 指向左儿子;leftType=1 时left 指向前驱;rightType=0 时right指向右儿子;rightType=1 时right指向后继。



java代码实现:

//线索二叉树
public class ThreadeBinaryTreeDemo {
    public static void main(String[] args) {
        HeroNode node1 = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");
        HeroNode node6 = new HeroNode(6, "2左");


        BinaryTree root = new BinaryTree(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        node2.setLeft(node6);

        //前序线索化二叉树
        root.preThreadedNodes();
        //前序遍历显示中序线索化二叉树
        root.preOrderThreadeNode();


        //中序线索化二叉树
//        root.infixThreadedNodes();
        //中序遍历显示中序线索化二叉树
//        root.infixOrderThreade();

    }
}


//创建二叉树
class BinaryTree {
    private HeroNode root;  //二叉树的根节点

    private HeroNode pre = null; //前驱

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

    //前序遍历
    public void preOrder() {
        if (root == null) {
            System.out.println("二叉树为空,前序遍历为空。");
        } else {
            root.preOrder();
        }
    }

    //中序遍历
    public void infixOrder() {
        if (root == null) {
            System.out.println("二叉树为空,中序遍历为空");
        } else {
            root.infixOrder();
        }
    }

    //后序遍历
    public void postOrder() {
        if (root == null) {
            System.out.println("二叉树为空,后序遍历为空");
        } else {
            root.postOrder();
        }
    }

    //前序遍历查找
    public HeroNode preOrderSreach(int no) {
        if (root != null) {
            return root.preOrderSreach(no);
        } else {
            return null;
        }
    }

    //中序遍历查找
    public HeroNode infixOrderSreach(int no) {
        if (root != null) {
            return root.infixOrderSreach(no);
        } else {
            return null;
        }
    }

    //后序遍历查找
    public HeroNode postOrderSreach(int no) {
        if (root != null) {
            return root.postOrderSreach(no);
        } else {
            return null;
        }
    }

    //删除结点
    //先判断当前的root是不是待删除结点 如果是就删除整颗二叉树,如果不是再调用结点的删除方法
    public Boolean delNode(int no) {
        if (root == null) {
            return false;
        } else {
            if (root.getId() == no) {
                root = null;
                return true;
            } else {
                return root.delNode(no);
            }
        }
    }

    //重载一个没有参数的方法
    public void infixThreadedNodes() {
        infixThreadedNodes(root);
    }

    public void preThreadedNodes() {
        preThreadedNodes(root);
    }

    //前序遍历 前序线索化二叉树
    public void preOrderThreadeNode() {
        if (root == null) {
            System.out.println("二叉树为空,前序线索化遍历失败");
            return;
        }
        HeroNode node = root;

        while (node.getRight() != null) {
            System.out.println(node);

            while (node.getLeftType() == 0) {
                System.out.println(node.getLeft());
                node = node.getLeft();
            }
            while (node.getRightType() == 1) {
                node = node.getRight();
            }
        }
        System.out.println(node);
    }

    //线索化前序二叉树
    public void preThreadedNodes(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) {
            preThreadedNodes(node.getLeft());
        }
        //线索化右子树
        if (node.getRightType() != 1) {
            preThreadedNodes(node.getRight());
        }

    }


    //遍历中序线索化二叉树
    public void infixOrderThreade() {
        if (root == null) {
            System.out.println("二叉树为空树!");
            return;
        }
        HeroNode node = root;

        while (node != null) {
            while (node.getLeftType() == 0) {
                node = node.getLeft();
            }

            System.out.println(node);
            while (node.getRightType() == 1) {
                node = node.getRight();
                System.out.println(node);
            }
            //替换这个遍历的结点
            node = node.getRight();
        }
    }


    //线索化二叉树  中序线索化二叉树
    public void infixThreadedNodes(HeroNode node) {
        //node == null , 没有办法线索化
        if (node == null) {
            return;
        }

        //线索化左子树
        infixThreadedNodes(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;

        //线索化右子树
        infixThreadedNodes(node.getRight());
    }


}


//创建节点的类
class HeroNode {
    private int id;
    private String name;
    private HeroNode left;  // 默认为空
    private HeroNode right;  // 默认为空

    //如果leftType=0 表示left指向左子树,如果lefTypet=1表示left指向前驱
    //如果rightType=0 表示right指向右子树,如果rightType=1表示right指向后继
    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 id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    /**
     * 前序遍历思路
     * 1.输出当前节点
     * 2.判断段当前节点的左子节点是否为空,递归前序遍历
     * 3.判断当前节点的右子节点是否为空,递归前序遍历
     */
    public void preOrder() {
        System.out.println(this);

        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    /**
     * 中续遍历思路
     * 1.判断段当前节点的左子节点是否为空,递归中序遍历
     * 2.输出当前节点
     * 3.判断当前节点的右子节点是否为空,递归中续遍历
     */
    public void infixOrder() {
        if (this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    /**
     * 后续遍历思路
     * 1.判断段当前节点的左子节点是否为空,递归后序遍历
     * 2.判断当前节点的右子节点是否为空,递归后续遍历
     * 3.输出当前节点
     */
    public void postOrder() {
        if (this.left != null) {
            this.left.postOrder();
        }
        if (this.right != null) {
            this.right.postOrder();
        }
        System.out.println(this);
    }

    /**
     * 前序遍历查找的思路
     * 1.判断当前节点是否为待查找节点,如果是直接返回,
     * 2.判断当前节点的左子节点是否为空,不为空则向左递归前序查找,找到则返回,找不到则返回null
     * 3.判断当前节点的右子节点是否为空,不为空则向右递归前序查找,找到则返回,找不到则返回null
     *
     * @param no 带查找的节点id
     * @return 返回该节点的信息
     */
    public HeroNode preOrderSreach(int no) {
//        System.out.println("前序遍历的次数~~~");

        if (this.id == no) {
            return this;
        }

        HeroNode heroNode = null;
        //判断当前节点的左子节点是否为空,不为空则向左递归前序查找,找到则返回,找不到则返回null
        if (this.left != null) {
            heroNode = this.left.preOrderSreach(no);
        }
        if (heroNode != null) {
            return heroNode;
        }
        //判断当前节点的右子节点是否为空,不为空则向右递归前序查找,找到则返回,找不到则返回null
        if (this.right != null) {
            heroNode = this.right.preOrderSreach(no);
        }
        return heroNode;
    }


    /**
     * 中序遍历查找的思路
     * 1.判断当前节点的左子节点是否为空,不为空则向左递归中序查找,找到则返回,找不到则返回null,
     * 2.判断当前节点是否为待查找节点,如果是直接返回
     * 3.判断当前节点的右子节点是否为空,不为空则向右递归中序查找,找到则返回,找不到则返回null
     *
     * @param no 带查找的节点id
     * @return 返回该节点的信息
     */
    public HeroNode infixOrderSreach(int no) {
        HeroNode heroNode = null;
        //判断当前节点的左子节点是否为空,不为空则向左递归中序查找,找到则返回,找不到则返回null
        if (this.left != null) {
            heroNode = this.left.infixOrderSreach(no);
        }
        if (heroNode != null) {
            return heroNode;
        }
//        System.out.println("中序遍历的次数~~~");

        if (this.id == no) {
            return this;
        }

        //判断当前节点的右子节点是否为空,不为空则向右递归中序查找,找到则返回,找不到则返回null
        if (this.right != null) {
            heroNode = this.right.infixOrderSreach(no);
        }
        return heroNode;
    }


    /**
     * 后序遍历查找的思路
     * 1.判断当前节点的左子节点是否为空,不为空则向左递归后序查找,找到则返回,找不到则返回null,
     * 2.判断当前节点的右子节点是否为空,不为空则向右递归后序查找,找到则返回,找不到则返回null
     * 3.判断当前节点是否为待查找节点,如果是直接返回
     *
     * @param no 带查找的节点id
     * @return 返回该节点的信息
     */
    public HeroNode postOrderSreach(int no) {
        HeroNode heroNode = null;
        //判断当前节点的左子节点是否为空,不为空则向左递归后序查找,找到则返回,找不到则返回null
        if (this.left != null) {
            heroNode = this.left.postOrderSreach(no);
        }
        if (heroNode != null) {
            return heroNode;
        }
        //判断当前节点的右子节点是否为空,不为空则向右递归后序查找,找到则返回,找不到则返回null
        if (this.right != null) {
            heroNode = this.right.postOrderSreach(no);
        }
        if (heroNode != null) {
            return heroNode;
        }
//        System.out.println("后序遍历的次数~~~~");

        if (this.id == no) {
            return this;
        }

        return heroNode;
    }


    /**
     * 删除结点的思路
     * 1.因为二叉树是单向是的,所以删除的时候只能删除当前结点的下一个结点。
     * 2.如果当前的左子节点不为空,判断当前的左子结点是否为待删除结点,如果是领this.left=null,让后返回true。
     * 3.如果当前的右子节点不为空,判断当前的右子结点是否为待删除结点,如果是就领this.right=null,让后返回true。
     * 4.如果 2 , 3 都没有找到待删除结点就向左递归,
     * 5.如果向左递归没有找到就向右递归
     *
     * @param no 待删除结点的id
     * @return 返回是否删除成功
     */
    public Boolean delNode(int no) {
        if (this.left != null && this.left.id == no) {
            this.left = null;
            return true;
        }
        if (this.right != null && this.right.id == no) {
            this.right = null;
            return true;
        }
        if (this.left != null) {
            if (this.left.delNode(no)) {
                return true;
            }
        }
        if (this.right != null) {
            if (this.right.delNode(no)) {
                return true;
            }
        }
        return false;
    }

}
目录
相关文章
05_二叉树的层次遍历II
05_二叉树的层次遍历II
|
7月前
|
算法
带你深入理解二叉树的遍历
带你深入理解二叉树的遍历
|
7月前
|
数据格式
树结构练习——排序二叉树的中序遍历
树结构练习——排序二叉树的中序遍历
|
算法
25 二叉树的遍历
25 二叉树的遍历
30 0
|
存储 算法 容器
遍历二叉树
大家好,我是王有志。今天我们继续学习数据结构与算法的内容,主要是如何遍历一棵二叉树,那么我们直接开始吧。
65 0
遍历二叉树
|
存储
线索化二叉树
线索化二叉树
59 0
|
存储
二叉树的遍历问题
二叉树的遍历问题
|
算法
二叉树的层次遍历
层次遍历就是即逐层地,从左到右访问所有节点
二叉树的层次遍历
先序、中序、后序遍历确定唯一树
快速学习先序、中序、后序遍历确定唯一树
先序、中序、后序遍历确定唯一树