題目描述
請實現兩個函數,分別用來序列化和反序列化二叉樹
題目解析:題目的要求似乎不是太清晰,序列化的意思是指將一些特定的數據結構,變成有格式信息的字符串。例如對一個鏈表,可以將1->2->3->4->NULL序列化爲"1,2,3,4"。對於序列化算法,必須支持反序列化,即在約定的格式下,可以將滿足格式要求的字符串重新構造爲原始的結構形式。實際上就是用序列來表示一棵二叉樹,然後還可以根據這個序列重建二叉樹。這裏沒有規定序列化的方式,不管序列化成什麼字符串,只要能夠還原重建二叉樹即可。
解題思路:
根據前面的面試題重建二叉樹,我們知道可以從前序遍歷和中序遍歷構造出一棵二叉樹。受此啓發,我們可以先把一棵二叉樹序列化成一個前序遍歷序列和一箇中序序列的結合,然後再反序列化時通過這兩個序列重構出原二叉樹。
但是這個思路有兩個缺點。一個缺點是該方法要求二叉樹中不能用有數值重複的結點。另外只有當兩個序列中所有數據都讀出後才能開始反序列化。如果兩個遍歷序列的數據是從一個流裏讀出來的,那就可能需要等較長的時間。
實際上如果二叉樹的序列化是從根結點開始的話,那麼相應的反序列化在根結點的數值讀出來的時候就可以開始了。因此我們可以根據前序遍歷的順序來序列化二叉樹,因爲前序遍歷是從根結點開始的。當在遍歷二叉樹碰到 NULL 指針時,這些 NULL 指針序列化成一個特殊的字符(比如‘$’)。另外,結點的數值之間要用一個特殊字符(比如’,’)隔開。以下圖中的二叉樹爲例:
1 1 2 / \ 3 2 3 4 / / \ 5 4 5 6
- 序列化:對於上圖中的樹,進行前序遍歷時,先訪問到1,然後2,然後4,4的左右子結點都爲空,可以用一個特殊字符替代,譬如用$,所以上圖中的二叉樹前序遍歷表示就是“1,2,4,$,$,$,3,5,$,$,6,$,$"。
- 反序列化:重建的時候,訪問的第一個結點爲根結點,接下來的數字是2,它是根結點的左子結點。接下來的是4,它是2的左子結點。然後遇到兩個$,說明4的左右子結點都是NULL。接下來結點回退,訪問4的父結點2,又是$,說明2的右子結點是NULL。再返回到根結點,這時候該建立它的右子結點了,下一個數值是3,說明3是根結點的右子結點,剩下的步驟和左子樹部分類似。
二叉樹的遍歷做多了後,遞歸實現前序遍歷和根據前序遍歷得到的序列重建二叉樹應該不難,這道題對我而言主要問題在於對輸入流的控制,一直用的不太好,就不拿自己的代碼出來誤導人了,找了個別人的代碼來看看。
代碼
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
private:
TreeNode* decode(char *&str) {
if(*str=='#'){
str++;
return NULL;
}
int num = 0;
while(*str != ',')
num = num*10 + (*(str++)-'0');
str++;
TreeNode *root = new TreeNode(num);
root->left = decode(str);
root->right = decode(str);
return root;
}
public:
char* Serialize(TreeNode *root) {
if(!root) return "#";
string r = to_string(root->val);
r.push_back(',');
char *left = Serialize(root->left);
char *right = Serialize(root->right);
char *ret = new char[strlen(left) + strlen(right) + r.size()];
strcpy(ret, r.c_str());
strcat(ret, left);
strcat(ret, right);
return ret;
}
TreeNode* Deserialize(char *str) {
return decode(str);
}
};