Kodowanie drzewa binarnego


12

Załóżmy, że masz pełne drzewo binarne (tzn. Każdy wewnętrzny węzeł ma dokładnie dwóch niepustych potomków). Każdy węzeł zawiera niezerową liczbę całkowitą. Zadanie polega na zakodowaniu i zdekodowaniu drzewa do / z listy liczb całkowitych.

Drzewo jest przechowywane wewnętrznie coś takiego:

struct node {
  int data;
  struct node *left, *right;
};

Musisz zaimplementować dwie funkcje:

int *encode(struct node *root);
struct node *decode(int *array);

Od Ciebie zależy, jak kodujesz i dekodujesz.

Punkty za:

  • minimalna długość kodowania
  • złożoność (idealnie liniowa w liczbie węzłów)
  • oryginalność

Brak punktów za długość kodu źródłowego i nie jesteś ograniczony do C.

Przykład drzewa:

     5
    / \
   3   2
      / \
     2   1
    / \
   9   9

1
Wymagania wejściowe i wyjściowe nie zaszkodzą.
Yasir Arsanukaev

2
@Yasir: Algorytm kodowania jest twoim zadaniem, więc nie mogę dostarczyć żadnych danych wejściowych i wyjściowych. int *to czarna skrzynka dla użytkownika.
Alexandru

Czy są jakieś ograniczenia dotyczące zakresu liczb całkowitych? Mówiąc dokładniej, jeśli używamy języka z dowolnie dużymi liczbami całkowitymi, czy możemy z niego korzystać? I czy rozmiar zakodowanych danych jest mierzony liczbą całkowitą lub liczbą bajtów?
sepp2k

Czy funkcje kodowania i dekodowania muszą być wolne od skutków ubocznych (innych niż przydział pamięci)? A może mogą na przykład przechowywać dane w zmiennych globalnych?
sepp2k

1
Zakładając, że same liczby całkowite danych są rzeczywistymi liczbami 32-bitowymi, istnieje proste kodowanie, które wykorzystuje tylko 32 * n bitów.
Anon.

Odpowiedzi:


2

~ 1,03 N

Wydaje się, że wszystkie dotychczasowe odpowiedzi wymagają co najmniej 2 * N * 32-bitów. (Z wyjątkiem rozwiązań w językach, które pozwalają na wartości całkowite dłuższe niż 32 bity, takich jak rozwiązania Haskell i Ruby - ale te nadal będą wymagały dodatkowych bajtów do kodowania, gdy dane będą większe niż 16 KB).

Oto rozwiązanie, które wymaga jedynie pułapu N + (N / 32) +1 do przechowywania. To zbliża się do 1,03125 N dla dużych N i jest poniżej 1,1 N dla wszystkich N większych niż 20.

Chodzi o to, aby zapisać dodatkowy bit dla każdego węzła, gdzie 1 to „hasChildren”. Te bity są upakowane w N / 32 słowach z góry.

int* encodeHelper(Node* n, int* code, int* pos, int* flag)
{
   int hasKids = (n->left!=0);
   code[*flag/32]|=hasKids<<(*flag&31);
   *flag+=1;
   if (hasKids) bencodeHelper(n->left, code, pos, flag);
   code[*pos]=n->data;
   *pos+=1;
   if (hasKids) bencodeHelper(n->right, code, pos, flag);
   return code;
}

int* encode(Node* h, int* sizeOut)
{
   int nnodes=countNodes(h);
   int nflags = (int)ceil(nnodes/32.0);
   int pos=nflags+1;
   int flag=32;
   int* out;
   *sizeOut = 1+nnodes+nflags;
   out = calloc(*sizeOut, sizeof(int));
   if (!h) return out;
   out[0]=nflags+1; //store start of data
   return encodeHelper(h,out,&pos,&flag);
}

Node* decodeHelper(int* code, int* pos, int* flag)
{
   Node*n = calloc(1, sizeof(Node));
   int hasKids = code[*flag/32]>>(*flag&31)&1;
   *flag+=1;
   if (hasKids) n->left = bdecodeHelper(code, pos, flag);
   n->data = code[*pos];
   *pos+=1;
   if (hasKids) n->right = bdecodeHelper(code, pos, flag);
   return n;
}

