数据结构与算法:树 线索化二叉树(中,前,后序)(三)

Tips: 采用java语言,关注博主,底部附有完整代码

工具:IDEA

本系列介绍的是数据结构:

这是第3篇目前计划一共有11篇:

  1. 二叉树入门
  2. 顺序二叉树
  3. 线索化二叉树 本篇
  4. 堆排序
  5. 赫夫曼树(一)
  6. 赫夫曼树(二)
  7. 赫夫曼树(三)
  8. 二叉排序树(BST)
  9. 平衡二叉排序树AVL
  10. 2-3树,2-3-4树,B树 B+树 B*树 了解
  11. 数据结构与算法:树 红黑树 (十一)

敬请期待吧~~

高光时刻

中序线索化前序线索化后续线索化
image-20220630160524721image-20220630164056323image-20220630165130764

回顾

先来回顾一下中序遍历

中序遍历gif

每一个结点有2个对象,最终导致叶子结点就有所浪费

当前一共有6个元素,分别是 [1,3,6,8,10,14], 最终就会导致有7个对象浪费(null)掉

出现问题

image-20220630160144118

线索化二叉树就是尽量少浪费对象,那么空对象如何处理呢?

如何解决

先来看完整流程图,在逐步分析:

解决问题gif

最终结果图:

image-20220630160524721

中序线索化二叉树 就是按照中序遍历的结果将

  • 叶子结点
  • 单子结点

按照中序的排列连接起来

当前中序遍历结果是 [8, 3, 10, 1, 14, 6]

以10举例,10是叶子结点,那么就按照中序遍历10左右的结点连接起来

  • 10左侧的值3 当作10的左子结点
  • 10右侧的值1 当作10的右子结点

因为10是叶子结点,如果连接起来的也叫左子结点和右子结点容易和真正有左子/右子结点的结点搞混

所以在线索化中,如果是通过连接连起来的就叫做前驱结点后继结点

例如 :

  • 10的前驱结点是3

  • 10的后继结点是1

  • 8的前驱结点是null

  • 8的后继结点是3

  • 3的左子结点是8

  • 3的右子结点是10

先按照树状图来看结点,如果他有左子结点或者右子结点,那么就按照排列好的顺序来,就是前驱结点和后继结点

直接来看一眼代码

中序

中序线索化

结点:

public class HeroNode {
    public int id;
    public String name;
    // 左子结点
    public HeroNode leftNode;
    // 右子结点
    public HeroNode rightNode;

    // [0 左子节点] [1 前驱节点]
    public int leftType;

    // [0 右子节点] [1 后继节点]
    public int rightType;

    public HeroNode(int id, String name) {
        this.id = id;
        this.name = name;
    }

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

Tree:

public class ThreadedBinaryTree {
   // root 结点
   private final HeroNode root;

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

    // 用于记录中序线索化上一个元素
    private HeroNode centerPre;

