线索化二叉树

线索化:

1. 线索化的目的

对于二叉树的任意一种遍历(前序遍历,中序遍历,后序遍历)都存在反向递归的情况,也就是说在我们查找到子节点的时候,就需要回退到上一个访问过的节点去寻找未被访问的节点,没有就继续反向递归,不能像正常的链表或者数组那样,存在某种规则可以直接询问下一个节点或者上一个节点,所以对于这种情况,我们就需要对二叉树进行线索化,那么线索化后的二叉树,就能像链表或者数组那样,存在某种规则,可以直接访问下一个节点或者上一个节点,便于我们遍历。

2.线索二叉树

任意一棵拥有 N 个节点的单向二叉树,都会右 2N 个指针域(一个节点2个指针域,分别指向左孩子和右孩子),拥有 N - 1 个分支线,每个分支线指向一个使用的指针域,那么空指针域的个数为: 2N - (N - 1) = N + 1 ,那么就意味着每 N 个节点就有 N+1 个空指针域是浪费掉的,所以这些空掉的指针域正好拿来帮助我们对二叉树进行线索化。

线索二叉树 等于是把一棵二叉树变成了一个双向链表,这样的链表对于二叉树节点的插入,删除,查找都带来了方便。

对二叉树以某种次序遍历使其变成线索二叉树的过程称作 线索化

备注:二叉树的线索化是针对某种次序来讲的,比如,前序遍历 有前序遍历的线索化,中序遍历 有中序遍历的线索化,二者不能混合使用。

意义:线索化避免了这棵树遍历时的递归回旋问题,使用一个循环一口气就可以将这棵树访问完毕,可以这么理解,树的本身访问到了叶子节点就出现了断点问题,就要开始递归回旋判断,然而我们使用了线索化以后就相当于将断点问题解决,这就是一条可以直达终点的通路。
能够对这颗树进行非递归遍历;能够给这棵树加上迭代器;

3.线索化的理解

可以用两句话概括分析:
A. 如果当前节点没有左孩子,就让左孩子指向父亲,同时让其节点类型变成THREAD;
B. 如果父亲节点没有右孩子,就让其右孩子指向当前节点,同时让其节点类型变成THREAD。

这里以前序遍历为例:
在这里插入图片描述

这里以中序遍历为例:
在这里插入图片描述
以上两个例子可以看出,二叉树的前驱节点和后继节点一定和固定的某个遍历顺序有关,不同遍历顺序,同一个节点的前驱和后继节点不同。

根据上面分析,再看下面两个例子的图,就明白图中的箭头指向的含义了:

先序线索化如下图:(根左右)
这里写图片描述
中序线索化如下图:(左根右)
这里写图片描述

4.节点结构

以下是线索化二叉树的节点数据结构,定义了一个 枚举类型 ThreadType 表示指针域的指向类型。

//线索化指针域类型
enum pointType
{
	LINK = 1, //链式类型,表示指向当前节点的(左右)孩子
	THREAD = 2, //线索化类型,表示当前节点没有孩子,需要指向自己的前驱或者后继
};
//线索化二叉树节点类型
template <class T>
struct TreeNodeThread
{
	T _data;   //值类型
	//左侧指针域,Link下指向自己的左孩子,Thread下指向自己的前驱节点
	TreeNodeThread<T>* _leftChild;
	//右侧指针域,Link下指向自己的右孩子,Thread下指向自己的后继节点
	TreeNodeThread<T>* _rightChild;
	pointType _left;  //左侧指针域类型
	pointType _right;  //右侧指针域类型

	TreeNodeThread(const T& data)
		:_data(data), _leftChild(NULL), _rightChild(NULL)
		, _left(LINK), _right(LINK)
	{}
};

5.代码

代码示例:

enum pointType
{
	LINK,THREAD
};

template <class T>
struct TreeNodeThread
{
	T _data;
	TreeNodeThread<T>* _leftChild;
	TreeNodeThread<T>* _rightChild;
	pointType _left;
	pointType _right;

	TreeNodeThread(const T& data)
		:_data(data),_leftChild(NULL),_rightChild(NULL)
		,_left(LINK),_right(LINK)
	{}
};

template <class T>
class BinaryTree
{
	typedef TreeNodeThread<T> Node;
protected:
	Node* _root;
public:
	BinaryTree()
		:_root(NULL)
	{}
	BinaryTree(T* arr, size_t n, const T& invalid)
	{
		int a = 0;
		_root = CreateTree(arr, n, invalid, a);
	}
	BinaryTree(const BinaryTree<T>& t)//拷贝构造函数
	{
		_root = _copy(t._root);
	}
	BinaryTree<T>& operator=(const BinaryTree<T>& t)//赋值运算符重载
	{
		if(&t != this)//两个对象的地址相比较
		{
			Destory(_root);
			_root = _copy(t._root);
		}
		return *this;
	}