Node* decode(int* code)
{
   int flag=32;
   int pos=code[0];
   if (!pos) return NULL;
   return decodeHelper(code, &pos, &flag);
}

(ukończ implementację tutaj)


5

Ten program Haskell koduje drzewo n węzłów w n liczbach całkowitych. Sztuczka polega na tym, że koduje dane węzła podwójnie, a następnie używa bitu niższego rzędu, aby wskazać, czy jest to węzeł liścia czy węzeł wewnętrzny.

Technicznie rzecz biorąc, Parsermonada tutaj jest nadmiernie zabijana, ponieważ utworzono tylko jeden parser, decodera ja mogłem umieścić logikę łańcuchową parsera bezpośrednio tam. Ale w ten sposób dekoder jest bardzo wyraźny, a Parserpomimo niewielkich rozmiarów jest rozsądną, prostą strukturą analizującą.

import Control.Monad (ap)

data Tree = Leaf Integer | Node Integer Tree Tree
  deriving (Eq, Show)

encode :: Tree -> [Integer]
encode (Leaf n)     = [n*2]
encode (Node n t u) = (n*2+1) : encode t ++ encode u

decode :: [Integer] -> Maybe Tree
decode = fullyParse decoder
  where
    decoder :: Parser Integer Tree
    decoder = do
      i <- next
      let n = i `div` 2
      if even i
        then return (Leaf n)
        else return (Node n) `ap` decoder `ap` decoder

-- A simple Parsing Monad
data Parser a b = P { runParser :: [a] -> Maybe (b, [a]) }

