本代码主要参考了大话数据结构的思想,由于其为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