眩しさだけは、忘れなかった。|

【数据结构-树】线索二叉树

注:仅为本人做笔记用!如需更详细的介绍请参考其它博文。

1 线索二叉树的存储结构

/* 1 线索二叉树的存储结构 */
typedef struct ThreadNode{
	ElemType data;						// 数据域 
	struct ThreadNode *lchild, *rchild;	// 左右指针域 
	int ltag, rtag;						// 左右线索标志:1 表示为线索 
}ThreadNode, *ThreadTree;

2 中序线索二叉树(LNR)

2.1 中序线索二叉树的构造

/* 2 中序线索二叉树 */
/* 2.1 中序线索二叉树的构造 */
// 用于构造线索二叉树的初始函数,T:待构造的树
void CreateInThread (ThreadNode T){		 
	ThreadTree pre = NULL;	// 用于记录
	if (T != NULL){
		InThread(T, pre);	// 中序线索化二叉树
		if (pre->rchild == NULL)	// 运行到此处时,一定只剩中序遍历的最后一个节点未线索化,因此需单独处理 
			pre->rtag = 1;
	} 
} 

// 思路:一边中序访问一边线索化,递归算法 
void InThread (ThreadTree &p, ThreadNode &pre){
	if (p != NULL){
		// step 1: 访问左子树/左结点,同时将其线索化 
		if (p->ltag == 0)
			InThread(p->lchild, pre);	
		// step 2: 访问根结点,同时将其线索化;此时 pre 为当前访问结点 p 的前驱结点 
		InVisit(p, pre)	
		// step 3: 访问右子树/右结点,同时将其线索化 
		if (p->rtag == 0)
			InThread(p->lchild, pre);	
	}
}

void InVisit (ThreadNode &p, ThreadNode &pre){
	// step 2.1: 如果发现当前访问结点 p 的左指针域为空,则使其指向前驱线索 pre 
	if (p->lchild == NULL){		 
		p->lchild = pre;		  
		p->ltag = 1;
	}	// 当前访问结点 p 的前驱线索建立完成 
	// step 2.2: 同时检查前驱结点 pre,如果前驱结点的右指针域为空,则使其指向当前访问结点 p
	if ((pre->rchild == NULL) && pre != NULL){  
		pre->child = p;
		pre->rtag = 1;
	}	// 前驱结点 pre 的后继线索建立完成 
	// step 2.3: 前驱结点 pre 往前遍历,指向当前访问结点 p 
	pre = p; 
}

2.2 中序线索二叉树的遍历

中序线索二叉树的遍历实质是找指定结点 p 的后继结点,其思路如下:

  • p->rtag == 1(即 p 无右孩子),则 p 的后继结点为p->rchild(即 p 的后继线索);
  • p->rtag == 0(即 p 有右孩子),则 p 的后继结点为 p 的右子树的最左下的结点
/* 2.2 中序线索二叉树的遍历 */
// 非递归算法 
// 该函数用于求 p 的右子树的最左下结点
ThreadNode *FirstNode (ThreadNode *p){
	// 如果是寻找中序遍历的第一个结点,则为整棵树的最左下结点,它不一定是叶结点 
	// 而对于指定结点 p,其后继结点为 p 的右子树的最左下的结点,它也不一定是叶结点 
	while (p->ltag == 0)
		p = p->lchild;
	return p;
} 

ThreadNode *NextNode (ThreadNode *p){
	if (p->rtag == 0) 
		return Firstnode(p->rchild);    // 找下一个后继结点 
	else 
		return p->rchild; // 指针域是后继线索,直接返回后继线索
}

void InOrder (ThreadNode &T){
	// 从中序遍历的第一个结点开始
	for (ThreadNode *p = FirstNode(T); p != NULL; p = NextNode(p))
		visit(p);
}

2.3 中序线索二叉树的逆遍历

中序线索二叉树的逆遍历实质是找指定结点 p 的前驱结点,其思路如下:

  • p->ltag == 1(即 p 无左孩子),则 p 的前驱结点为p->lchild(即 p 的前驱线索);
  • p->ltag == 0(即 p 有左孩子),则 p 的前驱结点为 p 的左子树的最右下的结点
/* 2.3 中序线索二叉树的逆遍历 */
// 非递归算法 
// 该函数用于求 p 的左子树的最右下结点
ThreadNode *LastNode (ThreadNode *p){
	// 如果是寻找中序逆遍历的第一个结点(即中序遍历的最后一个结点),则为整棵树的最右下结点,它不一定是叶结点 
	// 而对于指定结点 p,其前驱结点为 p 的左子树的最右下的结点,它也不一定是叶结点 
	while (p->rtag == 0)
		p = p->rchild;
	return p;
} 

