线索二叉树的前、中、后序创建和遍历

本文介绍了一种基于模板的二叉树线索化实现方法,并提供了前序、中序和后序线索化的具体实现代码。同时展示了如何利用线索化二叉树进行遍历,包括前序遍历、中序遍历和后序遍历。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#pragma  once
#include <iostream>
#include <string>
using namespace std;

enum NodeFlag { LINK, THREAD };

template <typename T>
struct BinaryTreeNodeThd
{
    BinaryTreeNodeThd(const T& data)
    : _data(data)
    , _parents(NULL)
    , _pLeftChild(NULL)
    , _LeftFlag(LINK)
    , _pRightChild(NULL)
    , _RightFlag(LINK)
    {}

    T _data;//数据域
    BinaryTreeNodeThd<T>* _parents;//双亲指针
    BinaryTreeNodeThd<T>* _pLeftChild;//左孩子指针
    NodeFlag _LeftFlag;//左孩子指针的标记
    BinaryTreeNodeThd<T>* _pRightChild;//右孩子指针
    NodeFlag _RightFlag;//右孩子指针的标记
};

template <typename T>
class BinaryTreeThd
{
    typedef BinaryTreeNodeThd<T> Node;
protected:
    Node* _pRoot;
public:
    BinaryTreeThd()//无参构造函数
    {}

    BinaryTreeThd(const T* arr, size_t size, const T& invalid)//使用数组内的数据构造二叉树
    {
        size_t index = 0;// 数组下标,因为要传引用,所以用一个表量表示
        _CreateTree(_pRoot, arr, size, index, invalid);//创建树
    }

    void PreThread()
    {
        Node* pPre = NULL;
        _PreThread(_pRoot, pPre);
        cout << "BinaryTree PreThread done" << endl << endl;
    }

    void PreOrderThd()
    {
        cout << "BinaryThreadTree PreOrder: ";
        Node* pCur = _pRoot;
        while (pCur)
        {
            while (pCur && pCur->_LeftFlag == LINK)//查找最左节点,并访问路径上的节点
            {
                cout << pCur->_data << " ";
                pCur = pCur->_pLeftChild;
            }
            cout << pCur->_data << " ";//访问最左节点
            pCur = pCur->_pRightChild;//将最左节点的右孩子当做一个新的子树处理
        }
        cout << endl << endl;
    }

    void InThread()
    {
        Node* pPre = NULL;
        _InThread(_pRoot, pPre);
        cout << "BinaryTree InThread done" << endl << endl;
    }

    void InOrderThd()
    {
        if (NULL == _pRoot)
            return;

        cout << "BinaryThreadTree InOrder: ";
        Node* pCur = _pRoot;
        while (pCur)
        {
            while (LINK == pCur->_LeftFlag)//当前节点的左孩子没有被线索化
                pCur = pCur->_pLeftChild;

            cout << pCur->_data << " ";
            while (pCur->_RightFlag == THREAD)/*节点已经取到最左的几点并访问过了,
                                                此时判断右节点标记是否为THREAD,有则证明原二叉树的此结点的右节点不存在,
                                                被线索成后继,所以访问他的后继,并且可能存在左单支树,使用while循环*/
            {
                pCur = pCur->_pRightChild;
                cout << pCur->_data << " ";
            }

            pCur = pCur->_pRightChild;/*上述while判断的当前节点的右标记不是THREAD,即没有被线索化,
                                        所以将其右子树当做新子树重新开始访问*/
        }
        cout << endl << endl;
    }

    void PostThread()
    {
        Node* pPre = NULL;
        _PostThread(_pRoot, pPre);
        cout << "BinaryTree PostThread done" << endl << endl;
    }

    void PostOrderThd()
    {
        cout << "BinaryThreadTree PostOrder: ";
        if (NULL == _pRoot)
            return;

        Node* pCur = _pRoot;
        Node* pPre = NULL;
        while (pCur)
        {
            while (pPre != pCur->_pLeftChild && LINK == pCur->_LeftFlag)//查找最左节点
                pCur = pCur->_pLeftChild;

            while (pCur && THREAD == pCur->_RightFlag)//持续访问后继节点
            {
                cout << pCur->_data << " ";//访问当前节点(第一次进来为最左节点)
                pPre = pCur;//更新pPre
                pCur = pCur->_pRightChild;//继续访问后继节点
            }
            if (pCur == _pRoot)//判断是否根节点,放置左、右单支子树
            {
                cout << pCur->_data << " " << endl << endl;
                return;
            }
            while(pCur && pPre == pCur->_pRightChild)/*当前节点的孩子节点已访问过
                                                     (因为线索化后右孩子指向孩子,未线索化右孩子指向右孩子)
                                                     即是判断上一节点是否访问过,访问过则寻找双亲节点*/
            {
                cout << pCur->_data << " ";//前一模块未访问,所以先访问该节点
                pPre = pCur;//更新pPre
                pCur = pCur->_parents;//查找双亲节点
            }
            if (pCur && LINK == pCur->_RightFlag)//经过以上,已将子树的左子树访问完毕,开始访问子树的右子树
                pCur = pCur->_pRightChild;
        }
        cout << endl << endl;
    }
private:
    void _CreateTree(Node*& pRoot, const T* arr, size_t size, size_t& index, const T& invalid)// 创建树
    {
        if (index < size && arr[index] != invalid)//若下标索引小于元素个数且当前元素不为无效值,才使用数据创建此节点
        {
            pRoot = new Node(arr[index]);

            _CreateTree(pRoot->_pLeftChild, arr, size, ++index, invalid);
            if (pRoot->_pLeftChild)//若该节点的右孩子存在,则将此节点赋给右孩子的双亲,防止叶子节点无左右孩子
                pRoot->_pLeftChild->_parents = pRoot;

            _CreateTree(pRoot->_pRightChild, arr, size, ++index, invalid);
            if (pRoot->_pRightChild)
                pRoot->_pRightChild->_parents = pRoot;
        }
    }

