美文网首页数据结构和算法
剑指offer - 序列化二叉树

剑指offer - 序列化二叉树

作者: Longshihua | 来源:发表于2019-10-21 11:38 被阅读0次

    题目

    请实现两个函数,分别序列化和反序列化二叉树。这里的序列化指的是将一棵二叉树保存到文件中,反序列化就是从文件中读取二叉树结点值重构原来的二叉树。

    思路

    使用前序遍历的顺序来序列化二叉树,因为前序遍历是从根结点开始的,在遍历二叉树碰到nullptr指针时,这些nullptr指针序列化为一个特殊的字符('$'),另外,结点的数值之间使用特殊字符(如:‘,’)隔开。如下图二叉树

    屏幕快照 2019-10-21 上午11.36.39.png

    代码实现

    #include <iostream>
    #include <fstream>
    using namespace std;
    
    // 定义二叉树
    struct BinaryTreeNode {
        int value;
        BinaryTreeNode *leftNode;
        BinaryTreeNode *rightNode;
        BinaryTreeNode *parent;
    };
    
    // ostream: 输出流
    // 采用前序遍历实现序列化二叉树
    void Serialize(const BinaryTreeNode *pRoot, ostream &stream) {
        if (pRoot == nullptr) { // 结点为空
            stream << "$,"; // 输出$,
            return;
        }
    
        stream << pRoot->value << ','; // 输出值
        Serialize(pRoot->leftNode, stream); // 递归左子树
        Serialize(pRoot->rightNode, stream); // 递归右子树
    }
    
    // 从流中读取数字
    bool ReadStream(istream &stream, int *number) {
        if (stream.eof()) // 判断文件是否为空或者是否读到文件结尾了
            return false;
    
        char buffer[32];
        buffer[0] = '\0';
    
        char ch;
        stream >> ch; // 输入字符
        int i = 0;
        while (!stream.eof() && ch != ',') {
            buffer[i++] = ch;
            stream >> ch;
        }
    
        bool isNumeric = false;
        if (i > 0 && buffer[0] != '$') {
            *number = atoi(buffer); // atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数
            isNumeric = true;
        }
    
        return isNumeric;
    }
    
    // 反序列化  istream: 输入流
    void Deserialize(BinaryTreeNode **pRoot, istream &stream) {
        int number;
        if (ReadStream(stream, &number)) {
            *pRoot = new BinaryTreeNode(); // 创建结点
            (*pRoot)->value = number;
            (*pRoot)->leftNode = nullptr;
            (*pRoot)->rightNode = nullptr;
    
            // 递归创建左、右子树
            Deserialize(&(*pRoot)->leftNode, stream);
            Deserialize(&(*pRoot)->rightNode, stream);
        }
    }
    

    辅助测试代码

    // 创建二叉树结点
    BinaryTreeNode* CreateBinaryTreeNode(int value){
        BinaryTreeNode* pNode = new BinaryTreeNode();
        pNode->value = value;
        pNode->leftNode = nullptr;
        pNode->rightNode = nullptr;
        pNode->parent = nullptr;
    
        return pNode;
    }
    
    // 连接二叉树
    void ConnectTreeNodes(BinaryTreeNode* pParent,
                          BinaryTreeNode* pLeft,
                          BinaryTreeNode* pRight){
        if(pParent != nullptr){
            pParent->leftNode = pLeft;
            pParent->rightNode = pRight;
    
            if(pLeft != nullptr)
                pLeft->parent = pParent;
            if(pRight != nullptr)
                pRight->parent = pParent;
        }
    }
    
    // 打印结点
    void PrintTreeNode(BinaryTreeNode* pNode){
        if(pNode != nullptr) { // 结点存在
            printf("value of this node is: %d\n", pNode->value);
    
            if(pNode->leftNode != nullptr) // 左子树
                printf("value of its left child is: %d.\n", pNode->leftNode->value);
            else
                printf("left child is null.\n");
    
            if(pNode->rightNode != nullptr) // 右子树
                printf("value of its right child is: %d.\n", pNode->rightNode->value);
            else
                printf("right child is null.\n");
        } else {
            printf("this node is null.\n");
        }
    
        printf("\n");
    }
    
    // 打印二叉树
    void PrintTree(BinaryTreeNode* pRoot) {
        PrintTreeNode(pRoot);
    
        if(pRoot != nullptr) {
            if(pRoot->leftNode != nullptr)
                PrintTree(pRoot->leftNode);
    
            if(pRoot->rightNode != nullptr)
                PrintTree(pRoot->rightNode);
        }
    }
    
    // 销毁二叉树
    void DestroyTree(BinaryTreeNode* pRoot) {
        if(pRoot != nullptr) {
            BinaryTreeNode* pLeft = pRoot->leftNode;
            BinaryTreeNode* pRight = pRoot->rightNode;
    
            delete pRoot;
            pRoot = nullptr;
    
            DestroyTree(pLeft);
            DestroyTree(pRight);
        }
    }
    

    测试代码部分

    // 是否是相同的树
    bool isSameTree(const BinaryTreeNode* pRoot1, const BinaryTreeNode* pRoot2) {
        if(pRoot1 == nullptr && pRoot2 == nullptr)
            return true;
    
        if(pRoot1 == nullptr || pRoot2 == nullptr)
            return false;
    
        if(pRoot1->value != pRoot2->value)
            return false;
    
        return isSameTree(pRoot1->leftNode, pRoot2->leftNode) &&
        isSameTree(pRoot1->rightNode, pRoot2->rightNode);
    }
    
    void Test(const char* testName, BinaryTreeNode* pRoot) {
        if(testName != nullptr)
            printf("%s begins: \n", testName);
    
        // 打印二叉树
        PrintTree(pRoot);
    
        // 文件名
        char* fileName = "test.txt";
        ofstream fileOut;
        fileOut.open(fileName); // 打开文件
    
        // 序列化二叉树
        Serialize(pRoot, fileOut);
        fileOut.close(); // 文件读写操作完成之后,将文件关闭以使文件重新变为可访问的
    
        // print the serialized file
        ifstream fileIn1;
        char ch;
        fileIn1.open(fileName);
        while(!fileIn1.eof()) // 如果读文件到达文件末尾,返回true
        {
            fileIn1 >> ch;
            cout << ch;
        }
        fileIn1.close();
        cout << endl;
    
        // 反序列化
        ifstream fileIn2;
        fileIn2.open(fileName);
        BinaryTreeNode* pNewRoot = nullptr;
        Deserialize(&pNewRoot, fileIn2);
        fileIn2.close();
    
        // 打印新的二叉树
        PrintTree(pNewRoot);
    
        // 新老二叉树是否相同
        if(isSameTree(pRoot, pNewRoot))
            printf("The deserialized tree is same as the oritinal tree.\n\n");
        else
            printf("The deserialized tree is NOT same as the oritinal tree.\n\n");
    
        // 销毁二叉树
        DestroyTree(pNewRoot);
    }
    
    //            8
    //        6      10
    //       5 7    9  11
    void Test1()
    {
        BinaryTreeNode* pNode8 = CreateBinaryTreeNode(8);
        BinaryTreeNode* pNode6 = CreateBinaryTreeNode(6);
        BinaryTreeNode* pNode10 = CreateBinaryTreeNode(10);
        BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode7 = CreateBinaryTreeNode(7);
        BinaryTreeNode* pNode9 = CreateBinaryTreeNode(9);
        BinaryTreeNode* pNode11 = CreateBinaryTreeNode(11);
    
        ConnectTreeNodes(pNode8, pNode6, pNode10);
        ConnectTreeNodes(pNode6, pNode5, pNode7);
        ConnectTreeNodes(pNode10, pNode9, pNode11);
    
        Test("Test1", pNode8);
    
        DestroyTree(pNode8);
    }
    
    //            5
    //          4
    //        3
    //      2
    void Test2()
    {
        BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
        BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
        BinaryTreeNode* pNode2 = CreateBinaryTreeNode(2);
    
        ConnectTreeNodes(pNode5, pNode4, nullptr);
        ConnectTreeNodes(pNode4, pNode3, nullptr);
        ConnectTreeNodes(pNode3, pNode2, nullptr);
    
        Test("Test2", pNode5);
    
        DestroyTree(pNode5);
    }
    
    //        5
    //         4
    //          3
    //           2
    void Test3()
    {
        BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
        BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
        BinaryTreeNode* pNode2 = CreateBinaryTreeNode(2);
    
        ConnectTreeNodes(pNode5, nullptr, pNode4);
        ConnectTreeNodes(pNode4, nullptr, pNode3);
        ConnectTreeNodes(pNode3, nullptr, pNode2);
    
        Test("Test3", pNode5);
    
        DestroyTree(pNode5);
    }
    
    void Test4()
    {
        BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
    
        Test("Test4", pNode5);
    
        DestroyTree(pNode5);
    }
    
    void Test5()
    {
        Test("Test5", nullptr);
    }
    
    //        5
    //         5
    //          5
    //         5
    //        5
    //       5 5
    //      5   5
    void Test6()
    {
        BinaryTreeNode* pNode1 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode2 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode3 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode4 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode61 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode62 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode71 = CreateBinaryTreeNode(5);
        BinaryTreeNode* pNode72 = CreateBinaryTreeNode(5);
    
        ConnectTreeNodes(pNode1, nullptr, pNode2);
        ConnectTreeNodes(pNode2, nullptr, pNode3);
        ConnectTreeNodes(pNode3, pNode4, nullptr);
        ConnectTreeNodes(pNode4, pNode5, nullptr);
        ConnectTreeNodes(pNode5, pNode61, pNode62);
        ConnectTreeNodes(pNode61, pNode71, nullptr);
        ConnectTreeNodes(pNode62, nullptr, pNode72);
    
        Test("Test6", pNode1);
    
        DestroyTree(pNode1);
    }
    
    int main(int argc, const char * argv[]) {
        // insert code here...
        std::cout << "Hello, World!\n";
    
        Test1();
        Test2();
        Test3();
        Test4();
        Test5();
        Test6();
        return 0;
    }
    

    参考

    《剑指offer》

    相关文章

      网友评论

        本文标题:剑指offer - 序列化二叉树

        本文链接:https://www.haomeiwen.com/subject/bekylctx.html