线索二叉树的学习

文章介绍了二叉树作为一种数据结构,如何平衡了数组和链表的优缺点,同时指出了其浪费指针域和难以获取节点前后继的问题。线索二叉树通过添加线索指针解决了这一问题,允许在不使用递归的情况下高效遍历。文章提供了线索二叉树的创建和中序遍历的实现过程。
摘要由CSDN通过智能技术生成

一、引入

首先我们来聊一聊二叉树相较于别的结构有什么优点和缺点

优点:

数组的话搜索比较便捷,可以直接用下标进行查找,但是如果进行了删除和插入的操作就比较麻烦

链表就恰好相反,插入和删除操作比较简单,但是查找起来比较慢。

而二叉树则是很好的中和了这两种结构,既有数组的优点有包含链表的优点。

缺点:

在二叉树的实现中往往会浪费掉许多的指针域,而对于一个有着n个节点的二叉链表,每个节点都有左右两个孩子的指针域,一共是有2n个指针域的,但其实是存在有n+1个空指针域,它们都存储着NULL,所以就造成了很大的浪费。

还有一个点就是,在二叉树中我在做遍历的时候,我是不知道每个节点它的前驱和后继是谁,如果我们想知道的话必须要从头到尾来遍历一遍,非常麻烦!

二、线索二叉树的原理和实现

我们把指向前驱和后继的指针成为线索,加上线索的二叉链表称为线索链表,相对应的二叉树就称为线索二叉树

                                                                                                                        ---大话数据结构

如何去实现线索二叉树呢?

在对一颗二叉树进行中序遍历的时候,将所有节点的rchild,如果它为空就修改成它的后继节点。然后再将这颗二叉树所有为空的lchild改为它的前驱节点。

 前驱:在这个遍历中排名在你前面一位的叫你的前驱结点

 后继:在这个遍历中排母在你后面一位的叫你的后继结点

这里还有一个问题,那我应该怎么才能判断出来某节点的lchild指向的是左孩子还是前驱节点呢?

所以我们这里新加两个标志域ltag和rtag,来判断这个节点的左/右指针域是指向的左/右孩子还是前驱/后继 ,为1就是指针被线索化了,为0就是指向的孩子。

为什么要对树进行线索化呢?有什么意义?

就是可以在不使用递归和栈的情况下更快地对树进行遍历,这样可以提高算法的效率,也就是说,我们将会使用线索来代替递归的功能,在原本不得不用递归来获得某一结点后继节点的情况下,我们使用线索的功能来解除这样的限制。

 具体实现线索二叉树

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
typedef enum{Link,Thread} PointerTag;
typedef struct BiThrNode
{
	char data;
	struct BiThrNode* lchild, * rchild;
	PointerTag ltag;
	PointerTag rtag;
} BiNode,*BiTree;
BiTree pre = NULL;//声明全局变量,表示在中序遍历过程中当前节点的前驱 
void CreatTree(BiTree* t)
{
	char ch;
	scanf("%c", &ch);
	getchar();
	if (ch == '0')
	{
		*t = NULL;
		return;
	}             //用户输入0代表当前节点的左/右孩子没有
	else
	{
		*t = (BiTree)malloc(sizeof(BiNode));
		(*t)->data = ch;
		printf("请输入左节点:\n");
		CreatTree(&(*t)->lchild);
		printf("请输入右节点:\n");
		CreatTree(&(*t)->rchild);
	}
	//利用前序遍历来创建一个普通的二叉树
}

void InThreading(BiTree t)//对二叉树进行中序线索化 
{
	if (t == NULL)  return;
	InThreading(t->lchild);//对左孩子进行线索化 
	if (t->lchild == NULL)//如果左孩子为空了
	{
		t->ltag = Thread;  //就将它的左孩子进行线索化,等于前驱
		t->lchild = pre;
	}
	if (pre != NULL && pre->rchild == NULL)
		//因为此时的t节点还没有访问到它的后继结点,就只能对它的前驱结点pre的右指针进行判断
        //如果为空,那么它就是pre的后继结点,可以举个例子进行模拟一下
		//就是因为中序遍历的性质,先将左结点遍历一遍再遍历右结点,自己模拟一遍就知道了
	{
		pre->rtag = Thread;
		pre->rchild = t;
	}
	pre = t;//始终保持pre是p的前驱

	InThreading(t->rchild);	//对右孩子进行线索化 
}

void InOrderThreading(BiTree t)//中序遍历线索化二叉树 
{
	while (t != NULL)
	{
		while (t->ltag==Link)//循环到中序序列第一个结点
		{
			t = t->lchild;
		}
		printf("%c ", t->data);
		while (t->rtag==Thread) 
		{
			t = t->rchild;
			printf("%c ", t->data);//打印后继
		}
		t = t->rchild;
	}
}
main()
{
	BiTree t = NULL;
	CreatTree(&t);
	InThreading(t);
	InOrderThreading(t);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值