¿Se permiten claves duplicadas en la definición de árboles de búsqueda binarios?


139

Estoy tratando de encontrar la definición de un árbol de búsqueda binario y sigo encontrando diferentes definiciones en todas partes.

Algunos dicen que para cualquier subárbol dado, la clave secundaria izquierda es menor o igual que la raíz.

Algunos dicen que para cualquier subárbol dado, la clave secundaria derecha es mayor o igual que la raíz.

Y mi antiguo libro de estructuras de datos de la universidad dice "cada elemento tiene una clave y no hay dos elementos que tengan la misma clave".

¿Existe una definición universal de un bst? Particularmente con respecto a qué hacer con árboles con múltiples instancias de la misma clave.

EDITAR: Tal vez no estaba claro, las definiciones que estoy viendo son

1) izquierda <= raíz <derecha

2) izquierda <raíz <= derecha

3) izquierda <raíz <derecha, de modo que no existan claves duplicadas.

Respuestas:


78

Muchos algoritmos especificarán que los duplicados están excluidos. Por ejemplo, los algoritmos de ejemplo en el libro MIT Algorithms generalmente presentan ejemplos sin duplicados. Es bastante trivial implementar duplicados (ya sea como una lista en el nodo o en una dirección particular).

La mayoría (que he visto) especifican los hijos izquierdos como <= y los hijos derechos como>. Hablando en términos prácticos, un BST que permite que cualquiera de los hijos derechos o izquierdos sea igual al nodo raíz, requerirá pasos computacionales adicionales para finalizar una búsqueda donde se permiten nodos duplicados.

Es mejor utilizar una lista en el nodo para almacenar duplicados, ya que insertar un valor '=' en un lado de un nodo requiere reescribir el árbol en ese lado para colocar el nodo como hijo, o el nodo se coloca como un gran -child, en algún momento a continuación, lo que elimina parte de la eficiencia de búsqueda.

Debe recordar que la mayoría de los ejemplos de aulas se simplifican para representar y entregar el concepto. No vale la pena ponerse en cuclillas en muchas situaciones del mundo real. Pero la declaración, "cada elemento tiene una clave y no hay dos elementos tienen la misma clave", no se viola mediante el uso de una lista en el nodo del elemento.

¡Así que ve con lo que dice tu libro de estructuras de datos!

Editar:

La definición universal de un árbol de búsqueda binaria implica almacenar y buscar una clave basada en atravesar una estructura de datos en una de dos direcciones. En el sentido pragmático, eso significa que si el valor es <>, atraviesa la estructura de datos en una de dos 'direcciones'. Entonces, en ese sentido, los valores duplicados no tienen ningún sentido.

Esto es diferente de BSP, o partición de búsqueda binaria, pero no tan diferente. El algoritmo para buscar tiene una de dos direcciones para 'viajar', o se hace (con éxito o no). Así que me disculpo porque mi respuesta original no abordaba el concepto de una 'definición universal', ya que los duplicados son realmente distintos tema (algo con lo que trata después de una búsqueda exitosa, no como parte de la búsqueda binaria).


1
¿Cuáles son las desventajas de usar una lista en el nodo?
Pacerier

1
@Pacerier Creo que en lugar de mantener una lista, podemos mantener un recuento de referencia en cada nodo y actualizar el recuento cuando se produce un duplicado. Tal algoritmo sería mucho más fácil y eficiente en la búsqueda y almacenamiento. Además, requeriría cambios mínimos en el algoritmo existente que no admite duplicados.
SimpleGuy

50

Si su árbol de búsqueda binario es un árbol rojo negro, o tiene la intención de realizar cualquier tipo de operaciones de "rotación de árboles", los nodos duplicados causarán problemas. Imagina que tu regla de árbol es esta:

izquierda <raíz <= derecha

Ahora imagine un árbol simple cuya raíz es 5, el niño izquierdo es nulo, y el niño derecho es 5. Si hace una rotación izquierda en la raíz, termina con un 5 en el niño izquierdo y un 5 en la raíz con el niño derecho siendo nulo Ahora algo en el árbol de la izquierda es igual a la raíz, pero su regla anterior supuso left <root.

Pasé horas tratando de averiguar por qué mis árboles rojos / negros ocasionalmente se desplazaban fuera de servicio, el problema era lo que describí anteriormente. ¡Ojalá alguien lea esto y se ahorre horas de depuración en el futuro!


18
¡No gire cuando tenga nodos iguales! Atraviesa el siguiente nivel y gíralo.
Rico

2
Otras soluciones son cambiar la regla de árbol para que sea left <= node <= right, o solo insertar antes de la primera aparición de un valor.
paxdiablo

¿Qué problemas puede causar esto en la práctica? Me parece que si está bien con left <= node <= right, entonces todas las operaciones de árbol rojo-negro funcionarán de todos modos.
Björn Lindqvist

39

Las tres definiciones son aceptables y correctas. Definen diferentes variaciones de un BST.

El libro de la estructura de datos de su universidad no pudo aclarar que su definición no era la única posible.

Ciertamente, permitir duplicados agrega complejidad. Si usa la definición "left <= root <right" y tiene un árbol como:

      3
    /   \
  2       4

luego agregar una clave duplicada "3" a este árbol dará como resultado:

      3
    /   \
  2       4
    \
     3

Tenga en cuenta que los duplicados no están en niveles contiguos.

Este es un gran problema cuando se permiten duplicados en una representación BST como la anterior: los duplicados pueden estar separados por cualquier número de niveles, por lo que verificar la existencia de duplicados no es tan simple como verificar los hijos inmediatos de un nodo.

Una opción para evitar este problema es no representar duplicados estructuralmente (como nodos separados) sino utilizar un contador que cuente el número de ocurrencias de la clave. El ejemplo anterior tendría un árbol como:

      3(1)
    /     \
  2(1)     4(1)

y después de insertar la clave duplicada "3" se convertirá en:

      3(2)
    /     \
  2(1)     4(1)

Esto simplifica las operaciones de búsqueda, eliminación e inserción, a expensas de algunos bytes adicionales y operaciones de contador.


Estoy muy sorprendido de que esto nunca haya sido mencionado en el libro de texto que estoy usando. El profesor tampoco lo mencionó, ni el hecho de que las claves duplicadas sean incluso un problema smh ...
Oloff Biermann

22

En un BST, todos los valores que descienden en el lado izquierdo de un nodo son menores que (o iguales a, ver más adelante) el propio nodo. De manera similar, todos los valores que descienden en el lado derecho de un nodo son mayores que (o iguales) al valor de los nodos (a) .

Algunos BST pueden optar por permitir valores duplicados, de ahí los calificadores "o igual a" anteriores.

El siguiente ejemplo puede aclarar:

            |
      +--- 14 ---+
      |          |
+--- 13    +--- 22 ---+
|          |          |
1         16    +--- 29 ---+
                |          |
               28         29

Esto muestra un BST que permite duplicados: puede ver que para encontrar un valor, comienza en el nodo raíz y baja por el subárbol izquierdo o derecho dependiendo de si su valor de búsqueda es menor o mayor que el valor del nodo.

Esto se puede hacer recursivamente con algo como:

def hasVal (node, srchval):
    if node == NULL:
         return false
    if node.val == srchval:
        return true
    if node.val > srchval:
        return hasVal (node.left, srchval)
    return hasVal (node.right, srchval)

y llamándolo con:

foundIt = hasVal (rootNode, valToLookFor)

Los duplicados agregan un poco de complejidad, ya que es posible que deba seguir buscando una vez que haya encontrado su valor para otros nodos del mismo valor.


(a) En realidad, podría ordenarlos en la dirección opuesta si así lo desea, siempre que ajuste la forma en que busca una clave específica. Un BST solo necesita mantener un cierto orden, ya sea que sea ascendente o descendente no es relevante.


Para el caso duplicado, ¿puede verificar si el elemento secundario correcto es el mismo que el nodo actual en la cláusula node.val == srchval: y luego, si es así, vaya a la derecha?
bneil

9

En el libro "Introducción a los algoritmos", tercera edición, de Cormen, Leiserson, Rivest y Stein, un árbol de búsqueda binario (BST) se define explícitamente como permitir duplicados . Esto se puede ver en la figura 12.1 y en lo siguiente (página 287):

"Las claves en un árbol de búsqueda binario siempre se almacenan de tal manera que satisfagan la propiedad del árbol de búsqueda binario: Sea xun nodo en un árbol de búsqueda binario. Si yes un nodo en el subárbol izquierdo de x, entonces y:key <= x:key. Si yes un nodo en el subárbol derecho de x, entonces y:key >= x:key".

Además, un árbol rojo-negro se define en la página 308 como:

"Un árbol rojo-negro es un árbol de búsqueda binario con un bit adicional de almacenamiento por nodo: su color"

Por lo tanto, los árboles rojo-negros definidos en este libro admiten duplicados.


4

Cualquier definición es válida. Siempre y cuando sea consistente en su implementación (siempre coloque nodos iguales a la derecha, siempre los ponga a la izquierda o nunca los permita), entonces está bien. Creo que es más común no permitirlos, pero sigue siendo un BST si están permitidos y se colocan a la izquierda o a la derecha.


1
Si tiene un conjunto de datos que contiene claves duplicadas, todos esos elementos deben almacenarse en el nodo 1 del árbol mediante un método diferente (lista vinculada, etc.). el árbol solo debe contener claves únicas.
nickf

1
También tenga en cuenta en la wiki que el subárbol derecho contiene valores "mayores o iguales que" la raíz. Por lo tanto, la definición de wiki es contradictoria.
SoapBox

1
+1: Diferentes personas usan diferentes definiciones. Si implementa un nuevo BST, debe asegurarse de ser explícito acerca de las suposiciones que está haciendo sobre las entradas duplicadas.
Sr. Fooz el

1
Parece que el consenso es (izquierda <= raíz <= derecha) al permitir duplicados. Pero esa definición de algunas personas de un BST no permite duplicaciones. O tal vez algunas personas lo enseñan de esa manera para evitar la complejidad adicional.
Tim Merrifield