instance Monad (Parser a) where
  return a = P ( \ts -> Just (a, ts) )
  p >>= q  = P ( \ts -> runParser p ts >>= (\(v,ts') -> runParser (q v) ts') )
  fail _   = P ( const Nothing )

next :: Parser a a
next = P n
 where n (t:ts) = Just (t,ts)
       n _      = Nothing

fullyParse :: Parser a b -> [a] -> Maybe b
fullyParse p ts = runParser p ts >>= consumedResult
  where
    consumedResult (v,[]) = Just v
    consumedResult _      = Nothing

-- Example
main :: IO ()
main = do
    putStrLn $ "example:  " ++ show ex
    putStrLn $ "encoding: " ++ show encEx
    putStrLn $ "decoding: " ++ show decEx
    putStrLn $ "worked?   " ++ show worked
  where
    ex = Node 5
          (Leaf 3)
          (Node 2
            (Node 2
              (Leaf 9)
              (Leaf 9)
            )
            (Leaf 1)
          )
    encEx = encode ex
    decEx = decode encEx
    worked = maybe False (ex ==) decEx

Uruchomienie tego spowoduje:

> runhaskell TreeEncoding.hs 
example:  Node 5 (Leaf 3) (Node 2 (Node 2 (Leaf 9) (Leaf 9)) (Leaf 1))
encoding: [11,6,5,5,18,18,2]
decoding: Just (Node 5 (Leaf 3) (Node 2 (Node 2 (Leaf 9) (Leaf 9)) (Leaf 1)))
worked?   True

4

W C.

#include <stdlib.h>
#include <stdio.h>

struct Node;
typedef struct Node Node;

struct Node
{
    int   data;
    Node* left;
    Node* right;
};
/* Private Functions */
static int*  encodeNode(Node* tree, int* store);
static Node* decodeNode(int** store);

/* Public Functions */
Node*   newNode(int data,Node* left,Node* right);
void    deleteTree(Node* tree);
int     countNodesTree(Node* tree);
int*    encode(Node *tree);
Node*   decode(int* store);
void    printTree(Node* tree);

Node* newNode(int data,Node* left,Node* right)
{
    Node* result    = (Node*)malloc(sizeof(Node));
    result->data    = data;
    result->left    = left;
    result->right   = right;

    return result;
}

void deleteTree(Node* tree)
{
    if (tree == NULL)
    {   return;
    }

    deleteTree(tree->left);
    deleteTree(tree->right);
    free(tree);
}

int countNodesTree(Node* tree)
{
    if (tree == NULL)
    {   return 0;
    }

    return    countNodesTree(tree->left)
            + countNodesTree(tree->right)
            + 1;
}

void printTree(Node* tree)
{
    if (tree == NULL)
    {
        fprintf(stdout, "- ");
    }
    else
    {
        fprintf(stdout, "%d ", tree->data);
        printTree(tree->left);
        printTree(tree->right);
    }
};

Kod:

int* encode(Node *tree)
{
    int     nodeCount   = countNodesTree(tree);
    int*    result      = (int*)malloc(sizeof(int) * (nodeCount * 2 + 1));

    // Put the node count in the first element.
    // This makes it easy to also serialize this object for transport
    // i.e. you can put it in a file or a stream (socket) and easily recover it.
    result[0]           = nodeCount;
    encodeNode(tree, result + 1);
    return result;
}

int* encodeNode(Node* tree, int* store)
{
    if (tree != NULL)
    {
        store[0]    = tree->data;
        /*
         * Slight overkill. for this question.
         * But works and makes future enhancement easy
         */
        store[1]    = (tree->left  == NULL ? 0 : 1)
                    + (tree->right == NULL ? 0 : 2);
        store += 2;

        store       = encodeNode(tree->left,  store);
        store       = encodeNode(tree->right, store);
    }
    return store;
}

Dekodowanie:

Node* decode(int* store)
{
    if (store == NULL)
    { fprintf(stderr, "Bad Input terminating: encode() always return non NULL\n");
      exit(1);
    }

    if (store[0] == 0)
    {
        return NULL;
    }

    store++;
    return decodeNode(&store);
}

Node* decodeNode(int** store)
{
    int     value   = (*store)[0];
    int     flag    = (*store)[1];
    (*store) += 2;

    Node*   left    = flag & 1 ? decodeNode(store) : NULL;
    Node*   right   = flag & 2 ? decodeNode(store) : NULL;

    return newNode(value, left, right);
}

Główny:

int main()
{
    Node*   t = newNode(5,
                        newNode(3, NULL, NULL),
                        newNode(2,
                                newNode(2,
                                        newNode(9, NULL, NULL),
                                        newNode(9, NULL, NULL)
                                       ),
                                newNode(1, NULL, NULL)
                               )
                       );

    printTree(t);
    fprintf(stdout,"\n");

    int*    e   = encode(t);
    Node*   d   = decode(e);
    printTree(d);
    fprintf(stdout,"\n");

    free(e);
    deleteTree(d);
    deleteTree(t);
}

Uwaga. Każdy węzeł jest zakodowany jako dwie liczby całkowite (plus jedna liczba całkowita dla liczby węzłów).
Dostarczone drzewo koduje w ten sposób:

 7, 5, 3, 3, 0, 2, 3, 2, 3, 9, 0, 9, 0 1, 0
 ^  ^
 ^  ^ Node 1
 ^
 Count

3

W Ruby, z takim samym kodowaniem jak @MtnViewMark :

class Node
        def initialize(data, left = nil, right = nil)
                @data, @left, @right = data, left, right
        end

        def encode
                "%d %s %s" % [@data<<1|1, @left.encode, @right.encode]
        end

        class << self
                def decode(str)
                        _decode(str.split.map &:to_i)
                end

                private

                def _decode(a)
                        n = a.shift
                        if n & 1 == 1
                                Node.new(n>>1, _decode(a), _decode(a))
                        else
                                Leaf.new(n>>1)
                        end
                end
        end
end

class Leaf < Node
        def encode
                (@data<<1).to_s
        end
end

tree=Node.decode("11 6 5 5 18 18 2")
print tree.encode

Koszt to jedna liczba całkowita na węzeł ( data << 1 | has_childs):

11 6 5 5 18 18 2

Wow - to wygląda szczuple i elegancko. Jednak nie wymaga tablicy int, prawda?
użytkownik nieznany

2

Biorąc pod uwagę drzewo binarne z nwęzłami, koduje je na liście 2n + 1liczb całkowitych. Algorytmy kodowania i dekodowania mają O(n)złożoność.

Używam liczby całkowitej 0 jako znacznika wartownika podczas kodowania, wskazując, kiedy rozwijam rekurencję. Następnie, kiedy dekoduję, umieszczam tworzone przeze mnie węzły na stosie (swego rodzaju) i używam zer na liście, aby śledzić, gdzie dodać następny węzeł. Nie próbowałem, ale jestem prawie pewien, że dekodowanie pękłoby, gdyby drzewo nie było kompletne.

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

// Prototypes
struct BTnode;
struct BTnode * bt_add_left(struct BTnode * node, int data);
struct BTnode * bt_add_right(struct BTnode * node, int data);
int             bt_depth(struct BTnode * tree);
int             bt_encode_preorder(int * list, struct BTnode * tree, int index);
struct BTnode * bt_node_create(int data);
int             bt_node_delete(struct BTnode * node);
void            bt_print_preorder(struct BTnode * tree);
int *           encode(struct BTnode * tree);
struct BTnode * decode(int * list);

// Binary tree node
struct BTnode
{
  int data;
  struct BTnode *left, *right;
};

// Add node to this node's left
struct BTnode * bt_add_left(struct BTnode * node, int data)
{
  struct BTnode * newnode = bt_node_create(data);
  node->left = newnode;
  return newnode;
}

// Add node to this node's right
struct BTnode * bt_add_right(struct BTnode * node, int data)
{
  struct BTnode * newnode = bt_node_create(data);
  node->right = newnode;
  return newnode;
}

// Determine depth of the tree
int bt_depth(struct BTnode * tree)
{
  int depth;
  int leftdepth = 0;
  int  rightdepth = 0;
  if( tree == NULL ) return 0;

  if( tree->left != NULL )
    leftdepth = bt_depth(tree->left);
  if( tree->right != NULL )
    rightdepth = bt_depth(tree->right);

  depth = leftdepth;
  if(rightdepth > leftdepth)
    depth = rightdepth;

  return depth + 1;
}

// Recursively add node values to integer list, using 0 as an unfolding sentinel
int bt_encode_preorder(int * list, struct BTnode * tree, int index)
{
  list[ index++ ] = tree->data;

  // This assumes the tree is complete (i.e., if the current node does not have
  // a left child, then it does not have a right child either)
  if( tree->left != NULL )
  {
    index = bt_encode_preorder(list, tree->left, index);
    index = bt_encode_preorder(list, tree->right, index);
  }

  // Add sentinel
  list[ index++ ] = 0;
  return index;
}

// Allocate memory for a node
struct BTnode * bt_node_create(int data)
{
  struct BTnode * newnode = (struct BTnode *) malloc(sizeof(struct BTnode));
  newnode->left = NULL;
  newnode->right = NULL;
  newnode->data = data;
  return newnode;
}

// Free node memory
int bt_node_delete(struct BTnode * node)
{
  int data;
  if(node == NULL)
    return 0;
  data = node->data;

  if(node->left != NULL)
    bt_node_delete(node->left);
  if(node->right != NULL)
    bt_node_delete(node->right);

  free(node);
  return data;
}

// Print all values from the tree in pre-order
void bt_print_preorder(struct BTnode * tree)
{
  printf("%d ", tree->data);
  if(tree->left != NULL)
    bt_print_preorder(tree->left);
  if(tree->right != NULL)
    bt_print_preorder(tree->right);
}

// Decode binary tree structure from a list of integers
struct BTnode * decode(int * list)
{
  struct BTnode * tree;
  struct BTnode * nodestack[ list[0] ];
  int i,j;

  // Handle trivial case
  if( list == NULL ) return NULL;

  tree = bt_node_create( list[1] );
  nodestack[ 1 ] = tree;

  j = 1;
  for(i = 2; i < list[0]; i++)
  {
    if( list[i] == 0 )
    {
      //printf("popping\n");
      j--;
    }
    else
    {
      if( nodestack[j]->left == NULL )
      {
        //printf("Adding %d to left of %d\n", list[i], nodestack[j]->data);
        nodestack[ j+1 ] = bt_add_left(nodestack[j], list[i]);
        j++;
      }
      else
      {
        //printf("Adding %d to right of %d\n", list[i], nodestack[j]->data);
        nodestack[ j+1 ] = bt_add_right(nodestack[j], list[i]);
        j++;
      }
    }
  }

  return tree;
}

// Encode binary tree structure as a list of integers
int * encode(struct BTnode * tree)
{
  int maxnodes, depth, length;
  int * list;
  int j;

  // Handle trivial case
  if(tree == NULL) return NULL;

  // Calculate maximum number of nodes in the tree from the tree depth
  maxnodes = 1;
  depth = bt_depth(tree);
  for(j = 0; j < depth; j++)
  {
    maxnodes += pow(2, j);
  }

  // Allocate memory for the list; we need two ints for each value plus the
  // first value in the list to indicate length
  list = (int *) malloc( ((maxnodes * 2)+1) * sizeof(int));
  length = bt_encode_preorder(list, tree, 1);
  list[ 0 ] = length;
  return list;
}

int main()
{
  struct BTnode * tree;
  struct BTnode * newtree;
  int * list;
  int i;

  /* Provided example

        5
       / \
      3   2
         / \
        2   1
       / \
      9   9
  */
  tree = bt_node_create(5);
  bt_add_left(tree, 3);
  struct BTnode * temp = bt_add_right(tree, 2);
  bt_add_right(temp, 1);
  temp = bt_add_left(temp, 2);
  bt_add_left(temp, 9);
  bt_add_right(temp, 9);
  printf("T (traversed in pre-order):  ");
  bt_print_preorder(tree);
  printf("\n");

  list = encode(tree);
  printf("T (encoded as integer list): ");
  for(i = 1; i < list[0]; i++)
    printf("%d ", list[i]);
  printf("\n");

  newtree = decode(list);
  printf("T' (decoded from int list):  ");
  bt_print_preorder(newtree);
  printf("\n\n");


  // Free memory
  bt_node_delete(tree);
  bt_node_delete(newtree);
  free(list);
  return 0;
}

Zapisano to jako encode.cskompilowane i wykonane. Ten program korzysta z podanego przez Ciebie drzewa przykładów i pomyślnie przetestowałem go na kilku innych.

$ gcc -Wall -lm -o encode encode.c
$ ./encode 
T (traversed in pre-order):  5 3 2 2 9 9 1 
T (encoded as integer list): 5 3 0 2 2 9 0 9 0 0 1 0 0 0 
T' (decoded from int list):  5 3 2 2 9 9 1

To właściwie to, co miałem na myśli :).
Alexandru

