Mã hóa cây nhị phân


12

Giả sử bạn có một cây nhị phân hoàn chỉnh (tức là mỗi nút bên trong có chính xác hai con cháu không trống). Mỗi nút chứa một số nguyên khác không. Bạn được giao nhiệm vụ mã hóa và giải mã cây thành / từ danh sách các số nguyên.

Cây được lưu trữ bên trong một cái gì đó như:

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

Và bạn phải thực hiện hai chức năng:

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

Nó phụ thuộc vào bạn cách bạn mã hóa và giải mã.

Điểm cho:

  • chiều dài mã hóa tối thiểu
  • độ phức tạp (lý tưởng tuyến tính về số lượng nút)
  • độc đáo

Không có điểm nào cho độ dài mã nguồn và bạn không bị hạn chế đối với C.

Cây ví dụ:

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

1
Yêu cầu đầu vào và đầu ra sẽ không bị tổn thương.
Yasir Arsanukaev

2
@Yasir: Thuật toán mã hóa là công việc của bạn nên tôi không thể cung cấp bất kỳ đầu vào và đầu ra nào. int *là một hộp đen cho người dùng.
Alexandru

Có bất kỳ hạn chế về phạm vi số nguyên? Cụ thể hơn nếu chúng ta sử dụng một ngôn ngữ có số nguyên lớn tùy ý, chúng ta có được phép sử dụng ngôn ngữ đó không? Và kích thước của dữ liệu được mã hóa được đo bằng số nguyên hay số byte?
sepp2k

Các chức năng mã hóa và giải mã có cần phải có hiệu ứng phụ (trừ phân bổ bộ nhớ) không? Hoặc họ có thể ví dụ lưu trữ dữ liệu trong các biến toàn cầu?
sepp2k

1
Giả sử chính các số nguyên dữ liệu là các số nguyên 32 bit thực tế, có một mã hóa đơn giản chỉ sử dụng các bit 32 * n.
Anon.

Câu trả lời:


2

~ 1,03 N

Dường như tất cả các câu trả lời cho đến nay chỉ mất ít nhất 2 * N * 32 bit để lưu trữ. (Ngoại trừ các giải pháp trong các ngôn ngữ cho phép các giá trị nguyên dài hơn 32 bit, như các giải pháp Haskell và Ruby - nhưng các giải pháp đó vẫn sẽ lấy thêm byte để mã hóa bất cứ khi nào dữ liệu lớn hơn 16K.)

Đây là một giải pháp chỉ mất N + trần (N / 32) +1 ints lưu trữ. Điều này tiếp cận 1.03125 N cho N lớn và dưới 1.1 N cho tất cả N lớn hơn 20.

Ý tưởng là lưu trữ thêm một bit cho mỗi nút trong đó 1 là "hasChildren". Các bit này được đóng gói thành N / 32 từ phía trước.

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

(bắt buộc thực hiện ở đây)


5

Chương trình Haskell này mã hóa một cây gồm n nút trong n Số nguyên. Thủ thuật là nó mã hóa dữ liệu của nút nhân đôi, và sau đó sử dụng bit thứ tự thấp hơn để cho biết đây là nút lá hay nút bên trong.

Về mặt kỹ thuật, Parserđơn nguyên ở đây bị giết quá mức, vì chỉ có một trình phân tích cú pháp được tạo decodervà tôi có thể đặt trình phân tích cú pháp logic trực tiếp vào đó. Nhưng theo cách này, bộ giải mã rất rõ ràng và Parsermặc dù kích thước nhỏ, là một khung phân tích cú pháp đơn giản hợp lý.

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

Chạy này giúp bạn:

> 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

Trong 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);
    }
};

Mã hóa:

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;
}

Giải mã:

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

Chủ yếu:

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

Ghi chú. Mỗi nút được mã hóa thành hai số nguyên (cộng một int cho số lượng nút).
Vì vậy, cây được cung cấp mã hóa như thế này:

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

