二叉树线索化

一、中序线索二叉树------4251637
在这里插入图片描述
建立线索二叉树的方法与递归建立二叉树的方法相同,对二叉树进行线索化的优势是不需递归就能遍历二叉树,减小遍历二叉树的时间复杂度
中序二叉树线索化并访问步骤

1.建立结点类 --左右孩子结点指针+左右孩子结点指针标签tag (标签用于判断节点的左孩子或右孩子是否被线索化–当tag为1时表示已被线索化 为0时表示不能线索化或结点不存在)
2.递归构造二叉树
3.递归线索化(即对不存在左孩子或右孩子的结点进行线索化,减少空间的浪费)

3.1.线索化需要建立当前结点的**前驱和后继**,并置tag值为1,此时需要用**pre指针**指来对前驱结点进行访问
3.2.第一个结点为4
3.3.每一步建立当前结点的前驱结点和先前结点的后继结点 --- 处理4的前驱->处理2的前驱->处理4的后继 
3.4.注意pre指针最终停在最右结点即7 此时要将最右结点的右孩子线索化 (这里处理为null)
3.5.pre指针刚开始为空指针不要忘记非空处理!

4.访问线索二叉树

4.1 递归当前树找到最左结点并输出
4.2 若最左结点有右孩子且并未被线索化(tag==0) 重复4.1  
	若最左结点右孩子已被线索化 (后继节点)  右孩子即为下一个结点   
template<class T>
// n+1个空指针域用于指示前驱和后继结点
class ThreadNode {
public:
	T data;
	ThreadNode<T>* lchild;
	ThreadNode<T>* rchild;
	ThreadNode<T>* parent;//用于建立后序遍历二叉树时寻找双亲
	int  ltag;//0表示为左孩子  1表示为前驱结点
	int	 rtag;//0表示为右孩子  1表示为后继结点
	ThreadNode(T data){
		this->data = data;
		rchild = nullptr;
		lchild = nullptr;
		ltag = 0;
		rtag = 0;
	}
	ThreadNode() {
		rchild = nullptr;
		lchild = nullptr;
		ltag = 0;
		rtag = 0;
	}
};
//中序线索二叉树
template<class T>
class InThreadTree {
private:
	ThreadNode<T>* root;
	//运用数组递归建立二叉树
	ThreadNode<T>* CreateThreadTree_Array(T* a, int length, int index) {
		ThreadNode<T>* node = nullptr;
		if (index < length) {
			node = new ThreadNode<T>(a[index]);
			node->lchild = CreateThreadTree_Array(a, length, 2 * index + 1);
			node->rchild = CreateThreadTree_Array(a, length, 2 * index + 2);

		}
		return node;

	}
	//中序遍历线索化二叉树  -- 引用符号注意出错
	void InThread(ThreadNode<T>* &p, ThreadNode<T>* &pre) {
		//p为当前结点,pre为前驱结点
		if (p == nullptr)return;
			InThread(p->lchild, pre);//递归建立左子树
			if (p->lchild == nullptr) {
				p->ltag = 1;
				p->lchild = pre;

			}
			if (pre != nullptr && pre->rchild == nullptr)//当前结点不是第一个结点 且 前驱结点的右孩子为空
			{
				pre->rtag = 1;
				pre->rchild = p;
			}
			pre = p;//将p赋值给pre
			InThread(p->rchild, pre);
	}
	//中序遍历  查找当前结点的第一个子节点
	ThreadNode<T>* FirstNode(ThreadNode<T>*node) {
		//已被线索化,node->lchid不肯能为空,用这个方法会死循环
		while (node->ltag==0) {
			node = node->lchild;
		}
		return node;
	}
	//中序遍历 查找下一个结点
	ThreadNode<T>* NextNode(ThreadNode<T>* p) {
		if (p->rtag == 0)return FirstNode(p->rchild);
		else return p->rchild;
	}
public:
	InThreadTree(T *a,int length){
		root=CreateThreadTree_Array(a, length, 0);
	}
	ThreadNode<T>* GetRoot() {
		return root;
	}
	void visit(ThreadNode<T>* root) {
		if (root != nullptr)
		{
			cout << root->data;
		}
	}
	//建立中序线索二叉树
	void CreateInThread() {
		ThreadNode<int>* pre = nullptr;
		if (root != nullptr) {
			InThread(root, pre);
			pre->rchild = nullptr;
			pre->rtag = 1;
		}
		//pre最终停在最右子树的最右结点,需要处理

	}
	//遍历中序二叉树
	void InOrder() {
		for (ThreadNode<T>* p = FirstNode(root); p != nullptr; p=NextNode(p))
			visit(p);

	}

};

二、先序线索化二叉树 --1245367在这里插入图片描述
与中序线索化类似,交换递归顺序
1.需要注意的是线索化的顺序问题 到达当前结点时就会把结点的前驱线索化(就算没有左孩子也会被线索化),如果不判断条件则会进入死循环 4->2->4->2 进入循环前先判断条件 ltag==0?进入:不进
2.同时在遍历线索二叉树时需要注意判断 ltag==0?进入:不进 否则也会回到前驱结点造成死循环