    void _PreThread(Node*& pRoot, Node*& pPre)
    {
        if (NULL == pRoot)
            return;
        if (NULL == pRoot->_pLeftChild)//若左孩子不存在,则将左孩子线索化,指向双亲
        {
            pRoot->_pLeftChild = pPre;
            pRoot->_LeftFlag = THREAD;//改变标记
        }
        if (pPre && NULL == pPre->_pRightChild)//若pPre存在,且pPre的右孩子不存在,将pPre的右孩子线索化,指向孩子节点
        {/*因为线索化后,右孩子节点指向孩子节点,在当前节点时,并不明确它的孩子是谁,所以只能将保存的上一节点的右孩子线索化
           且最后一个节点的右孩子不用线索化,因为无孩子*/
            pPre->_pRightChild = pRoot;
            pPre->_RightFlag = THREAD;//改变标记
        }
        pPre = pRoot;//更新pPre节点
        if (pRoot->_LeftFlag == LINK)//若当前节点的左孩子的左标记为没有线索化,才递归
            _PreThread(pRoot->_pLeftChild, pPre);
        if(pRoot->_RightFlag == LINK)//若当前节点的右孩子的右标记为没有线索化,才递归
            _PreThread(pRoot->_pRightChild, pPre);
    }

    void _InThread(Node*& pRoot, Node*& pPre)
    {
        if (NULL == pRoot)
            return;     

        if (pRoot->_LeftFlag == LINK)//若当前节点的左孩子的左标记为没有线索化,才递归
            _InThread(pRoot->_pLeftChild, pPre);
        //_LeftFlag     

        if (NULL == pRoot->_pLeftChild)//若左孩子不存在,则将左孩子线索化,指向双亲
        {
            pRoot->_pLeftChild = pPre;
            pRoot->_LeftFlag = THREAD;//改变标记
        }
        if (pPre && NULL == pPre->_pRightChild)//若pPre存在,且pPre的右孩子不存在,将pPre的右孩子线索化,指向孩子节点
        {/*因为线索化后,右孩子节点指向孩子节点,在当前节点时,并不明确它的孩子是谁,所以只能将保存的上一节点的右孩子线索化
         且最后一个节点的右孩子不用线索化,因为无孩子*/
            pPre->_pRightChild = pRoot;
            pPre->_RightFlag = THREAD;//改变标记
        }
        pPre = pRoot;//更新pPre节点
        if (pRoot->_RightFlag == LINK)//若当前节点的右孩子的右标记为没有线索化,才递归
            _InThread(pRoot->_pRightChild, pPre);
    }


    void _PostThread(Node*& pRoot, Node*& pPre)
    {
        if (NULL == pRoot)
            return;

        if (pRoot->_LeftFlag == LINK)//若当前节点的左孩子的左标记为没有线索化,才递归
            _PostThread(pRoot->_pLeftChild, pPre);

        if (pRoot->_RightFlag == LINK)//若当前节点的右孩子的右标记为没有线索化,才递归
            _PostThread(pRoot->_pRightChild, pPre);

        if (NULL == pRoot->_pLeftChild)//若左孩子不存在,则将左孩子线索化,指向双亲
        {
            pRoot->_pLeftChild = pPre;
            pRoot->_LeftFlag = THREAD;//改变标记
        }
        if (pPre && NULL == pPre->_pRightChild)//若pPre存在,且pPre的右孩子不存在,将pPre的右孩子线索化,指向孩子节点
        {/*因为线索化后,右孩子节点指向孩子节点,在当前节点时,并不明确它的孩子是谁,所以只能将保存的上一节点的右孩子线索化
         且最后一个节点的右孩子不用线索化,因为无孩子*/
            pPre->_pRightChild = pRoot;
            pPre->_RightFlag = THREAD;//改变标记
        }
        pPre = pRoot;//更新pPre节点
    }

};

