数据结构之线索二叉树(C++)

注:代码中使用的"ObjArrayList.h"头文件参看之前博文:数据结构之顺序列表。


//文件名:"ThBiTree.h"
#pragma once
#ifndef THBITREE_H_
#define THBITREE_H_
#include "ObjArrayList.h"


/*
.	线索二叉树 类模板定义
.
.	存储结构:线索链表
.		在二叉链表基础上增加标识位,对结点空域(左右子树指针域)增加遍历线索的存储
.		例如:
.			ltag	lchild				|	rtag	rchild 
.			0		左子树域			|	0		右子树域
.			1		遍历线索前驱域		|	1		遍历线索后继域
.	优点:
.		1.增加空间利用率:
.			解析:n 个结点的二叉树(二叉链表存储),在其 2n 个指针域中,就会有 n+1 个空域,只用了 n-1 个域,造成空间浪费,
.				故将这 n+1 个空域用来存放其遍历序列(先序、中序、后序)的前驱、后继结点。
.		2.加快遍历速度:
.			解析:可以直接以线索遍历,无需递归,相比二叉链表遍历更加快。
.		3.解决了二叉链表只能查找左右孩子,不能查找其在某种序列中的前驱后继的问题。
.
.	注:另外增加线索树头结点(非根结点),并作如下规定:
.		1.头结点的 lchild 域存放指向线索链表的根结点;
.		2.头结点的 rchild 域存放指向遍历序列的最后一个结点;
.		3.(中序、先序、后序)序列的第一个结点的 lchild 域指向头结点;
.		4.(中序、先序、后序)序列的最后一个结点的 rchild 域指向头结点。
*/

//线索二叉树结点
template <typename ElemType>
struct ThNode
{
	ElemType * data;			//数据域
	ThNode<ElemType> *lchild, *rchild;	//左右子树针指域
	int ltag, rtag;				//左右标识域	0|子树域	1|线索域
};

template <typename ElemType>
class ThBiTree
{
private:
	ThNode<ElemType> * bt;		//树根结点
	ThNode<ElemType> * head;	//线索链表头结点
	ThNode<ElemType> * pre;		//前驱结点临时变量
	int tag = -1;				//线索树类型 -1|空树 0|中序树 1|先序树 2|后序树

	ThNode<ElemType> * _PreOrderCreate_R(ObjArrayList<ElemType> *list, int &index);	//先序遍历递归构造二叉树(对象型结点)

	void _InOrderThreading_R(ThNode<ElemType> *p);					//中序遍历 递归 创建线索
	void _PreOrderThreading_R(ThNode<ElemType> *p);					//先序遍历 递归 创建线索
	void _PostOrderThreading_R(ThNode<ElemType> *p);				//后序遍历 递归 创建线索

public:
	ThBiTree(ObjArrayList<ElemType> *list);	//有参构造

	/*
	.	中序线索(既可向后,又可向前遍历)
	.	优点:即可向前查找前驱,又可向后查找后继;
	.	缺点:
	.		1.前驱算法,需要对中序序列最后一个结点进行查找,没有后序线索前驱查找快;
	.		2.后继算法,需要对中序序列第一个结点进行查找,没有先序线索后继查找快。
	.	注:整体比较中和。
	*/
	void InOrderThreading();												//中序遍历 创建二叉树线索
	void InOrderThreadingDisplay();											//中序序列 遍历显示
	void InOrderThreadingDisplay_Reverse();									//中序序列 反向遍历显示
	ThNode<ElemType> * GetInOrderPrecursor(ThNode<ElemType> *p);			//获取中序序列 前驱结点
	ThNode<ElemType> * GetInOrderSuccessor(ThNode<ElemType> *p);			//获取中序序列 后继结点

	/*
	.	先序线索(只可向后,不可向前遍历)
	.	优点:后继查找算法 比 中序线索的后继查找 更快;
	.	缺点:前驱查找 需查找双亲结点,使整个算法又复杂了。
	*/
	void PreOrderThreading();												//先序遍历 创建二叉树线索
	void PreOrderThreadingDisplay();										//先序序列 遍历显示
	ThNode<ElemType> * GetPreOrderPrecursor(ThNode<ElemType> *p);			//获取先序序列 前驱结点(需寻找双亲结点,效果不好,不实现)
	ThNode<ElemType> * GetPreOrderSuccessor(ThNode<ElemType> *p);			//获取先序序列 后继结点

