1.背景介绍
现有一棵结点数目为n的二叉树,采⽤二叉链表的形式存储,对于每个结点均有指向左右孩子的两个指针域。
结点为n的二叉树一共有n-1条有效分支路径。那么,则二叉链表中存在2n - ( n-1 ) = n + 1个空指针域。那么,这些空指针造成了空间浪费。
当对二叉树进行中序遍历时可以得到二叉树的中序序列。如图所示,二叉树的中序遍历结果为DBEAC,可以得知A的前驱结点为E,后继结点为C。
这种关系的获得是建⽴在完成遍历后得到的,那么可不可以在建⽴二叉树时就记录下前驱后继的关系呢,那么在后续寻找前驱结点和后继结点时将⼤⼤提升效率。
2.线索化
将某结点的空指针域指向该结点的前驱后继,定义规则如下:
若结点的左子树为空,则该结点的左孩子指针指向其前驱结点。
若结点的右子树为空,则该结点的右孩子指针指向其后继结点。
这种指向前驱和后继的指针称为线索。将一棵普通二叉树以某种次序遍历,并添加线索的过程称为线索化。
3.线索化的改进
可以将一棵二叉树线索化为一棵线索二叉树,那么新的问题产⽣了。我们如何区分一个结点的 lchild指针是指向左孩子还是前驱结点呢?
为了解决这一问题,现需要添加
标志位
ltag,rtag。并定义规则如下:
ltag为0时,指向左孩子,为1时指向前驱
rtag为0时,指向右孩子,为1时指向后继
在遍历过程中,如果当前结点没有左孩子,需要将该结点的 lchild 指针指向遍历过程中的前一个结点,所以在遍历过程中,要设置两个指针,一个指针(名为 pre ),时刻指向当前访问结点(cur)的前一个结点。
4. 线索化的优势
递归遍历需要使⽤系统栈,非递归遍历需要使⽤内存中的空间来帮助遍历,⽽线索化之后就不需 要这些辅助了,直接可以像遍历数组一样遍历。
线索二叉树核心目的在于加快查找结点的前驱和后继的速度。如果不使⽤线索的话,当查找一个 结点的前驱与后继需要从根节点开始遍历,当然,如果二叉树数据量较小时,可能线索化之后作⽤不大,但是当数据量很⼤时,线索化所带来的性能提升就会比较明显。
代码:
threadedB Tree.h
#ifndef THREADED_BTREE_H
#define THREADED_BTREE_H
typedef int Element;
// 线索二叉树的节点结构
typedef struct treeNode {
Element data;
struct treeNode *left;
struct treeNode *right;
int lTag; // 0表示left指向左节点,1表示left指向前驱
int rTag; // 0表示right指向右节点,1表示right指向后继
}TBTNode;
// 二叉树的描述信息结构
typedef struct {
TBTNode *root; // 二叉树的根节点
int count; // 二叉树的节点个数
}ThreadedBTree;
ThreadedBTree *createThreadedBTree(TBTNode *root);
void releaseThreadedBTree(ThreadedBTree *tree);
TBTNode *createTBTNode(Element e);
void insertThreadedBTree(ThreadedBTree *tree, TBTNode *parent, TBTNode *left, TBTNode *right);
void visitTBTNode(TBTNode *node);
// 中序线索化树,在中序遍历的过程中,建立左右孩子为空指针的值的确定
void inOrderThreadedTree(ThreadedBTree *tree);
// 按照已经线索化后的树,进行中序遍历
void inOrderTBTree(ThreadedBTree *tree);
#endif
threadedB Tree.c
#include <stdio.h>
#include <stdlib.h>
#include "threadedBTree.h"
ThreadedBTree *createThreadedBTree(TBTNode *root) {
ThreadedBTree *tree = (ThreadedBTree *) malloc(sizeof(ThreadedBTree));
if (tree == NULL) {
fprintf(stderr, "tree malloc failed!\n");
return NULL;
}
if (root) {
tree->root = root;
tree->count = 1;
} else {
tree->root = NULL;
tree->count = 0;
}
return tree;
}
// 后序遍历释放节点,但在找后继节点时,需要找非线索化的节点
static void freeTBTNode(ThreadedBTree *tree, TBTNode *node) {
if (node) {
if (node->lTag == 0) {
freeTBTNode(tree, node->left);
}
if (node->rTag == 0) {
freeTBTNode(tree, node->right);
}
free(node);
tree->count--;
}
}
/* 释放线索树 */
void releaseThreadedBTree(ThreadedBTree *tree) {
if (tree) {
freeTBTNode(tree, tree->root);
printf("tree have %d node!\n", tree->count);
}
}
/* 产生新节点,初始化左右指针为NULL */
TBTNode *createTBTNode(Element e) {
TBTNode *node = (TBTNode *) malloc(sizeof(TBTNode));
if (node == NULL) {
fprintf(stderr, "tree node malloc failed!\n");
return NULL;
}
node->data = e;
node->left = node->right = NULL;
node->lTag = node->rTag = 0;
return node;
}
/* 树的节点访问 */
void visitTBTNode(TBTNode *node) {
if (node) {
printf("\t%c", node->data & 0xff);
}
}
void insertThreadedBTree(ThreadedBTree *tree, TBTNode *parent, TBTNode *left, TBTNode *right) {
if (tree && parent) {
parent->left = left;
parent->right = right;
if (left)
tree->count++;
if (right)
tree->count++;
}
}
static TBTNode *pre = NULL;
static void inOrderThreading(TBTNode *node) {
if (node) {
inOrderThreading(node->left);
if (node->left == NULL) { // 更新当前节点的前驱
node->lTag = 1;
node->left = pre;
}
// 当前节点会不会是前面那个节点后继节点
if (pre && pre->right == NULL) {
pre->rTag = 1;
pre->right = node;
}
pre = node;
inOrderThreading(node->right);
}
}
void inOrderThreadedTree(ThreadedBTree *tree) {
if (tree) {
inOrderThreading(tree->root);
}
}
void inOrderTBTree(ThreadedBTree *tree) {
TBTNode *node = tree->root;
while (node) {
while (node->lTag == 0) {
node = node->left;
}
visitTBTNode(node);
// 一直向右开始遍历,只要右边是后继标记,就查看
while (node->rTag && node->right) {
node = node->right;
visitTBTNode(node);
}
node = node->right;
}
}
main.c
#include <stdio.h>
#include "threadedBTree.h"
ThreadedBTree *initTree() {
TBTNode *nodeA = createTBTNode('A');
TBTNode *nodeB = createTBTNode('B');
TBTNode *nodeC = createTBTNode('C');
TBTNode *nodeD = createTBTNode('D');
TBTNode *nodeE = createTBTNode('E');
TBTNode *nodeF = createTBTNode('F');
TBTNode *nodeG = createTBTNode('G');
TBTNode *nodeH = createTBTNode('H');
TBTNode *nodeK = createTBTNode('K');
ThreadedBTree *tree = createThreadedBTree(nodeA);
insertThreadedBTree(tree, nodeA, nodeB, nodeE);
insertThreadedBTree(tree, nodeB, NULL, nodeC);
insertThreadedBTree(tree, nodeE, NULL, nodeF);
insertThreadedBTree(tree, nodeC, nodeD, NULL);
insertThreadedBTree(tree, nodeF, nodeG, NULL);
insertThreadedBTree(tree, nodeG, nodeH, nodeK);
return tree;
}
int main() {
ThreadedBTree *tree = initTree();
// 1. 对原二叉树进行中序线索化,n + 1个空节点,按照左孩子指针指向前驱,右孩子指针指向后继
inOrderThreadedTree(tree);
// 2. 根据线索化后的树,进行中序遍历
inOrderTBTree(tree);
releaseThreadedBTree(tree);
return 0;
}
在CLion上的运行结果: