使用一维向量实现非二叉树的算法?

时间:2010-01-20 15:41:34

标签: algorithm

树的每个节点可能有任意数量的子节点。 我需要一种方法来构造和遍历这些树,但是使用一维向量或列表来实现它们。

5 个答案:

答案 0 :(得分:7)

如果你只能使用一个向量(未指定问题),并且节点不应该包含它自己的列表,只有一些指针(向量中的地址),那么你可以试试这个:

  1. 每个节点保存其兄弟的地址
  2. 给定后的第一个节点(如果它不是它的兄弟)是child,带有指向第二个孩子的指针等等
  3. 每个孩子都有自己的孩子
  4. 对于像这样的树:

    A
    | \
    B  E ___
    |\  \ \ \
    C D  F G H
    

    你的矢量看起来像:

    idx:    0 1 2 3 4 5 6 7
    nodes:  A B C D E F G H
    next:   _ 4 3 _ _ 6 7 _
    

    其中_是空指针

    编辑:
    另一种方法:

    1. 每个节点保存由其子节点占用的向量中的区域地址
    2. 孩子们彼此相邻
    3. 向量中存在空节点,标记兄弟群组的结尾
    4. 对于这种方法,给定树看起来像:

      idx:    0 1 2 3 4 5 6 7 8 9 A B
      nodex:  A _ B E _ C D _ F G H _
      child:  2   5 8   _ _   _ _ _
      

      通过这种方式,您可以轻松找到任意随机节点的子节点并重新组织数组而无需移动所有元素(只需将子节点复制到表的末尾,更新指针并将下一个子节点添加到表的末尾)

答案 1 :(得分:2)

你基本上做的是通过将向量元素配对到cons单元格来为Lisp解释器编写内存管理器的开头。这是我刚刚在C中聚集的事情:

#include <stdbool.h>
#include <iso646.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#define MEMORY_SIZE 1024
#define NIL 0
#define IVAL_MASK 0x8000000

struct pair
{
    bool allocated;
    size_t first;
    size_t second;
};

size_t allocate_pair(struct pair vector[])
{
    for (size_t i = 1; i < MEMORY_SIZE; i++)
        {
            if (not vector[i].allocated)
                {
                    vector[i].allocated = true;
                    return i;
                }
        }

    return NIL;
}

size_t make_pair(struct pair vector[], size_t a, size_t b)
{
    size_t the_pair = allocate_pair(vector);

    if (the_pair != NIL)
        {
            vector[the_pair].first = a;
            vector[the_pair].second = b;
            return the_pair;
        }
    else
        {
            fprintf(stderr,
                    "Out of pairs -- make_pair(%p, %zu, %zu)",
                    vector,
                    a,
                    b);
            exit(EXIT_FAILURE);
        }
}

size_t first(struct pair vector[], size_t index)
{
    assert(vector[index].allocated);

    return vector[index].first;
}


size_t second(struct pair vector[], size_t index)
{
    assert(vector[index].allocated);

    return vector[index].second;
}

void print_pair_aux(struct pair[], size_t, size_t);
void print_pair(struct pair vector[], size_t p)
{
    assert(vector[p].allocated);

    size_t a = first(vector, p);
    size_t b = second(vector, p);

    printf("(");

    print_pair_aux(vector, a, b);

    printf(")");
}

void print_pair_aux(struct pair vector[], size_t a, size_t b)
{
    if (a == NIL)
        printf("NIL");
    else if (a >= IVAL_MASK)
        printf("%zu", a &~ IVAL_MASK);
    else
        print_pair(vector, a);

    if (b == NIL)
        printf("");
    else if (b >= IVAL_MASK)
        printf(" . %zu", b &~ IVAL_MASK);
    else
        {
            printf(" ");
            print_pair_aux(vector,
                           first(vector, b),
                           second(vector, b));
        }

}

int main(void) 
{
    struct pair *vector = calloc(MEMORY_SIZE, sizeof *vector);

#define cons(A,B) make_pair(vector, (A), (B))
#define ival(x) ((x) | IVAL_MASK)

    size_t a = cons(ival(3), cons(ival(4), NIL));
    size_t b = cons(ival(2), cons(a, NIL));
    size_t c = cons(ival(6), cons(ival(7), cons(ival(8), NIL)));
    size_t d = cons(ival(5), cons(c, NIL));
    size_t e = cons(ival(1), cons(b, cons(d, NIL)));

    print_pair(vector, e);
    puts("");
}
$ cc -std=c99 try.c
$ ./a.out
(1 (2 (3 4)) (5 (6 7 8)))

答案 2 :(得分:1)

在数组中存储完整二叉树的标准方法(用于二进制堆实现)很不错,因为您可以按照级别顺序树遍历的顺序使用元素数组表示树。使用该方案,可以使用快速技巧来计算父节点和子节点位置。移动到一棵树,其中每个节点可以有任意数量的元素,这会使这种方案陷入困境。

然而,有几种方案可以将任意树表示为二叉树。 Donald Knuth的计算机程序设计,第I卷,第2.3节对它们进行了详细讨论。

如果允许节点本身包含指针,则可以存储每个节点的子指示列表。你的情况可以吗?

答案 3 :(得分:1)

您可以使用一维链表实现它,而且开销很小。

每个父节点都包含指向其子节点的指针。(但这需要决定节点的最大数量是否事先知道。)

对于以A为根节点且B,C,D为子节点的树,其表示如下:

A - &gt;乙 A - &gt; C A - &gt; d

请注意,A中有3个链接。

克服节点数量上限的一种方法是在节点中添加额外的指针。

所以,现在A - &gt;(孩子)B - &gt;(adj) - &gt;(adj)C - &gt;(adj) - &gt; d

在这种情况下,当删除发生时更新树非常复杂。

如果您可以告诉您在各种操作上的时间限制,那么设计更好的数据结构会更容易。

答案 4 :(得分:0)

对节点的值没有限制,假设你只能使用一个列表,我会按如下方式构造它:

将每个节点表示为( val ; [ int ; ...] ),其中val是节点的值,每个int是其中一个子节点列表中的位置。如有必要,请使用不可打印的分隔符。

遍历非常缓慢。