//先序线索二叉树
template<class T>
class PreThreadTree{
private:
	ThreadNode<T>* root;
	int high;
	//运用数组建立二叉树
	ThreadNode<T>* CreateThreadTree_Array(T* a, int length, int index) {
		ThreadNode<T>* node = nullptr;
		if (index < length) {
			node = new ThreadNode<T>(a[index]);
			this->high++;
			node->lchild = CreateThreadTree_Array(a, length, 2 * index + 1);
			node->rchild = CreateThreadTree_Array(a, length, 2 * index + 2);

		}
		return node;

	}
	void PreThread(ThreadNode<T>*& p, ThreadNode<T>*& pre) {
			if (p == nullptr)return;
			if (p->lchild == nullptr) {
				p->lchild = pre;//!!
				p->ltag = 1;
			}
			if (pre != nullptr && pre->rchild == nullptr) {
				pre->rchild = p;
				pre->rtag = 1;
			}
			pre = p;
			//如果这里没有条件判断!! 在访问最左结点时已经建立了前驱结点,最终会变成死循环
			if (p->ltag == 0)//判断左孩子是否已被线索化
			{
				PreThread(p->lchild, pre);
			}
			if (p->rtag == 0) {
				PreThread(p->rchild, pre);
			}

	}
	ThreadNode<T>* NextNode(ThreadNode<T>* p) {
		if (p->lchild != nullptr && p->ltag==0)return p->lchild;
		return p->rchild;
	}
public:
	PreThreadTree(T* a,int length) {
		root = CreateThreadTree_Array(a, length, 0);
	}
	ThreadNode<T>* GetRoot() {
		return root;
	}
	void visit(ThreadNode<T>* root) {
		if (root != nullptr)
		{
			cout << root->data;
		}
	}
	void CreatePreThread() {
		ThreadNode<T>* pre=nullptr;
		if (root != nullptr) {
			PreThread(root, pre);
			pre->rchild = nullptr;
			pre->rtag = 1;
		}

	}
	void PreOrder() {
		ThreadNode<T>* p;
		for (p = root; p != nullptr; p = NextNode(p)) {
			cout << p->data;
		}
	}
};

三、后序线索化二叉树----- 4526731
在这里插入图片描述
1.后序遍历也是改变递归顺序,在递归函数上并不需要新的条件判断
✳2.在建立结点类时需要建立三叉链表(加入父指针)因为从2->6并没有后继线索能够到达,此时只能通过根节点来查找下一节点
3.在遍历输出二叉树时需要注意 这里的最后一个结点为根节点与先序和中序遍历不同,在查找下一节点时需要对根节点进行特殊处理,使用pre指针来判断当前是否已经遍历过一遍二叉树并又回到根节点.

//后序线索二叉树
template<class T>
class PostThreadTree {
private:
	ThreadNode<T>* root;
	int high;
	//运用数组建立二叉树
	ThreadNode<T>* CreateThreadTree_Array(T* a, int length, int index,ThreadNode<T>* parent) {
		ThreadNode<T>* node = nullptr;
		if (index < length) {
			node = new ThreadNode<T>(a[index]);
			this->high++;
			node->parent = parent;
			parent = node;//逐层建立node的parent结点
			node->lchild = CreateThreadTree_Array(a, length, 2 * index + 1, parent);
			node->rchild = CreateThreadTree_Array(a, length, 2 * index + 2, parent);

		}
		return node;

	}
	void PostThread(ThreadNode<T>*& p, ThreadNode<T>*& pre) {
		if (p == nullptr)return;
		PostThread(p->lchild, pre);
		PostThread(p->rchild, pre);
		if (p->lchild == nullptr)
		{
			p->lchild = pre;
			p->ltag = 1;
		}
		//顺序,先判断是否为空再取右孩子
		if (pre != nullptr && pre->rchild == nullptr) {
			pre->rchild = p;
			pre->rtag = 1;
		}
		pre = p;
	}
	ThreadNode<T>* FistNode(ThreadNode<T>* p) {
		while(p->ltag==0) { p = p->lchild; }
		return p;
	}
	ThreadNode<T>* NextNode(ThreadNode<T>* p, ThreadNode<T>* &pre) {
		if (p == nullptr)return nullptr;
		if (p->rtag == 0)
		{
			if (p == root && pre == p->rchild) {
				return root;
			}
			if (p->rchild != pre)
			{
				pre = p;
				return FistNode(p->rchild);
			}
			else {
				p = NextNode(p->parent, p);
			}
		}
		else {
			pre = p;
			return p->rchild;
		}

	}
	
public:
	PostThreadTree(T* a, int length) {
		ThreadNode<T>* parent = nullptr;
		root = CreateThreadTree_Array(a, length, 0, parent);
	}
	ThreadNode<T>* GetRoot() {
		return root;
	}
	void visit(ThreadNode<T>* root) {
		if (root != nullptr)
		{
			cout << root->data;
		}
	}
	void CreatePostThread() {
		ThreadNode<T>* pre=nullptr;
		if (root == nullptr)return;
		PostThread(root, pre);
	}
	void PostOrder() {
		ThreadNode<T>* pre = nullptr;
		//到根节点终止
		for (ThreadNode<T>* p = FistNode(root); p != root; p = NextNode(p,pre))
		{
			visit(p);
		}
		visit(root);
	}
};


二叉树的线索化最难的是对特殊点以及条件的判断,写代码时请务必小心

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值