c++ 实现线索二叉树

  本代码主要参考了大话数据结构的思想,由于其为c语言版,所以本人对其中一些函数进行了封装,并增加了一些新的功能。

  由于本代码的基本思想在大话数据结构书中已经说的很清楚了,所以此处就不再进行说明代码原理。

  并且,在每个函数的后面,都写了其作用,本人也尽可能地在必要的地方加了注释。并且为了省事,用了shared_ptr。

  写此代码的目的是为了让简单线索二叉树的代码更加规范一点(很多c代码都是用全局变量等不规范的形式)。此代码在vs2019上能执行,编译器需要支持c++11以上。

一、线索二叉树节点类~

using std::cout;
using std::endl;
using std::shared_ptr;
using std::make_shared;

namespace {
	enum  class PointerTag
	{
		Linked, Thread
	};
}

template<typename T = int>
struct ThreadNode {
public:
	using ThreadNodePtr = shared_ptr<ThreadNode<T>>;

public:
	T _data;
	ThreadNodePtr _lchild;
	ThreadNodePtr _rchild;
	PointerTag _lTag;/*左右标签*/
	PointerTag _rTag;

	ThreadNode(const T& data) :
		_data(data),
		_lchild(nullptr),
		_rchild(nullptr),
		_lTag(PointerTag::Linked),
		_rTag(PointerTag::Linked) {}

	ThreadNode(const ThreadNode& other):
		_data(other._data),
		_lchild(other._lchild),
		_rchild(other._rchild),
		_lTag(other._lTag),
		_rTag(other._rTag){}
};

二、线索二叉树类~

	template<typename T = int>
	class ThreadTree {
	public:
		using ThreadNodePtr = shared_ptr<ThreadNode<T>>;

	private:
		ThreadNodePtr _root;/*根节点*/
		ThreadNodePtr  _hot;//刚被访问过的节点
		ThreadNodePtr _head;//线索二叉树头节点哨兵

	public:
		ThreadTree() :_root(nullptr), _hot(nullptr), _head(nullptr) {}

		ThreadTree(T* PrevOrder, T* InOrder, const int& num) :_hot(nullptr), _head(nullptr) {//以前序和中序序列创建二叉树
			_root = buildTree(PrevOrder, InOrder, num);
		}

		void InThread();//从根节点开始,线索化二叉树

		void InOrderTrav_Thread()const;//以线索二叉树的方式进行遍历

	private:

		ThreadNodePtr buildTree(T* PrevOrder, T* InOrder, const int& num);//以前序和中序序列创建二叉树

		void InThreading(ThreadNodePtr);	/*对根节点进行中序线索化*/
	};

三、完整头文件~

#pragma once
using std::cout;
using std::endl;
using std::shared_ptr;
using std::make_shared;

namespace my_thread_tree {


	namespace {
		enum  class PointerTag
		{
			Linked, Thread
		};
	}

	template<typename T = int>
	struct ThreadNode {
	public:
		using ThreadNodePtr = shared_ptr<ThreadNode<T>>;

	public:
		T _data;
		ThreadNodePtr _lchild;
		ThreadNodePtr _rchild;
		PointerTag _lTag;/*左右标签*/
		PointerTag _rTag;

		ThreadNode(const T& data) :
			_data(data),
			_lchild(nullptr),
			_rchild(nullptr),
			_lTag(PointerTag::Linked),
			_rTag(PointerTag::Linked) {}

		ThreadNode(const ThreadNode& other):
			_data(other._data),
			_lchild(other._lchild),
			_rchild(other._rchild),
			_lTag(other._lTag),
			_rTag(other._rTag){}
	};

	template<typename T = int>
	class ThreadTree {
	public:
		using ThreadNodePtr = shared_ptr<ThreadNode<T>>;

	private:
		ThreadNodePtr _root;/*根节点*/
		ThreadNodePtr  _hot;//刚被访问过的节点
		ThreadNodePtr _head;//线索二叉树头节点哨兵

	public:
		ThreadTree() :_root(nullptr), _hot(nullptr), _head(nullptr) {}

		ThreadTree(T* PrevOrder, T* InOrder, const int& num) :_hot(nullptr), _head(nullptr) {//以前序和中序序列创建二叉树
			_root = buildTree(PrevOrder, InOrder, num);
		}

		void InThread();//从根节点开始,线索化二叉树

		void InOrderTrav_Thread()const;//以线索二叉树的方式进行遍历

	private:

		ThreadNodePtr buildTree(T* PrevOrder, T* InOrder, const int& num);//以前序和中序序列创建二叉树

		void InThreading(ThreadNodePtr);	/*对根节点进行中序线索化*/
	};

