数据结构-难点突破(线索化二叉树与遍历 C++中序线索化二叉树,前序线索二叉树,后序线索二叉树)

思路来源:数据结构(十七) – C语言版 – 树 - 二叉树的线索化及遍历 – 先序线索化、中序线索化、后序线索化

1. 中序线索化二叉树

这里给出上述博客的中序线索化二叉树的大致内容,有什么不懂,可以移步上述博客:

二叉树的遍历有四种方式:先序遍历、中序遍历、后序遍历、层序遍历。

那么根据遍历来进行线索化的方式也就有四种方式:先序线索化、中序线索化、后序线索化、层序线索化,其实严格意义上来说,除了遍历的顺序不同,其他的没什么太大的区别。对于线索化来将也是一样的。

原来的二叉链表的节点中包含了两个指针域(左右指针)、一个数据域,很难确定节点的两个指针域保存的是子树还是线索了。那么为了解决这样的问题,我们需要在结构定义的基础上加入两个标志位( ltag、rtag )分别用来表示当前指针的指向的含义。

ltag=0:节点左子树指向这个节点的左孩子
ltag=1:节点左子树指向这个节点的前驱

rtag=0:节点的右子树指向这个节点的右孩子
rtag=1:节点的右子树指向这个节点的后继

中序线索化二叉树步骤

在中序遍历的基础上,将原本遍历函数中输出的语句修改成线索化的代码即可。

  1. 如果当前节点 current 左指针域为空,则需要将其做指针域加上前驱线索
  2. 如果当前节点的上一次访问的节点( prev 指针)右子树为空,则需要为 prev 指针指向后继节点
  3. 使 prev 指针跟上当前节点指针

测试中序线索二叉树构造类

这里为了方便,选择了二叉搜索树的构造方式来创建树

#include <vector>
#include <iostream>

struct TreeNode
{
    int data;
    TreeNode *left = nullptr;
    TreeNode *right = nullptr;
    int ltag = 0;
    int rtag = 0;
    TreeNode(int _data) : data(_data) {}
};

class Tree
{
private:
    void InsertNode(int data)
    {
        if (root == nullptr)
        {
            root = new TreeNode(data);
        }
        else
        {
            TreeNode *prev = nullptr;
            TreeNode *node = root;
            while (node != nullptr)
            {
                prev = node;
                if (node->data >= data)
                {
                    node = node->left;
                }
                else
                {
                    node = node->right;
                }
            }
            TreeNode *next = new TreeNode(data);
            if (prev->data >= data)
            {
                prev->left = next;
            }
            else
            {
                prev->right = next;
            }
        }
    }

    void _InodesDisplay(TreeNode *root)
    {
        if (root == nullptr)
            return;
        _InodesDisplay(root->left);
        std::cout << root->data << " ";
        _InodesDisplay(root->right);
    }

    void Destroy(TreeNode *root)
    {
        if (root == nullptr)
        {
            return;
        }
        Destroy(root->left);
        Destroy(root->right);
        delete root;
    }

public:
    TreeNode *root;

    Tree(const std::vector<int> &vet)
    {
        this->root = nullptr;
        for (int i = 0; i < vet.size(); i++)
        {
            InsertNode(vet[i]);
        }
    }

    ~Tree()
    {
        Destroy(root);
    }

    //中序遍历
    void InodesDisplay()
    {
        _InodesDisplay(root);
        std::cout << "\n";
    }
};

中序线索二叉树实现与测试:

//思路:https://blog.csdn.net/songshuai0223/article/details/106551499?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166795700016782425681940%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166795700016782425681940&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-106551499-null-null.142^v63^control,201^v3^control_2,213^v2^t3_esquery_v1&utm_term=%E7%BA%BF%E7%B4%A2%E4%BA%8C%E5%8F%89%E6%A0%91&spm=1018.2226.3001.4187

#include "CreatTree.h"

using namespace std;

//中序线索化二叉树
void InorderSpan(TreeNode *root, TreeNode *&prev)
{
    if (root == nullptr)
        return;

    InorderSpan(root->left, prev);

    //当前节点左指针为空,左指针指向前驱
    if (root->left == nullptr)
    {
        root->left = prev;
        root->ltag = 1;
    }
    // prev节点右指针为空,右指针指向当前节点(后继节点)
    if (prev != nullptr && prev->right == nullptr)
    {
        prev->right = root;
        prev->rtag = 1;
    }
    prev = root;

    InorderSpan(root->right, prev);
}

TreeNode *InorderSpan(TreeNode *&root)
{
    TreeNode *ret = root;
    root = nullptr; //摘头,Tree类防止析构函数崩溃
    TreeNode *prev = nullptr;
    InorderSpan(ret, prev);
    return ret;
}

//中序线索化二叉树的遍历

TreeNode *GetNextNode(TreeNode *root)
{
    if (root == nullptr)
    {
        return nullptr;
    }
    // 右标志位 1,可以直接得到后继节点
    if (root->rtag == 1)
    {
        return root->right;
    }
    // 右标志位0,则要找到右子树最左下角的节点
    else
    {
        TreeNode *ret = root->right;
        while (ret != nullptr && ret->ltag == 0)
        {
            ret = ret->left;
        }
        return ret;
    }
}
void DisplayTree(TreeNode *root)
{
    if (root == nullptr)
        return;
    //找最左下的节点
    while (root->ltag == 0)
    {
        root = root->left;
    }
    cout << root->data << " ";
    //根据后继节点打印这颗树
    while (root->right != nullptr)
    {
        root = GetNextNode(root);
        cout << root->data << " ";
    }
}

