背景:
对于给定的一序列单词,在重建一棵字典树之后,将该树进行保留,实现进程之间的数据共享。
方案设计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 ) )