題目描述
序列化是將數據結構或對象轉換爲一系列位的過程,以便它可以存儲在文件或內存緩衝區中,或通過網絡連接鏈路傳輸,以便稍後在同一個或另一個計算機環境中重建。
設計一個算法來序列化和反序列化二叉搜索樹。 對序列化/反序列化算法的工作方式沒有限制。 您只需確保二叉搜索樹可以序列化爲字符串,並且可以將該字符串反序列化爲最初的二叉搜索樹。
編碼的字符串應儘可能緊湊。
注意:不要使用類成員/全局/靜態變量來存儲狀態。 你的序列化和反序列化算法應該是無狀態的。
思路
參考leetcode-297 Serialize and Deserialize Binary Tree (二叉樹的序列化與反序列化)。
該題的解法是對於所有二叉樹一般化的解法,可以直接套用在本題,因爲二叉搜索樹是一種特殊的二叉樹。
那麼如何利用二叉搜索樹的性質,對一般化的解法進行優化呢?
在一般化的解法中,序列化時,當遇到空節點時,我們必須在字符串中添加"null"或其他字符,來標記這個位置應該是空節點,以便在反序列化時構造正確的樹。
而對於二叉搜索樹,可以不需要"null"來標記空節點,那麼在構造樹時,利用其性質——左孩子的值小於父節點,右孩子的值大於父節點——來判斷某個位置是否應該爲空。
反序列化時,將字符串轉爲隊列,按照先序構造樹,每次訪問到一個位置,傳入一對上下界,用於判斷隊列首位的值是否在這個範圍內——
- 若在範圍內,說明這個值應該處於樹的這個位置,那麼出隊,並遞歸構造左右子樹。構造左子樹時,將此節點值作爲上界(左孩子的值必須小於此節點);同理,構造右子樹時,將此節點值作爲下界。
- 若不在範圍內,說明隊首的值不應該在此位置,結束遞歸。
代碼
public class Codec {
// 將BST序列化爲字符串
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
serialize(root, sb);
return sb.toString();
}
// 前序遍歷,遇到空節點不添加null
public void serialize(TreeNode root, StringBuilder sb) {
if (root == null) return;
sb.append(root.val).append(",");
serialize(root.left, sb);
serialize(root.right, sb);
}
// 將字符串反序列化爲BST
public TreeNode deserialize(String data) {
if (data.isEmpty()) return null;
// 將字符串轉爲隊列
Queue<String> q = new LinkedList<>(Arrays.asList(data.split(",")));
return deserialize(q, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
public TreeNode deserialize(Queue<String> q, int lower, int upper) {
// 隊列爲空,說明樹已經構造完畢,遞歸結束
if (q.isEmpty()) return null;
// 取隊首元素的值
String s = q.peek();
int val = Integer.parseInt(s);
// 如果值不在範圍中,說明這個位置應該爲空,結束本層遞歸
if (val < lower || val > upper) return null;
// 如果值在範圍中,將其出隊,用值構造樹
q.poll();
TreeNode root = new TreeNode(val);
// 將root值作爲上界,構造左子樹
root.left = deserialize(q, lower, val);
// 將root值作爲下界,構造右子樹
root.right = deserialize(q, val, upper);
return root;
}
}
執行用時:6 ms, 在所有 Java 提交中擊敗了93.02%的用戶
內存消耗:40.9 MB, 在所有 Java 提交中擊敗了100.00%的用戶