Come serializzare albero binario

voti
23

Sono andato a un colloquio oggi, dove mi è stato chiesto di serializzare un albero binario. Ho implementato un approccio basata su array, dove i figli di nodo i (numerazione a livello ordine di attraversamento) erano al * indice 2 i per il figlio sinistro e 2 * i + 1 per il figlio destro. L'intervistatore sembrava più o meno contento, ma mi chiedo che cosa significa esattamente serialize? Ha in particolare riguardano l'appiattimento l'albero per la scrittura su disco, o che la serializzazione di un albero anche includono semplicemente ruotando l'albero in una lista collegata, dicono. Inoltre, come potremmo fare per appiattire l'albero in una lista (doppiamente) collegato, e poi ricostruirlo? Si può ricreare l'esatta struttura dell'albero dalla lista collegata?

È pubblicato 06/01/2011 alle 04:48
fonte dall'utente
In altre lingue...                            


10 risposte

voti
6

Approccio 1: Fare sia Inorder e Preorder attraversamento ai searialize i dati degli alberi. Su uso de-serializzazione pre-ordine e fare BST sulla Inorder per formare correttamente l'albero.

È necessario sia perché A -> B -> C può essere rappresentato come il pre-ordine, anche se la struttura può essere diversa.

Approccio 2: Usa # come una sentinella dovunque il bambino a sinistra oa destra è nullo .....

Risposto il 23/02/2013 a 20:49
fonte dall'utente

voti
0

Come sull'esecuzione di un attraversamento in ordine e mettendo la chiave radice e tutte le chiavi dei nodi in uno std :: list o altro contenitore di vostra scelta, che appiattisce l'albero. Quindi, è sufficiente serializzare lo std :: list o il contenitore di vostra scelta utilizzando la libreria Boost.

L'inverso è semplice e quindi rigenerare l'albero usando standard di inserimento di un albero binario. Questo potrebbe non essere del tutto efficace per un grande albero, ma runtime per convertire l'albero in uno std :: lista è O (n) al massimo e per ricostruire l'albero è O (log n) al massimo.

Sto per fare questo per serializzare un albero ho appena codificato in C ++ come sto convertendo il mio database da Java a C ++.

Risposto il 12/03/2013 a 18:59
fonte dall'utente

voti
12

Tutti questi articoli parlano soprattutto della parte serializzazione. La parte deserializzazione è un po 'difficile da fare in un solo passaggio.

Ho implementato una soluzione efficiente per la deserializzazione troppo.

Problema: serializzare e deserializzare un albero binario che contiene i numeri positivi.

Serializzazione parte:

  1. Utilizzare 0 per rappresentare nullo.
  2. Serializzare alla lista di interi che utilizzano preordine attraversamento.

Deserializzazione parte:

  1. Prende in lista di numeri interi e utilizza metodo di supporto ricorsivo per la deserializzazione.
  2. deserializzatore ricorsivo restituisce una coppia (nodo BTNode, int nextIndexToRead) dove nodo è il nodo albero costruito finora, e nextIndexToRead è la posizione del numero successivo da leggere nell'elenco serializzato dei numeri.

Di seguito è riportato il codice in 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;
    }
}
Risposto il 24/08/2013 a 21:28
fonte dall'utente

voti
0

Il modo migliore è quello di utilizzare un carattere speciale (come # come commento precedente menzionato) come sentinella. E 'meglio che costruire una matrice attraversamento in ordine simmetrico e un array di attraversamento in preordine / postorder, sia nello spazio di complessità saggio e la complessità tempo saggio. è anche modo più semplice da implementare.

lista collegata non è una buona misura qui dal al fine di ricostruire l'albero, è meglio avere il tempo di accesso elemento const

Risposto il 19/03/2014 a 23:07
fonte dall'utente

voti
2

Utilizzando pre traversal ordine, serializzare albero binario. Utilizzare lo stesso attraversamento pre ordine per deserializzare albero. Fate attenzione sui casi limite. Qui nodi nulli sono rappresentati da "#"

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;
    } 
