2012-02-03 18 views
10

Estamos aprendiendo B-trees en clase y se nos ha pedido que los implementemos en código. El docente nos ha dejado la elección del lenguaje de programación y quiero intentarlo en C#. Mi problema es que la estructura siguiente es ilegal en C#,¿Cómo se puede representar un nodo de árbol B?

unsafe struct BtreeNode 
     { 
      int key_num;  // The number of keys in a node 
      int[] key;   // Array of keys 
      bool leaf;   // Is it a leaf node or not? 
      BtreeNode*[] c;  // Pointers to next nodes 
     } 

En concreto, no se le permite crear un puntero para apuntar a la estructura misma. ¿Hay algún enfoque alternativo o enfoque alternativo que pueda usar? Estoy bastante seguro de que DEBE haber una manera de hacer esto dentro del código administrado, pero no puedo resolverlo.

EDIT: respuesta de Eric me señaló en la dirección correcta. Esto es lo que terminé usando,

class BtreeNode 
{ 
     public List<BtreeNode> children;  // The child nodes 
     public static int MinDeg;    // The Minimum Degree of the tree 
     public bool IsLeaf { get; set; }  // Is the current node a leaf or not? 
     public List<int> key;     // The list of keys 
... 
} 
+4

¿Por qué quieres usar una estructura en lugar de una clase? – CodesInChaos

+1

, por supuesto, puede usar C# para árboles B – Adrian

+9

No intente usar código inseguro en C# hasta que sea un experto; lo entenderás mal y será doloroso y difícil. Por el contrario, aprenda la forma segura de hacer las cosas primero; C# está diseñado para que la forma segura de hacer las cosas sea casi siempre más fácil que la manera insegura. –

Respuesta

26

Casualmente, acabo de implementar un btree en C#, para un proyecto personal. Fue divertido. Construí un btree de teclas de tamaño variable ordenadas lexicográficamente (hasta 64 bytes) que presentaban una serie de desafíos, particularmente en torno a averiguar cuándo una página de almacenamiento estaba demasiado llena o demasiado vacía.

Mi consejo, habiendo hecho eso, es la construcción de una capa de abstracción que capta sólo los algoritmos btree en su forma más abstracta, como una clase base abstracta. Una vez que obtuve todas las reglas de btree capturadas en esa forma, especialicé la clase base de varias maneras diferentes: como un btree regular de tamaño de clave fija de 2-3, como uno de mis btrees de fantasía de clave de tamaño variable, y así sucesivamente. .

Para empezar, en ningún caso debe usted hacer esto con punteros. El código inseguro rara vez es necesario y nunca es fácil. Solo los programadores C# más avanzados deben desconectar el sistema de seguridad; cuando lo hace, se responsabiliza por el tipo y la seguridad de la memoria del programa. Si no está dispuesto a hacerlo, deje el sistema de seguridad encendido.

En segundo lugar, no hay ninguna razón para hacer de esto una estructura. Las estructuras se copian por valor en C#; un nodo btree no es un valor .

En tercer lugar, no es necesario para mantener el número de llaves en un nodo; la matriz de teclas sabe cuántas teclas contiene.

En cuarto lugar, me gustaría utilizar un List<T> en lugar de una matriz; ellos son mas flexibles

En quinto lugar, tiene que decidir si las vidas clave en el nodoo en la matriz . De cualquier manera puede funcionar; Mi preferencia es la clave para vivir en el nodo, porque veo que la clave está asociada con el nodo.

En sexto lugar, es útil saber si un nodo bt es la raíz o no; podrías considerar tener dos bools, uno "¿esta es una hoja?" y uno "¿es esta la raíz?" Por supuesto, un btree con un solo elemento en él tiene un único nodo que es tanto de hoja como de raíz.

Séptimo, probablemente va a construir esto para que sea mutable; normalmente uno no hace públicos campos mutables en una clase C#. Puede considerar hacerles propiedades. Además, la lista de los niños puede ser cultiva y encogido, pero su identidad no cambia, por lo que sea referencialmente de sólo lectura:

así que probablemente estructurar mi nodo básico como:

class Node 
{ 
    public int Key { get; set; } 
    public bool IsRoot { get; set; } 
    public bool IsLeaf { get; set; } 
    private List<Node> children = new List<Node>(); 
    public List<Node> Children { get { return this.children; } } 
} 

¿Tiene sentido?

+1

Poner nodos 'struct' en una sola matriz que respalda la colección basada en btree podría ser una buena idea como optimización del rendimiento. Pero, por supuesto, uno usaría índices en lugar de punteros en ese caso. Por supuesto, esta pregunta se trata principalmente de aprender cómo funcionan los btrees, por lo que el código mucho más claro con clases es preferible aquí. – CodesInChaos

+0

@Eric Lippert, ¿Honestamente? La idea de "Listas" es nueva para mí. Ya es hora de que vaya a clase ahora, pero probaré tu sugerencia más tarde en el día e informaré. En cuanto a su 3er punto, conservo el número de claves en el nodo porque así es como mi texto (Introducción a los algoritmos por Cormen, Leiserson ..et al) muestra las cosas como. Es cierto, el conjunto también tiene esa información, pero creo que mi profesor preferiría que se mencionara explícitamente. – chronodekar

+8

@chronodekar: Recuerde, los algoritmos presentados en CLR suponen un enfoque muy parecido al de C para el mundo. En los lenguajes más modernos hay abstracciones de mayor nivel que las matrices, y los objetos son mucho más autodescriptivos. Y también recuerde: ** cada redundancia en una estructura de datos no solo es un desperdicio de memoria, sino también un error que está por ocurrir **. Los campos que tienen que ser exactamente iguales a otros campos presentan una oportunidad para que se desincronicen. –

14

Utilice una clase en lugar de un stuct. Y tira los punteros.

class BtreeNode 
{ 
    int key_num;  // The number of keys in a node 
    int[] key;   // Array of keys 
    bool leaf;   // Is it a leaf node or not? 
    BtreeNode[] c;  // Pointers to next nodes 
} 

Cuando se declara una variable de un tipo de clase, es implícitamente una referencia (muy similar a un puntero en c), ya que cada clase es un tipo de referencia.

7

Todo lo que necesita para darse cuenta de que un puntero en C es "algo similar" a una referencia en C#. (Hay varias diferencias, pero para los fines de esta pregunta puede concentrarse en las similitudes.) Ambos permiten un nivel de indirección: el valor no son los datos en sí mismos, es una forma de llegar a los datos.

El equivalente de lo anterior sería algo así como:

class BtreeNode 
{ 
    private int keyNumber; 
    private int[] keys; 
    private bool leaf; 
    private BtreeNode[] subNodes; 

    // Members (constructors etc) 
} 

(no me acuerdo mucho de los árboles B, pero si la matriz "claves" que aquí se corresponde con el valor "keyNumber" de cada subnodo, es posible que no desee la variable keys en absoluto.)

+0

solo una nota (aunque es bastante irrelevante para la pregunta), con claves [] por separado, puede permitir menos errores de caché mientras se busca por clave. Es probable que las claves [] ocupen una sola (? depende del tamaño) de la línea de caché, mucho más rápido que la indirección de BtreeNode. De nuevo, es totalmente irrelevante para la pregunta del OP. . – bestsss

+0

@bestsss: Por otro lado, significa que hay más objetos en total, por lo que puede terminar con más errores de caché en un nivel superior. Definitivamente lo implementaría * sin * la optimización primero, y luego cotejarlo si el rendimiento fuera un problema. –

+0

Por supuesto ... no hay teclas [] para comenzar. Esas optimizaciones son en su mayoría innecesarias de todos modos. Señalaba que tener claves explícitas puede ser un aumento en el rendimiento. – bestsss

Cuestiones relacionadas