	/*
	.	后序线索(只可向前,不可向后遍历)
	.	优点:前驱查找算法 比 中序线索的前驱查找 更快;
	.	缺点:后继查找 需查找双亲结点,使整个算法又复杂了。
	*/
	void PostOrderThreading();												//后序遍历 创建二叉树线索
	void PostOrderThreadingDisplay_Reverse();								//后序序列 反向遍历显示
	ThNode<ElemType> * GetPostOrderPrecursor(ThNode<ElemType> *p);			//获取后序序列 前驱结点
	ThNode<ElemType> * GetPostOrderSuccessor(ThNode<ElemType> *p);			//获取后序序列 后继结点(需寻找双亲结点,效果不好,不实现)
};

template <typename ElemType>
ThBiTree<ElemType>::ThBiTree(ObjArrayList<ElemType> *list)
{
	/*
	.	有参构造:默认采用先序遍历顺序表构造二叉树
	*/
	int index = 0;
	this->bt = _PreOrderCreate_R(list, index);
}


template <typename ElemType>
ThNode<ElemType> *ThBiTree<ElemType>::_PreOrderCreate_R(ObjArrayList<ElemType> *list, int &index)
{
	/*
	.	先序遍历递归创建二叉树
	.	入参:
	.		ObjArrayList<ElemType> *list : 先序遍历组合的对象结点顺序表
	.		int &index: 顺序表遍历索引
	.		int seq: 以完全二叉树,对每个结点排序
	.	出参:
	.		ThNode<ElemType> * : 树根结点指针
	.	注:以顺序表(对象元素结点)创建
	*/
	ThNode<ElemType> *p = NULL;
	//1.超出顺序表时返回,结束
	if (index >= list->Length())
	{
		return p;
	}
	//2.元素为 NULL 时,意为空结点
	else if (list->Get(index) == NULL)
	{
		return p;
	}
	//3.创建根结点,并递归创建其左右子树
	else
	{
		p = new ThNode<ElemType>;
		p->data = list->Get(index);
		p->ltag = 0;	//初始化 标志 为 0, 即子树指针域
		p->lchild = _PreOrderCreate_R(list, ++index);
		p->rtag = 0;
		p->rchild = _PreOrderCreate_R(list, ++index);
	}
	return p;
}

template <typename ElemType>
void ThBiTree<ElemType>::InOrderThreading()
{
	/*
	.	中序遍历 创建二叉树线索
	*/
	//标记树为 中序线索树 0
	this->tag = 0;
	//1.初始化 线索链表 头结点
	this->head = new ThNode<ElemType>;
	this->head->data = NULL;
	this->head->ltag = 0;
	this->head->rtag = 1;
	//二叉树为空
	if (this->bt == NULL) {
		//1.线索链表头结点
		this->head->lchild = this->head;
		this->head->rchild = this->head;
	}
	else
	{
		//1.头结点 lchild 指向 树根
		this->head->lchild = this->bt;
		//2.初始化 前驱结点 临时变量
		this->pre = this->head;
		//3.中序遍历 递归 创建线索
		_InOrderThreading_R(this->bt);
		//4.中序序列最后一个元素 rchild 指向头结点
		this->pre->rtag = 1;
		this->pre->rchild = this->head;
		//5.头结点 rchild 指向中序序列最后一个元素
		this->head->rchild = this->pre;
	}
}

template <typename ElemType>
void ThBiTree<ElemType>::_InOrderThreading_R(ThNode<ElemType> *p)
{
	/*
	.	中序遍历 递归 创建线索
	.	算法:左根右
	.		访问根时的操作:可能创建当前结点的前驱线索,可能创建前驱结点的后继线索,两个操作并列
	*/
	if (p != NULL)
	{
		//1.左子树
		_InOrderThreading_R(p->lchild);
		//2.根
		//2.1.若左子树指针域为空,创建序列前驱线索
		//注:这一步中已经将中序序列的第一个结点 lchild 指向 头结点
		if (p->lchild == NULL)
		{
			//1.置标识域为 1,并创建序列前驱线索
			p->ltag = 1;
			p->lchild = this->pre;
		}
		//2.2.若前驱右子树指针域为空,创建其后继 为当前结点
		//注:这一步中已经将头结点 rchild 指向 中序序列的第一个结点
		if (this->pre->rchild == NULL)
		{
			//1.置标识域为 1
			this->pre->rtag = 1;
			this->pre->rchild = p;
		}
		//2.3.将全局前驱结点指针移至当前结点
		this->pre = p;
		//3.右子树
		_InOrderThreading_R(p->rchild);
	}
}