Risposto il 18/01/2016 a 17:56
fonte dall'utente

voti
0

Ho cercato di ottenere l'essenza di esso. Quindi ecco la mia implementazione Java. Come già detto, questo è un albero binario non è un BST. Per la serializzazione, un attraversamento preordine sembra funzionare più facile (per una stringa con "NULL" per i nodi nulli). Si prega di verificare il codice qui sotto con un esempio completo di chiamate di ricorsione. Per deserializzazione, la stringa viene convertito in un LinkedList dove remove (0) riceve l'elemento piano in un O (1) tempo di esecuzione. Si prega di consultare anche un esempio completo nei commenti del codice per la deserializzazione. Speranza che aiuterà qualcuno lotta meno di me :) Il tempo complessivo di funzionamento per ciascun metodo (serializzare e deserializzare) è lo stesso tempo di esecuzione per albero binario di attraversamento, vale a dire, O (n), dove n è il numero di nodi (voci) nell'albero

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();


}

}

Risposto il 21/12/2017 a 16:43
fonte dall'utente

voti
-1

Ecco un altro modo di serializzazione albero binario con ordine di attraversamento (modificato) il livello. [Basta copiare incollare, funziona] Copre tutto sbilanciato, equilibrato, giusto distorta, albero obliquo sinistro.

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
Risposto il 28/11/2018 a 00:14
fonte dall'utente

voti
0

Ecco una risposta in ritardo in Python. Esso utilizza (profondità prima) preorder serializzazione e restituisce un elenco di strings. Deserializzazione restituisce l'albero.

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

Testare:

tree1 = Node('root', Node('left'), Node('right'))
t = deserialize(serialize(tree1))
print(str(t.right.val))
Risposto il 28/02/2019 a 02:19
fonte dall'utente

voti
-1

Serializzazione è il processo di conversione di uno struttura di dati o un oggetto in una sequenza di bit in modo che possa essere memorizzato in un buffer di file o memoria, o trasmessa attraverso un collegamento di connessione di rete da ricostruire successivamente nello stesso o in un altro ambiente informatico.

Deserializzazione è il processo di conversione della stringa di nuovo alla struttura ad albero originale.

Concetto di serializzazione e deserializzazione è molto simile a quello che fa un compilatore di codice. Ci sono più fasi in tutto il processo di compilazione, ma cercheremo di tenerlo astratta.

Dato un pezzo di codice, compilatore rompe diverse componenti ben definiti in token (per esempio, int è un token, doppio è un altro modo, {è un gettone,} è un altro gettone, ecc). [Link ad una dimostrazione del livello astratto della compilazione] [1].

Serializzazione: Usiamo la logica attraversamento in preordine per la serializzazione albero in una stringa. Noi aggiungeremo "X" per indicare un puntatore nullo / nodo in un albero. Inoltre, per mantenere la nostra logica deserializzazione in mente, abbiamo bisogno di aggiungere "" dopo ogni valore del nodo serializzato in modo che il processo di deserializzazione può accedere ogni nodo valore split con "".

Link Leetcode: https://leetcode.com/problems/serialize-and-deserialize-binary-tree/

Spiegazione da Spalle contro spalle canale Youtube SWE : 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));
Risposto il 16/07/2019 a 22:52
fonte dall'utente

voti
0

Non sto usando il pre-ordine, ma sto usando BFS. Questa è una domanda da leetcode

La maggior parte delle persone implementazione non sono corrette quando si utilizza il pre-ordine: il risultato atteso dovrebbe essere

"[1,2,3, null, null, 4,5]", ma invece di maggioranza la gente stampare l'output come "[1,2,3, null, null, 4,5, null, null]" dal momento che sono senza contare i livelli.

Ecco la mia implementazione con il risultato corretto.

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)
Risposto il 09/10/2019 a 02:02
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more