前,中,后序线索化二叉树并遍历

 先看一个问题:

       

            将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗完全二叉树. 

 问题分析:
        1) 当我们对上面的二叉树进行中序遍历时,数列为 {8, 3, 10, 1, 6, 14 }
        2) 但是 6, 8, 10, 14 这几个节点的左右指针,并没有完全的利用上.(对于完全二叉树来说如果不进行线索化将会浪费n+1个指针,n为节点个数)
        3) 如果我们希望充分的利用 各个节点的左右指针, 让各个节点可以指向自己的前后节点,怎么办?
        4) 解决方案- 线索二叉树

线索化二叉树的基本介绍:

        1) n 个结点的二叉链表中含有 n+1 【公式 2n-(n-1)=n+1】 个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")
        2) 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(ThreadedBinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
        3) 一个结点的前一个结点,称为 前驱结点
        4) 一个结点的后一个结点,称为后继结点

中序线索化的图解:

        

 

 说明: 当线索化二叉树后,Node 节点的 属性 left 和 right ,有如下情况:


1) left 指向的是左子树,也可能是指向的前驱节点. 比如 ① 节点 left 指向的左子树, 而 ⑩ 节点的 left 指向的就是前驱节点


2) right 指向的是右子树,也可能是指向后继节点,比如 ① 节点 right 指向的是右子树,而⑩ 节点的 right 指向的是后继节点.

3)所以我们需要定义变量来区分到底是指向的左子树还是前驱节点……

例如:

private int leftType;// 0表示指向左子树,1表示前驱节点
private int rightType; // 0表示指向右子树,1表示后继节点

以下代码实现前,中,后序线索化二叉树并遍历:


import java.util.ArrayList;
import java.util.List;

/**
 * 实现线索化二叉树
 */
public class ThreadedBinaryTree {

    private int size; //二叉树的节点个数
    private HeroNode2 root;

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    //为了实现线索化,需要创建一个指向当前节点的前驱节点的指针
    private HeroNode2 pre = null;

    public HeroNode2 getRoot() {
        return root;
    }

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

    /**
     * 无参的前序线索化二叉树重载方法
     */
    public void prefixThreadedNodes() {
        this.prefixThreadedNodes(root);
    }

    /**
     * 前序线索化二叉树,就是按照前序遍历的顺序来线索化二叉树
     *
     * @param heroNode2
     */
    public void prefixThreadedNodes(HeroNode2 heroNode2) {

        if (heroNode2 == null) {
            return;
        }

        //1、如果当前节点的左子节点为空,就设置leftType=1
        if (heroNode2.getLeft() == null) {
            //让当前节点的左指针指向前驱节点
            heroNode2.setLeft(pre);
            //设置leftType=1
            heroNode2.setLeftType(1);
        }

        //2、处理后继节点
        if (pre != null && pre.getRight() == null) {
            //让前驱节点的右指针指向当前节点
            pre.setRight(heroNode2);
            pre.setRightType(1);
        }
        //3、 !!!!! 每处理一个节点后,让当前节点是下一个节点的前驱节点
        pre = heroNode2;

        //4、线索化左指针
        if (pre.getLeftType() == 0) {
            prefixThreadedNodes(heroNode2.getLeft());
        }

        //5、再线索化右指针
        if (pre.getRightType() == 0) {
            prefixThreadedNodes(heroNode2.getRight());
        }
    }

    /**
     * 遍历前序线索化后的二叉树
     */
    public void prefixOrderThreaded() {
        //定义一个变量存储遍历的节点,从root开始
        HeroNode2 temp = root;
        int count = 0;
        //循环遍历
        while (temp != null) {
            while (temp.getLeftType() == 0 && temp.getRightType() == 0) {
                System.out.println(temp);
                count++;
                temp = temp.getLeft();
            }
            System.out.println(temp);
            count++;
            while (temp.getRightType() == 1 && temp.getLeftType() == 1) {
                temp = temp.getRight();
                System.out.println(temp);
                count++;
            }
            temp = temp.getLeft();
            System.out.println(temp);
            count++;

            if (count == size) {
                break;
            }

        }
    }

