字典树的序列化和反序列化

背景:

对于给定的一序列单词,在重建一棵字典树之后,将该树进行保留,实现进程之间的数据共享。

方案设计1:

常见的是采用struct或者class,作为节点信息和父子关系信息的存储。在子节点设计的时候采用指针数组的方式。

代码:

#include<cstdio>
#define MARKER ')'
#define N 26
using namespace std;
// A node of N-ary tree
struct Node {
   char key;
   Node *child[N];  // An array of pointers for N children
};

// A utility function to create a new N-ary tree node
Node *newNode(char key)
{
    Node *temp = new Node;
    temp->key = key;
    for (int i = 0; i < N; i++)
        temp->child[i] = NULL;
    return temp;
}
// This function stores the given N-ary tree in a file pointed by fp
void serialize(Node *root, FILE *fp)//先序遍历的方式
{
    // Base case
    if (root == NULL) return;

    // Else, store current node and recur for its children
    fprintf(fp, "%c ", root->key);//如果没有key字段呢??而是位置信息作为
    for (int i = 0; i < N && root->child[i]; i++)
         serialize(root->child[i],  fp);

    // Store marker at the end of children
    fprintf(fp, "%c ", MARKER);
}

// This function constructs N-ary tree from a file pointed by 'fp'.
// This functionr returns 0 to indicate that the next item is a valid
// tree key. Else returns 0
int deSerialize(Node *&root, FILE *fp)
{
    // Read next item from file. If theere are no more items or next
    // item is marker, then return 1 to indicate same
    char val;
    if ( !fscanf(fp, "%c ", &val) || val == MARKER )
       return 1;

    // Else create node with this item and recur for children
    root = newNode(val);
    for (int i = 0; i < N; i++)
      if (deSerialize(root->child[i], fp))
         break;

    // Finally return 0 for successful finish
    return 0;
}

// A utility function to create a dummy tree shown in above diagram
Node *createDummyTree()
{
    Node *root = newNode('A');
    root->child[0] = newNode('B');
    root->child[1] = newNode('C');
    root->child[2] = newNode('D');
    root->child[0]->child[0] = newNode('E');
    root->child[0]->child[1] = newNode('F');
    root->child[2]->child[0] = newNode('G');
    root->child[2]->child[1] = newNode('H');
    root->child[2]->child[2] = newNode('I');
    root->child[2]->child[3] = newNode('J');
    root->child[0]->child[1]->child[0] = newNode('K');
    return root;
}

// A utlity function to traverse the constructed N-ary tree
//I use recursion method,and we also use a stack for the DFS
void traverse(Node *root)
{
    if (root)
    {
        printf("%c ", root->key);
        for (int i = 0; i < N; i++)
            traverse(root->child[i]);
    }
}

// Driver program to test above functions
int main()
{
    // Let us create an N-ary tree shown in above diagram
    Node *root = createDummyTree();

    // Let us open a file and serialize the tree into the file
    FILE *fp = fopen("tree.txt", "w");
    if (fp == NULL)
    {
        puts("Could not open file");
        return 0;
    }
    serialize(root, fp);
    fclose(fp);

    // Let us deserialize the storeed tree into root1
    Node *root1 = NULL;
    fp = fopen("tree.txt", "r");
    deSerialize(root1, fp);
    //test some nodes,and print relative keys
    printf("%c\n", root1->child[0]->key);//B
    printf("%c\n", root1->child[2]->child[1]->key);
    printf("%c\n", root1->child[2]->child[0]->key);
    printf("%c\n", root1->child[1]->key);

    printf("Constructed N-Ary Tree from file is:\n");
    traverse(root1);
    printf("\n");
    return 0;
}

上述代码所构建的树结构如下:

    A:
       |+--B:
            |+--E:
            |+--F:
            |+--K:
       |+--C:
       |+--D:
           |+--G:
           |+--H:
           |+--I:
           |+--J:

我们将整个深度优先遍历的结果存储在tree.txt中,这个就是序列化,通过这个tree.txt 结果再重建出树状结构就称为反序列化。
这里写图片描述

方案设计2:

采用vector取代数组对子节点列表进行存储,vector<Tree*> m_children。 另外,也可以用Tree **这样的二级指针来对子节点列表进行存储。

代码2:

#include<iostream>
#include<stack>
#include<queue>
#include<string.h>
#include<cassert>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MARKER ')'
#define N 26
template<class TreeData>
struct Tree
{
    private:

        static int TreeDataCompare(Tree* op1, Tree* op2)
        {
            return op1->GetData() - op2->GetData();
        }