template <typename ElemType>
void ThBiTree<ElemType>::InOrderThreadingDisplay()
{
	/*
	.	中序序列遍历显示
	.	算法:
	.		1.树根非空时,寻找树的第一个中序遍历结点(左下)
	.		2.结点非空且不是头结点时,访问结点,并判断右指针域类型:
	.			2.1.若 rchild 为右子树,寻找右子树第一个中序遍历结点(左下)
	.			2.2.若 rchild 为后继结点,指针移向后继结点
	.		3.循环 2 ...
	*/
	ThNode<ElemType> *h = this->head;	//头结点(作为遍历结束标识)
	ThNode<ElemType> *p = h->lchild;	//根结点:头结点 lchild 指向根结点
	int tag = 0;	//右子树指针域类型标识	0|子树	1|线索

	//树根为空
	if (p == NULL)
		return;
	
	//结点不是头结点时,访问结点(无需判断非空)
	while (p != h)
	{
		//1.若 标识记录为子树 且 结点非空,寻找子树的第一个中序遍历结点(左下)
		while (tag == 0 && p->ltag == 0)
			p = p->lchild;
		//2.访问结点
		cout << p->data << " ";
		//3.标识记录:若 rchild 类型为子树,tag = 0;若 rchild 类型为线索,tag = 1。
		//注:使用 tag 标识的原因:在 p = p->rchild; 后,无法得知前驱结点的 rchild 类型
		tag = p->rtag == 0 ? 0 : 1;
		//4.指针 p 移至 rchild 指向结点
		p = p->rchild;
	}
}

template <typename ElemType>
void ThBiTree<ElemType>::InOrderThreadingDisplay_Reverse()
{
	/*
	.	中序序列 反向遍历显示
	*/
	ThNode<ElemType> *h = this->head;	//头结点
	ThNode<ElemType> *p = h->rchild;	//中序序列最后一个结点
	//遍历到头结点时,结束
	while (p != h)
	{
		cout << p->data << " ";
		p = GetInOrderPrecursor(p);
	}
}

template <typename ElemType>
ThNode<ElemType> * ThBiTree<ElemType>::GetInOrderPrecursor(ThNode<ElemType> *p)
{
	/*
	.	获取中序序列前驱结点
	.	算法:
	.		1.p->ltag == 1: 直接按线索取前驱;
	.		2.p->ltag == 0: 取左子树中序序列的最后一个元素(右下)
	*/
	if (p == NULL)
		return NULL;

	if (p->ltag == 1)
		p = p->lchild;
	else
	{
		p = p->lchild;
		while (p->rtag == 0)
			p = p->rchild;
	}
	return p;
}

template <typename ElemType>
ThNode<ElemType> * ThBiTree<ElemType>::GetInOrderSuccessor(ThNode<ElemType> *p)
{
	/*
	.	获取中序序列后继结点
	.	算法:
	.		1.p->rtag == 1: 直接按线索取前驱;
	.		2.p->rtag == 0: 取右子树中序序列的第一个元素(左下)
	*/
	if (p == NULL)
		return NULL;

	if (p->rtag == 1)
		p = p->rchild;
	else
	{
		p = p->rchild;
		while (p->ltag == 0)
			p = p->lchild;
	}
	return p;
}

template <typename ElemType>
void ThBiTree<ElemType>::PreOrderThreading()
{
	/*
	.	先序遍历 创建二叉树线索
	*/
	//标记树为 先序线索树 1
	this->tag = 1;
	//1.初始化 线索链表 头结点
	this->head = new ThNode<ElemType>;
	this->head->data = NULL;
	this->head->ltag = 0;
	this->head->rtag = 1;
	//二叉树为空
	if (this->bt == NULL) {
		//1.线索链表头结点
		this->head->lchild = this->head;
		this->head->rchild = this->head;
	}
	else
	{
		//1.头结点 lchild 指向 树根
		this->head->lchild = this->bt;
		//2.初始化 前驱结点 临时变量
		this->pre = this->head;
		//3.先序遍历 递归 创建线索
		_PreOrderThreading_R(this->bt);
		//4.先序序列最后一个元素 rchild 指向头结点
		this->pre->rtag = 1;
		this->pre->rchild = this->head;
		//5.头结点 rchild 指向先序序列最后一个元素
		this->head->rchild = this->pre;
	}
}

