M-am dus la un interviu astăzi, în cazul în care am fost rugat să serializam un arbore binar. Am implementat o abordare bazată pe matrice în cazul în care copiii din nodul i (numerotare în nivelul de ordinul parcurgeri) au fost la 2 * indicele i pentru copil stânga și 2 * i + 1 pentru copil dreapta. Intervievatorul părea mai mult sau mai puțin mulțumit, dar mă întreb ce anume înseamnă serializare? Are se referă în mod specific la aplatizare copac pentru scrierea pe disc, sau ar serializarea un copac, de asemenea, doar includ transformarea copac într-o listă legată, să zicem. De asemenea, cum vom merge despre aplatizare copac într-o listă (de două ori), legat, și apoi reconstruind-l? Poți recrea structura exactă a arborelui din lista legată?
Cum serializa binar copac
Abordarea 1: Nu atât Inorder și pentru precomandă parcurgeri la searialize datele de copac. La utilizarea de-serializarea de pre-comandă și de a face BST Inorder pentru a forma în mod corespunzător copac.
Ai nevoie de ambele pentru că A -> B -> C poate fi reprezentat ca precomandă, chiar dacă structura poate fi diferită.
Abordarea 2: Utilizați # ca santinelă copilul ori de câte ori la stânga sau la dreapta este nul .....
Cum despre efectuarea unei parcurgeri în ordine și pune cheia de bază și toate cheile nod într-o std :: listă sau alt container de alegere, care aplatizează copac. Apoi, pur și simplu serialize std :: lista sau containerul de alegere folosind biblioteca impuls.
Reversul este simplu și apoi reconstrui arborele folosind inserare standard, la un arbore binar. Acest lucru nu poate fi în întregime eficient pentru un copac foarte mare, dar runtime pentru a converti copac într-o std :: Lista este O (n), cel mult și pentru a reconstrui arborele este O (log n) cel mult.
Sunt pe cale de a face acest lucru pentru a serializa un copac am codat în C ++ ca eu sunt de conversie de la baza mea de date Java la C ++.
Toate aceste articole vorbesc mai ales despre partea serializare. Partea deserializarea este puțin dificil de a face într-o singură trecere.
Am implementat o soluție eficientă pentru deserializarea prea.
Problemă: serialize și deserializati un arbore binar care conține numere pozitive.
parte serializare:
- Utilizați 0 pentru a reprezenta nul.
- Serializa la lista de numere întregi folosind precomandă traversal.
parte deserializarea:
- Ia în lista de numere întregi și utilizează metoda de ajutor recursiv pentru deserializarea.
- deserializare recursive returnează o pereche (nod BTNode, int nextIndexToRead) în cazul în care nodul este nod copac construit până în prezent, și nextIndexToRead este poziția de numărul următor pentru a fi citite în lista serializate numerelor.
Mai jos este codul în Java:
public final class BinaryTreeSerializer
{
public static List<Integer> Serialize(BTNode root)
{
List<Integer> serializedNums = new ArrayList<Integer>();
SerializeRecursively(root, serializedNums);
return serializedNums;
}
private static void SerializeRecursively(BTNode node, List<Integer> nums)
{
if (node == null)
{
nums.add(0);
return;
}
nums.add(node.data);
SerializeRecursively(node.left, nums);
SerializeRecursively(node.right, nums);
}
public static BTNode Deserialize(List<Integer> serializedNums)
{
Pair pair = DeserializeRecursively(serializedNums, 0);
return pair.node;
}
private static Pair DeserializeRecursively(List<Integer> serializedNums, int start)
{
int num = serializedNums.get(start);
if (num == 0)
{
return new Pair(null, start + 1);
}
BTNode node = new BTNode(num);
Pair p1 = DeserializeRecursively(serializedNums, start + 1);
node.left = p1.node;
Pair p2 = DeserializeRecursively(serializedNums, p1.startIndex);
node.right = p2.node;
return new Pair(node, p2.startIndex);
}
private static final class Pair
{
BTNode node;
int startIndex;
private Pair(BTNode node, int index)
{
this.node = node;
this.startIndex = index;
}
}
}
public class BTNode
{
public int data;
public BTNode left;
public BTNode right;
public BTNode(int data)
{
this.data = data;
}
}
Cel mai bun mod este de a utiliza un caracter special (cum ar fi # ca comentariu anterior menționat) ca santinelă. Este mai bine decât construirea unei parcurgeri matrice inordine si un traversal matrice precomandă / postordine, atât în complexitate spațiu înțelept și complexitate timp înțelept. este, de asemenea, mod mai ușor de implementat.
Lista de legat nu este o alegere bună aici, deoarece, în scopul de a reconstrui copac, mai bine ai avea timp de acces element de const
Utilizarea pre parcurgeri comanda, serializa arbore binar. Utilizați aceeași traversal ordine prestabilită a deserializati copac. Fii atent cu privire la cazurile de margine. Aici nodurile nule sunt reprezentate de „#“
public static String serialize(TreeNode root){
StringBuilder sb = new StringBuilder();
serialize(root, sb);
return sb.toString();
}
private static void serialize(TreeNode node, StringBuilder sb){
if (node == null) {
sb.append("# ");
} else {
sb.append(node.val + " ");
serialize(node.left, sb);
serialize(node.right, sb);
}
}
public static TreeNode deserialize(String s){
if (s == null || s.length() == 0) return null;
StringTokenizer st = new StringTokenizer(s, " ");
return deserialize(st);
}
private static TreeNode deserialize(StringTokenizer st){
if (!st.hasMoreTokens())
return null;
String val = st.nextToken();
if (val.equals("#"))
return null;
TreeNode root = new TreeNode(Integer.parseInt(val));
root.left = deserialize(st);
root.right = deserialize(st);
return root;
}
Am fost încercarea de a obține esențialul. Deci, aici este punerea în aplicare meu Java. După cum sa menționat, acest lucru este un arbore binar nu un BST. Pentru serializarea, o precomandă traversal pare a fi de lucru mai ușor (la un șir de caractere, cu „NULL“ pentru nodurile nule). Vă rugăm să verificați codul de mai jos, cu un exemplu complet de apeluri recursivitate. Pentru deserializing, șirul este convertit la un LinkedList unde elimina (0) devine elementul superior într-un O (1) timpul de funcționare. Vă rugăm să consultați, de asemenea, un exemplu complet în comentariile de cod pentru deserializing. Sper că va ajuta pe cineva lupta mai puțin decât am făcut-o :) Timpul total de funcționare pentru fiecare metodă (serializarea și deserializati) este în același timp de funcționare pentru parcurgeri arbore binar, adică, O (n), unde n este numărul de noduri (intrări) în copac
import java.util.LinkedList; import java.util.List;
public class SerDesBinTree {
public static class TreeEntry<T>{
T element;
TreeEntry<T> left;
TreeEntry<T> right;
public TreeEntry(T x){
element = x;
left = null;
right = null;
}
}
TreeEntry<T> root;
int size;
StringBuilder serSB = new StringBuilder();
List<String> desList = new LinkedList<>();
public SerDesBinTree(){
root = null;
size = 0;
}
public void traverseInOrder(){
traverseInOrder(this.root);
}
public void traverseInOrder(TreeEntry<T> node){
if (node != null){
traverseInOrder(node.left);
System.out.println(node.element);
traverseInOrder(node.right);
}
}
public void serialize(){
serialize(this.root);
}
/*
* 1
* / \
* 2 3
* /
* 4
*
* ser(1)
* serSB.append(1) serSB: 1
* ser(1.left)
* ser(1.right)
* |
* |
* ser(1.left=2)
* serSB.append(2) serSB: 1, 2
* ser(2.left)
* ser(2.right)
* |
* |
* ser(2.left=null)
* serSB.append(NULL) serSB: 1, 2, NULL
* return
* |
* ser(2.right=null)
* serSB.append(NULL) serSB: 1, 2, NULL, NULL
* return
*
* |
* ser(1.right=3)
* serSB.append(3) serSB: 1, 2, NULL, NULL, 3
* ser(3.left)
* ser(3.right)
*
* |
* ser(3.left=4)
* serSB.append(4) serSB: 1, 2, NULL, NULL, 3, 4
* ser(4.left)
* ser(4.right)
*
* |
* ser(4.left=null)
* serSB.append(NULL) serSB: 1, 2, NULL, NULL, 3, 4, NULL
* return
*
* ser(4.right=null)
* serSB.append(NULL) serSB: 1, 2, NULL, NULL, 3, 4, NULL, NULL
* return
*
* ser(3.right=null)
* serSB.append(NULL) serSB: 1, 2, NULL, NULL, 3, 4, NULL, NULL, NULL
* return
*
*/
public void serialize(TreeEntry<T> node){
// preorder traversal to build the string
// in addition: NULL will be added (to make deserialize easy)
// using StringBuilder to append O(1) as opposed to
// String which is immutable O(n)
if (node == null){
serSB.append("NULL,");
return;
}
serSB.append(node.element + ",");
serialize(node.left);
serialize(node.right);
}
public TreeEntry<T> deserialize(TreeEntry<T> newRoot){
// convert the StringBuilder into a list
// so we can do list.remove() for the first element in O(1) time
String[] desArr = serSB.toString().split(",");
for (String s : desArr){
desList.add(s);
}
return deserialize(newRoot, desList);
}
/*
* 1
* / \
* 2 3
* /
* 4
*
* deser(root, list) list: 1, 2, NULL, NULL, 3, 4, NULL, NULL, NULL
* root = new TreeEntry(1) list: 2, NULL, NULL, 3, 4, NULL, NULL, NULL
* root.left = deser(root.left, list) // **
* root.right = deser(root.right, list) // *-*
* return root // ^*^
*
*
* so far subtree
* 1
* / \
* null null
*
* deser(root.left, list)
* root.left = new TreeEntry(2) list: NULL, NULL, 3, 4, NULL, NULL, NULL
* root.left.left = deser(root.left.left, list) // ***
* root.left.right = deser(root.left.right, list) // ****
* return root.left // eventually return new TreeEntry(2) to ** above after the two calls are done
*
* so far subtree
* 2
* / \
* null null
*
* deser(root.left.left, list)
* // won't go further down as the next in list is NULL
* return null // to *** list: NULL, 3, 4, NULL, NULL, NULL
*
* so far subtree (same, just replacing null)
* 2
* / \
* null null
*
* deser(root.left.right, list)
* // won't go further down as the next in list is NULL
* return null // to **** list: 3, 4, NULL, NULL, NULL
*
* so far subtree (same, just replacing null)
* 2
* / \
* null null
*
*
* so far subtree // as node 2 completely returns to ** above
* 1
* / \
* 2 null
* / \
* null null
*
*
* deser(root.right, list)
* root.right = new TreeEntry(3) list: 4, NULL, NULL, NULL
* root.right.left = deser(root.right.left, list) // *&*
* root.right.right = deser(root.right.right, list) // *---*
* return root.right // eventually return to *-* above after the previous two calls are done
*
* so far subtree
* 3
* / \
* null null
*
*
* deser(root.right.left, list)
* root.right.left = new TreeEntry(4) list: NULL, NULL, NULL
* root.right.left.left = deser(root.right.left.left, list) // *(*
* root.right.left.right = deser(root.right.left.right, list) // *)*
* return root.right.left // to *&*
*
* so far subtree
* 4
* / \
* null null
*
* deser(root.right.left.left, list)
* // won't go further down as the next in list is NULL
* return null // to *(* list: NULL, NULL
*
* so far subtree (same, just replacing null)
* 4
* / \
* null null
*
* deser(root.right.left.right, list)
* // won't go further down as the next in list is NULL
* return null // to *)* list: NULL
*
*
* so far subtree (same, just replacing null)
* 4
* / \
* null null
*
*
* so far subtree
* 3
* / \
* 4 null
* / \
* null null
*
*
* deser(root.right.right, list)
* // won't go further down as the next in list is NULL
* return null // to *---* list: empty
*
* so far subtree (same, just replacing null of the 3 right)
* 3
* / \
* 4 null
* / \
* null null
*
*
* now returning the subtree rooted at 3 to root.right in *-*
*
* 1
* / \
* / \
* / \
* 2 3
* / \ / \
* null null / null
* /
* 4
* / \
* null null
*
*
* finally, return root (the tree rooted at 1) // see ^*^ above
*
*/
public TreeEntry<T> deserialize(TreeEntry<T> node, List<String> desList){
if (desList.size() == 0){
return null;
}
String s = desList.remove(0); // efficient operation O(1)
if (s.equals("NULL")){
return null;
}
Integer sInt = Integer.parseInt(s);
node = new TreeEntry<T>((T)sInt);
node.left = deserialize(node.left, desList);
node.right = deserialize(node.right, desList);
return node;
}
public static void main(String[] args) {
/*
* 1
* / \
* 2 3
* /
* 4
*
*/
SerDesBinTree<Integer> tree = new SerDesBinTree<>();
tree.root = new TreeEntry<Integer>(1);
tree.root.left = new TreeEntry<Integer>(2);
tree.root.right = new TreeEntry<Integer>(3);
tree.root.right.left = new TreeEntry<Integer>(4);
//tree.traverseInOrder();
tree.serialize();
//System.out.println(tree.serSB);
tree.root = null;
//tree.traverseInOrder();
tree.root = tree.deserialize(tree.root);
//tree.traverseInOrder();
// deserialize into a new tree
SerDesBinTree<Integer> newTree = new SerDesBinTree<>();
newTree.root = tree.deserialize(newTree.root);
newTree.traverseInOrder();
}
}
Aici este un alt mod de serializarea arbore binar folosind nivelul (modificat) ordine traversal. [Doar copiați și inserați, funcționează] acoperă toate, arbore dezechilibrat, echilibrat, dreapta la stânga oblică oblică.
class TreeNode():
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def getHeight(root):
if root == None:
return 0
return max(getHeight(root.left), getHeight(root.right)) + 1
treeArray = []
def levelOrderTraversal(root, level, numOfNodes):
if level <= 0 and numOfNodes <=0:
return
numOfNodes -= 1
if root != None and level == 1:
treeArray.append(root.val)
elif root == None and level == 1:
treeArray.append("$")
if root != None:
levelOrderTraversal(root.left, level-1, numOfNodes)
levelOrderTraversal(root.right, level-1, numOfNodes)
else:
levelOrderTraversal(root, level-1, numOfNodes)
levelOrderTraversal(root, level-1, numOfNodes)
def treeToIntArray(root):
h = getHeight(root)
for i in range(1, h+1):
levelOrderTraversal(root,i, i*2)
return treeArray
def intArrayToTree():
n = len(treeArray)
treeArrayOfObjects = [0]*len(treeArray)
for i in range(n):
if treeArray[i] != "$":
root = TreeNode(treeArray[i])
treeArrayOfObjects[i] = root
#Linking the child nodes
for i in range(n):
if treeArray[i] != "$":
root = treeArrayOfObjects[i]
if 2 * i + 1 < n:
root.left = treeArrayOfObjects[2*i + 1]
if 2 * i + 2 < n:
root.right = treeArrayOfObjects[2*i + 2]
treeArray[i] = root
return treeArrayOfObjects[0]
"""
root = TreeNode(7)
root.left = TreeNode(3)
root.right = TreeNode(9)
root.left.left = TreeNode(1)
root.left.right = None
root.right.left = None
root.right.right = TreeNode(4)
"""
root = TreeNode(7)
root.right = TreeNode(9)
root.right.right = TreeNode(4)
root.right.right.right = TreeNode(5)
print treeToIntArray(root)
root = intArrayToTree()
print root.val
print root.right.val
print root.right.right.val
print root.right.right.right.val
Aici este un răspuns târziu în Python. Se folosește (adâncime mai întâi) precomandă serializarea și returnează o listă strings. Deserializarea se întoarce copac.
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
# This method serializes the tree into a string
def serialize(root):
vals = []
def encode(node):
vals.append(str(node.val))
if node.left is not None:
encode(node.left)
else:
vals.append("L")
if node.right is not None:
encode(node.right)
else:
vals.append("R")
encode(root)
print(vals)
return vals
# This method deserializes the string back into the tree
def deserialize(string_list):
def create_a_tree(sub_list):
if sub_list[0] == 'L' or sub_list[0] == 'R':
del sub_list[0]
return
parent = Node(sub_list[0])
del sub_list[0]
parent.left = create_a_tree(sub_list)
parent.right = create_a_tree(sub_list)
return parent
if len(string_list) != 0:
root_node = create_a_tree(string_list)
else:
print("ERROR - empty string!")
return 0
return root_node
A testa:
tree1 = Node('root', Node('left'), Node('right'))
t = deserialize(serialize(tree1))
print(str(t.right.val))
Serializarea este procesul de conversie a unei structuri de date sau a unui obiect într-o secvență de biți, astfel încât să poată fi stocate într-un fișier sau tampon de memorie, sau transmise printr-o legătură conexiune de rețea pentru a fi reconstruit mai târziu, în același sau un alt mediu de calculator.
Deserializarea este procesul de conversie șir înapoi la structura copac originală.
Conceptul de serializarea si deserializarea este foarte similar cu ceea ce un compilator nu pentru cod. Există mai multe faze în întregul proces de compilare, dar vom încerca să-l păstrați abstracte.
Având în vedere o bucată de cod, compilator rupe diferite componente bine definite în jetoane (de exemplu, int este un simbol, dublu este un alt simbol, {este un jeton,} este un alt simbol, etc). [Link la o demonstrație a nivelului abstract de compilare] [1].
Serializare: Noi folosim logica parcurgeri precomandă pentru serializarea copac la un șir. Vom adăuga „X“ pentru a desemna un nul pointer / nod într-un copac. În plus, pentru a păstra logica noastră deserializarea în minte, trebuie să adăugați „“ după fiecare valoare nod serializate, astfel încât procesul de deserializarea poate avea acces la fiecare valoare de nod split cu „“.
Link Leetcode: https://leetcode.com/problems/serialize-and-deserialize-binary-tree/
Explicație prin spate în spate SWE Youtube canal : https://www.youtube.com/watch?v=suj1ro8TIVY
For example:
You may serialize the following tree:
1
/ \
2 3
/ \
4 5
as "[1,2,null,null,3,4,null,null,5,null,null,]"
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root == null)
return "X,";
String leftSerialized = serialize(root.left);
String rightSerialized = serialize(root.right);
return root.val + "," + leftSerialized + rightSerialized;
}
private TreeNode deserializeHelper(Queue<String> queue)
{
String nodeValue = queue.poll();
if(nodeValue.equals("X"))
return null;
TreeNode newNode = new TreeNode(Integer.valueOf(nodeValue));
newNode.left = deserializeHelper(queue);
newNode.right = deserializeHelper(queue);
return newNode;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
Queue<String> queue = new LinkedList<>();
queue.addAll(Arrays.asList(data.split(",")));
return deserializeHelper(queue);
}
}
//Codec object will be instantiated and called as such:
//Codec codec = new Codec();
//codec.deserialize(codec.serialize(root));
Eu nu sunt folosind precomandă dar eu sunt , folosind BFS. Aceasta este o întrebare de la leetcode
Majoritatea oamenilor de punere în aplicare sunt incorecte atunci când se utilizează precomandă: rezultatul așteptat ar trebui să fie
"[1,2,3, null, null, 4,5]", dar în schimb oamenii majoritare de imprimare de ieșire ca "[1,2,3, nul, nul, 4,5, null, null]", deoarece acestea sunt nu de numărare nivelurile.
Aici este punerea în aplicare a mea cu rezultatul corect.
class Node(object):
def __init__(self,data):
self.left = None
self.right = None
self.data = data
def serialize(root):
queue = [(root,0)]
result = []
max_level_with_value = 0
while queue:
(node,l) = queue.pop(0)
if node:
result.append((node.data,l))
queue.extend([(node.left,l+1),
(node.right,l+1)
])
max_level_with_value = max(max_level_with_value,l)
else:
result.append(('null',l))
filter_redundant(result,max_level_with_value)
def filter_redundant(result,max_level_with_value):
for v,l in result:
if l<= max_level_with_value:
print(v)
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.right.left = Node(4)
root.right.right = Node(5)
serialize(root)













