线索二叉树的存储结构及其作用(C语言)

Posted bfhonor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线索二叉树的存储结构及其作用(C语言)相关的知识,希望对你有一定的参考价值。

一、 线索二叉树

(一)线索二叉树的作用

  • 能否从一个指定结点开始中序遍历?
  • 思路:
    从根节点出发,重新进行一次中序遍历,指针q记录当前访问的结点,指针 pre 记录上一个被访问的结点
    ①当q == p时,pre为前驱
    ②当pre == p时,q为后继
  • 缺点:找前驱、后继很不方便;遍历操作必须从根节点开始。
//辅助全局变量,用于查找结点p的前驱
BiTNode *p;		//p指向目标结点
BiTNode * pre = NULL;	//指向当前访问结点的前驱
BiTNode * final = NULL;	//用于记录最终结果

//中序遍历
void findPre(BiTree T){
	if(T != NULL){
		InOrder(T -> lchild);	//递归遍历左子树
		visit(T);		//访问根节点
		InOrder(T -> rchild);	//递归遍历右子树
	}
}
//访问结点q
void visit(BiTNode * q){
	if(q==p)	//当前访问结点刚好是结点p
		final = pre;	//找到p的前驱
	else
		pre = q;		//pre指向当前访问的结点
}
  • 中序线索二叉树

(二)线索二叉树的存储结构

  • 规定
    ①、若无左子树,令lchild指向其前驱结点;
    ②、若无右子树,令rchild指向其后继结点;还需增加两个标志域标识指针域是指向其左右孩子还是前驱后继结点

1. 中序线索二叉树的存储

  • 初步建成的树,ltag、rtag=0
  • 最后还要检查pre的rchild是否为NULL,如果是,则令rtag=1
//线索二叉树结点
typedef struct ThreadNode{
	ElemType data;
	struct ThreadNode *lchild,*rchild;
	int ltag,rtag;//左、右线索标志
}ThreadNode,ThreadTree;

//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;

//中序遍历二叉树,一边遍历一边线索化
void InThread(ThreadTree T){
	if(T != NULL){
		InThread(T -> lchild);	//中序遍历左子树
		visit(T);	//访问根节点
		InThread(T -> rchild)l	//中序遍历右子树
	}
}
void visit(ThreadNode *q){
	if(q -> lchild == NULL){//左子树为空,建立前驱线索
		q -> lchild = pre;
		q -> ltag = 1;
	}
	if(pre!=NULL && pre->rchild==NULL){
		pre -> rchild = q;	//建立前驱结点的后继线索
		pre -> rtag = 1;
	}
	pre = q;
}

//中序线索化二叉树T
void CreateInThread(ThreadTree T){
	pre=NULL;	//pre初始为NULL
	if(T != NULL){	//非空二叉树才能线索化
		InThread(T);//中序线索化二叉树
		if(pre -> rchild == NULL)
			pre -> rtag = 1;	//处理遍历的最后一个结点
	}
}
  • 注意:这里我们会注意到我们只修改了那些tag=1的条件(即只要左子树为空,或者只要前驱结点不为空及其前驱结点的右子树为空就要修改tag的值为1。)之所以这样是因为我们一开始就设置tag=0,

2. 先序线索二叉树存储


  • 初步建成的树,ltag、rtag=0
//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;

//先序遍历二叉树,一边遍历一边线索化
void PreThread(ThreadTree T){
	if(T != NULL){
		visit(T);//先处理根节点
		if(T->ltag == 0)//lchild不是前驱线索,👈👈注意❗❗❗❗❗[倘若不加这个条件会产生转圈圈问题]
			PreThread(T -> lchild);
		PreThread(T -> rchild);
	}
}
void visit(ThreadNode *q){
	if(q -> lchild == NULL){//左子树为空,建立前驱线索
		q -> lchild = pre;
		q -> ltag = 1;
	}
	if(pre!=NULL && pre->rchild==NULL){
		pre -> rchild = q;	//建立前驱结点的后继结点
		pre -> rtag = 1;
	}
	pre = q;
}

//先序线索化二叉树T
void CreatePreThread(ThreadTree T){
	pre = NULL;	//pre初始为NULL
	if(T != NULL){	//非空二叉树才能线索化
		PreThread(T);//先序线索化二叉树
		if(pre->rchild == NULL)
			pre->rtag = 1;//处理遍历的最后一个结点❗❗❗❗❗❗
	}
}

3. 后序线索二叉树


  • 初步建成的树,ltag、rtag=0
//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;

//后序遍历二叉树,一边遍历,一边线索化
void PostThread(ThreadTree T){
	if(T != NULL){
		PostThread(T -> lchild);//后序遍历左子树
		PostThread(T -> rchild);//后序遍历右子树
		visit(T);//访问根节点
	}
}

void visit(ThreadNode *q){
	if(q->lchild == NULL){//左子树为空,建立前驱线索
		q->lchild = pre;
		q->ltag = 1;
	}
	if(pre!=NULL && pre-rchild==NULL){
		pre->rchild = q;//建立前驱结点的后继线索
		pre->rtag = 1;
	}
	pre = q;
}

//后序线索化二叉树T
void CreatePostThread(ThreadTree T){
	pre = NULL;//pre初始为NULL
	if(T != NULL){//非空二叉树才能线索化
		PostThread(T);//后序线索化二叉树
		if(pre->rchild == NULL)
			pre->rtag = 1;//处理遍历的最后一个结点
	}
}

4. 易错点

  • 最后一个结点的rchild、rtag的处理。
  • 先序线索化中,注意处理转圈圈问题。当ltag==0时,才能对左子树先序线索化。

(三)三种线索二叉树

以上是关于线索二叉树的存储结构及其作用(C语言)的主要内容,如果未能解决你的问题,请参考以下文章

数据结构(C语言版)严蔚敏->二叉树(链式存储结构)的构造及其几种遍历方式(先序中序后序层次)和线索二叉树

数据结构(C语言版)严蔚敏->二叉树(链式存储结构)的构造及其几种遍历方式(先序中序后序层次)和线索二叉树

数据结构(C语言版)严蔚敏->二叉树(链式存储结构)的构造及其几种遍历方式(先序中序后序层次)和线索二叉树

数据结构(C语言版)严蔚敏->二叉树(链式存储结构)的构造及其几种遍历方式(先序中序后序层次)和线索二叉树

二叉树的定义常见的性质及其存储结构(C语言)

C语言数据结构与算法----树和二叉树全面总结(中)