ThreadNode *NextNode (ThreadNode *p){
	if (p->ltag == 0) 
		return Lastnode(p->lchild);	// 找上一个前驱结点 
	else 
		return p->lchild; // 指针域是前驱线索,直接返回前驱线索
}

void InReverse (ThreadNode &T){
	// 从中序遍历的最后一个结点开始
	for (ThreadNode *p = LastNode(T); p != NULL; p = NextNode(p))
		visit(p);
}

3 先序线索二叉树(NLR)

3.1 先序线索二叉树的构造

/* 3 先序线索二叉树 */
/* 3.1 先序线索二叉树的构造 */
// 用于构造线索二叉树的初始函数,T:待构造的树
void CreatePreThread (ThreadNode T){		 
	ThreadTree pre = NULL;	// 用于记录
	if (T != NULL){
		preThread(T, pre);	// 先序线索化二叉树
		if (pre->rchild == NULL)	// 运行到此处时,一定只剩先序遍历的最后一个节点未线索化,因此需单独处理 
			pre->rtag = 1;
	} 
} 

// 思路:一边先序访问一边线索化,递归算法 
void PreThread (ThreadTree &p, ThreadNode &pre){
	if (p != NULL){
		// step 1: 访问根结点,同时将其线索化;此时 pre 为当前访问结点 p 的前驱结点 
		PreVisit(p, pre)
		// step 2: 访问左子树/左结点,同时将其线索化,注意此处判断线索标志是必要的! 
		if (p->ltag == 0)
			PreThread(p->lchild, pre);	
		// step 3: 访问右子树/右结点,同时将其线索化 
		if (p->rtag == 0)
			PreThread(p->rchild, pre);	
	}
}

void PreVisit (ThreadNode &p, ThreadNode &pre){
	// step 1.1: 如果发现当前访问结点 p 的左指针域为空,则使其指向前驱线索 pre 
	if (p->lchild == NULL){		 
		p->lchild = pre;		  
		p->ltag = 1;
	}	// 当前访问结点 p 的前驱线索建立完成 
	// step 1.2: 同时检查前驱结点 pre,如果前驱结点的右指针域为空,则使其指向当前访问结点 p
	if ((pre->rchild == NULL) && pre != NULL){  
		pre->child = p;
		pre->rtag = 1;
	}	// 前驱结点 pre 的后继线索建立完成 
	// step 1.3: 前驱结点 pre 往前遍历,指向当前访问结点 p 
	pre = p; 
}

3.2 先序线索二叉树的遍历

先序线索二叉树的遍历实质是找指定结点 p 的后继结点,其思路如下:

  • p->rtag == 1(即 p 无右孩子),则 p 的后继结点为p->lchild(即 p 的后继线索);
  • p->rtag == 0(即 p 有右孩子)且p->ltag == 1(即 p 无左孩子),则 p 的后继结点为p->rchild(即 p 的右孩子);
  • p->rtag == 0(即 p 有右孩子)且p->ltag == 0(即 p 有左孩子),则 p 的后继结点为p->lchild(即 p 的左孩子)。
/* 3.2 先序线索二叉树的遍历 */
ThreadNode *NextNode (ThreadNode *p){
	if (p->rtag == 1) 
		return p->lchild;	
	if ((p->rtag == 0) && (p->ltag == 1))
		return p->rchild; 
	if ((p->rtag == 0) && (p->ltag == 0))
		return p->lchild; 
}

void PreOrder (ThreadNode &T){
	// 从先序遍历的第一个结点开始
	for (ThreadNode *p = T; p != NULL; p = NextNode(p))
		visit(p);
}

3.3 先序线索二叉树的逆遍历(难实现)

先序线索二叉树的遍历实质是找指定结点 p 的前驱结点

但是,因为先序遍历顺序是 NLR,而 p 的左右孩子都是后继结点,所以找不到 p 的前驱结点。因此,需要得知 p 的父节点,才能找到其前驱结点。

4 后序线索二叉树(LRN)

4.1 后序线索二叉树的构造

/* 4 后序线索二叉树 */
/* 4.1 后序线索二叉树的构造 */
// 用于构造线索二叉树的初始函数,T:待构造的树
void CreatePostThread (ThreadNode T){		 
	ThreadTree pre = NULL;	// 用于记录
	if (T != NULL){
		preThread(T, pre);	// 后序线索化二叉树
		if (pre->rchild == NULL)	// 运行到此处时,可能只剩后序遍历的最后一个节点(根结点)未线索化,因此需单独处理 
			pre->rtag = 1;
	} 
} 