template <typename ElemType>
void ThBiTree<ElemType>::_PreOrderThreading_R(ThNode<ElemType> *p)
{
	/*
	.	先序遍历 递归 创建线索
	*/
	if (p != NULL)
	{
		//1.p->lchild 为空,增加前驱索引
		if (p->lchild == NULL)
		{
			p->ltag = 1;
			p->lchild = this->pre;
		}
		//2.前驱 rchild 为空,增加后继索引
		if (this->pre->rchild == NULL)
		{
			this->pre->rtag = 1;
			this->pre->rchild = p;
		}
		//3.前驱临时变量指向当前结点
		this->pre = p;
		//若 lchild 和 rchild 被更改为线索,则不做递归
		if (p->ltag == 0)
			_PreOrderThreading_R(p->lchild);
		if (p->rtag == 0)
			_PreOrderThreading_R(p->rchild);
	}
}

template <typename ElemType>
void ThBiTree<ElemType>::PreOrderThreadingDisplay()
{
	/*
	.	先序序列遍历显示
	.	算法:
	.		1.p->ltag == 0: 直接取左子树根结点;
	.		2.1.p->rtag == 0: 直接取右子树根结点;
	.		2.2.p->rtag == 1: 直接按线索取后继。
	*/
	ThNode<ElemType> *h = this->head;		//头结点
	ThNode<ElemType> *p = h->lchild;		//根结点

	//遍历到头结点时,结束
	while (p != h)
	{
		//1.输出结点
		cout << p->data << " ";
		//2.寻找后继结点
		//2.1.(ltag == 0),后继为左子树根结点
		if (p->ltag == 0)
			p = p->lchild;
		//2.2.(rtag == 0 || rtag == 1),后继为右子树根结点或线索
		else
			p = p->rchild;
	}
}

template <typename ElemType>
ThNode<ElemType> * ThBiTree<ElemType>::GetPreOrderPrecursor(ThNode<ElemType> *p)
{
	/*
	.	获取先序序列前驱结点
	.	算法:
	.		1.根结点时:前驱为空;
	.		2.ltag == 1 时:直接取前驱线索;
	.		3.ltag == 0 时:需先找到其双亲结点,这一步只能通过从根开始先序遍历找,效果并不好
	.			3.1.若是双亲的左子树:则前驱为其双亲结点;
	.			3.2.若是双亲的右子树:则前驱为其双亲左子树的先序遍历最后一个结点:
	.				算法:从子树根开始搜索
	.				3.2.1.若有右子树,则指针移至右子树根结点;
	.				3.2.2.若无右子树、有左子树,则指针移至左子树根结点;
	.				3.2.3.若无左子树、也无右子树,则返回该结点,即为前驱。
	.				如此循环 3.2.1 、3.2.2、3.2.3..... 
	*/

}

template <typename ElemType>
ThNode<ElemType> * ThBiTree<ElemType>::GetPreOrderSuccessor(ThNode<ElemType> *p)
{
	/*
	.	获取先序序列后继结点
	.	算法:
	.		1.p->ltag == 0: 直接取左子树根结点;
	.		2.1.p->rtag == 0: 直接取右子树根结点;
	.		2.2.p->rtag == 1: 直接按线索取后继。
	*/
	if (p == NULL)
		return NULL;

	if (p->ltag == 0)
		p = p->lchild;
	else
		p = p->rchild;
	return p;
}

template <typename ElemType>
void ThBiTree<ElemType>::PostOrderThreading()
{
	/*
	.	后序遍历 创建二叉树线索
	*/
	//标记树为 后序线索树 2
	this->tag = 2;
	//1.初始化 线索链表 头结点
	this->head = new ThNode<ElemType>;
	this->head->data = NULL;
	this->head->ltag = 0;
	this->head->rtag = 1;
	//二叉树为空
	if (this->bt == NULL) {
		//1.线索链表头结点
		this->head->lchild = this->head;
		this->head->rchild = this->head;
	}
	else
	{
		//1.头结点 lchild 指向 树根
		this->head->lchild = this->bt;
		//2.初始化 前驱结点 临时变量
		this->pre = this->head;
		//3.后序遍历 递归 创建线索
		_PostOrderThreading_R(this->bt);
		//4.后序序列最后一个元素 rchild 指向头结点
		this->pre->rtag = 1;
		this->pre->rchild = this->head;
		//5.头结点 rchild 指向后序序列最后一个元素
		this->head->rchild = this->pre;
	}
}

template <typename ElemType>
void ThBiTree<ElemType>::_PostOrderThreading_R(ThNode<ElemType> *p)
{
	/*
	.	后序遍历 递归 创建线索
	*/
	if (p != NULL)
	{
		_PostOrderThreading_R(p->lchild);
		_PostOrderThreading_R(p->rchild);
		//1.p->lchild 为空,增加前驱索引
		if (p->lchild == NULL)
		{
			p->ltag = 1;
			p->lchild = this->pre;
		}
		//2.前驱 rchild 为空,增加后继索引
		if (this->pre->rchild == NULL)
		{
			this->pre->rtag = 1;
			this->pre->rchild = p;
		}
		//3.前驱临时变量指向当前结点
		this->pre = p;
	}
}

