前言
如需多次按前中后序遍历二叉树,使用线索二叉树可以加速二叉树的遍历。
一、方法可行性
二叉树的叶子节点存在一个或两个空指针,重复利用空指针可以帮助遍历二叉树。
二叉树节点总数: n2 + n0 + n1, 边总数: 2 * n2 + n1
由节点数=边数 + 1, 得到等式 2 *n2 + n1 + 1 = n2 + n1 + n0,也就是n2 = n0 - 1
二叉树中的空指针数量:2 * n0 + n1 > n2 + n0 + n1
所以空指针足够存储整棵树,也就能用来存储该节点的前驱与后继节点。
以中序遍历为例,草图中的紫边是利用原来的空节点指向节点的前驱,而红边则是指向节点的后继。
二、主要代码实现
1.新的树节点定义
typedef struct Node {
int data;
struct Node *lchild, *rchild;
int ltag, rtag;
} Node;
增加标志ltag,rtag位
ltag = rtag = 0, 表示该节点为正常节点(左右子树都不为空),lchild与rchild指向其左右孩子
ltag = 1, 表示lchild指向的是该节点的前驱pre
rtag = 1, 则表示rchild指向的是该节点的后继
通过rtag与rchild,可以将整个树的中序遍历顺序串联起来
2.为中序遍历增加线索
用正常二叉树的中序遍历,为叶子节点增加线索,pre用来保存中序遍历过程中的前驱结点
void build_thread(Node *root) {
if (root == NULL) return ;
static Node *pre = NULL;
build_thread(root->lchild);
if (root->lchild == NULL) {
root->lchild = pre;
root->ltag = THREAD;
}
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = root;
pre->rtag = THREAD;
}
pre = root;
build_thread(root->rchild);
}
3.中序遍历输出
用most_left()函数找到当前节点下方,最靠左的叶子节点,就可以沿着上一步建立的线索遍历二叉树
Node *most_left(Node *p) {
while (p != NULL && p->ltag == NORMAL && p->lchild != NULL) p = p->lchild;
return p;
}
void output(Node *root) {
Node *p = most_left(root);
while (p != NULL) {
printf("%d ", p->data);
if (p->rtag == THREAD) {
p = p->rchild;
} else {
p = most_left(p->rchild);
}
}
return ;
}
三、所有代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define NORMAL 0
#define THREAD 1
typedef struct Node {
int data;
struct Node *lchild, *rchild;
int ltag, rtag;
} Node;
Node *getNewNode(int val) {
Node *p = (Node *)malloc(sizeof(Node));
p->data = val;
p->lchild = p->rchild = NULL;
p->ltag = p->rtag = NORMAL;
return p;
}
Node *insert(Node *root, int val) {
if (root == NULL) return getNewNode(val);
if (root->data == val) return root;
if (val < root->data) root->lchild = insert(root->lchild, val);
else root->rchild = insert(root->rchild, val);
return root;
}
void build_thread(Node *root) {
if (root == NULL) return ;
static Node *pre = NULL;
build_thread(root->lchild);
if (root->lchild == NULL) {
root->lchild = pre;
root->ltag = THREAD;
}
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = root;
pre->rtag = THREAD;
}
pre = root;
build_thread(root->rchild);
}
Node *most_left(Node *p) {
while (p != NULL && p->ltag == NORMAL && p->lchild != NULL) p = p->lchild;
return p;
}
void output(Node *root) {
Node *p = most_left(root);
while (p != NULL) {
printf("%d ", p->data);
if (p->rtag == THREAD) {
p = p->rchild;
} else {
p = most_left(p->rchild);
}
}
return ;
}
void in_order(Node *root) {
if (root == NULL) return ;
if (root->ltag == NORMAL) in_order(root->lchild);
printf("%d ",root->data);
if (root->rtag == NORMAL) in_order(root->rchild);
}
void clear(Node *root) {
if (root == NULL) return ;
if (root->ltag == NORMAL) clear(root->lchild);
if (root->rtag == NORMAL) clear(root->rchild);
free(root);
}
int main() {
srand(time(0));
#define MAX_OP 20
Node *root = NULL;
for (int i = 0; i < MAX_OP; i++) {
int val = rand() % 100;
root = insert(root, val);
}
build_thread(root);
output(root), printf("\n");
in_order(root); //普通中序遍历对比
#undef MAX_OP
clear(root);
return 0;
}