Je suis allé à une interview aujourd'hui où on m'a demandé de sérialiser un arbre binaire. Je mis en œuvre une approche basée sur la baie où les enfants du noeud i (numérotation au niveau d'ordre traversal) étaient au 2 * indice i pour l'enfant gauche et 2 * i + 1 pour l'enfant à droite. L'intervieweur semblait plus ou moins heureux, mais je me demande ce que serialize signifie exactement? Est-il plus particulièrement à l'aplatissement concernent l'arbre pour l'écriture sur le disque, ou si la sérialisation d'un arbre comprennent aussi simplement tourner l'arbre dans une liste chaînée, par exemple. De plus, comment pourrions-nous aller sur aplatir l'arbre dans une liste chaînée (doublement), puis le reconstruire? Pouvez-vous recréer la structure exacte de l'arbre de la liste chaînée?
Comment faire pour sérialiser Binary Tree
Approche 1: Faire les deux Inorder et Précommande traversal pour searialize les données sur les arbres. Lors de l'utilisation de-sérialisation précommande et ne BST sur Inorder pour former correctement l'arbre.
Vous avez besoin à la fois parce que A -> B -> C peut être représenté comme pré-commande, même si la structure peut être différente.
Approche 2: Utiliser # comme sentinelle whereever l'enfant gauche ou à droite est nul .....
Que diriez-vous effectuer une commande traversal et de mettre la clé racine et toutes les clés de nœud dans un std :: liste ou un autre récipient de votre choix qui aplatit l'arbre. Ensuite, sérialisation simplement le std :: liste ou d'un conteneur de votre choix à l'aide de la bibliothèque Boost.
L'inverse est simple, puis reconstruire l'arbre en utilisant l'insertion standard à un arbre binaire. Cela peut ne pas être tout à fait efficace pour un très grand arbre, mais l'exécution pour convertir l'arbre en std :: liste est O (n) au plus et de reconstruire l'arbre est O (log n) au plus.
Je suis sur le point de le faire pour sérialiser un arbre que je viens codé en C ++ comme je suis en train de convertir ma base de données Java en C ++.
Tous ces articles parlent surtout de la part de sérialisation. La partie de désérialisation est un peu difficile à faire en une seule passe.
J'ai mis en place une solution efficace pour désérialisation aussi.
Problème: sérialisation et la désérialisation un arbre binaire contenant des nombres positifs.
partie sérialisation:
- Utilisez 0 pour représenter null.
- Sérialisation à la liste des entiers à l'aide de pré-commande traversal.
partie Désérialisation:
- Prend la liste des entiers et utilise la méthode d'aide récursive pour désérialisation.
- Récursif désérialiseur renvoie une paire (noeud BTNode, int nextIndexToRead) lorsque le noeud est le noeud de l'arbre construit à ce jour, et nextIndexToRead est la position du numéro suivant à lire dans la liste des numéros sérialisés.
Voici le code en 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;
}
}
La meilleure façon est d'utiliser un caractère spécial (comme # comme commentaire précédent mentionné) comme sentinelle. Il vaut mieux que de construire un tableau de parcours infixe et un pré-commande / postorder tableau traversal, aussi bien dans la complexité de l'espace sage et la complexité du temps sage. il est également plus facile à mettre en œuvre.
liste chaînée est pas un bon ajustement ici car pour reconstruire l'arbre, il vaut mieux avoir le temps d'accès des éléments const
Utilisation de pré commande traversal, sérialisation arbre binaire. Utilisez le même pré commande traversal désérialiser arbre. Faites attention sur les cas de pointe. Ici, les noeuds nuls sont représentés par « # »
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;
}
J'ai essayé d'obtenir l'essentiel de celui-ci. Voici donc mon implémentation de Java. Comme mentionné précédemment, c'est un arbre binaire et non un BST. Pour sérialisation, un parcours de l'semble fonctionner plus facile (à une chaîne avec « NULL » pour les noeuds nuls). S'il vous plaît vérifier le code ci-dessous un exemple complet des appels récursifs. Pour désérialisation, la chaîne est converti en un LinkedList où remove (0) reçoit l'élément supérieur dans un joint (1) temps de fonctionnement. S'il vous plaît voir aussi un exemple complet dans les commentaires du code pour désérialisation. L'espoir qui va aider quelqu'un lutte moins que moi :) Le temps de fonctionnement global pour chaque méthode (sérialisation et la désérialisation) est en même temps de course pour traversal arbre binaire, c.-à-O (n) où n est le nombre de noeuds (entrées) dans l'arbre
importation java.util.LinkedList; importation 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();
}
}
Voici une autre façon de sérialisation arbre binaire utilisant l'ordre de niveau (modifié) traversal. [Il suffit de copier coller, ça marche] Couvre tout déséquilibre, équilibré, déviées vers la droite, à gauche arbre biaisé.
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
Voici une réponse tardive en Python. Il utilise (profondeur d' abord) sérialisation pré - commande et retourne une liste de strings. Désérialisation retourne l'arbre.
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
Tester:
tree1 = Node('root', Node('left'), Node('right'))
t = deserialize(serialize(tree1))
print(str(t.right.val))
La sérialisation est le processus de conversion d'une structure de données ou un objet dans une séquence de bits de sorte qu'il peut être stocké dans un fichier ou un tampon mémoire ou transmise sur une liaison de connexion de réseau à reconstruire plus tard dans le même ou un autre environnement informatique.
Désérialisation est le processus consistant à convertir la chaîne de retour à l'arborescence d'origine.
Le concept de sérialisation et désérialisation est très similaire à ce qu'un compilateur fait à coder. Il y a plusieurs phases dans l'ensemble du processus de compilation, mais nous allons essayer de le garder abstrait.
Étant donné un morceau de code, compilateur casse différents composants bien définis en jetons (par exemple, int est un jeton, double est un autre jeton, {est un jeton,} est un autre jeton, etc.). [Lien vers une démonstration du niveau de la compilation abstraite] [1].
Sérialisation: Nous utilisons la logique de pré-commande traversal pour arbre sérialisation à une chaîne. Nous allons ajouter « X » pour désigner un pointeur / nœud nul dans un arbre. De plus, pour garder notre logique de désérialisation à l'esprit, nous devons ajouter « » après chaque valeur de noeud sérialisé afin que le processus de désérialisation peut accéder à chaque fraction de la valeur de nœud avec « ».
Lien Leetcode: https://leetcode.com/problems/serialize-and-deserialize-binary-tree/
Explication par Back To Back SWE chaîne Youtube : 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));
Je n'utilise pas pré-commande , mais je me sers BFS. Ceci est une question de leetcode
La majorité des personnes sont mise en œuvre incorrecte lors de l'utilisation de précommande: le résultat attendu devrait être
"[1,2,3, null, null, 4,5]", mais la majorité des gens imprimer la sortie comme "[1,2,3, null, null, 4,5, null, null]" car ils sont sans compter les niveaux.
Voici ma mise en œuvre avec le bon résultat.
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)