template <typename ElemType>
void ThBiTree<ElemType>::PostOrderThreadingDisplay_Reverse()
{
	/*
	.	后序序列 反向遍历显示
	.	算法:从根结点(后序序列最后一个结点)开始,向前(查找前驱)遍历
	.	注:正向遍历显示,可在此基础上加个栈		
	*/
	ThNode<ElemType> *h = this->head;	//头结点
	ThNode<ElemType> *p = h->rchild;	//根结点(后序序列最后一个结点)
	//遍历到头结点时,结束
	while (p != h)
	{
		cout << p->data << " ";
		p = GetPostOrderPrecursor(p);
	}
}

template <typename ElemType>
ThNode<ElemType> * ThBiTree<ElemType>::GetPostOrderPrecursor(ThNode<ElemType> *p)
{
	/*
	.	获取后序序列前驱结点
	.	算法:
	.		1.若结点 ltag == 1:直接取前驱线索结点;
	.		2.若结点 ltag == 0:
	.			2.1.若其 rtag == 0(此时存在左右子树):则前驱为其右子树根结点;
	.			2.2.若其 rtag == 1(此时只存在左子树):则前驱为其左子树根结点;
	*/
	if (p == NULL)
		return NULL;

	if (p->ltag == 1 || p->rtag == 1)
		p = p->lchild;
	else
		p = p->rchild;
	return p;
}

template <typename ElemType>
ThNode<ElemType> * ThBiTree<ElemType>::GetPostOrderSuccessor(ThNode<ElemType> *p)
{
	/*
	.	获取后序序列后继结点
	.	算法:
	.		1.若结点 rtag == 1:直接取后继线索结点;
	.		2.若结点 rtag == 0:需先获取其双亲结点,用递归或栈从头遍历实现
	.			2.1.若其为双亲的左子树,则后继为其双亲右子树后序遍历第一个结点;
	.			2.2.若其为双亲的左子树,且其双亲无右子树,则后继为其双亲结点;
	.			2.3.若其为双亲的右子树,则后继为其双亲结点。
	*/
	
}

#endif // !THBITREE_H_
//文件名:"ThBiTree_Test.cpp"
#include "stdafx.h"
#include <iostream>
#include "ThBiTree.h"
#include "ObjArrayList.h"
using namespace std;

//树结点元素
struct Element
{
	char a;
	int b;
public:
	friend ostream & operator <<(ostream& out, Element *p)
	{
		/*
		.	友元函数重载输出操作符,实现对象输出
		*/
		out << p->a;
		return out;
	}
};

int main()
{
	ObjArrayList<Element> *list = new ObjArrayList<Element>(15);
	list->Add(0, new Element{ 'A',1 });
	list->Add(1, new Element{ 'B',2 });
	list->Add(2, new Element{ 'C',3 });
	list->Add(3, NULL);
	list->Add(4, NULL);
	list->Add(5, new Element{ 'D',4 });
	list->Add(6, new Element{ 'E',5 });
	list->Add(7, NULL);
	list->Add(8, new Element{ 'G',7 });
	list->Add(9, NULL);
	list->Add(10, NULL);
	list->Add(11, new Element{ 'F',6 });
	list->Add(12, NULL);
	list->Add(13, NULL);
	list->Add(14, NULL);
	cout << endl << "----------------------中序线索树----------------------------" << endl;
	ThBiTree<Element> *t_in = new ThBiTree<Element>(list);
	t_in->InOrderThreading();
	cout << endl << "正向遍历测试:查找后继" << endl;
	t_in->InOrderThreadingDisplay();
	cout << endl << "反向遍历测试:查找前驱" << endl;
	t_in->InOrderThreadingDisplay_Reverse();
	cout << endl << "----------------------先序线索树----------------------------" << endl;
	ThBiTree<Element> *t_pre = new ThBiTree<Element>(list);
	t_pre->PreOrderThreading();
	cout << endl << "正向遍历测试:查找后继" << endl;
	t_pre->PreOrderThreadingDisplay();
	cout << endl << "----------------------后序线索树----------------------------" << endl;
	ThBiTree<Element> *t_post = new ThBiTree<Element>(list);
	t_post->PostOrderThreading();
	cout << endl << "反向遍历测试:查找前驱" << endl;
	t_post->PostOrderThreadingDisplay_Reverse();
	
	return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_39469127/article/details/80547548