czy to nie powiedzie się dekodowanie, jeśli dane zawierają 0?
AShelly

@AShelly Wyraźnie powiedział, że 0 nie będzie uwzględnione w drzewie. Gdyby tak było, to tak, to by się nie powiodło.
Daniel Standage

2

Mój kod koduje drzewo podczas przechodzenia w przedsprzedaży, każdy liść w dwóch liczbach wewnętrznych (jego dane, po których następuje 0), a każdy wewnętrzny węzeł w jednej liczbie wewnętrznej (po danych następuje lewe dziecko, potem jego prawo). Dla pełnego drzewa binarnego (jak go zdefiniujesz) z n węzłami, n musi być nieparzyste, i są (n + 1) / 2 liście i (n-1) / 2 wewnętrzne węzły, więc to jest 3n / 2 + 1 / 2 liczby całkowite do kodowania.

ostrzeżenie: niesprawdzone, wystarczy wpisać.

struct node {
  int data;
  struct node *left, *right;
};

void encodeInternal(struct node *root, vector<int> *buf) {
  buf->push_back(root->data);
  if (root->left) {
    encodeInternal(root->left, buf);
    encodeInternal(root->right, buf);
  } else {
    buf->push_back(0);
  }
}
int *encode(struct node *root) {
  vector<int> buf;
  encodeInternal(root, &buf);
  return &buf[0];
}