    /**
     * 无参的中序线索化二叉树重载方法
     */
    public void infixThreadedNodes() {
        this.infixThreadedNodes(root);
    }

    /**
     * 中序线索化二叉树
     *
     * @param heroNode2
     */
    public void infixThreadedNodes(HeroNode2 heroNode2) {

        if (heroNode2 == null) {
            return;
        }
        //一、先线索化左子树
        infixThreadedNodes(heroNode2.getLeft());

        //二、如果当前节点的左子节点为空,就设置leftType=1
        if (heroNode2.getLeft() == null) {
            //让当前节点的左指针指向前驱节点
            heroNode2.setLeft(pre);
            //设置leftType=1
            heroNode2.setLeftType(1);
        }

        //三、处理后继节点
        if (pre != null && pre.getRight() == null) {
            //让前驱节点的右指针指向当前节点
            pre.setRight(heroNode2);
            pre.setRightType(1);
        }
        //四、 !!!!! 每处理一个节点后,让当前节点是下一个节点的前驱节点
        pre = heroNode2;

        //五、再线索化右指针
        infixThreadedNodes(heroNode2.getRight());
    }

    /**
     * 遍历中序线索化后的二叉树
     */
    public void infixOrderThreaded() {
        //定义一个变量,存储当前遍历的节点,从root开始
        HeroNode2 temp = root;
        while (temp != null) {
            //循环找出左指针指向的是前驱节点而不是左子树的节点
            while (temp.getLeftType() == 0) {
                temp = temp.getLeft();
            }
            //找到了,直接输出
            System.out.println(temp);
            //循环找出右指针指向的是右子树而不是后继节点的节点
            while (temp.getRightType() == 1) {
                temp = temp.getRight();
                //输出
                System.out.println(temp);
            }
            temp = temp.getRight();
        }
    }

    /**
     * 无参的后序线索化二叉树重载方法
     */
    public void suffixThreadedNodes() {
        this.suffixThreadedNodes(root);
    }

    /**
     * 后序线索化二叉树,就是按照后序遍历的顺序来线索化二叉树
     *
     * @param heroNode2
     */
    public void suffixThreadedNodes(HeroNode2 heroNode2) {

        if (heroNode2 == null) {
            return;
        }

        //1、线索化左指针
        if (heroNode2.getLeftType() == 0) {
            suffixThreadedNodes(heroNode2.getLeft());
        }

        //2、再线索化右指针
        if (heroNode2.getRightType() == 0) {
            suffixThreadedNodes(heroNode2.getRight());
        }

        //3、如果当前节点的左子节点为空,就设置leftType=1
        if (heroNode2.getLeft() == null) {
            //让当前节点的左指针指向前驱节点
            heroNode2.setLeft(pre);
            //设置leftType=1
            heroNode2.setLeftType(1);
        }

        //4、处理后继节点
        if (pre != null && pre.getRight() == null) {
            //让前驱节点的右指针指向当前节点
            pre.setRight(heroNode2);
            pre.setRightType(1);
        }
        //5、 !!!!! 每处理一个节点后,让当前节点是下一个节点的前驱节点
        pre = heroNode2;
    }

