深度遍历不仅仅有递归的方法噢,还有通过建立线索二叉树进行遍历的方法!
什么是线索二叉树
在二叉树的结点上加上线索的二叉树称为线索二叉树,本文所讲述的线索二叉树主要用于中序遍历。
定义二叉树的时候,我们定义的每一个结点都是属于同一个结构体的,对于二叉树中的叶子结点来说,它的两个子结点都是指向NULL的,这样就造成了结构体的资源浪费。如果能将这些为空的子结点利用起来,将会大大增强二叉树的资源利用率!
常规的二叉树如图所示:
可以看到二叉树的许多结点的子结点指向的都是NULL,这些资源是没有被利用的。如果能将其利用起来,将大大增强二叉树的资源利用率。
线索二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继。
线索二叉树是利用二叉树的空链指针完成前驱和后继的添加,利用前驱后继完成中序遍历。
线索二叉树的实现形式
本文使用树的生成方式可以参照我的blog,其具体结点状况如下:
https://blog.csdn.net/weixin_44791964/article/details/98229328
线索二叉树相比于普通二叉树增加了一个结点p,该结点实际上并不属于真正的树,该结点的存在有助于线索二叉树的生成与遍历。
在最初状态下,p结点左子树指向根节点,右子树指向自身。
根据每个结点的定义可知,每个结点接有两个子树,分别为左子树和右子树,左子树所连接的结点是要进行遍历的上一个结点,右子树所连接的结点是要进行遍历的下一个结点。
但是对于左下角的结点c而言,其为中序遍历的第一个结点,无上一个结点,因此将其左结点定义为结点p。
但是对于右下角的结点d而言,其为中序遍历的最后一个结点,无下一个结点,因此将其右结点定义为结点p。
此时原子树可以形成如下图所示的闭环。
线索二叉树的代码实现
线索二叉树的初始化
与普通二叉树不同的是,线索二叉树的结构体增加了两个元素,分别是ltag和rtag,分别用于指示左子树与右子树此时指向的是子结点还是线索
其具体定义如下:
//Link代表0,表示此时指向左右孩子
//Thread代表1,表示此时指向前驱后继的指针
typedef enum {Link,Thread} PointerTag;
struct Node{
Ele data; //data用于存储信息
struct Node* LeftChild;
struct Node* RightChild;
PointerTag ltag;
PointerTag rtag;
};
typedef struct Node Node, *Tree;
在完成结构体的定义后,要进行线索二叉树的初始化,在初始化的时候,我们先假设所有结点的子树都指向左右孩子。
void init(Tree* t){ //当输入的字符为空格的时候,代表叶子结点
//输入其它的字符代表当前结点的data值
//输入顺序为当前结点、左结点、右结点。
Ele c;
scanf("%c", &c);
if (' ' == c){
*t = NULL;
}
else{
*t = (Tree)malloc(sizeof(Node));
if (*t == NULL){
printf("内存分配失败");
exit(1);
}
(*t)->data = c;
(*t)->ltag = Link;
(*t)->rtag = Link;
init(&(*t)->LeftChild); //生成左结点
init(&(*t)->RightChild); //生成右结点
}
}
此时输入合适的字符即可生成二叉树。
线索的串联
在进行所有的结点串联前,应当定义全局变量。
typedef struct Node Node, *Tree;
Tree pre;
该结点用于在进行结点串联时保存上一个结点的地址。
与此同时,由于线索二叉树相对于普通二叉树添加了一个p结点,需要在main函数中进行定义。
int main(){
Tree p,BiTree = NULL;
init(&BiTree);
InitThreading(&p,BiTree);
printf("中序遍历的结果为:");
scan(p);
printf("\n");
return 0;
}
在main函数中,p元素被传入InitThreading函数进行初始化并完成线索的串联过程。
InitThreading函数的代码如下:
void InitThreading(Tree *p, Tree T){
*p = (Tree)malloc(sizeof(Node));
(*p)->ltag = Link;
(*p)->rtag = Thread;
(*p)->RightChild = *p; //p结点右子树指向自身
if (!T){
(*p)->LeftChild = *p; //当树为空时,p结点的左右子树都指向自身
}
else{
(*p)->LeftChild = T; //p结点左子树指向根节点
pre = *p; //在最初情况下pre指向p
Threading(T);
pre->RightChild = *p;
pre->rtag = Thread; //使得中序遍历最后一个结点的右子树指向结点p
(*p)->RightChild = pre; //使得结点p的右子树指向中序遍历最后一个结点。
}
}
在InitThreading函数中,Threading函数用于线索的串联。
具体代码如下:
void Threading(Tree t){
if (t != NULL){
Threading(t->LeftChild);
if (!t->LeftChild){ //如果没有左孩子,则将左孩子指向上一个访问的结点
t->ltag = Thread;
t->LeftChild = pre;
}
if (!pre->RightChild){ //如果没有左孩子,则将左孩子指向上一个访问的结点
pre->rtag = Thread;
pre->RightChild = t;
}
pre = t;
Threading(t->RightChild);
}
}
全部实现代码
#include <stdio.h>
#include <stdlib.h>
typedef char Ele;
//Link代表0,表示此时指向左右孩子
//Thread代表1,表示此时指向前驱后继的指针
typedef enum {Link,Thread} PointerTag;
struct Node{
Ele data; //data用于存储信息
struct Node* LeftChild;
struct Node* RightChild;
PointerTag ltag;
PointerTag rtag;
};
typedef struct Node Node, *Tree;
Tree pre;
void init(Tree* t){ //当输入的字符为空格的时候,代表叶子结点
//输入其它的字符代表当前结点的data值
//输入顺序为当前结点、左结点、右结点。
Ele c;
scanf("%c", &c);
if (' ' == c){
*t = NULL;
}
else{
*t = (Tree)malloc(sizeof(Node));
if (*t == NULL){
printf("内存分配失败");
exit(1);
}
(*t)->data = c;
(*t)->ltag = Link;
(*t)->rtag = Link;
init(&(*t)->LeftChild); //生成左结点
init(&(*t)->RightChild); //生成右结点
}
}
void Threading(Tree t){
if (t != NULL){
Threading(t->LeftChild);
if (!t->LeftChild){ //如果没有左孩子,则将左孩子指向上一个访问的结点
t->ltag = Thread;
t->LeftChild = pre;
}
if (!pre->RightChild){ //如果没有左孩子,则将左孩子指向上一个访问的结点
pre->rtag = Thread;
pre->RightChild = t;
}
pre = t;
Threading(t->RightChild);
}
}
void InitThreading(Tree *p, Tree T){
*p = (Tree)malloc(sizeof(Node));
(*p)->ltag = Link;
(*p)->rtag = Thread;
(*p)->RightChild = *p; //p结点右子树指向自身
if (!T){
(*p)->LeftChild = *p; //当树为空时,p结点的左右子树都指向自身
}
else{
(*p)->LeftChild = T; //p结点左子树指向根节点
pre = *p; //在最初情况下pre指向p
Threading(T);
pre->RightChild = *p;
pre->rtag = Thread; //使得中序遍历最后一个结点的右子树指向结点p
(*p)->RightChild = pre; //使得结点p的右子树指向中序遍历最后一个结点。
}
}
void visit(Ele e){
printf("%c", e);
}
void scan(Tree p){
Tree temp;
temp = p->LeftChild; //取得根节点
while (temp != p){
while (temp->ltag == Link){ //取得线索二叉树遍历的第一个结点
temp = temp->LeftChild;
}
visit(temp->data);
while (temp->rtag == Thread && temp->RightChild != p){
temp = temp->RightChild;
visit(temp->data);
}
temp = temp->RightChild;
}
}
int main(){
Tree p,BiTree = NULL;
init(&BiTree);
InitThreading(&p,BiTree);
printf("中序遍历的结果为:");
scan(p);
printf("\n");
return 0;
}
实现结果为:
GITHUB下载连接
https://github.com/bubbliiiing/Data-Structure-and-Algorithm
希望得到朋友们的喜欢。
有问题的朋友可以提问噢。