struct decodeResult {
  int encoded_size;
  struct node *n;
}
struct decodeResult decodeInternal(int *array) {
  struct node *n = (struct node*)malloc(sizeof(struct node));
  n->data = array[0];
  if (array[1] == 0) {
    n->left = n->right = NULL;
    return (decodeResult){2, n};
  } else {
    decodeResult L = decodeInternal(array + 1);
    decodeResult R = decodeInternal(array + 1 + L.encoded_size);
    n->left = L.n;
    n->right = R.n;
    return (decodeResult){1 + L.encoded_size + R.encoded_size, n};
  }
}
struct node *decode(int *array) {
  return decodeInternal(array).n;
}

1

Oto moja próba. Przechowuje drzewo w tablicy o rozmiarze 2 ** głębokość + 1. Służy a[0]do przechowywania rozmiaru i a[size]do przechowywania indeksu pierwszego „pustego węzła”, który napotyka podczas pierwszej głębokości. (Pusty węzeł to miejsce, w którym dziecko byłoby przechowywane, gdyby rodzic je miał). Każdy pusty węzeł zawiera indeks następnego pustego węzła, który zostanie napotkany.

Ten schemat pozwala uniknąć rezerwowania bitów w celu oznaczenia elementów potomnych obecności, dzięki czemu każdy węzeł może korzystać z pełnego zakresu liczb całkowitych. Pozwala również na niezrównoważone drzewa - rodzic może mieć tylko jedno dziecko.