1
¡incorrecto! Ya sea izquierda <= raíz <derecha O izquierda <raíz <= derecha, O izquierda> raíz> = derecha O izquierda> = raíz> derecha
Mitch Wheat

3

Trabajando en una implementación de árbol rojo-negro, tuve problemas para validar el árbol con varias teclas hasta que me di cuenta de que con la rotación de inserción rojo-negro, debe aflojar la restricción para

left <= root <= right

Como ninguna de la documentación que estaba buscando permitía claves duplicadas y no quería reescribir los métodos de rotación para dar cuenta de ello, decidí modificar mis nodos para permitir múltiples valores dentro del nodo, y no duplicar claves en el árbol.


2

Esas tres cosas que dijiste son todas ciertas.

  • Las llaves son únicas
  • A la izquierda hay teclas menos que esta
  • A la derecha hay llaves mayores que esta

Supongo que podría invertir su árbol y colocar las teclas más pequeñas a la derecha, pero en realidad el concepto "izquierda" y "derecha" es solo eso: un concepto visual que nos ayuda a pensar en una estructura de datos que realmente no tiene una izquierda o bien, entonces realmente no importa.


1

1.) izquierda <= raíz <derecha

2.) izquierda <raíz <= derecha

3.) left <root <right, de modo que no existan claves duplicadas.

Puede que tenga que ir y desenterrar mis libros de algoritmos, pero en la parte superior de mi cabeza (3) está la forma canónica.

(1) o (2) solo aparecen cuando comienza a permitir nodos duplicados y coloca nodos duplicados en el árbol (en lugar del nodo que contiene una lista).


¿Podría explicar por qué left <= root <= right no es ideal?
Helin Wang

Eche un vistazo a la respuesta aceptada por @paxdiablo: puede ver que el valor duplicado puede existir >=. Ideal depende de sus requisitos, pero si tiene muchos valores duplicados y permite que existan los duplicados en la estructura, su bst puede terminar siendo lineal, es decir, O (n).
Robert Paulson

1

Claves duplicadas • ¿Qué sucede si hay más de un elemento de datos con la misma clave? - Esto presenta un pequeño problema en los árboles rojo-negros. - Es importante que los nodos con la misma clave se distribuyan en ambos lados de otros nodos con la misma clave. - Es decir, si las claves llegan en el orden 50, 50, 50, • desea que los segundos 50 vayan a la derecha de la primera, y los terceros 50 a la izquierda de la primera. • De lo contrario, el árbol se desequilibra. • Esto podría manejarse mediante algún tipo de proceso de aleatorización en el algoritmo de inserción. - Sin embargo, el proceso de búsqueda se vuelve más complicado si se deben encontrar todos los elementos con la misma clave. • Es más sencillo prohibir artículos con la misma clave. - En esta discusión asumiremos que no se permiten duplicados

Se puede crear una lista vinculada para cada nodo del árbol que contiene claves duplicadas y almacenar datos en la lista.


1

Solo quiero agregar más información a lo que respondió @Robert Paulson.

Supongamos que el nodo contiene clave y datos. Por lo tanto, los nodos con la misma clave pueden contener datos diferentes.
(Por lo tanto, la búsqueda debe encontrar todos los nodos con la misma clave)

1) izquierda <= cur <derecha

2) izquierda <cur <= derecha

3) izquierda <= cur <= derecha

4) left <cur <right && cur contiene nodos hermanos con la misma clave.

5) left <cur <right, de modo que no existan claves duplicadas.

1) y 2) funciona bien si el árbol no tiene ninguna función relacionada con la rotación para evitar la asimetría.
Pero esta forma no funciona con el árbol AVL o el árbol Rojo-Negro , porque la rotación romperá el principal.
E incluso si search () encuentra el nodo con la clave, debe atravesar hasta el nodo hoja para los nodos con clave duplicada.
Hacer la complejidad del tiempo para la búsqueda = theta (logN)

3) funcionará bien con cualquier forma de BST con funciones relacionadas con la rotación.
Pero la búsqueda tomará O (n) , arruinando el propósito de usar BST.
Digamos que tenemos el árbol como abajo, con 3) principal.

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

Si buscamos (12) en este árbol, incluso si encontramos 12 en la raíz, debemos seguir buscando tanto el niño izquierdo como el derecho para buscar la clave duplicada.
Esto toma O (n) tiempo, como he dicho.

4) es mi favorito personal. Digamos que hermano significa el nodo con la misma clave.
Podemos cambiar el árbol de arriba a abajo.

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

Ahora cualquier búsqueda tomará O (logN) porque no tenemos que atravesar hijos para la clave duplicada.
Y este director también funciona bien con el árbol AVL o RB .


0

La relación de orden de elementos <= es un orden total, por lo que la relación debe ser reflexiva, pero comúnmente un árbol de búsqueda binario (también conocido como BST) es un árbol sin duplicados.

De lo contrario, si hay duplicados, debe ejecutar dos veces o más la misma función de eliminación.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.