二叉树中序线索化和中序线索遍历

所谓中序化,就是把闲着空余的指针指向遍历的前驱和后继,举个例子:

        如图所示,DEF节点没有左孩子和右孩子,(对于C的右孩子指针同理)浪费了指针,所以要让DEF的左右孩子指针指向DEF的前驱和后继,(左孩子指向前驱,右孩子指向后继)这个前驱和后继是相对于顺序表而言的,这个顺序就是中序遍历的顺序,即图中的 D B E A F C 。

那么指针指向的是怎样的呢,我画图表示一下:

        因为D没有前驱,所以指向空,D的后继是B,所以 D 的右孩子指向 B ,E,F 同理,左孩子指向各自的前驱,右孩子指向各自的后继,对于 C,因为只有右孩子为空,所以只有右孩子指向后继,也就是空。

接下来我们可以思考如何实现部分节点的指针的重构呢?

既然是中序遍历,我们可以回忆一下普通的中序遍历是怎么进行的:

void inOrder(struct TreeNode* T) {
	if (T == NULL) {
		;
	}
	else {
		inOrder(T->lchild);
		printf("%d ", T->val);
		inOrder(T->rchild);
	}
}

        如上面代码所示,T 就是每次中序遍历的节点,也就是说 T 按照中序遍历的顺序不断更新。
在这个函数中,实现的是 T 指向节点值的打印,那么也就是说我们可以更换printf("%d ", T->val);
让函数在中序遍历时实现其他功能,即(改变节点左右孩子的功能)。

类似于这样:

void inOrder(struct TreeNode* T) {
	if (T == NULL) {
		;
	}
	else {
		inOrder(T->lchild);
		if(T-lchild == NULL){
        T->lchild = T 的前驱
        }
        if(T-rchild == NULL){
        T->rchild = T 的后继
        }
		inOrder(T->rchild);
	}
}

        那么接下来就有一个问题,如何找到前驱和后继,还有,如何体现节点的线索化,既然 T 是按照中序遍历来进行的,我们可以让一个指针 pre 和 T 相等,但是每次比 T 前一个节点,实现方法就是,每次更新 pre = T 之后,T接着进行遍历。其原理与链表中:pre = cur,cur = cur->next 类似,在二叉树中,因为 指针pre 需要更新值,所以*pre = T;,传入函数的是TreeNode** pre二级指针。

刚才提到要体现节点的线索化,原因就是为了中序遍历线索二叉树,我们要给结构体加入左右tag,标记指向的是孩子还是前驱后继,代码如下:

typedef struct TreeNode {
	char val;
	struct TreeNode* lchild;
	struct TreeNode* rchild;
	int ltag;
	int rtag;
}TreeNode;

void inOrderThreadTree(TreeNode* T,TreeNode** pre) {
	if (T == NULL) {
		;
	}
	else {
		inOrderThreadTree(T->lchild,pre);
		//printf("%c ", T->val);
		if (T->lchild == NULL) {
			T->ltag = 1;
			T->lchild = *pre;
		}if (*pre != NULL &&(*pre)->rchild == NULL) {
			(*pre)->rtag = 1;
			(*pre)->rchild = T;
		}
		*pre = T;//更新*pre的值,T继续向前遍历一个节点
		inOrderThreadTree(T->rchild,pre);
	}
}

这里第一个 if (T->lchild == NULL)很容易理解,如果左孩子为空指向前驱 *pre,第二个 if 意思是*pre的右孩子为空的时指向 T 即 *pre 的后继,也就是说,*pre 和 T 这样进行遍历的:

        我画了几个例子帮助理解,黑色箭头代表 if 成立指向的新构建的线索。还有当遍历到最终节点时,这时要让 pre->rtag = 1; pre->rchild = NULL;因为最后一个节点因为没有后继节点没有进入递归,所以要把右孩子置为空。

接着要中序遍历线索二叉树,首先要找到第一个节点:

TreeNode* GetFirst(TreeNode* T) {
	while (T->lchild&& T->ltag!=1)
	{
		T = T->lchild;
	}
	return T;
}

        接着要找下一个节点,如果右孩子rtag为1,那么也就是说右孩子就是后继,直接返回右孩子,其实就是根,如果右孩子rtag为0的话,也就是说有右孩子,需要继续找到新子二叉树的最左节点,getfirst(node->rchild),所以最后就是 左->根->右 的顺序,代码如下:

TreeNode* GetNext(TreeNode* node) {
	if (node->rtag == 1) {
		node = node->rchild;
	}
	else {
		node = GetFirst(node->rchild);
	}
	return node;
}

最后是主函数,要循环中序遍历:

int main() {
	char array[15] = "ABD##E##CF###";
	int index = 0;
	TreeNode* pre = NULL;
	TreeNode* T;
	MyTreeCreat(&T, array, &index);
	inOrderThreadTree(T,&pre);
	pre->rtag = 1;
	pre->rchild = NULL;
	for (TreeNode* node = GetFirst(T); node != NULL; node = GetNext(node)) {
		printf("%c ", node->val);
	}
	return 0;
}

主要是for循环这一步,先找到第一个节点,然后一直在GetNext()函数中循环,直到最后为跳出。结果如下:

这就是文章的全部内容了,希望对你有所帮助,如有错误欢迎指出。

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值