	template<typename T>
	void ThreadTree<T>::InOrderTrav_Thread()const
	{
		ThreadNodePtr p = _root;//指向根节点
		while (p != _head) {//当没有遍历到头节点哨兵时
			while (p->_lTag == PointerTag::Linked)//一直往左,直到左孩子记录的为前驱而不是左孩子时
				p = p->_lchild;
			cout << "数据为:\t" << p->_data << "\t引用计数为:\t" << p.use_count() << endl;
			while (p->_rTag == PointerTag::Thread && p->_rchild != _head) {//遍历到直到p的右孩子为右孩子而不是后继时
				p = p->_rchild;
				cout << "数据为:\t" << p->_data << "\t引用计数为:\t" << p.use_count() << endl;
			}
			p = p->_rchild;//p进入右子树根
		}
		cout << endl;
	}

	template<typename T>
	shared_ptr<ThreadNode<T>>  ThreadTree<T>::buildTree(T* PrevOrder, T* InOrder, const int& num)
	{
		if (PrevOrder == nullptr || InOrder == nullptr || num <= 0)
		{
			return nullptr;
		}
		ThreadNodePtr root =make_shared<ThreadNode<T>>(PrevOrder[0]);
		// 前序遍历的第一个数据就是根节点数据    

		//中序遍历,根节点左为左子树,右为右子树    
		int rootposition = -1;
		for (int i = 0; i < num; i++)
		{
			if (InOrder[i] == root->_data)
			{
				rootposition = i;
			}
		}
		if (rootposition == -1)
			return nullptr;
		//重建左子树(根节点)递归    
		int LeftNum = rootposition;
		T* PrevOrderLeft = PrevOrder + 1; //前序第二个即为根节点的左子树    
		T* InOrderLeft = InOrder; //中序第一个 即为其左子树。    
		root->_lchild = buildTree(PrevOrderLeft, InOrderLeft, LeftNum);

		//重建右子树(根节点)递归    
		int RightNum = num - LeftNum - 1;//右半边子树
		T* PrevOrderRight = PrevOrder + 1 + LeftNum;
		T* InOrderRight = InOrder + LeftNum + 1;
		root->_rchild = buildTree(PrevOrderRight, InOrderRight, RightNum);
		return root;
	}

	template<typename T>
	void ThreadTree<T>::InThreading(ThreadNodePtr p)
	{
		if (p != nullptr) {
			InThreading(p->_lchild);
			if (p->_lchild == nullptr) {
				p->_lTag = PointerTag::Thread;
				p->_lchild = _hot;//p的左孩子设为前驱
			}

			if (_hot != nullptr && _hot->_rchild == nullptr) {//前驱的右孩子为空时
				_hot->_rTag = PointerTag::Thread;
				_hot->_rchild = p;//将前驱的右孩子设为p
			}
			_hot = p;
			InThreading(p->_rchild);
		}
	}

	template<typename T>
	void ThreadTree<T>::InThread() {//从根节点开始,线索化二叉树
		if (_root == nullptr)
			return;

		//头哨兵节点初始化
		_head = make_shared<ThreadNode<T>>(0);//反正不会访问这个,所以初值不妨就设成0
		_head->_rTag = PointerTag::Thread;
		_head->_lchild = _root;
		_head->_rchild = _head;

		_hot = _head;//将_hot设定为_head
		InThreading(_root);//线索化二叉树
		//遍历之后,_hot必然为中序遍历最右边那个节点
		_hot->_rTag = PointerTag::Thread;
		_hot->_rchild = _head;//就将头哨兵作为_hot的右孩子
		_head->_rchild = _hot;//并且将_hot作为头哨兵的右孩子
	}

}//namespace my_thread_tree

四、测试cpp~

#include <iostream>
#include "ThreadTree.h"
using namespace std;
using namespace my_thread_tree;

int main() {

	//int a[] = { 3,1,0,2,5,4,6 };
	//int b[] = { 0,1,2,3,4,5,6 };

	char a[] = { 'A','B','D','H','I','E','J','C','F','G'};
	char b[] = { 'H','D','I','B','J','E','A','F','C','G' };
	ThreadTree<char> TT(a, b, 10);//传参时传入两个数组,分别为树的前序和中序遍历序列
	TT.InThread();
	TT.InOrderTrav_Thread();

	return 0;
}

引用计数这个可以忽略,主要是玩下看有多少个指针指向这个对象

数据为: H       引用计数为:     2
数据为: D       引用计数为:     4
数据为: I       引用计数为:     2
数据为: B       引用计数为:     4
数据为: J       引用计数为:     2
数据为: E       引用计数为:     3
数据为: A       引用计数为:     5
数据为: F       引用计数为:     2
数据为: C       引用计数为:     4
数据为: G       引用计数为:     4
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值