二叉树线索化遍历详解

二叉树线索化遍历

说明

  1. 二叉树在线索化之后,不能使用之前的前序中序后续遍历方法,因为将一些叶子节点连通后,可能出现循环递归
  2. 因此需要编写新的线索化遍历方法
  3. 以中序线索化遍历为例
  4. 先根据中序线索化后的前驱节点类型寻找中序遍历的第一个节点,它的前驱接节点类型为 1 ,找到第一个前驱节点后,依次判断当前节点的后继节点类型是否为 1,如果是,则这个节点也是线索化之后的叶子节点,直接输出
  5. 如果后继节点类型不是 1 ,说明这个后继节点不是叶子节点,而是非叶子节点,指针后移
  6. 前序后续类似,源码见下

源码及分析

遍历方法
//线索化二叉树的遍历
    public void threadedList(){
        //定义指针变量node用于遍历,初始化为root
        HeroNode node = root;
        //依次扫描中序线索化二叉树
        while (node != null){

            //根据节点类型找到中序线索二叉树的第一个节点
            while (node.getLeftType() == 0){
                node = node.getLeft();
            }
            //循环结束后找到,则直接输出
            System.out.println(node);
            //然后判断它的后继节点类型,如果是线索化过的则直接输出
            while (node.getLeftType() == 1){
                node = node.getRight();
                System.out.println(node);
            }
            //否则指针后移
            node = node.getRight();
        }
    }

    //编写中序线索化二叉树的方法
    /**
     *
     * @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);
    }
完整代码
package algorithm.tree.threadedtree;

/**
 * @author AIMX_INFO
 * @version 1.0
 */
public class ThreadedBinaryTreeDemo {
    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());

        //线索化遍历测试
        System.out.println("线索化遍历");
        threaderBinaryTree.threadedList();

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

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

    //线索化二叉树的遍历
    public void threadedList(){
        //定义指针变量node用于遍历,初始化为root
        HeroNode node = root;
        //依次扫描中序线索化二叉树
        while (node != null){

            //根据节点类型找到中序线索二叉树的第一个节点
            while (node.getLeftType() == 0){
                node = node.getLeft();
            }
            //循环结束后找到,则直接输出
            System.out.println(node);
            //然后判断它的后继节点类型,如果是线索化过的则直接输出
            while (node.getLeftType() == 1){
                node = node.getRight();
                System.out.println(node);
            }
            //否则指针后移
            node = node.getRight();
        }
    }

    //编写中序线索化二叉树的方法
    /**
     *
     * @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;
        }
    }
}

//创建节点
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;
    }
}

posted @ 2021-06-06 22:50  mx_info  阅读(390)  评论(0)    收藏  举报