序列化與反序列化二叉樹
阿新 • • 發佈:2018-06-16
nbsp 同時 null roo 自定義 [] value 搜索樹 pub
2018-06-16 18:53:36
序列化 (Serialization)將對象的狀態信息轉換為可以存儲或傳輸的形式的過程。反序列化顧名思義就是通過信息流對對象進行重建的過程。
一般來說序列化和反序列化有如下的作用:
1、以某種存儲形式使自定義對象持久化; 2、將對象從一個地方傳遞到另一個地方。 3、使程序更具維護性。 本篇文章主要討論的是二叉樹的序列化和反序列化,分別討論了普通二叉樹和二叉搜索樹的情況。 一、普通二叉樹的序列化和反序列化 問題描述: 問題求解: 序列化和反序列化二叉樹本質上其實是問如何將二叉樹描述成字符串形式,如何從字符串中提取並重建二叉樹。和以往建立二叉樹是一樣的,如果我們通過前序遍歷的方式對二叉樹所有節點進行表示,那麽在重建二叉樹的時候,就可以完全按照前序遍歷的方式進行重建。問題在於,如何對數據進行表示,由於各個數的位數不同,所以一般來說會使用空格作為分隔符,同時采用"#“來表示空節點。但是這樣的序列化方式顯然是比較浪費空間的,因為一個整形數本身是由4個字節來表示的,如果單純的使用其10進制的表示那麽占用的字節數又可以會達到11個,這樣對空間和效率都會產生不利的影響。 事實上,我們完全可以使用4個字節來表示一個整形數,同時可以再加上一個字節來表示是否為空節點,這樣對空間的占用就會小很多,另外,由於歸一化了長度,即所有數據的長度都為5個字節,那麽就不需要使用空格來作為分隔符了,這樣又進一步減少了空間的開銷,因此,采用這種方法來序列化和反序列化二叉樹是比較可取的。public class SerializeandDeserializeTree { // Encodes a tree to a single string. public String serialize(TreeNode root) { StringBuffer sb = new StringBuffer(); preOrder(root, sb); return sb.toString(); } private void preOrder(TreeNode root, StringBuffer stringBuffer) { if (root == null) { stringBuffer.append("00000"); } else { byte[] bytes = intToByte(root.val); stringBuffer.append(‘1‘).append((char) bytes[0]).append((char) bytes[1]).append((char) bytes[2]).append((char) bytes[3]); preOrder(root.left, stringBuffer); preOrder(root.right, stringBuffer); } } private byte[] intToByte(int val) { return new byte[]{ (byte) (val >> 24), (byte) (val >> 16), (byte) (val >> 8), (byte) val }; } // Decodes your encoded data to tree. public TreeNode deserialize(String data) { return helper(data.toCharArray(), new int[]{0}); } private TreeNode helper(char[] data, int[] pos) { if (pos[0] >= data.length) return null; if (data[pos[0]] == ‘0‘) { pos[0] += 5; return null; } int val = (data[pos[0] + 1]) << 24 & 0xff000000 | (data[pos[0] + 2]) << 16 & 0x00ff0000 | (data[pos[0] + 3]) << 8 & 0x0000ff00 | (data[pos[0] + 4]) << 0 & 0x000000ff; pos[0] += 5; TreeNode root = new TreeNode(val); root.left = helper(data, pos); root.right = helper(data, pos); return root; } }
二、 二叉搜索樹的序列化和反序列化
問題描述:
問題求解:
二叉搜索樹是二叉樹的特例,當然是可以使用上面的思路直接AC的,但是本題中特別要求了,序列化的字符串要盡可能的緊湊,考慮到二叉搜索樹的性質,我們可以進一步的簡化序列化的長度。正如上面提到的,對於整形數使用4個字節表示可以大大簡化存儲數量,這個是毋庸置疑的,只是在二叉搜索樹中,我們不再需要額外存儲空節點信息,因為對於二叉搜索樹來說,其性質是左子樹所有節點都要比根節點小,右子樹所有節點都要比根節點大,因此我們可以利用這個性質來判斷當前流數據是否為此根節點的子樹。 綜上所述,我們需要維護一下每個根節點左右子樹的大小範圍以判斷當前的流數據是否應該掛在當前根節點下,如果不在範圍內,直接返回空值即可。因而,對於整個前序遍歷的序列化字符串來說,我們只需要4個字節來表述整形數即可。public class SerializeandDeserializeBST { // Encodes a tree to a single string. public String serialize(TreeNode root) { StringBuffer sb = new StringBuffer(); preOrder(root, sb); return sb.toString(); } byte[] intToByte(int val) { return new byte[]{ (byte)(val >> 24), (byte)(val >> 16), (byte)(val >> 8), (byte)val }; } void preOrder(TreeNode root, StringBuffer sb) { if (root != null) { byte[] tmp = intToByte(root.val); sb.append((char) tmp[0]).append((char) tmp[1]).append((char) tmp[2]).append((char) tmp[3]); preOrder(root.left, sb); preOrder(root.right, sb); } } // Decodes your encoded data to tree. public TreeNode deserialize(String data) { return helper(data.toCharArray(), new int[]{0}, Integer.MIN_VALUE, Integer.MAX_VALUE); } private TreeNode helper(char[] data, int[] pos, int low, int high) { if(pos[0] >= data.length) return null; int val = data[pos[0] + 0] << 24 & (0xff000000) | data[pos[0] + 1] << 16 & (0x00ff0000) | data[pos[0] + 2] << 8 & (0x0000ff00) | data[pos[0] + 3] << 0 & (0x000000ff); if(val < low || val > high) return null; TreeNode root = new TreeNode(val); pos[0] += 4; root.left = helper(data, pos, low, val); root.right = helper(data, pos, val, high); return root; } int byteToInt(byte[] bytes) { return bytes[0] & (0xff000000) | bytes[1] & (0x00ff0000) | bytes[2] & (0x0000ff00) | bytes[3] & (0x000000ff); } }
序列化與反序列化二叉樹