wynik:

empty tree:  [0]
head node only:  [2,5,0]
example tree: [16,5,3,2,5,14,2,1,0,0, 0,0,9,9,15,0,4];

Enkoder:

//utility
 int findDepth(Node* n) {
    int l = 0 ,r = 0;
    if (n) {
       l = 1 + findDepth(n->left);
       r = 1 + findDepth(n->right);
    }
    return ( l > r ) ? l : r;
 }

//Encode Function
 int* encodeTree(Node* head) {
    int* out;
    int depth = findDepth(head);
    int size = depth>0;
    while (depth--) size*=2;
    out = calloc(size+1,sizeof(int));
    out[0]=size;
    encodeNode(head, out,1, out+size);
    return out;
 }

 void encodeNode(Node* n, int* a, int idx, int* pEmpty) {
    if (n) {
       a[idx]=n->data;
       encodeNode(n->left,a,idx*2,pEmpty);
       encodeNode(n->right,a,idx*2+1,pEmpty);
    }
    else if (idx<a[0]) {
       *pEmpty = idx;
       pEmpty = a+idx;
    }
 }

Dekoder:

 //Decode Function
 Node* decodeArray(int* a) {
    return (a[0]) ?  decodeNode(a,1,a+a[0]) : NULL;
 }

 Node* decodeNode(int* a, int idx, int* pEmpty) {
    Node* n = NULL;
    if (idx== *pEmpty)
       *pEmpty=a[idx];
    else {
       n = calloc(1,sizeof(Node));
       n->data = a[idx];
       if (idx*2<a[0]) {
          n->left = decodeNode(a, idx*2, pEmpty);
          n->right = decodeNode(a, idx*2+1, pEmpty);
       }
    }
    return n;
 }

(dzięki @ Daniel Sobral za naprawienie formatowania)


1

Scala:

trait Node {
  def encode (): Array[Int]
}

case object Node {
  def decode (a: Array[Int]): InnerNode = {
    if (a.length == 1) InnerNode (a(0)) else {
      val r = InnerNode (a(1)) 
      val l = decode (a.tail.tail) 
      InnerNode (a(0), l, r) 
    }
  }
}

case object Leaf extends Node {
  def encode (): Array[Int] = Array.empty
}

case class InnerNode (val data: Int, var l: Node=Leaf, var r: Node=Leaf) extends Node {
  def encode (): Array[Int] = Array (data) ++ r.encode () ++ l.encode () 
}

object BinTreeTest extends App {
  println (Node.decode (Array (1, 2, 3, 4, 5)).encode.mkString (", "))
}

Jest to podejście wykorzystujące przestarzałą składnię, ale kompiluje się bez błędów w Scali 2.9.1. Generuje Drzewo i dekoduje każde zakodowane Drzewo do tej samej Tablicy, której użyto do kodowania. Może dziś w jakiś sposób pozbyłem się przestarzałych ostrzeżeń.

Wow - to było proste. Pierwszy pomysł zadziałał natychmiast.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.