    public void threadedCenter(HeroNode node) {
        // 如果 node == null 就退出递归
        if (node == null) {
            return;
        }

        // 线索化左子树
        threadedCenter(node.leftNode);


        // 处理左子树 如果 leftNode == null 就认为他是前驱结点
        if (node.leftNode == null) {
            // 设置为前驱节点
            node.leftNode = centerPre;
            // 设置为前驱类型
            node.leftType = 1;
        }

        // 处理右子树 如果rightNode == null 就认为他是后继结点
        if (centerPre != null && centerPre.rightNode == null) {
            centerPre.rightNode = node;
            // 设置为后继结点类型
            centerPre.rightType = 1;
        }

        // pre后移
        centerPre = node;

        // 线索化右子树
        threadedCenter(node.rightNode);
    }
}

中序线索化遍历

public void showThreadedCenter(HeroNode node) {
        if (node == null) {
            return;
        }

        // 如果是左子结点就遍历
        if (node.leftType == 0) {
            showThreadedPre(node.leftNode);
        }
  
        System.out.println(node);

        // 如果是右子结点就遍历
        if (node.rightType == 0) {
            showThreadedPre(node.rightNode);
        }
    }

中序线索化和普通树结构遍历是类似的,只需要判断是否是左子 / 右子结点即可!

前序

前序遍历解决前解决后
image-20220630164041936image-20220630164050608image-20220630164056323

可以看到前序遍历,如果前序线索化后,只浪费了一个结点 !

前序线索化

# ThreadedBinaryTree.java
  
// 记录前序线索化上一个元素
public HeroNode prePre;

public void threadedPre(HeroNode node) {
    // 如果node == null 就退出递归
    if (node == null) {
        return;
    }

    // 和中序思路一样
    if (node.leftNode == null) {
        node.leftNode = prePre;
        node.leftType = 1;
    }
    if (prePre != null && prePre.rightNode == null) {
        prePre.rightNode = node;
        prePre.rightType = 1;
    }

    prePre = node;

    // 如果是左子结点点就去线索化 是前序结点就不走
    if (node.leftType == 0) {
        // 线索化左子树
        threadedPre(node.leftNode);
    }

    // 如果是右子结点就去线索化 是后继结点就不走
    if (node.rightType == 0) {
        // 线索化右子树
        threadedPre(node.rightNode);
    }
}
// endregion

这里思路和中序线索化一样,就不过多赘述了,来看看遍历吧

前序线索化遍历

# ThreadedBinaryTree.java
  
// region TODO 前序线索化遍历
// @author: android 超级兵
// @create: 2022/6/8 09:55
//
public void showThreadedPre() {
  showThreadedPre(root);
}
public void showThreadedPre(HeroNode root) {
    if (root == null) {
        return;
    }
    System.out.println(root);
    if (root.leftType == 0) {
        showThreadedPre(root.leftNode);
    }

    if (root.rightType == 0) {
        showThreadedPre(root.rightNode);
    }
}

后续

后续遍历线索前线索后
image-20220630165037838image-20220630165049816image-20220630165130764

代码思路和前序一样:直接看

后续线索化

# ThreadedBinaryTree.java
  
/*
 * 作者:android 超级兵
 * 创建时间: 6/7/22 5:48 PM
 */
private HeroNode lastPre;

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

public void threadedLast(HeroNode node) {
    if (node == null) {
        return;
    }

    // 如果是左子结点才遍历
    if (node.leftType == 0) {
        // 线索化左子树
        threadedLast(node.leftNode);
    }

    // 如果是右子结点才遍历
    if (node.rightType == 0) {
        // 线索化右子树
        threadedLast(node.rightNode);
    }

    if (node.leftNode == null) {
        node.leftNode = lastPre;
        node.leftType = 1;
    }

    if (lastPre != null && lastPre.rightNode == null) {
        lastPre.rightNode = node;
        lastPre.rightType = 1;
    }

    lastPre = node;
}
//endregion

后续线索化遍历

# ThreadedBinaryTree.java
  
public void showThreadedLast() {
    showThreadedLast(root);
}

public void showThreadedLast(HeroNode root) {
		// 左子结点就遍历
    if (root.leftType == 0) {
        showThreadedLast(root.leftNode);
    }

  	// 右子结点就遍历
    if (root.rightType == 0) {
        showThreadedLast(root.rightNode);
    }

    System.out.println(root);
}

完整代码

原创不易,您的点赞就是对我最大的支持!

其他树结构文章:

  1. 二叉树入门
  2. 顺序二叉树
  3. 线索化二叉树 本篇
  4. 堆排序
  5. 赫夫曼树(一)
  6. 赫夫曼树(二)
  7. 赫夫曼树(三)
  8. 二叉排序树(BST)
  9. 平衡二叉排序树AVL
  10. 2-3树,2-3-4树,B树 B+树 B*树 了解
  11. 数据结构与算法:树 红黑树 (十一)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

s10g

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

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

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

打赏作者

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

抵扣说明:

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

余额充值