Tips: 采用java语言,关注博主,底部附有完整代码
工具:IDEA
本系列介绍的是数据结构: 树
这是第3篇目前计划一共有11篇:
- 二叉树入门
- 顺序二叉树
- 线索化二叉树 本篇
- 堆排序
- 赫夫曼树(一)
- 赫夫曼树(二)
- 赫夫曼树(三)
- 二叉排序树(BST)
- 平衡二叉排序树AVL
- 2-3树,2-3-4树,B树 B+树 B*树 了解
- 数据结构与算法:树 红黑树 (十一)
敬请期待吧~~
高光时刻
中序线索化 | 前序线索化 | 后续线索化 |
---|---|---|
![]() | ![]() | ![]() |
回顾
先来回顾一下中序遍历
每一个结点有2个对象,最终导致叶子结点就有所浪费
当前一共有6个元素,分别是 [1,3,6,8,10,14], 最终就会导致有7个对象浪费(null)掉
出现问题
线索化二叉树就是尽量少浪费对象,那么空对象如何处理呢?
如何解决
先来看完整流程图,在逐步分析:
最终结果图:
中序线索化二叉树 就是按照中序遍历的结果将
- 叶子结点
- 单子结点
按照中序的排列连接起来
当前中序遍历结果是 [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);
}
}
中序线索化和普通树结构遍历是类似的,只需要判断是否是左子 / 右子结点即可!
前序
前序遍历 | 解决前 | 解决后 |
---|---|---|
![]() | ![]() | ![]() |
可以看到前序遍历,如果前序线索化后,只浪费了一个结点 !
前序线索化
# 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);
}
}
后续
后续遍历 | 线索前 | 线索后 |
---|---|---|
![]() | ![]() | ![]() |
代码思路和前序一样:直接看
后续线索化
# 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);
}
原创不易,您的点赞就是对我最大的支持!
其他树结构文章: