7.6、线索二叉树
由于二叉树结构中各种遍历(中序、前序、后序、层次)不知道结点的前驱和后继,可以利用那些没有孩子的结点的指针指向它的前驱和后继;没有前驱或者后继就指向NULL
让 左孩子指向前驱 右孩子指向后继
如果在存储上,需要定义两个变量来表示这个结点指向的是前驱和后继还是孩子结点
typedef struct LinkTree{
ElemType data;
struct LinkTree *lchild,*rchild;
int ltag,rtag;//0表示是孩子,1表示是前驱和后继
}*LinkTree,TreeNode;
中序遍历:
先序遍历:
后序遍历:
代码实现
全局配置
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
#define MaxSize 100
#define ElemType int
#define boolean int
#define true 1
#define false 0
typedef struct LinkTree{
ElemType data;
struct LinkTree *lchild,*rchild;
int ltag,rtag;
}*LinkTree,TreeNode;
//初始化一颗二叉树
boolean InitTree(LinkTree *root,ElemType data){
*root = (LinkTree)malloc(sizeof(TreeNode));
if(root == NULL) return false;
(*root)->data = data;
(*root)->lchild = NULL;
(*root)->rchild = NULL;
(*root)->ltag = 0;
(*root)->rtag = 0;
return true;
}
//添加一个左孩子
boolean AddLeftChild(ElemType data,TreeNode *Node){
TreeNode *lchild = (TreeNode *)malloc(sizeof(TreeNode));
if(lchild == NULL) return false;
lchild->data = data;
lchild->lchild = NULL;
lchild->rchild =NULL;
lchild->ltag = 0;
lchild->rtag = 0;
Node->lchild = lchild;
return true;
}
//添加一个右孩子
boolean AddRightChild(ElemType data,TreeNode *Node){
TreeNode *right = (TreeNode *)malloc(sizeof(TreeNode));
if(right == NULL) return false;
right->data = data;
right->lchild = NULL;
right->rchild =NULL;
right->ltag = 0;
right->rtag =0;
Node->rchild = right;
return true;
}
中序线索化
//线索化的全局变量
TreeNode *pre = NULL;//线索化遍历的前驱结点
//线索化操作
void Vist(TreeNode *node){
if(node->lchild == NULL){//左孩子为NULL,就指向它的前驱
node->lchild = pre;
node->ltag = 1;
}
if(pre != NULL && pre->rchild == NULL){//如果前驱结点的右孩子为NULL,就指向它的后继
pre->rchild = node;
pre->rtag = 1;
}
pre = node;
}
//中序线索化
void InThred(LinkTree T){
if(T != NULL){
InThred(T->lchild);
Vist(T);
InThred(T->rchild);
}
}
先序线索化
//线索化的全局变量
TreeNode *pre = NULL;//线索化遍历的前驱结点
//先序遍历
void PreTread(LinkTree T){
if(T != NULL){
Vist(T);
if(T->ltag == 0)//注意:先序线索化时候,要注意循环寻找的问题
PreTread(T->lchild);
if(T->rtag == 0)
PreTread(T->rchild);
}
}
//创建先序线索化
void CreatePreThread(LinkTree T){
pre = NULL;
if(T != NULL){
PreTread(T);
if(pre -> rchild == NULL){
pre->rtag = 1;
}
}
}
后序线索化
//线索化的全局变量
TreeNode *pre = NULL;//线索化遍历的前驱结点
//后序遍历
void PostThread(LinkTree T){
if(T != NULL){
PostThread(T->lchild);
PostThread(T->rchild);
Vist(T);
}
}
//创建后序线索化
void CreatePostOrder(LinkTree T){
pre = NULL;
if(T != NULL){
PostThread(T);
if(pre->rchild == NULL){
pre->rtag = 1;
}
}
}
中序线索二叉树的前驱、后继、遍历
//遍历打印
void visit(TreeNode *p){
printf("%d,",p->data);
}
//找到以p为根的第一个被中序遍历的结点
TreeNode * FirstNode(TreeNode *p){
//循环寻找最左下的结点
while(p->ltag == 0) p = p->lchild;
return p;
}
//在中序二叉树中寻找它的后继结点
TreeNode * NextNode(TreeNode *p){
//右子树
if(p->rtag == 0) return FirstNode(p->rchild);
else return p->rchild;
}
//找到以p为根的最后一个被中序遍历的结点
TreeNode * LastNode(TreeNode *p){
//循环寻找最右下的结点
while(p->rtag == 0) p = p->rchild;
return p;
}
//在中序二叉树中寻找它的前驱结点
TreeNode * PreNode(TreeNode *p){
//左子树
if(p->ltag == 0) return LastNode(p->lchild);
else return p->lchild;
}
//对线索二叉树进行中序遍历
void InOrder(TreeNode *T){
for(TreeNode *p = FirstNode(T);p != NULL;p = NextNode(p)){
visit(p);
}
}
//对线索二叉树进行逆中序遍历
void InOrder1(TreeNode *T){
for(TreeNode *p = LastNode(T);p != NULL;p = PreNode(p)){
visit(p);
}
}
int main(){
LinkTree root;
InitTree(&root,1);
AddLeftChild(2,root);
AddRightChild(3,root);
AddLeftChild(4,root->lchild);
AddRightChild(5,root->lchild);
AddLeftChild(6,root->rchild);
AddRightChild(7,root->lchild->lchild);
//中序线索化
CreateInThread(root);
TreeNode *p = NextNode(root);
printf("root的中序后继结点:%d\n",p->data);
TreeNode *q = PreNode(root);
printf("root的中序前驱结点:%d\n",q->data);
//中序线索二叉树遍历
printf("中序线索二叉树遍历: ");
InOrder(root);
printf("\n");
//中序线索二叉树逆中序遍历
printf("中序线索二叉树逆中序遍历: ");
InOrder1(root);
}
//结果
root的中序后继结点:6
root的中序前驱结点:5
中序线索二叉树遍历: 4,7,2,5,1,6,3,
中序线索二叉树逆中序遍历: 3,6,1,5,2,7,4,
中序线索二叉树 | 先序线索二叉树 | 后序线索二叉树 | |
---|---|---|---|
找前驱 | OK | NO | OK |
找后继 | OK | OK | NO |