3

Trong Ruby, với cùng mã hóa hơn @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

Chi phí là một số nguyên cho mỗi nút ( data << 1 | has_childs):

11 6 5 5 18 18 2

Wow - trông gầy và thanh lịch. Tuy nhiên, nó không mất một mảng int, phải không?
người dùng không xác định

2

Cho một cây nhị phân có ncác nút, điều này mã hóa nó trong một danh sách các 2n + 1số nguyên. Cả hai thuật toán mã hóa và giải mã đều có O(n)độ phức tạp.

Tôi sử dụng số nguyên 0 làm điểm đánh dấu sentinel khi mã hóa, biểu thị khi tôi mở ra đệ quy. Sau đó, khi tôi giải mã, tôi đặt các nút cây tôi đang tạo trên một ngăn xếp (sắp xếp) và sử dụng các số 0 trong danh sách để theo dõi vị trí cần thêm nút tiếp theo. Tôi chưa thử, nhưng tôi khá chắc chắn việc giải mã sẽ bị phá vỡ nếu cây chưa hoàn thành.

#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;
}

Lưu cái này encode.csau đó được biên dịch và thực thi. Chương trình này sử dụng cây mẫu mà bạn cung cấp và tôi đã thử nghiệm nó trên một vài cây khác thành công.

$ 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

Đó là khá nhiều những gì tôi đã nghĩ trong đầu :).
Alexandru

Điều này sẽ không giải mã được nếu dữ liệu chứa 0?
AShelly

@AShelly Ông nói rõ ràng rằng 0 sẽ không được bao gồm trong cây. Nếu có, thì điều này sẽ thất bại.
Daniel Standage

2

Mã của tôi mã hóa cây trong một giao dịch đặt hàng trước, mỗi lá trong hai int (dữ liệu của nó theo sau là 0) và mỗi nút bên trong trong một int (dữ liệu của nó được theo sau bởi con trái của nó, sau đó là bên phải của nó). Đối với cây nhị phân hoàn chỉnh (như bạn xác định) với n nút, n phải là số lẻ và có (n + 1) / 2 lá và (n-1) / 2 nút bên trong, vì vậy đó là 3n / 2 + 1 / 2 số nguyên cho mã hóa.

cảnh báo: chưa được kiểm tra, chỉ cần gõ nó vào.

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

Đây là sự cố gắng của tôi. Nó lưu trữ cây trong một mảng có kích thước 2 ** sâu + 1. Nó sử dụng a[0]để giữ kích thước và a[size]để giữ chỉ mục của "nút trống" đầu tiên mà nó gặp trong một giao dịch theo chiều sâu. (Một nút trống là nơi một đứa trẻ sẽ được lưu trữ nếu cha mẹ có một cái). Mỗi nút trống giữ chỉ mục của nút trống tiếp theo sẽ gặp phải.

Lược đồ này tránh các bit dự trữ để đánh dấu các phần tử con, vì vậy mỗi nút có thể sử dụng phạm vi số nguyên đầy đủ. Nó cũng cho phép cây không cân đối - cha mẹ có thể chỉ có một con.

đầu ra:

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];

Bộ mã hóa:

//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;
    }
 }

Bộ giải mã:

 //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;
 }

(cảm ơn @daniel sobral đã sửa định dạng)


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 (", "))
}

Đây là một cách tiếp cận sử dụng cú pháp không dùng nữa, nhưng biên dịch không có lỗi trong Scala 2.9.1. Nó tạo ra một Cây và giải mã mọi Cây được mã hóa thành cùng một Mảng như được sử dụng để mã hóa. Có lẽ tôi bằng cách nào đó thoát khỏi những cảnh báo không được chấp nhận ngày hôm nay.

Wow - đó là một điều đơn giản. Ý tưởng đầu tiên làm việc ngay lập tức.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.