基于C++的ADT封装——泛型中序线索二叉树

线索二叉树设计理念

        众所周知,在一棵结点数为n的普通二叉树中,存在n+1个空指针域,而这些空指针域占用了大量内存而不能被很好地利用。同时,普通二叉树的遍历方式一般是通过递归或非递归调栈来实现,这个过程中损耗了一定的时间或额外空间。因此,我们便想到,是否可以利用这些空指针域对结点进行串联,使得所有结点之间构成一组线性关系,从而能更加方便地进行特定的遍历方式呢?于是,线索二叉树便诞生了,而将一棵普通二叉树转化为线索二叉树的过程,我们称之为线索化


线索二叉树的分类

        线索化的过程基于树的遍历顺序,我们需要通过某种特定的遍历顺序将结点串联。因此,线索二叉树分为了先序线索二叉树、中序线索二叉树和后序线索二叉树。以下内容以中序线索二叉树为例,讲解具体的代码实现。


线索二叉树的结构

        在一棵普通二叉树中,每一个结点通常含有一个数据域和两个指针域;而在线索二叉树中,每个结点则需要另外两个bool类型的标记域,一个对应左指针域,一个对应右指针域。而对应结点的标记则表示了当前指针域是否需要线索化:若当前指针域为空指针域,则需要线索化,标记位为true,反之则为false。

        下图则是一棵经过中序线索化的二叉树,每个结点分别由[左指针域,左标记位,值,右标记位,右指针域]组成,其中黑色部分为二叉树的树形结构,而红色部分则是经过线索化的结构。在一棵中序线索二叉树中,经过线索化的左指针都指向中序遍历的上一个结点,称为当前结点的前驱;而右指针都指向中序遍历的下一个结点,称为当前结点的后继


线索化代码实现

显然,要想实现二叉树的中序线索化,我们就需要通过中序遍历二叉树的方式来对其进行线索化的操作,而线索化需要修改空指针域的指向,这便要求我们通过双指针的方式来访问二叉树。

这个过程只需要我们在设置结点标记位的同时将原左空指针域指向前驱,原右空指针域指向后继即可。具体代码如下:

template <class T>
void ThreadedBinaryTree<T>::InThread(TrNode<T> *cur, TrNode<T> *&pre)
{
    if (!cur)
        return;
    if (cur->lc)
        InThread(cur->lc, pre);
    if (!cur->lc)
    {
        cur->lc = pre;
        cur->Ltag = true;
    }
    if (pre && !pre->rc)
    {
        pre->rc = cur;
        pre->Rtag = true;
    }
    pre = cur;
    if (cur->rc)
        InThread(cur->rc, pre);
    else
        cur->Rtag = true;
}

获取结点的前驱与后继

显然,在遍历二叉树的过程中我们需要获得特定结点的前驱与后继。

于后继而言,当右标记域为true,则表示右指针已经过线索化,直接指向了当前结点的后继;而当标记域为false,其右指针却未必指向了当前结点的后继。

在我们刚才创建的二叉树中,A的后继为E,但右指针却指向了D。不过我们可以发现,我们仍然可以通过橙色标出的路径从A访问到它的后继E,如下图所示。

 通过中序遍历的顺序我们不难发现,我们进入当前结点的右结点后,再不断进入左结点,则可以得到当前结点的后继。因此,我们可以得出如下代码:

template <class T>
TrNode<T> *ThreadedBinaryTree<T>::succesor(TrNode<T> *p)
{
    if (p->Rtag)
        return p->rc;
    p = p->rc;
    if (!p)
        return nullptr;
    while (!p->Ltag)
        p = p->lc;
    return p;
}

类似地,我们可以得出获取前驱的代码:

template<class T>
TrNode<T> *ThreadedBinaryTree<T>::precursor(TrNode<T> *p)
{
    if(p->Ltag)
        return p->lc;
    p = p->lc;
    if(!p)
        return nullptr;
    while(!p->Rtag)
        p = p->rc;
    return p;
}

中序遍历线索化二叉树

经过线索化之后,我们便可以像遍历数组那样对二叉树进行遍历了。不过在此之前,我们需要找到中序遍历的起点,不过这并不复杂,因为我们只需要从头结点不断进入左结点便可以到达中序遍历的起点了。这段代码如下:

template <class T>
void ThreadedBinaryTree<T>::InOrder(TrNode<T> *p, std::function <void(TrNode<T> *)> vis)
{
    auto tmp = p;
    while (!tmp->Ltag)
    {
        tmp = tmp->lc;
    }
    for (; tmp; tmp = succesor(tmp))
        vis(tmp);
}

泛型中序线索化二叉树ADT封装

trnode.h

#ifndef _trnode_H
#define _trnode_H
#include <iostream>

template<class T>
class TrNode
{
public:
    T e;
    bool Ltag, Rtag;
    TrNode *lc, *rc;
    size_t cnt;

    TrNode(const T x);
    ~TrNode() = default;
};

template<class T>
TrNode<T>::TrNode(const T x)
    : e(x), lc(nullptr), rc(nullptr), cnt(1), Ltag(false), Rtag(false) {}

#endif

threadedbinarytree.h

#ifndef _threadedbinarytree_H
#define _threadedbinarytree_H
#include <iostream>
#include <functional>
#include "trnode.h"

template<class T>
class ThreadedBinaryTree
{
    static TrNode<T> *pre;
    static TrNode<T> *precursor(TrNode<T> *p);
    static TrNode<T> *succesor(TrNode<T> *p);
    static void InThread(TrNode<T> *cur, TrNode<T> *&pre);

