数据结构之线索二叉树的中序遍历(详细解释)

据本人上一篇博客的解释大家应该对线索二叉树有一定认识了,线索二叉树是利用了指向NULL的指针让其不浪费,所以一个结点有两个指针,分别为左指针和右指针,让本来指向NULL的左指针和右指针分别指向该结点在中序遍历后的前驱结点和后继结点,如果没有前驱结点则指向NULL就可以。

每一个结点都是如下的样子:

b29767d71c334f75a1e90efb5647d66d.png

结构体的具体实现代码如下:

typedef struct stu
{
    char name[28];
    int num;
    int ltag,rtag    //标志结点
    struct stu *lchild,*rchild //左指针右指针
};

我们在实行的时候需要注意在创建二叉树的时候需要给ltag和rtag赋值。

代码如下:

struct stu* create()
{
	stu *p;
	p = new stu();
	cout << "请按照顺序输入名字和学号,学号为0时为NULL:" << endl;
	cin >> p->name >> p->num;
	if (p->num == 0)
	{
		p = NULL;
	}
	else
	{
		p->ltag = 0;
		p->rtag = 0;
		p->lchild = create();
		p->rchild = create();
	}
	return p;
}

与一般的二叉树创建就只是多了一个给标志是左孩子还是线索的赋值而已,但这个赋值在后面的利用之中,很重要,在这篇代码之中标志为0时代表有孩子,标志为1是代表没有孩子并指向前驱结点或者后继结点。

具体的线索化是将其二叉树遍历一遍之后给标志赋予其该有的值,然后在设置一个头结点指向该二叉树的根结点,当二叉树不等于头结点的时候一直进行循环,然后让二叉树遍历最后的一个结点的右指针指向头结点。

具体的线索化代码如下:

struct stu* pre = NULL;
void inthread(stu* p)
{
	if (p != NULL)
	{
		inthread(p->lchild);
		if (p->lchild == NULL)
		{
			p->lchild = pre;
			p->ltag = 1;
		}
		if (pre->rchild == NULL&&pre!=NULL)
		{
			pre->rchild = p;
			pre->rtag = 1;
		}
		pre = p;
		inthread(p->rchild);
	}
}

struct stu* head(stu* p)
{
	stu* T;
	T = new stu();
	T->ltag = 0;
	T->rtag = 1;
	T->lchild = T;
	if (p == NULL)
		T->lchild = T;
	else
	{
		T->lchild = p;
		T->ltag = 1;
		pre = T;
		inthread(p);//出来之后的pre是二叉树访问的最后一个节点,将他的后继设为头节点
		pre->rtag = 1;
		pre->rchild = T;
		T->rchild = pre;
	}
	return T;
}

线索化的设置很简单,这里最重要的是头结点的设置,先创建一个头结点然后让其左标志为0(即为有左孩子),让右标志为1(没有右孩子)。然后如果根结点为空时,不进行线索化,因为没有相对应的数据,所以左孩子等于其本身。如果根结点不为空时,让头结点的左指针指向二叉树的右节点,然后让左标志为1即让其进行线索化,然后利用线索化函数,找到二叉树访问的最后一个节点pre,让pre的右标志为1,让其右指针指向头结点作为循环结束的调节。

最终的实现遍历的代码如下:

#include<iostream>
using namespace std;

typedef struct stu {
	char name[28];
	int num;
	int ltag, rtag;		
	struct stu* lchild, *rchild;	
}stu;
stu* pre = NULL;
struct stu* create()
{
	stu *p;
	p = new stu();
	cout << "请按照顺序输入名字和学号,学号为0时为NULL:" << endl;
	cin >> p->name >> p->num;
	if (p->num == 0)
	{
		p = NULL;
	}
	else
	{
		p->ltag = 0;
		p->rtag = 0;
		p->lchild = create();
		p->rchild = create();
	}
	return p;
}

void inthread(stu* p)
{
	if (p != NULL)
	{
		inthread(p->lchild);
		if (p->lchild == NULL)
		{
			p->lchild = pre;
			p->ltag = 1;
		}
		if (pre->rchild == NULL&&pre!=NULL)
		{
			pre->rchild = p;
			pre->rtag = 1;
		}
		pre = p;
		inthread(p->rchild);
	}
}

struct stu* head(stu* p)
{
	stu* T;
	T = new stu();
	T->ltag = 0;
	T->rtag = 1;
	T->lchild = T;
	if (p == NULL)
		T->lchild = T;
	else
	{
		T->lchild = p;
		T->ltag = 1;
		pre = T;
		inthread(p);
		pre->rtag = 1;
		pre->rchild = T;
		T->rchild = pre;
	}
	return T;
}

void print(stu* p)
{
	stu* s;
	s = p->lchild;
	while(s != p)
	{
		while (s->ltag == 0)
			s = s->lchild;
		cout << s->name << "\t" << s->num << endl;
		while (s->rtag == 1 && s->rchild != p)
		{
			s = s->rchild;
			cout << s->name << "\t" << s->num << endl;
		}
		s = s->rchild;
	}
}

int main()
{
	stu* p;
	stu* T;
	p = create();
	T = head(p);
	print(T);
	return 0;
}

输出函数那,当p结点等于头结点的时候才循环结束,首先找到中序遍历的第一个结点即最左下角的结点,然后对其进行输出,然后如果该结点没有右孩子则找它的后继结点并进行输出操作,如果有右孩子则找到右孩子(右孩子没有左孩子)输出。一直输出到根结点,然后根结点有右孩子所以跳出最下面的循环然后进入根结点的右子树再进行中序遍历找到左下角的结点输出,依次类推直到s指向头结点才算结束(如果没有设置头结点无法判断什么时候该循环要结束)。

输入的结构图如下所示:

3b623c9451b346a38a0ab0535a4800be.png 

该程序的输入数据和输出结果如下图:

 0251cc46da5149d4b706a976fa684570.png

fde853eb9ee44e7daa09383b3bf14a3e.png 

 好了,今天所要讲述的内容就是如上的内容二叉树中序遍历的线索化以及线索化之后的输出问题,线索化解决了指针的浪费也同时方便我们寻找某一个结点的后继或者是前驱结点。

希望大家都可以在自己的世界里慢慢走,不要随波逐流!!!走自己想走的路就好。

猜你喜欢

转载自blog.csdn.net/m0_61886762/article/details/124609052