一、中序线索二叉树------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);
}
};
二叉树的线索化最难的是对特殊点以及条件的判断,写代码时请务必小心