    /**
     * 遍历后序线索化后的二叉树
     */
    public void suffixOrderThreaded() {
        //定义一个变量,存储当前遍历的节点,从root开始
        HeroNode2 temp = root;
        while (temp != null) {
            //循环找出左指针指向的是前驱节点而不是左子树的节点
            while (temp.getLeftType() == 0) {
                temp = temp.getLeft();
            }
            //找到了,直接输出
            System.out.println(temp);
            //循环找出右指针指向的是右子树而不是后继节点的节点
            while (temp.getRightType() == 1) {
                temp = temp.getRight();
                System.out.println(temp);
            }

            if (temp == root) {
                //若节点是二叉树的根,则其后继节点为空
                temp = null;
            } else if (temp == temp.getParent().getRight() || (temp == temp.getParent().getLeft() && temp.getParent().getRight() == null)) {
                // 若节点是其双亲的右孩子,或是其双亲的左孩子,且其双亲没有右子树,则其后继节点为其双亲节点
                temp = temp.getParent();
            } else if (temp == temp.getParent().getLeft() && temp.getParent().getRight() != null) {
                // 若节点是其双亲的左孩子,而且其双亲有右子树,则其节点为双亲右子树上,按后序遍历列出的第一个节点,该节点就是temp的后继节点
                List<HeroNode2> heroNode2s = new ArrayList<>();
                //后序遍历右子树的部分,并存入集合中
                temp.getParent().getRight().postOrderList(heroNode2s);
                //取出集合中的第一个
                temp = heroNode2s.get(0);
            }
        }
    }

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

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

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

    //根据no删除节点
    public void dropByNO(int no) {

        boolean flag;
        if (this.root != null) {
            flag = this.root.deleteByNO(no, this.root);
        } else {
            System.out.println("二叉树为空");
            return;
        }

        if (flag) {
            System.out.println("删除成功");
        } else {
            System.out.println("没有找到要删除的节点");
        }
    }

    //前序查找
    public void preSearch(int no) {
        //接收返回值
        HeroNode2 temp = null;
        if (this.root != null) {
            temp = this.root.prefixSearch(no);
        } else {
            System.out.println("二叉树为空");
        }
        if (temp == null) {
            System.out.println("没有找到no=" + no + "的节点");
        } else {
            System.out.println("找到了no=" + no + "的节点:" + temp);
        }
    }

    //中序查找
    public void inSearch(int no) {
        HeroNode2 temp = null;
        if (this.root != null) {
            temp = this.root.infixSearch(no);
        } else {
            System.out.println("二叉树为空");
        }
        if (temp == null) {
            System.out.println("没有找到no=" + no + "的节点");
        } else {
            System.out.println("找到了no=" + no + "的节点:" + temp);
        }
    }

    //后序查找
    public void sufSearch(int no) {
        HeroNode2 temp = null;
        if (this.root != null) {
            temp = this.root.suffixSearch(no);
        } else {
            System.out.println("二叉树为空");
        }
        if (temp == null) {
            System.out.println("没有找到no=" + no + "的节点");
        } else {
            System.out.println("找到了no=" + no + "的节点:" + temp);
        }
    }
}

 

实现节点的创建: 


import java.util.List;

public class HeroNode2 {
    private Integer no;
    private String name;
    private HeroNode2 left;
    private HeroNode2 right;
    private HeroNode2 parent;
    private int leftType;// 0表示指向左子树,1表示前驱节点
    private int rightType; // 0表示指向右子树,1表示后继节点

    public HeroNode2 getParent() {
        return parent;
    }

    public void setParent(HeroNode2 parent) {
        this.parent = parent;
    }

    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 HeroNode2(Integer no, String name, HeroNode2 left, HeroNode2 right) {
        this.no = no;
        this.name = name;
        this.left = left;
        this.right = right;
    }

    public HeroNode2(Integer no, String name) {
        this.no = no;
        this.name = name;
    }


    //前序遍历
    public void prefixOrder(){
        //根
        System.out.println("输出当前节点:" + this); //输出父节点
        //有左子节点就递归
        if (this.left != null){
            this.left.prefixOrder();
        }
        //有右子节点就递归
        if (this.right != null){
            this.right.prefixOrder();
        }
    }

