当前位置:   article > 正文

数据结构——线索化二叉树_数据结构线索化二叉树主函数

数据结构线索化二叉树主函数

这里主要是想写一下自己对于线索化过程中的递归理解

在讲解之前,先介绍几个概念
  • 前驱,通俗的讲,就是在某种遍历条件下,A在B前面输出,所以,B的前驱就是A。
  • 后继,同样的道理,A的后继也就是B了。(这里一定要理解清楚,因为后面程序的思路就是根据这里来写出的)
  • 头节点,这里可以类比链表中的头节点,可以说是与树中的数据没有什么直接的联系,即用户是不知道有这么一个节点存在的。但是,为了方便后面代码的实现,我们在这里还是引入了这个概念。
  • pre,这个是代码中指定的全局指针变量,始终指向刚刚访问过的节点。什么意思捏,就是说,p若刚刚访问过A,然后p就离开了,(这里的离开,指的是去访问别的节点了。若不懂没有关系,之后我们对着代码来讲。)那么,pre在p离开之前,pre也会指向A,然后p离开。注意,这里不是说是在函数内部,不会随着递归函数的进行,而被赋予新值。

进入主题啦,加油!

  1. 线索化二叉树。(至于,这里怎么定义二叉树的类型,我就不多说了,可以看书本上的定义即可)首先看一下这部分代码:

     void InOrderThreading(BiThrTree& Thrt, BiThrTree T)
     {
     	Thrt = (BiThrTree)malloc(sizeof(BiThrNode));
     	if (Thrt == NULL)
     	{
     		printf("内存分配失败\n");
     		exit(-1);
     	}
     	Thrt->data = -1;//头指针数据域为无效值-1
     	Thrt->RTag = Thread;//头指针有其后继
     	Thrt->rchild = Thrt;//暂时令其后继为自己
     	Thrt->LTag = Link;//头指针没有前驱
     	if (!T)//如果T为空树时,令其左指针指向自己
     	{
     		Thrt->lchild = Thrt;
     	}
     	else
     	{
     		Thrt->lchild = T;
     		pre = Thrt;
     		InThreading(T);
     		//线索化最后一个节点
     		pre->rchild = Thrt;
     		pre->RTag = Thread;
     		Thrt->rchild = pre;
     	}
     	
     }
     //end**
     
     //start**
     void InThreading(BiThrTree P)
     {
     	if (P)
     	{
     		InThreading(P->lchild);//先线索化左子树
     		/*
     			如果满足该if语句,说明已经到了某个子树的左边的尽头
     		*/
     		if (!P->lchild)
     		{
     			P->LTag = Thread;
     			P->lchild = pre;
     		}
     		/*
     			不管上面if语句是否满足,相对P来说,其都是有一个前驱元素X存在的
     			从另一方面来说,就是X的后继就是P
     			而pre的定义就是P刚刚访问后的节点,对于中序遍历来说,pre就是P的前驱
     			所以,下一步应该计算pre的后继元素
     		*/
     		if (!pre->rchild)
     		{
     			pre->RTag = Thread;
     			pre->rchild = P;
     		}
     		pre = P;//因为P马上就要==P->rchild了,在此之前,用pre来来保存P访问节点
     		InThreading(P->rchild);//再线索化左子树
     	}
     }
     //end**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    我认为难点就在 void InThreading(BiThrTree P)函数中,他是怎么就写出来的,我最近也一直在“研究”递归,被弄得云里雾里的。但是,我还是在这里写一哈自己的想法,一是为了方便之后的复习,二是希望万能的网友能和我一起交流这个问题,共同进步。

  2. 首先明白一点,我在这里实现的是中序遍历的线索化,所以,第一个要设置为Thread的节点必然是最左边的那个节点,即P->lchild==NULL。对应的代码就是if§这个判断语句,因为只有当该节点不为NULL时才可以继续走下去。InThreading(P->lchild);这条语句明显是一个递归语句,那么什么时候才能执行下面的语句捏。根据递归的定义可知,只有当最后一个InThreading(P->lchild)执行完毕后,就会返回到上一个InThreading(P->lchild)这里,然后接着往下执行。

  3. 现在的p是什么捏?是根的左子树中第一个没有左孩子的节点。所以,这个是我们要线索化的节点。自然的就会去写

    if (!P->lchild)
             		{
             			P->LTag = Thread;
             			P->lchild = pre;
             		}
     // 这个代码。因为pre的定义可知,所以p->lchild应该赋值为pre。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  4. 那么,你找了p是否会有前驱(因为在之后的递归中,可能不满足if(!P->lchild)这个条件),那么自然你现在得去找pre的后继。这里就得用到我之前所讲的那个后继这个概念了。

  5. 所以现在去找pre的后继。执行代码

     if (!pre->rchild)
    		{
    			pre->RTag = Thread;
    			pre->rchild = P;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
  6. 后继线索也寻找完毕后,那么就要去看p有没有右孩子了。但是,在此之前,根据pre的定义可知,pre要始终指向p之前访问过的节点。所以,在p=p->rchild之前,先执行

    pre=p;
    
    • 1

    然后执行

     InThreading(P->rchild);
    
    • 1

那么,这样这个 void InThreading(BiThree p)函数分析完毕。
可能还不是特别清楚,我现在拿一颗树来做个演示。下面还有我的实例代码,可以参考一下,对着这个图分析一下。(好吧,这个图画的太丑了,大家还是尽力理解吧,可以自己尝试画图,这样才可以深入理解递归)
这个是第一张图片

这个是第二张图片

这个是完整代码,可能对着分析一下

	//start**
void InOrderTraverse_Thr(BiThrTree T)
{
	BiThrTree P = T->lchild;//P指向根节点
	while (T != P)
	{
		while (P->LTag == Link)
		{
			P = P->lchild;//中序遍历的老套路
		}
		printf("%5d", P->data);//所以此时该输出数据了
		/*
		进入这个while循环,代表该节点左边没有子树了,
		右边也没有子树了,并且不是中序遍历的最后一个节点
		因为最后一个节点的rchild指向头指针
		*/
		while (P->RTag == Thread && P->rchild != T)
		{
			P = P->rchild;
			printf("%5d", P->data);
		}
		P = P->rchild;
	}

}
//end**


//start**
void InOrderThreading(BiThrTree& Thrt, BiThrTree T)
{
	Thrt = (BiThrTree)malloc(sizeof(BiThrNode));
	if (Thrt == NULL)
	{
		printf("内存分配失败\n");
		exit(-1);
	}
	Thrt->data = -1;//头指针数据域为无效值-1
	Thrt->RTag = Thread;//头指针有其后继
	Thrt->rchild = Thrt;//暂时令其后继为自己
	Thrt->LTag = Link;//头指针没有前驱
	if (!T)//如果T为空树时,令其左指针指向自己
	{
		Thrt->lchild = Thrt;
	}
	else
	{
		Thrt->lchild = T;
		pre = Thrt;
		InThreading(T);
		//线索化最后一个节点
		pre->rchild = Thrt;
		pre->RTag = Thread;
		Thrt->rchild = pre;
	}
	
}
//end**

//start**
void InThreading(BiThrTree P)
{
	if (P)
	{
		InThreading(P->lchild);//先线索化左子树
		/*
			如果满足该if语句,说明已经到了某个子树的左边的尽头
		*/
		if (!P->lchild)
		{
			P->LTag = Thread;
			P->lchild = pre;
		}
		/*
			不管上面if语句是否满足,相对P来说,其都是有一个前驱元素X存在的
			从另一方面来说,就是X的后继就是P
			而pre的定义就是P刚刚访问后的节点,对于中序遍历来说,pre就是P的前驱
			所以,下一步应该计算pre的后继元素
		*/
		if (!pre->rchild)
		{
			pre->RTag = Thread;
			pre->rchild = P;
		}
		pre = P;//因为P马上就要==P->rchild了,在此之前,用pre来来保存P访问节点
		InThreading(P->rchild);//再线索化左子树
	}
}
//end**
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/771077
推荐阅读
相关标签
  

闽ICP备14008679号