//释放线索化二叉树的节点
void Destroy(TreeNode *root)
{
    if (root == nullptr)
        return;
    while (root->ltag == 0)
    {
        root = root->left;
    }
    TreeNode *next = root->right;
    delete root;
    root = nullptr;
    while (next != nullptr)
    {
        TreeNode *node = next;
        next = GetNextNode(next);
        delete node;
        node = nullptr;
    }
}

int main(int argc, char const *argv[])
{
    Tree tree({3, 2, 4, 6, 7, 1, 2, 7, 1, 0, 7, 9});
    tree.InodesDisplay();

    TreeNode *ret = InorderSpan(tree.root);
    DisplayTree(ret);
    //销毁线索化二叉树
    Destroy(ret);
    return 0;
}

在这里插入图片描述

2. 前序线索二叉树

思路和中序线索二叉树类似:

首先大致递归思路是前序遍历顺序。

  1. 如果当前节点 current 左指针域为空,则需要将其做指针域加上前驱线索
  2. 如果当前节点的上一次访问的节点( prev 指针)右子树为空,则需要为 prev 指针指向后继节点
  3. 使 prev 指针跟上当前节点指针
#include "CreatTree.h"

using namespace std;

//前序线索二叉树
void PreorderSpan(TreeNode *root, TreeNode *&prev)
{
    if (root == nullptr)
    {
        return;
    }
    if (root->left == nullptr)
    {
        root->left = prev;
        root->ltag = 1;
    }
    if (prev != nullptr && prev->right == nullptr)
    {
        prev->right = root;
        prev->rtag = 1;
    }
    prev = root;

    //因为是先修改的Node的tag,所以这里需要判断tag,否则会出现死循环
    if (root->ltag == 0)
        PreorderSpan(root->left, prev);
    if (root->rtag == 0)
        PreorderSpan(root->right, prev);
}

TreeNode *PreorderSpan(TreeNode *&root)
{
    TreeNode *prev = nullptr;
    TreeNode *ret = root;
    root = nullptr;
    PreorderSpan(ret, prev);
    return ret;
}

void DisplayTree(TreeNode *root)
{
    if (root == nullptr)
    {
        return;
    }
    while (root != nullptr)
    {
        while (root->ltag == 0)
        {
            cout << root->data << " ";
            root = root->left;
        }
        cout << root->data << " ";
        root = root->right;
    }
}

void Destroy(TreeNode *root)
{
    if (root == nullptr)
    {
        return;
    }
    while (root != nullptr)
    {
        while (root->ltag == 0)
        {
            TreeNode *next = root->left;
            delete root;
            root = next;
        }
        TreeNode *next = root->right;
        delete root;
        root = next;
    }
}

int main(int argc, char const *argv[])
{
    Tree tree({2, 4, 1, 5, 2, 6, 7, 3, 1, 0, 9});
    tree.InodesDisplay();
    TreeNode *node = PreorderSpan(tree.root);
    DisplayTree(node);
    Destroy(node);
    return 0;
}

在这里插入图片描述

3. 后序线索二叉树

首先按照后序递归的方式进行递归,在打印的步骤出修改成:

如果当前节点的左子树为空,让左子树指针指向前驱。
当前节点的右子树为空,让右子树指针指向后继。

唯一不同的是:在进行遍历线索二叉树时,后序遍历需要记录父节点,这里采用参考博客的思路
先按照 根节点->右孩子->左孩子 的方式来遍历访问节点,并且将顺序记录一下,最后将刚才记录的顺序翻转即可

这里选择将反向输出的节点放入栈中达到逆向目的。

#include "CreatTree.h"

#include <stack>

using namespace std;

//后序线索二叉树
void PostorderSpan(TreeNode *root, TreeNode *&prev)
{
    if (root == nullptr)
        return;
    PostorderSpan(root->left, prev);
    PostorderSpan(root->right, prev);

    if (root->left == nullptr)
    {
        root->left = prev;
        root->ltag = 1;
    }
    if (prev != nullptr && prev->right == nullptr)
    {
        prev->rtag = 1;
        prev->right = root;
    }
    prev = root;
}

TreeNode *PostorderSpan(TreeNode *&root)
{
    TreeNode *ret = root;
    root = nullptr;
    TreeNode *prev = nullptr;
    PostorderSpan(ret, prev);
    return ret;
}

//遍历线索二叉树
//先按照 根节点->右孩子->左孩子 的方式来遍历访问节点,并且将顺序记录一下,最后将刚才记录的顺序翻转即可(使用栈)
TreeNode *GetNextNode(TreeNode *root)
{
    if (root == nullptr)
        return nullptr;

    //将前驱节点返回,最后逆置相当于找后继节点
    if (root->ltag == 1)
    {
        return root->left;
    }
    else
    {
        // root->ltag = 0;有左子树
        if (root->rtag == 1)
        {
            //存在后继节点
            return root->left;
        }
        else if (root->right != nullptr && root->rtag == 0)
        {
            //左右子树都存在,向右子树走
            return root->right;
        }
        else
        {
            return root->left;
        }
    }
}

void DisplayTree(TreeNode *root)
{
    stack<TreeNode *> st;
    while (root != nullptr)
    {
        st.push(root);
        root = GetNextNode(root);
    }

    while (!st.empty())
    {
        cout << st.top()->data << " ";
        st.pop();
    }
}

int main(int argc, char const *argv[])
{
    Tree tree({5, 1, 0, 2});
    tree.InodesDisplay();
    TreeNode *node = PostorderSpan(tree.root);
    DisplayTree(node);
    return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NUC_Dodamce

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值