// 思路:一边后序访问一边线索化,递归算法 
void PostThread (ThreadTree &p, ThreadNode &pre){
	if (p != NULL){
		// step 1: 访问左子树/左结点,同时将其线索化
		if (p->ltag == 0)
			PostThread(p->lchild, pre);	
		// step 2: 访问右子树/右结点,同时将其线索化 
		if (p->rtag == 0)
			PostThread(p->rchild, pre);	
		// step 3: 访问根结点,同时将其线索化;此时 pre 为当前访问结点 p 的前驱结点 
		PostVisit(p, pre)
	}
}

void PostVisit (ThreadNode &p, ThreadNode &pre){
	// step 3.1: 如果发现当前访问结点 p 的左指针域为空,则使其指向前驱线索 pre 
	if (p->lchild == NULL){		 
		p->lchild = pre;		  
		p->ltag = 1;
	}	// 当前访问结点 p 的前驱线索建立完成 
	// step 3.2: 同时检查前驱结点 pre,如果前驱结点的右指针域为空,则使其指向当前访问结点 p
	if ((pre->rchild == NULL) && pre != NULL){  
		pre->child = p;
		pre->rtag = 1;
	}	// 前驱结点 pre 的后继线索建立完成 
	// step 3.3: 前驱结点 pre 往前遍历,指向当前访问结点 p 
	pre = p; 
}

4.2 后序线索二叉树的遍历(难实现)

后序线索二叉树的遍历实质是找指定结点 p 的后继结点

但是,因为后序遍历顺序是 LRN,而 p 的左右孩子都是前驱结点,所以找不到 p 的后继结点。因此,需要得知 p 的父节点,才能找到其后继结点。

4.3 后序线索二叉树的逆遍历

后序线索二叉树的逆遍历实质是找指定结点 p 的前驱结点,其思路如下:

  • p->ltag == 1(即 p 无左孩子),则 p 的后继结点为p->lchild(即 p 的前驱线索);
  • p->ltag == 0(即 p 有左孩子)且p->rtag == 1(即 p 无右孩子),则 p 的后继结点为p->lchild(即 p 的左孩子);
  • p->ltag == 0(即 p 有左孩子)且p->rtag == 0(即 p 有右孩子),则 p 的后继结点为p->rchild(即 p 的右孩子)。
/* 4.3 后序线索二叉树的逆遍历 */
ThreadNode *NextNode (ThreadNode *p){
	if (p->ltag == 1) 
		return p->rchild;	
	if ((p->ltag == 0) && (p->rtag == 1))
		return p->lchild; 
	if ((p->ltag == 0) && (p->rtag == 0))
		return p->rchild; 
}

void PostReverse (ThreadNode &T){
	// 从后序遍历的最后一个结点开始
	for (ThreadNode *p = T; p != NULL; p = NextNode(p))
		visit(p);
}

本文作者:漫舞八月(Mount256)

本文链接:https://www.cnblogs.com/Mount256/p/16531142.html

版权声明:本作品采用CC 4.0 BY-SA许可协议进行许可。

posted @   漫舞八月(Mount256)  阅读(94)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开
  1. 1 Main Menu Theme Syd Matters
  2. 2 Luminous Memory (Acyanxi Remix) Acyanxi
  3. 3 夏影 麻枝准
  4. 4 潮騒の香り 水月陵
  5. 5 stand still 井口裕香 (いぐち ゆか)
  6. 6 流星雨 麻枝准
  7. 7 Summer Fantasy 傅许
  8. 8 失う 米白
  9. 9 epilogue 霜月はるか
  10. 10 夏に君を待ちながら 小原好美
  11. 11 桜のような恋でした 鹿乃 (かの)
  12. 12 風は微かに、熱を残し… 水月陵
  13. 13 夏凪ぎ 麻枝准/やなぎなぎ
  14. 14 空に光る 戸越まごめ
  15. 15 木漏れ日 riya
  16. 16 Songbirds Homecomings (ホームカミングス)
  17. 17 宝物になった日 麻枝准/やなぎなぎ
  18. 18 夏影~あの飛行機雲を超えた、その先へ~ 雪桜草 (雪樱草)
  19. 19 快晴 Orangestar (蜜柑星P),初音未来 (初音ミク)
  20. 20 永遠 霜月はるか
  21. 21 Sion 天門
  22. 22 遙かな年月-piano- 麻枝准
  23. 23 夏恋慕 kobasolo/春茶
  24. 24 夏凪ぎ-piano ver.- MANYO/麻枝准
  25. 25 Goodbye Seven Seas -piano ver.- MANYO/麻枝准
  26. 26 Light Years 麻枝准/やなぎなぎ
  27. 27 優しさの記憶 鹿乃 (かの)
夏影 - 麻枝准
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.