    //中序遍历
    public void infixOrder(){
        //左
        if (this.left != null){
            this.left.infixOrder();
        }
        //根
        System.out.println("输出当前节点:" + this);
        //右
        if (this.right != null){
            this.right.infixOrder();
        }
    }

    //后序遍历
    public void suffixOrder(){

        //有左子节点就递归
        if (this.left != null){
            this.left.suffixOrder();
        }
        //有右子节点就递归
        if (this.right != null){
            this.right.suffixOrder();
        }
        //根
        System.out.println("输出当前节点:" + this);
    }

    //后序遍历添加进list
    public void postOrderList(List<HeroNode2> heroNode2s){
        if(this.left!=null && this.leftType==0){
            this.left.postOrderList(heroNode2s);
        }
        if(this.right!=null && this.rightType==0){
            this.right.postOrderList(heroNode2s);
        }
        heroNode2s.add(this);
    }

    //前序查找
    public HeroNode2 prefixSearch(int no){
        //根
        if (this.no == no){
            //System.out.println("找到了no="+no+"的节点:" + this);
            return this;
        }
        HeroNode2 temp = null;
        //左递归
        if (this.left != null){
            temp = this.left.prefixSearch(no);
        }
        //说明左子树找到了
        if (temp != null){
            return temp;
        }
        //右递归
        if (this.right != null){
            temp = this.right.prefixSearch(no);
        }
        return temp;
    }
    //中序查找
    public HeroNode2 infixSearch(int no){
        HeroNode2 temp = null;
        //左递归
        if (this.left != null){
            temp = this.left.infixSearch(no);
        }
        //说明左子树找到了
        if (temp != null){
            return temp;
        }
        //根
        if (this.no == no){
            //System.out.println("找到了no="+no+"的节点:" + this);
            return this;
        }
        //右递归
        if (this.right != null){
            temp = this.right.infixSearch(no);
        }
        return temp;
    }
    //后序查找
    public HeroNode2 suffixSearch(int no){
        HeroNode2 temp = null;
        //左递归
        if (this.left != null){
            temp = this.left.suffixSearch(no);
        }
        //说明左子树找到了
        if (temp != null){
            return temp;
        }
        //右递归
        if (this.right != null){
            temp = this.right.suffixSearch(no);
        }
        //根
        if (this.no == no){
            //System.out.println("找到了no="+no+"的节点:" + this);
            return this;
        }
        return temp;
    }

    //根据no删除指定节点
    //说明:1、如果删除的节点是叶子节点,那么直接删除叶子节点
    //说明:2、如果要删除的节点是父节点,则直接删除整棵子树
    //说明:3、如果要删除的节点是根节点,则报错,返回信息:不能删除根节点
    public boolean deleteByNO(int no, HeroNode2 root){

        boolean flag = false;
        //如果要删除根节点,直接将整棵树删除
        if (root.no == no){
            throw new RuntimeException("不能删除根节点");
        }

        if (this.left != null && this.left.no == no){
            this.left = null;
            return true;
        }

        if (this.right != null && this.right.no == no){
            this.right = null;
            return true;
        }

        //递归左子树
        if (this.left != null){
            flag = this.left.deleteByNO(no,root);
        }
        //递归右子树
        if (this.right != null){
            flag = this.right.deleteByNO(no,root);
        }

        return flag;
    }

    public HeroNode2() {
    }

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

    public Integer getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

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

    public HeroNode2 getLeft() {
        return left;
    }

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

    public HeroNode2 getRight() {
        return right;
    }

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

 

 测试前,中,后序线索化和遍历的代码:


/**
 * 测试中序线索化二叉树
 */
public class ThreadedBinaryTreeDemo {