	Node* _copy(Node* root)
	{
		if(NULL == root)
			return NULL;
		Node* cur = new Node(root->_data);
		cur->_leftChild = _copy(root->_leftChild);
		cur->_rightChild = _copy(root->_rightChild);
		return cur;
	}
	//线索化访问二叉树
	//先序线索化-------根左右
	//当前节点左孩子没有就指向父亲,父亲的右孩子没有就指向下一个节点(即当前节点)
	void PreOrderThread()
	{
		Node* prev = NULL;
		_PreOrderThread(_root,prev);
	}
	void _PreOrderThread(Node* root, Node*& prev)
	{
		if(NULL == root)
			return;
		if(root->_leftChild == NULL)
		{
			root->_leftChild = prev;
			root->_left = THREAD;
		}
		if(prev && prev->_rightChild == NULL)
		{
			prev->_rightChild = root;
			prev->_right = THREAD;
		}
		prev = root;
		if(root->_left == LINK)
		{
			_PreOrderThread(root->_leftChild,prev);
		}
		if(root->_right == LINK)//如果父节点没有右孩子,那么此时不判断是否线索化的话,父节点的右孩子也指向左孩子,这样会再次执行左孩子的过程
		{
			_PreOrderThread(root->_rightChild,prev);
		}
	}
	void showPreOrderThread()
	{
		cout<<"先序遍历线索化:"<<endl;
		_showPreOrderThread(_root);
		cout<<endl;
	}
	void _showPreOrderThread(Node* root)//先序--根左右
	{
		if(root == NULL)
			return;
		Node* cur = root;
		while(cur)
		{
			while(cur->_left == LINK)
			{
				cout<<cur->_data<<" ";
				cur = cur->_leftChild;
			}
			cout<<cur->_data<<" ";
			cur = cur->_rightChild;
		}
	}

	//中序线索化-------左根右
	void MidOrderThread()
	{
		Node* prev = NULL;
		_MidOrderThread(_root,prev);
	}
	void _MidOrderThread(Node* root, Node*& prev)
	{
		if(NULL == root)
			return;
		_MidOrderThread(root->_leftChild,prev);
		if(root->_leftChild == NULL)
		{
			root->_leftChild = prev;
			root->_left = THREAD;
		}
		if(prev && prev->_rightChild == NULL)
		{
			prev->_rightChild = root;
			prev->_right = THREAD;
		}
		prev = root;
		_MidOrderThread(root->_rightChild,prev);//为什么这时候不用判断是否是线索化,因为路线是左根右,不会出现回退现象,即将访问的孩子和我的父亲没有直接关系
	}
	void showMidOrderThread()
	{
		cout<<"中序遍历线索化:"<<endl;
		_showMidOrderThread(_root);
		cout<<endl;
	}
	void _showMidOrderThread(Node* root)//左根右
	{
		if(NULL == root)
			return;
		Node* cur = root;
		while(cur)
		{
			while(cur->_left == LINK)
			{
				cur = cur->_leftChild;
			}
			cout<<cur->_data<<" ";
			while(cur->_right == THREAD)
			{
				cur = cur->_rightChild;
				cout<<cur->_data<<" ";
			}
			cur = cur->_rightChild;
		}
	}
//层序遍历
void LevelOrder()
	{
		cout<<"层序遍历:"<<endl;
		_LevelOrder(_root);
		cout<<endl;
	}
	void _LevelOrder(Node* root)
	{
		assert(root);
		queue<Node*> q;//先进先出
		q.push(root);
		while(!q.empty())
		{
			Node* cur = q.front();
			cout<<cur->_data<<" ";
			if(cur->_leftChild != NULL)
			{
				q.push(cur->_leftChild);
			}
			if(cur->_rightChild != NULL)
			{
				q.push(cur->_rightChild);
			}
			q.pop();
		}
	}
	
};

迭代器:我们采用中序遍历的方式来设计迭代器:

//在类BinaryTree中添加以下代码:
typedef TreeIterator<T,T&,T*> Iterator;
//迭代器------中序遍历
	Iterator Begin()
	{
		if(_root == NULL)
			return NULL;
		Node* cur = _root;
		while(cur->_left == LINK)
		{
			cur = cur->_leftChild;
		}
		return Iterator(cur);
	}
	Iterator End()
	{
		return NULL;
	}


//中序遍历迭代器----左根右
template <class T,class Ref,class Ptr>
class TreeIterator
{
	typedef TreeNodeThread<T> Node;
	typedef TreeIterator<T,Ref,Ptr> Self;
protected:
	Node* _node;
public:
	TreeIterator(Node* node)
		:_node(node)
	{}
	Self& operator++()//前置++
	{
		if(_node->_right == THREAD)
		{
			_node = _node->_rightChild;
		}
		else
		{
			Node* cur = _node->_rightChild;
			while(cur && cur->_left == LINK)
			{
				cur = cur->_leftChild;
			}
			_node = cur;
		}
		return *this;
	}
	Self operator++(int)//后置++
	{
		Self cur(_node);
		++*this;
		return cur;
	}
	Self& operator--()//前置--
	{
		if(_node->_left == THREAD)
		{
			_node = _node->_leftChild;
		}
		else
		{
			Node* cur = _node->_leftChild;
			while(cur && cur->_right == LINK)
			{
				cur = cur->_rightChild;
			}
			_node = cur;
		}
		return *this;
	}
	Self operator--(int)//后置--
	{
		Self cur(_node);
		--*this;
		return cur;
	}
	Ref operator*()
	{
		return _node->_data;
	}
	bool operator!=(const Self& t)
	{
		return _node != t._node;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值