In the topic of data structure and algorithm, the related knowledge of linked list and binary tree and the implementation of sequential storage and chain storage of binary tree have been introduced:
1. Background
- 1. The above figure is a binary tree structure. In the left and right subtrees of other nodes except node ABCD,
至少
there is a pointer to空
;- 2. When designing the structure of the nodule to save data, the
leftChild
sumrightChild
pointer will be designed to point to the左子树
sum右子树
;leftChild
3. If the sumrightChild
pointer of the node points toNULL
, the applied pointer is taken浪费
;- 4. In order for
合理的利用
the leftChild and rightChild pointers to point to one when they do not point , we can point them to OR based on有意义
the preorder,中序
, and postorder traversal logic of the binary tree .前驱
后续
As shown below:
- 1.
中序遍历
The sequence obtained after use in the above figure is:H->D->I->B->J->E->A->F->C->G
;- 2. Judging from the results obtained by the in-order traversal above, we can regard
中序遍历
the obtained results结果
as one链表
traversal result;- 3. Compare the left pointer leftChild and the right pointer rightChild to
前驱
pointers and后续
pointers in a doubly linked list;- 4. So we get a "H->D->I->B->J->E->A->F->C->G"
双向链表
;- 5. In this way, we can fully use the wasted
leftChild
andrightChild
pointer space利用
.
Through the above analysis, we solved the following two problems:
- 1. The problem of wasting space;
- 2. Under in-order traversal, quickly obtain the predecessor and subsequent problems of the node.
Thinking: How do we distinguish whether the leftChild(rightChild) of a node points to the left child (right child) or the predecessor (successor)?
- 1. The node E in the figure, its leftChild points to the left child J, and its rightChild points to the subsequent node A;
- 2. Node I, its leftChild points to the predecessor D, and its rightChild points to the subsequent B;
- 3. Node B, its leftChild points to the left child D, and its rightChild points to the right child E;
Second, the design of clued binary tree nodes
We need to design nodes reasonably, and design a flag for leftChild and rightChild to record whether they point to the left and right children, or the predecessor and subsequent nodes.
ltag
1. Add and use in the nodertag
to marklchild(rchild)
the point to be左孩子(右孩子)
or前驱(后续)
;
3. Code Implementation
1. Prepare
1. Define some states and data types
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 //存储空间初始分配量
//Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int Status;
typedef char CElemType;
//Link==0表示指向左右孩子指针
//Thread==1表示指向前驱或后继的线索
typedef enum {Link,Thread} PointerTag;
复制代码
2. Create a character array for building a binary tree
//字符型以空格符为空
CElemType Nil='#';
int indexs = 1;
typedef char String[24]; //0号单元存放串的长度
String str;
Status StrAssign(String T,char *chars)
{
int i;
if(strlen(chars)>MAXSIZE)
return ERROR;
else
{
T[0]=strlen(chars);
for(i=1;i<=T[0];i++)
T[i]=*(chars+i-1);
return OK;
}
}
复制代码
2. Node implementation
//线索二叉树存储结点结构
typedef struct BiThrNode{
//数据
CElemType data;
//左右孩子指针
struct BiThrNode *lchild,*rchild;
//左右标记
PointerTag LTag;
PointerTag RTag;
}BiThrNode,*BiThrTree;
复制代码
3. Construct the binary tree
Status CreateBiThrTree(BiThrTree *T){
CElemType h;
//获取字符
h = str[indexs++];
if (h == Nil) {
*T = NULL;
}else{
*T = (BiThrTree)malloc(sizeof(BiThrNode));
if (!*T) {
exit(OVERFLOW);
}
//生成根结点(前序)
(*T)->data = h;
//递归构造左子树
CreateBiThrTree(&(*T)->lchild);
//存在左孩子->将标记LTag设置为Link
if ((*T)->lchild) (*T)->LTag = Link;
//递归构造右子树
CreateBiThrTree(&(*T)->rchild);
//存在右孩子->将标记RTag设置为Link
if ((*T)->rchild) (*T)->RTag = Link;
}
return OK;
}
复制代码
- 1.
str
It isStrAssign
constructed by a function字符数组
, and the第0
position of the array stores the array长度
;- 2. The initial value of indexes is
1
, with recursion依次递增
;- 3.
前序
The logic used is first created双亲结点
, then constructed左子树
, and finally constructed右子树
;- 4. After the construction
左子树(右子树)
, judge whether the current node exists左孩子(右孩子)
. If there is左孩子(右孩子)
, the current node'sltag(rtag)
mark isLink
, which means itlchild(rchild)
points to左孩子(右孩子)
.
4. In-order traversal of the binary tree T, the in-order threading
BiThrTree pre; //全局变量,始终指向刚刚访问过的结点
//中序遍历进行中序线索化
void InThreading(BiThrTree p){
if (p) {
//递归左子树线索化
InThreading(p->lchild);
//当前结点无左孩子
if (!p->lchild) {
//标记前驱
p->LTag = Thread;
//指向前驱
p->lchild = pre;
}else
{
p->LTag = Link;
}
//前面刚访问过的结点(pre)没有右孩子
if (!pre->rchild) {
//标记后继
pre->RTag = Thread;
//pre结点的rchild指针指向后继(当前结点p)
pre->rchild = p;
}else
{
pre->RTag = Link;
}
//记录当前结点,方便下一次操作
pre = p;
//递归右子树线索化
InThreading(p->rchild);
}
}
复制代码
- 1. First recursively
左子树
carry out clues;- 2. Operate on the current node p, if the current node has
左孩子
, then mark ltag asThread
and willlchild
point to the node of the last operationpre
; otherwise, ltag is marked asLink
;- 3. Operate on the node of the last
pre
operation, if there右孩子
is pre, mark rtag asThread
, andrchild
point to pre后续p
; otherwise, mark rtag asLink
;- 4.
记录
The current operation is结点
convenient for the next operation;- 5. Recursive
右子树
threading.
The final result obtained through the above operation is as follows:
Through the above threading operation on the in-order traversal of the binary tree, we get an equivalent
双向链表
structure, so we add a head node to it. The advantages of adding a head node are:双向遍历
, that is, you can start from the first node and follow the subsequent traversal; you can also start from the last node and follow the predecessor.
- 1. Point the head node to the
lchild
binary tree根结点
;rchild
2. Traverse the pointer of the head node in order最后一个结点
;- 3. Point to
第一个结点
the in-order traversal ;lchild
头结点
- 4. Point to
最后一个结点
the in-order traversal .rchild
头结点
Threaded binary tree code implementation for adding head nodes
Status InOrderThreading(BiThrTree *Thrt , BiThrTree T){
//创建头结点
*Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
if (! *Thrt) {
exit(OVERFLOW);
}
//头结点的ltag指向Link,rtag指向Thread
(*Thrt)->LTag = Link;
(*Thrt)->RTag = Thread;
//rchild指向后续,暂时指向自己
(*Thrt)->rchild = (*Thrt);
//若二叉树空
if (!T) {
//lchild指向左孩子,即自己
(*Thrt)->lchild = *Thrt;
}else{
//二叉树不为空,lchild指向二叉树的根结点
(*Thrt)->lchild = T;
//记录头结点,方便后面的线索化操作
pre = (*Thrt);
//中序遍历进行线索化
InThreading(T);
//经过中序遍历的线索化后pre最终指向了最后一个结点,pre的rchild指向头结点
pre->rchild = *Thrt;
//最后一个结点线索化
pre->RTag = Thread;
//头结点的rchild指向pre
(*Thrt)->rchild = pre;
}
return OK;
}
复制代码
5. Inorder Traversal of Threaded Binary Trees
Status visit(CElemType e)
{
printf("%c ",e);
return OK;
}
Status InOrderTraverse_Thr(BiThrTree T){
BiThrTree p;
p = T->lchild; //p指向根结点,从根结点开始遍历
//空树或遍历结束时,p == T
while(p!=T)
{
//从当前子树的根结点一路找到最左边的结点,即中序遍历的第一个结点
while(p->LTag == Link) {
p = p->lchild;
}
//打印当前结点
if(!visit(p->data)) {
return ERROR;
}
//从当前结点一路找到后续结点,即中序遍历的最后一个结点
while(p->RTag == Thread && p->rchild != T)
{
p = p->rchild;
visit(p->data);
}
//遍历下一个子树
p = p->rchild;
}
return OK;
}
复制代码
6. Debug
BiThrTree H,T;
StrAssign(str,"ABDH##I##EJ###CF##G##");
CreateBiThrTree(&T); //按前序产生二叉树
InOrderThreading(&H,T); //中序遍历,并中序线索化二叉树
InOrderTraverse_Thr(H);
复制代码
4. Summary
Binary tree threading is to solve two problems:
- 1. The
空间浪费
problem;快速获取
2. The problem of nodes under in-order traversal前驱和后续
.