    public:
        Tree(TreeData data):
            m_data(data)
        {
        }
        Tree()//必要的初始化操作
        {}

    public:
        void AppendChild(Tree* child)
        {
            m_children.push_back(child);

            // sort children trees by treedata
//          sort(m_children.begin(), m_children.end(), TreeDataCompare);//是否对子树进行排序
        }

        Tree* PutChildByData(TreeData data)
        {
            // data already exists
            for(int i = 0; i < m_children.size(); i++)
                if(data == m_children[i]->GetData())
                    return m_children[i];

            // Append new child to children array
            Tree* newChild = new Tree(data);
            AppendChild(newChild);

            return newChild;
        }

        void PutPathByDataArray(const TreeData* szData)
        {
            if (*szData == 0)
                return;

            Tree* child = PutChildByData(*szData);

            child->PutPathByDataArray(szData+1);
        }

        // This function stores the given N-ary tree in a file pointed by fp
        void serialize(Tree *root, FILE *fp)
        {
            // Base case
            if (root == NULL) return;

            // Else, store current node and recur for its children
            fprintf(fp, "%c ", root->m_data);
            std::cout<<root->m_data<<" ";
            for (int i = 0; i < root->m_children.size() && root->m_children[i]; i++)
                 serialize(root->m_children[i],  fp);

            // Store marker at the end of children
            fprintf(fp, "%c ", MARKER);
        }
        // This function constructs N-ary tree from a file pointed by 'fp'.
        // This functionr returns 0 to indicate that the next item is a valid
        // tree key. Else returns 0
        int deSerialize(Tree *&root, FILE *fp)
        {
            // Read next item from file. If theere are no more items or next
            // item is marker, then return 1 to indicate same
            char val;
            if ( !fscanf(fp, "%c ", &val) || val == MARKER )
               return 1;

            root = newNode(val);//子序列需要分配空间??否则无法进行递归调用。
            //但是子节点又不能是NULL
            //因为此时的vector的size是变动的
            for (int i = 0; i < N; i++)
            {
                root->m_children.push_back(NULL);//
//                root->m_children[i] = NULL;//是不行的因为数据插入的时候,是没有数据的,而遍历的时候才[]进行,[]是用以元素访问

                if (deSerialize(root->m_children[i], fp))
                    break;

            }
            // Finally return 0 for successful finish
            return 0;
        }
        // A utlity function to traverse the constructed N-ary tree
        void traverse(Tree *root)
        {
            if (root)
            {
                printf("%c ", root->m_data);
                for (int i = 0; i < root->m_children.size(); i++)
                    traverse(root->m_children[i]);
            }
        }
        //另一种方式的BFS遍历
        void MyBFS(Tree *root)
        {
            queue<Tree *> nodeQueue;  //使用C++的STL标准模板库
            nodeQueue.push(root);
            Tree *node;
            vector<int> numflag;
            int laynum=0;//记录层号
            numflag.push_back(1);//记录每层节点数量
            while(!nodeQueue.empty()){
                node = nodeQueue.front();
                nodeQueue.pop();
                std::cout<<node->m_data<<"->";
<<node->m_children.size()<<std::endl;//
                laynum++;
                int larycunt =0;
                int count_nodes=0;
                if(node->m_children.size() != 0)
                {
                    std::cout<<node->m_children.size()<<std::endl;//
                    count_nodes = count_nodes + node->m_children.size();//
                }
                for(i=0; i< node->m_children.size(); ++i)//遍历所有的子分支是否存在
                {
                    nodeQueue.push(node->m_children[i]);//循环的方式入队列
                    //记录下子节点所有的节点数,
                }
            }
            //json的方式对他们进行保留再以json方式进行打印
        }
    private:
        TreeData m_data;
        vector<Tree*> m_children;
    public:
        int GetChildrenNum()
        {
            return m_children.size();
        }

        Tree* GetChildByIndex(int index)
        {
            return m_children[index];
        }

        TreeData GetData()
        {
            return m_data;
        }

        // Fill children to the specified queue
        virtual void FillQueueWithChildren(queue<Tree*>& queue)
        {
            for(int i = 0; i < m_children.size(); i++)
            {
                if(m_children[i])
                    queue.push(m_children[i]);
            }
        }
};


template<class Tree>
class TraverseStack
{
    public:
        TraverseStack(Tree* tree):
            m_tree(tree)
        {
            m_tree->FillQueueWithChildren(m_children);
        }

