分析线索二叉树的前驱、后继求法

图片

图1

如图可以得到该二叉树的

先序:1 2 4 5 7 8 10 3 6 9

中序:4 2 7 5 10 8 1 3 9 6

后序:4 7 10 8 5 2 9 6 3 1

 

给出该线索二叉树的结构体:

typedefint ElemType;

 

// 线索二叉树结构体

typedefstruct Clue_binary_tree_node {

       ElemType data; // 数据域

       struct Clue_binary_tree_node *l_child,*r_child; // 左孩子、右孩子

       int l_tag = 0, r_tag = 0; // 标记

}Clue_binary_tree_node, *Clue_binary_tree;

 

先来分析先序的情况:

图片

图2

(1)最简单的一种情况就是,它是叶子结点(如4、7、10),即无左右孩子,则,它的l_tag及r_tag全为1(默认为0是代表它有左右孩子),此时求前驱和后继只需要返回它的左右孩子即可,即:

if (1== bt -> l_tag) {

       return bt -> l_child;

(后继同理)

(2)该结点有一个孩子(如8、3、6)

在考虑线索的情况下,先判断它的孩子是左孩子还是右孩子,如果是左孩子,那么它的后继,可以直接根据r_child求出,如果是右孩子,那么它的前驱,可以直接根据l_child求出。剩下另一半(前驱或后继)可以根据不考虑线索的情况下的求法求出。

       在不考虑线索的情况下,先说后继,因为此时是先序(先根后左再右),则无论该结点是有左孩子还是右孩子,它的后继都是它的孩子。再说前驱,同样因为是先序(先根后左再右),如果它位于左孩子的位置,则它的顺序就应该在它的父亲结点之后,即它的前驱就是它的父亲结点;如果它位于右孩子的位置,则它的顺序就应该在它的左兄弟结点之后(若存在左兄弟,否则就是它的父亲结点),因为它的左兄弟结点可能有多个子孙,同样满足这个情况,故,该结点应该位于左兄弟的子孙中(如果没有子孙就是左兄弟)最下且最靠右的结点的后面,例如:

图片

图3

如图中结点3的前驱就是它兄弟结点2的子孙中最下面且最靠右的结点10,同理结点6的前驱是结点3,因为结点6没有左兄弟,故它的前驱就是它的父亲结点。结点8的前驱是结点7,因为它的左兄弟结点7没有子孙,故它的前驱就是它的左兄弟。

满足如下图的样式

图片图4

图中蓝色的线最多连到哪,它的前驱就到哪(最后可能是左孩子也可能是右孩子)。

(3)该结点有2个孩子(如结点2、5),发现,它的前驱,同样满足上述规则,当它为左孩子时,它的前驱就是它的父亲结点,当它是右孩子时,它的前驱就是它的左兄弟子孙中最靠下靠右的结点。它的后继,更简单,就是它的左孩子。

 

总结:当线索二叉树是先序线索二叉树时,要你求某一个结点的前驱时,分两种大情况:

1.    它的(l_tag == 1),直接返回 l_child 即可

2.    它的(l_tag != 1),沿着图4的轨迹找即可

要你求某一个结点的后继时,分两大种情况:

1. 它的(r_tag == 1),直接返回 r_child 即可

2.    它的(r_tag != 1),有左孩子则是左孩子,没有左孩子则是右孩子。
 

详细代码如下:

bool find_node_parent(Clue_binary_tree target, Clue_binary_tree root, Clue_binary_tree &temp);

Clue_binary_tree get_pre_node(Clue_binary_tree bt, Clue_binary_tree root);

Clue_binary_tree get_suc_node(Clue_binary_tree bt);



// 求前驱

Clue_binary_tree get_pre_node(Clue_binary_tree bt, Clue_binary_tree root) {

	// 如果该结点为空,则返回空

	if (NULL == bt) { 

		return NULL;

	}

	// 如果该节点的左孩子存储的就是它的前驱,则返回左孩子

	if (1 == bt -> l_tag) {

		return bt -> l_child;

	}

	Clue_binary_tree temp = NULL;

	// 如果找不到该节点的父亲节点,则该节点为根,根在先序遍历中是不可能有前驱

	if (!find_node_parent(bt, root, temp)) {

		return NULL;

	}

	// 此时temp即为bt的父亲结点,如果它的左孩子为空,则它就是该结点的前驱

	if (!temp -> l_child) {

		return temp;

	} else {

		temp = temp -> l_child

		while(temp -> r_child) {

			temp = temp -> r_child;

		}

		if (temp -> l_child) return temp -> l_child;

		else return temp;

	}

} 



// 求后继

Clue_binary_tree get_suc_node(Clue_binary_tree bt) {

	if (NULL == bt) {

		return NULL;

	}

	if (1 == bt -> r_tag) {

		return bt -> r_child;

	}

	if (bt -> l_child) {

		return bt -> l_child;

	} else {

		return bt -> r_child;

	}

}



bool find_node_parent(Clue_binary_tree target, Clue_binary_tree root, Clue_binary_tree &temp) {

	if ((root -> l_child == target) || (root -> r_child == target)) {

		temp = root;

		return true;

	} else {

		if (find_node_parent(target, root -> l_child, temp)) return true;

		if (find_node_parent(target, root -> r_child, temp)) return true;

	}

}



再来分析中序的情况 :

图片

图5

如图5是中序线索二叉树

如果不用程序来求中序,简便方法:

图片
图片

正好就是它的中序序列。

下面还是分3种情况来分析:

(1)   叶子结点:因为它的左右孩子就是存储的它中序前驱后继的信息,故直接返回左右孩子即可得到该结点的前驱后继。

(2)   只有1个孩子的结点:先来分析如果它只有右孩子,因为中序(先左后根再右),若该结点只有右孩子,那么它的前驱一定是它的父亲结点(信息就存储在它的左孩子),但是它的后继,则不单纯是它的右孩子,应该是它右子孙中最靠左的那个结点;如果它只有左孩子,那么它的前驱则是它左子孙中最靠右的那个结点,它的后继的信息则存储在它的右孩子中。

(3)   有2个孩子的结点:它的前驱是它的左子孙中最靠右的那个结点,它的后继是它的右子孙中最靠左的那个结点

 

总结:当线索二叉树是中序线索二叉树时,要你求某一个结点的前驱时,分两种大情况:

1.    它的(1 == l_tag)时,直接return l_child。

2.    它的 (0 == l_tag)时,找它的左子孙中最靠右的那个结点。

要你求某一个结点的后继时,分两大种情况:

3.    它的(1 == r_tag)时,直接return r_child

4.    它的(0 == r_tag)时,找它的右子孙中最靠左的那个结点。

 

具体代码实现如下:

// 求前驱
Clue_binary_tree get_pre_node(Clue_binary_tree bt) {
	if (NULL == bt) {
		return NULL;
	}
	if (1 == bt -> l_tag) {
		return bt -> l_child; 
	}
	Clue_binary_tree temp = bt -> l_child;
	while (temp -> r_child) {
		temp = temp -> r_child;
	}
	return temp;
}

// 求后继
Clue_binary_tree get_suc_node(Clue_binary_tree bt) {
	if (NULL == bt) {
		return NULL;
	}
	if (1 == bt -> r_tag) {
		return bt -> r_child;
	}
	Clue_binary_tree temp = bt -> r_child;
	while (temp -> l_child) {
		temp = temp -> l_child;
	}
	return temp;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值