    public static void main(String[] args) {
        //创建二叉树的节点
        HeroNode2 root = new HeroNode2(1,"tqf");
        HeroNode2 heroNode2 = new HeroNode2(3,"tom");
        HeroNode2 heroNode3 = new HeroNode2(6,"jack");
        HeroNode2 heroNode4 = new HeroNode2(8,"zmj");
        HeroNode2 heroNode5 = new HeroNode2(10,"tsl");
        HeroNode2 heroNode6 = new HeroNode2(14,"hyj");

        //将节点加入二叉树中,并指定他们之间的关系
        root.setLeft(heroNode2);
        root.setRight(heroNode3);
        heroNode2.setParent(root);
        heroNode3.setParent(root);

        heroNode2.setLeft(heroNode4);
        heroNode2.setRight(heroNode5);
        heroNode4.setParent(heroNode2);
        heroNode5.setParent(heroNode2);

        heroNode3.setLeft(heroNode6);
        heroNode6.setParent(heroNode3);

        System.out.println("中序遍历二叉树:");
        root.infixOrder(); // 8 3 10 1 14 6

        System.out.println("前序遍历二叉树:");
        root.prefixOrder(); // 1 3 8 10 6 14

        System.out.println("后序遍历二叉树:");
        root.suffixOrder(); //8 10 3 14 6 1

        //测试线索化
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setSize(6);
        threadedBinaryTree.setRoot(root);
        /*//直接调用重载方法进行中序线索化
        threadedBinaryTree.infixThreadedNodes();

        //测试中序线索化后的二叉树是否满足要求
        System.out.println("测试中序线索化后的二叉树是否满足要求==============================");
        System.out.println("8号节点的后继节点:" + heroNode4.getRight());// 3
        System.out.println("10号节点的前驱节点:" + heroNode5.getLeft()); // 3
        System.out.println("10号节点的后继节点:" + heroNode5.getRight()); // 1
        System.out.println("14号节点的前驱节点:" + heroNode6.getLeft()); // 1
        System.out.println("14号节点的后继节点:" + heroNode6.getRight()); // 6

        System.out.println("测试中序线索化后的二叉树的中序遍历结果===================================================");
        threadedBinaryTree.infixOrderThreaded(); // 8 3 10 1 14 6*/

        /*//直接调用重载方法进行前序线索化
        threadedBinaryTree.prefixThreadedNodes();

        //测试前序线索化后的二叉树是否满足要求
        System.out.println("测试前序线索化后的二叉树是否满足要求==============================");
        System.out.println("8号节点的前驱节点:" + heroNode4.getLeft());// 3
        System.out.println("8号节点的后继节点:" + heroNode4.getRight());// 10
        System.out.println("10号节点的前驱节点:" + heroNode5.getLeft()); // 8
        System.out.println("10号节点的后继节点:" + heroNode5.getRight()); // 6
        System.out.println("14号节点的前驱节点:" + heroNode6.getLeft()); // 6
        System.out.println("6号节点的后继节点:" + heroNode3.getRight()); // 14

        System.out.println("测试前序线索化后的二叉树的前序遍历结果===================================================");
        threadedBinaryTree.prefixOrderThreaded(); // 1 3 8 10 6 14*/
        

        //直接调用重载方法进行前序线索化
        threadedBinaryTree.suffixThreadedNodes();

        //测试前序线索化后的二叉树是否满足要求
        System.out.println("测试后序线索化后的二叉树是否满足要求==============================");
        System.out.println("8号节点的后继节点:" + heroNode4.getRight());// 10
        System.out.println("10号节点的前驱节点:" + heroNode5.getLeft()); // 8
        System.out.println("10号节点的后继节点:" + heroNode5.getRight()); // 3
        System.out.println("14号节点的前驱节点:" + heroNode6.getLeft()); // 3
        System.out.println("14号节点的后继节点:" + heroNode6.getRight()); // 6
        System.out.println("6号节点的后继节点:" + heroNode3.getRight()); // 1

        System.out.println("测试后序线索化后的二叉树的后序遍历结果===================================================");
        threadedBinaryTree.suffixOrderThreaded(); // 8 10 3 14 6 1
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

终有弱水替沧海i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值