void FunTest()
{
    //char arr[] = "124##5##36##7";
    //char* arr = "124###35##6";
    char* arr = "12#3##45#6#7##8";
    BinaryTreeThd<char> bt(arr, strlen(arr), '#');
    BinaryTreeThd<char> bt1(arr, strlen(arr), '#');
    BinaryTreeThd<char> bt2(arr, strlen(arr), '#');
    bt.PreThread();
    bt.PreOrderThd();
    bt1.InThread();
    bt1.InOrderThd();
    bt2.PostThread();
    bt2.PostOrderThd();
}
线索二叉树是一种对普通二叉树进行优化的数据结构,其中每个节点除了左右子树指针之外,还有指向其后继节点的线索指针。线索化后,可以使用线索指针直接遍历整个树,而无需使用递归或栈等数据结构。 下面是使用 C 语言实现线索二叉树后序遍历的代码: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> // 定义线索二叉树节点结构体 typedef struct ThreadedBinaryTreeNode { int data; // 节点数据 struct ThreadedBinaryTreeNode *leftChild; // 左子树指针 struct ThreadedBinaryTreeNode *rightChild; // 右子树指针 bool leftTag; // 左线索标记 bool rightTag; // 右线索标记 } ThreadedBinaryTreeNode; // 全局变量,记录上一次遍历的节点 ThreadedBinaryTreeNode *preNode = NULL; // 创建新节点 ThreadedBinaryTreeNode* createNode(int data) { ThreadedBinaryTreeNode *newNode = (ThreadedBinaryTreeNode*)malloc(sizeof(ThreadedBinaryTreeNode)); newNode->data = data; newNode->leftChild = NULL; newNode->rightChild = NULL; newNode->leftTag = false; newNode->rightTag = false; return newNode; } // 中序线索二叉树 void threadedInorder(ThreadedBinaryTreeNode *root) { if (root != NULL) { // 先线索化左子树 threadedInorder(root->leftChild); // 处理当节点 if (root->leftChild == NULL) { root->leftChild = preNode; root->leftTag = true; } if (preNode != NULL && preNode->rightChild == NULL) { preNode->rightChild = root; preNode->rightTag = true; } preNode = root; // 线索化右子树 threadedInorder(root->rightChild); } } // 中序遍历线索二叉树 void inorderTraversal(ThreadedBinaryTreeNode *root) { ThreadedBinaryTreeNode *p = root; while (p != NULL) { while (p->leftTag == false) { p = p->leftChild; } printf("%d ", p->data); while (p->rightTag == true) { p = p->rightChild; printf("%d ", p->data); } p = p->rightChild; } } // 遍历线索二叉树 void preorderTraversal(ThreadedBinaryTreeNode *root) { ThreadedBinaryTreeNode *p = root; while (p != NULL) { printf("%d ", p->data); if (p->leftTag == false) { p = p->leftChild; } else if (p->rightTag == false) { p = p->rightChild; } else { while (p != NULL && p->rightTag == true) { p = p->rightChild; } if (p != NULL) { p = p->rightChild; } } } } // 后序遍历线索二叉树 void postorderTraversal(ThreadedBinaryTreeNode *root) { ThreadedBinaryTreeNode *p = root; while (p->leftTag == false || p->rightTag == false) { if (p->leftTag == false) { p = p->leftChild; } else if (p->rightTag == false) { p = p->rightChild; } } printf("%d ", p->data); while (p != root) { ThreadedBinaryTreeNode *q = root; while (q->leftChild != p && q->rightChild != p) { if (q->rightTag == false) { q = q->rightChild; } else { q = q->rightChild; while (q != NULL && q->leftTag == true) { q = q->leftChild; } } } p = q; printf("%d ", p->data); } } int main() { // 创建示例线索二叉树 ThreadedBinaryTreeNode *root = createNode(1); root->leftChild = createNode(2); root->rightChild = createNode(3); root->leftChild->leftChild = createNode(4); root->leftChild->rightChild = createNode(5); root->rightChild->leftChild = createNode(6); root->rightChild->rightChild = createNode(7); // 中序线索二叉树 threadedInorder(root); // 遍历线索二叉树 printf("Inorder traversal: "); inorderTraversal(root); printf("\n"); printf("Preorder traversal: "); preorderTraversal(root); printf("\n"); printf("Postorder traversal: "); postorderTraversal(root); printf("\n"); return 0; } ``` 上述代码中,`createNode` 函数用于创建新节点;`threadedInorder` 函数用于中序线索二叉树,其中使用全局变量 `preNode` 记录上一次遍历的节点;`inorderTraversal` 函数用于中序遍历线索二叉树;`preorderTraversal` 函数用于遍历线索二叉树;`postorderTraversal` 函数用于后序遍历线索二叉树。最后在 `main` 函数中创建示例线索二叉树,并测试各种遍历方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值