¿Un árbol de búsqueda binario permite un vértice duplicado?

En un árbol de búsqueda binaria (BST), todas las claves en el subárbol izquierdo de una clave deben ser más pequeñas y todas las claves en el subárbol derecho deben ser mayores. Entonces, un Árbol de búsqueda binaria, por definición, tiene claves distintas.

¿Cómo permitir duplicados donde cada inserción inserta una clave más con un valor y cada eliminación elimina una ocurrencia?

Una solución simple es permitir las mismas teclas en el lado derecho (también podríamos elegir el lado izquierdo). Por ejemplo, considere la inserción de las claves 12, 10, 20, 9, 11, 10, 12, 12 en un Árbol de búsqueda binario vacío

12
/ \
10 20
/ \ /
9 11 12
/ \
10 12

Una mejor solución es aumentar cada nodo de árbol para almacenar el conteo junto con campos regulares como clave, punteros izquierdo y derecho.
La inserción de las claves 12, 10, 20, 9, 11, 10, 12, 12 en un Árbol de búsqueda binario vacío crearía lo siguiente.

12 (3)
/ \
10 (2) 20 (1)
/ \
9 (1) 11 (1)

El recuento de una clave se muestra entre paréntesis

Este enfoque tiene las siguientes ventajas sobre el enfoque simple anterior.

1) La altura del árbol es pequeña independientemente del número de duplicados. Tenga en cuenta que la mayoría de las operaciones BST (buscar, insertar y eliminar) tienen una complejidad de tiempo como O (h) donde h es la altura de BST. Entonces, si podemos mantener la altura pequeña, obtenemos una menor cantidad de comparaciones clave.

2) Buscar, Insertar y Eliminar se vuelven más fáciles de hacer. Podemos usar los mismos algoritmos de inserción, búsqueda y eliminación con pequeñas modificaciones (ver el código a continuación).

3) Este enfoque también es adecuado para BST de equilibrio automático (Árbol AVL, ÁRBOL NEGRO ROJO, etc.). Estos árboles implican rotaciones, y una rotación puede violar la propiedad BST de la solución simple ya que una misma clave puede estar en el lado izquierdo o derecho después de la rotación.

A continuación se muestra la implementación en C del árbol de búsqueda binaria normal con recuento con cada clave. Básicamente, este código se toma del código para insertar y eliminar en BST. Los cambios realizados para manejar duplicados están resaltados, el resto del código es el mismo.

// C program to implement basic operations (search, insert and delete)

// on a BST that handles duplicates by storing count with every node

#include

#include

node struct

{

key; int key;

int count;

struct node *left, *right;

};

// A utility function to create a new BST node

struct node *newNode(int item)

{

struct node *temp = (struct node *)malloc(sizeof(struct node));

temp->key = item;

temp->left = temp->right = NULL;

temp->count = 1;

temp; return temp;

}

// A utility function to do inorder traversal of BST

void inorder(struct node *root) inorder(struct node *root)

{

if (root != NULL)

{

inorder(root->left);

printf("%d(%d) ", root->key, root->count);

inorder(root->right);

}

}

/* A utility function to insert a new node with given key in BST */

struct node* insert(struct node* node, int key)

{

/* If the tree is empty, return a new node */

if (node == NULL) return newNode(key);

// If key already exists in BST, icnrement count and return

if (key == node->key)

{

(node->count)++;

node; return node;

}

/* Otherwise, recur down the tree */

if (key key)

node->left = insert(node->left, key);

else

node->right = insert(node->right, key);

/* return the (unchanged) node pointer */

node; return node;

}

/* Given a non-empty binary search tree, return the node with

minimum key value found in that tree. Note that the entire

tree does not need to be searched. */

struct node * minValueNode(struct node* node)

{

struct node* current = node;

/* loop down to find the leftmost leaf */

while (current->left != NULL)

current = current->left;

current; return current;

}

/* Given a binary search tree and a key, this function

deletes a given key and returns root of modified tree */

struct node* deleteNode(struct node* root, int key)

{

// base case

if (root == NULL) return root;

// If the key to be deleted is smaller than the

// root's key, then it lies in left subtree

if (key key)

root->left = deleteNode(root->left, key);

// If the key to be deleted is greater than the root's key,

// then it lies in right subtree

else if (key > root->key)

root->right = deleteNode(root->right, key);

// if key is same as root's key

else

{

// If key is present more than once, simply decrement

// count and return

if (root->count > 1)

{

(root->count)--;

return root;

}

// ElSE, delete the node

// node with only one child or no child

if (root->left == NULL)

{

struct node *temp = root->right;

free(root);

temp; return temp;

}

else if (root->right == NULL)

{

struct node *temp = root->left;

free(root);

temp; return temp;

}

// node with two children: Get the inorder successor (smallest

// in the right subtree)

struct node* temp = minValueNode(root->right);

// Copy the inorder successor's content to this node

root->key = temp->key;

// Delete the inorder successor

root->right = deleteNode(root->right, temp->key);

}

return root;

}

// Driver Program to test above functions

int main()

{

/* Let us create following BST

12(3)

/ \

10(2) 20(1)

/ \

9(1) 11(1) */

struct node *root = NULL;

root = insert(root, 12);

root = insert(root, 10);

root = insert(root, 20);

root = insert(root, 9);

root = insert(root, 11);

root = insert(root, 10);

root = insert(root, 12);

root = insert(root, 12);

printf("Inorder traversal of the given tree \n");

inorder(root);

printf("\nDelete 20\n");

root = deleteNode(root, 20);

printf("Inorder traversal of the modified tree \n");

inorder(root);

printf("\nDelete 12\n");

root = deleteNode(root, 12);

printf("Inorder traversal of the modified tree \n");

inorder(root);

printf("\nDelete 9\n");

root = deleteNode(root, 9);

printf("Inorder traversal of the modified tree \n");

inorder(root);

return 0;

}

Un vértice duplicado puede ir hacia la izquierda o hacia la derecha. Para los árboles con equilibrio automático, puede decidir ir siempre a la derecha en caso de un vértice duplicado al insertar nuevos elementos, pero al realizar consultas , debe estar preparado para encontrarlos a la izquierda o a la derecha. La consulta no es mucho más difícil con la nueva invariante, y en general se generaliza bastante bien, ya que tendrá que caminar por el árbol para atravesar todas las claves iguales.

La implementación de C ++ std :: map <> funciona con claves iguales como nodos separados en un árbol de búsqueda binario balanceado.

Probablemente podría hacer alguna variante de un BST que podría admitir vértices duplicados, pero hay otra forma de hacerlo. Si necesita almacenar duplicados, almacene cada valor junto con un recuento de cuántas veces ocurre. Al insertar un valor, si descubre que ya existe, no agregue ningún nodo, solo incremente el recuento. Al eliminar un valor, disminuya el recuento y solo elimine el nodo si el recuento se ha reducido a cero.

En el escenario donde los valores que está insertando tienen propiedades adicionales que no son parte de la clave de búsqueda, mantenga un BST de claves únicas y almacene en cada nodo un puntero a una lista de valores que contiene la clave.

Parte de la razón por la cual muchos BST no admiten claves duplicadas es que es más difícil mantener el árbol equilibrado en ese escenario. Suponga que la regla de cómo se posicionan las teclas iguales entre sí es que la tecla insertada en segundo lugar siempre se inserta a la derecha o siempre a la izquierda. Puede mostrar que en ese caso, si se inserta la misma clave N veces, el árbol tendrá una altura de al menos N. Recuerde que las operaciones del árbol se ejecutan en O (altura), por lo que esta es una mala noticia para la complejidad de las operaciones del árbol. Claro, puede tener reglas más complicadas para insertar nodos, unas que insertan nodos iguales a veces a la izquierda y a veces a la derecha, pero a las implementaciones de BST generalmente les gusta evitar esas complicaciones simplemente al no admitir duplicados. Cuando comienzas a tener rotaciones de árboles, la situación se complica aún más si se permiten duplicados.

Creo que tienes claves duplicadas, no vértices duplicados, en la mayoría de esos casos.

Para complementar la buena respuesta de Eugene, una opción es utilizar más información sobre las teclas. Supongamos que estamos almacenando productos ordenados por precio en el BST. Luego, para productos de igual precio, utilizamos una identificación de producto única para romper los lazos.

si lo hace

More Interesting

¿Cuáles son los rompecabezas de algoritmos de notación O más interesantes?

¿Cómo se puede calcular su edad en días? Necesito el algoritmo más simplificado para resolverlo.

¿Cuál es el algoritmo para imprimir el alfabeto 'A' como patrón?

¿Qué algoritmo usa Matlab para calcular las raíces de un polinomio de alto rango?

Cómo paralelizar un método recursivo en Java

Si pudiéramos reescribir las leyes del universo con el único fin de optimizar la computación, ¿cuáles serían estas leyes?

Cómo encontrar el enésimo número faltante más pequeño de una matriz de números

¿Debo comenzar a aprender programación de computadoras con CS50 o con un libro de estructuras de datos y algoritmos?

¿Por qué el algoritmo ikj es más rápido que el algoritmo ijk para la multiplicación de matrices?

¿Cuál es el mejor algoritmo de clasificación manual? Por ejemplo, si tuviera una pila de papeles que quisiera ordenar alfabéticamente, ¿cuál sería la forma más eficiente de hacerlo? ¿Qué pasaría si estuvieras de acuerdo con que uno o dos se alejen de su posición ordenada?

Cómo completar consultas en tiempo O (1) en un problema RANGESUM en SPOJ

¿Cuál es el intercambio de espacio temporal en las estructuras de datos?

¿Qué te dirías a ti mismo cuando recién comenzaste a programar, aprender algoritmos?

¿Cuál es una explicación intuitiva para el algoritmo de maximización condicional de expectativa (ECM)?

Cómo usar el código VHDL para generar el seno de un ángulo dado usando el algoritmo CORDIC