    TrNode<T> *root;
    size_t size;

    void build(TrNode<T> *root);
    void BuildTree();
    void CreateThread();
    void DestroyTree();

public:
    TrNode<T> *GetRoot() const;
    size_t Size();
    static void InOrder(TrNode<T> *p, std::function<void(TrNode<T> *)>);
    static void rInOrder(TrNode<T> *p, std::function<void(TrNode<T> *)>);
    int Count(const T x) const;
    void PrintAll() const;
    void rPrintAll() const;

    ThreadedBinaryTree();
    ~ThreadedBinaryTree();
};

template<class T>
TrNode<T> *ThreadedBinaryTree<T>::pre = nullptr;

//获取前驱
template<class T>
TrNode<T> *ThreadedBinaryTree<T>::precursor(TrNode<T> *p)
{
    if(p->Ltag)
        return p->lc;
    p = p->lc;
    if(!p)
        return nullptr;
    while(!p->Rtag)
        p = p->rc;
    return p;
}

//获取后继
template <class T>
TrNode<T> *ThreadedBinaryTree<T>::succesor(TrNode<T> *p)
{
    if (p->Rtag)
        return p->rc;
    p = p->rc;
    if (!p)
        return nullptr;
    while (!p->Ltag)
        p = p->lc;
    return p;
}

//中序顺序遍历
template <class T>
void ThreadedBinaryTree<T>::InOrder(TrNode<T> *p, std::function <void(TrNode<T> *)> vis)
{
    auto tmp = p;
    while (!tmp->Ltag)
    {
        tmp = tmp->lc;
    }
    for (; tmp; tmp = succesor(tmp))
        vis(tmp);
}

//中序逆序遍历
template <class T>
void ThreadedBinaryTree<T>::rInOrder(TrNode<T> *p, std::function<void(TrNode<T> *)> vis)
{
    auto tmp = p;
    while (!tmp->Rtag)
    {
        tmp = tmp->rc;
    }
    for (; tmp; tmp = precursor(tmp))
        vis(tmp);
}

//获取根节点
template <class T>
TrNode<T> *ThreadedBinaryTree<T>::GetRoot() const
{
    return root;
}

//获取二叉树中的元素个数
template <class T>
size_t ThreadedBinaryTree<T>::Size()
{
    return size;
}

//递归建树
template <class T>
void ThreadedBinaryTree<T>::build(TrNode<T> *root)
{
    std::cout << "Please enter the value of the left child of " << root->e << ":" << std::endl;
    T tmp;
    std::cin >> tmp;
    if (tmp != (T)-1)
    {
        size++;
        root->lc = new TrNode<T>(tmp);
        build(root->lc);
    }
    std::cout << "Please enter the value of the right child of " << root->e << ":" << std::endl;
    std::cin >> tmp;
    if (tmp != (T)-1)
    {
        size++;
        root->rc = new TrNode<T>(tmp);
        build(root->rc);
    }
    if (root->lc)
        root->cnt += root->lc->cnt;
    if (root->rc)
        root->cnt += root->rc->cnt;
}

//建树
template <class T>
void ThreadedBinaryTree<T>::BuildTree()
{

    std::cout << "Please enter the value of the headnode:" << std::endl;
    T tmp;
    std::cin >> tmp;
    if (tmp != (T)-1)
    {
        size++;
        this->root = new TrNode<T>(tmp);
        build(this->root);
    }
    if (this->root)
        this->size = this->root->cnt;
}

//销毁树
template <class T>
void ThreadedBinaryTree<T>::DestroyTree()
{
    auto destroy = [](TrNode<T> *root)
    {
        delete root;
        root = nullptr;
    };
    InOrder(root, destroy);
    this->size = 0;
}

//递归线索化
template <class T>
void ThreadedBinaryTree<T>::InThread(TrNode<T> *cur, TrNode<T> *&pre)
{
    if (!cur)
        return;
    if (cur->lc)
        InThread(cur->lc, pre);
    if (!cur->lc)
    {
        cur->lc = pre;
        cur->Ltag = true;
    }
    if (pre && !pre->rc)
    {
        pre->rc = cur;
        pre->Rtag = true;
    }
    pre = cur;
    if (cur->rc)
        InThread(cur->rc, pre);
    else
        cur->Rtag = true;
}

//二叉树线索化
template <class T>
void ThreadedBinaryTree<T>::CreateThread()
{
    if (root)
        InThread(root, pre);
}

//计数树中值为x的元素个数
template <class T>
int ThreadedBinaryTree<T>::Count(const T x) const
{
    int cnt = 0;
    auto count = [=, &cnt] (TrNode<T> *root) 
    {
        if(root->e == x)
            cnt++;
        return;
    };
    InOrder(root, count);
    return cnt;
}

//中序顺序打印
template<class T>
void ThreadedBinaryTree<T>::PrintAll() const
{
    auto print = [](TrNode<T> *root)
    {
        std::cout << root->e << ' ';
    };
    InOrder(root, print);
    std::cout << std::endl;
}

//中序逆序打印
template <class T>
void ThreadedBinaryTree<T>::rPrintAll() const
{
    auto print = [](TrNode<T> *root)
    {
        std::cout << root->e << ' ';
    };
    rInOrder(root, print);
    std::cout << std::endl;
}

//构造函数
template <class T>
ThreadedBinaryTree<T>::ThreadedBinaryTree()
    : root(nullptr), size(0)
{
    BuildTree();
    CreateThread();
}

//析构函数
template <class T>
ThreadedBinaryTree<T>::~ThreadedBinaryTree()
{
    DestroyTree();
}

#endif
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值