        Tree* GetNextChild()
        {
            if (m_children.empty())
                return NULL;

            // pop head of the untraversed children queue
            Tree* head = m_children.front();
            m_children.pop();
            return head;
        }
        Tree* GetTree()
        {
            return m_tree;
        }
    private:
        Tree*           m_tree;
        queue<Tree*>    m_children;
};

template<class Tree>
class BFSTraverser
{
    public:
        BFSTraverser(Tree* root):m_root(root){}
        virtual ~BFSTraverser(){}
    public:
        typedef TraverseStack<Tree> PATHSTACKITEM;
        typedef vector<PATHSTACKITEM > PATHSTACK;
    public:
        virtual void Traverse()
        {
            m_pathStack.clear();
            // push the root stack item
            PATHSTACKITEM rItem(m_root);
            m_pathStack.push_back(rItem);

            while(!m_pathStack.empty())
            {
                PATHSTACKITEM& top = m_pathStack.back();
                Tree* nextChild = top.GetNextChild();
                if (!nextChild)
                {
                    // output pathStack
                    if(top.GetTree()->GetChildrenNum() == 0)
                        OutputStack();

                    // go back along the path to parent TraverseStack element
                    m_pathStack.pop_back();
                    continue;
                }

                assert(nextChild);

                // pre order output root‘s path
                if(nextChild == top.GetTree()->GetChildByIndex(0))
                    ;//OutputStack();

                // push new TraverseStack element to path
                PATHSTACKITEM newStackItem(nextChild);

                // enlonger the current path to the untraversed child
                m_pathStack.push_back(newStackItem);
                continue;
            }
        }

        void OutputStack()
        {
            cout << m_pathStack[0].GetTree()->GetData();//打印根节点
            for(int i = 1; i < m_pathStack.size(); i++)
            {
                if(i>0)
                {
                    cout << m_pathStack[i].GetTree()->GetData();//其实位置信息本身就可以存储值
                    //思路不同,可以用vector也可以用二级指针的数组方式,但是vector需要用m_data存储数据,而二维数组的方式则是用位置信息记录了m_data的信息
                }
            }
            cout << endl;
        }

    private:
        Tree* m_root;
        PATHSTACK m_pathStack;

};

int main()
{
    Tree<char> test('a');
    Tree<char> test1('#');//
    Tree<char> test3('d');
    Tree<char> test2('c');
    Tree<char> test5('a');
    Tree<char> test4('e');
    test1.PutPathByDataArray("hello");
    test1.PutPathByDataArray("helkl");
    test.AppendChild(&test1);
    test.AppendChild(&test4);
    test.PutChildByData('m');//单个字母
    test1.PutChildByData('K');
    test4.PutChildByData('f');
    test4.PutChildByData('z');
    test4.PutChildByData('x');
    //通过string的方式进行添加
    BFSTraverser< Tree<char> > Travers(&test);
    Travers.Traverse();
    test.MyBFS(&test);
    return 0;

}

运行结果如下所示:
这里写图片描述

对于如果想要进行字典树进行序列化和反序列化话,如下:

int main()
{
    Tree<char> test('a');
    Tree<char> test1('d');//是一个空节点
    Tree<char> test3('d');
    Tree<char> test2('c');
    Tree<char> test5('a');
    Tree<char> test4('e');
    test1.PutPathByDataArray("hello");
    test1.PutPathByDataArray("helkl");
    test.AppendChild(&test1);
    test.AppendChild(&test4);
    test.PutChildByData('m');//单个字母
    test1.PutChildByData('K');
    test4.PutChildByData('f');
    test4.PutChildByData('z');
    test4.PutChildByData('x');
    //通过string的方式进行添加
    // Let us open a file and serialize the tree into the file
    FILE *fp = fopen("tree_new_1.txt", "w");
    if (fp == NULL)
    {
        puts("Could not open file");
        return 0;
    }
    printf("result of DFS:\n");
    test.serialize(&test, fp);
    printf("\n");
    fclose(fp);

    // Let us deserialize the storeed tree into root1
    Tree<char> *root1;
    Tree<char> NULLROOT('#');
    root1 = &NULLROOT;
    fp = fopen("tree_new_1.txt", "r");
    printf("Start deSerialize N-Ary Tree from file:\n");
    root1->deSerialize(root1, fp);

    printf("Traverse Constructed N-Ary Tree from file,result is:\n");
    root1->traverse(root1);
    return 0;

}

这里写图片描述
从上图可以看出重建的结果,即反序列化的结果和序列化的结果是一致的。
再检查下序列化的文件:
这里写图片描述
a d h e l l o ) ) k l ) ) ) ) ) K ) ) e f ) z ) x ) ) m ) )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值