Algoritmo para probar si un árbol binario es un árbol de búsqueda y contar ramas completas

10

Necesito crear un algoritmo recursivo para ver si un árbol binario es un árbol de búsqueda binario, así como contar cuántas ramas completas hay (un nodo primario con nodos secundarios izquierdo y derecho) con una variable de conteo global asumida. Esta es una tarea para mi clase de estructuras de datos.

Hasta ahora tengo

void BST(tree T) {
   if (T == null) return
   if ( T.left and T.right) {
      if (T.left.data < T.data or T.right.data > T.data) {
        count = count + 1
        BST(T.left)
        BST(T.right)
      }
   }
}

Pero realmente no puedo entender esto. Sé que este algoritmo no resolverá el problema porque la cuenta será cero si la segunda declaración if no es verdadera.

¿Alguien podría ayudarme con esto?

OghmaOsiris
fuente
¿Cómo se <define el operador de comparación en los nodos?
Joe
¿Desea calcular el recuento incluso si no es un árbol de búsqueda binario?
Joe
1
¿Se supone que su algoritmo devuelve algo, como trueo false?
Joe
2
Tal vez debería intentar definir primero dos funciones separadas: una para verificar si es un BST y otra para contar las ramas completas. Eso debería ser más manejable.
sepp2k
1
@OghmaOsiris Me imagino que dijo eso porque la pregunta es básicamente "Aquí está mi código, ¿cómo lo hago funcionar?". Si el código no fuera de la variedad pseudo (ish), definitivamente sería una pregunta SO.
sepp2k

Respuestas:

10

Como otros ya han indicado en los comentarios, realmente tiene dos funciones no relacionadas aquí: probar si el árbol es un árbol de búsqueda y contar las ramas completas. A menos que la tarea lo requiera específicamente, escribiría dos funciones separadas.

Veamos primero contar las ramas completas primero. Eso significa contar los nodos que tienen un hijo izquierdo y un hijo derecho. Entonces necesita incrementar el contador ( count = count + 1) cuando ambos T.lefty T.rightno son nulos (no T.left.datay T.right.data: los datos no importan para esta tarea).

if (T.left and T.right) {
    count = count + 1

Además, debe explorar el subárbol izquierdo, incluso si el subárbol derecho está vacío, y debe explorar el subárbol derecho, incluso si el subárbol izquierdo está vacío. Así que mira dónde pones las llamadas recursivas.

Para probar si el árbol es un árbol de búsqueda, debe inspeccionar los valores de los datos. Ya tienes algo parecido a la comparación correcta; no del todo bien. Escriba algunos árboles de ejemplo con varias formas (no muy grandes, de 2 a 5 nodos) y ejecute su algoritmo para ver qué sucede.

Aún necesita encontrar un lugar para colocar el resultado de la verificación de validez. Nuevamente, observe dónde coloca las llamadas recursivas (si solo hace esta parte, hay varias soluciones, pero en esta etapa no se preocupe si solo ve una).

Finalmente, una vez que haya logrado escribir ambas funciones por separado, y las haya probado en algunos ejemplos, póngalas juntas cuidadosamente (si la tarea lo requiere).

Gilles 'SO- deja de ser malvado'
fuente
gracias, volví a leer la pregunta y se suponía que eran métodos separados.
OghmaOsiris
7

En cosas como esta, a menudo es más fácil pensar hacia atrás, así que primero considera lo que necesitas. De su descripción, enumeremoslos:

  • Recursividad
  • Validez
  • Conteo de nodos completos

OK, esa es una lista bastante corta, esto debería ser manejable. Comencemos con un método vacío y agregaré una descripción de lo que debería estar sucediendo.

valid_bst () {
}

Ahora validez. ¿Cómo se verifica la validez? En el chat dijiste que un árbol es válido "si ... todos los niños restantes son menores que los padres, y los hijos correctos son mayores que los padres". Estoy seguro de que querías permitir la igualdad también. Eso seria t.left.value <= t.value <= t.right.value.

valid_bst () {
    This node is valid if t.left.value <= t.value <= t.right.value
}

¿Pero qué pasa si falta uno de los niños? Por lo que ha dicho, creo que sabe que el nodo todavía es válido si falta uno (o ambos). Agreguemos esto, reestructurando ligeramente:

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
}

Bien, ahora sabemos si este nodo es válido. ¿Cómo verificamos si todo el árbol es válido? No está en una matriz, por lo que probablemente no podemos / no queremos recorrerlo linealmente. Su tarea da la respuesta: recursividad. Pero, ¿cómo acumulamos una respuesta usando la recursividad? Tenemos acceso a tres piezas de información, si este nodo es válido y el resultado de las llamadas que preguntan si los nodos izquierdo y derecho son válidos. Obviamente, el árbol solo es válido si los tres son verdaderos.

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
    Is the left child valid?
    Is the right child valid?
    This tree is only valid if this node and both its children are.
}

Si está prestando atención, eso incluso nos dice qué debe devolver nuestra función.

Ahora, ¿cómo integramos el conteo? Usted dice lo que cuenta ("un nodo primario con nodos secundarios izquierdo y derecho"), y eso no debería ser difícil de traducir al código real. Compruebe si se cumple esa condición e incremente el contador de forma adecuada. Solo recuerde que esto tiene que estar en algún lugar donde se alcance cada vez que sea cierto.

Y, por supuesto, he omitido algunos detalles, como la condición de detención de recursión y las comprobaciones de nulo.

Kevin
fuente
6

Mis tres comentarios anteriores son tres pistas para problemas con su código.

  1. A menos que ya haya definido específicamente cómo un operador de comparación debe manejar el tipo de datos de nodo, lo más probable es que comparar dos nodos directamente no haga lo que desea. Lo que probablemente quiso decir fue comparar los campos almacenados en los nodos, por ejemplonode1.value < node2.value
  2. en este momento, solo está agregando al conteo si el tercero ifes cierto, ¿está seguro de que eso es lo que quería hacer? Por cierto, es posible que desee verificar que la declaración if haga lo que desea.
  3. Supongo que desea devolver verdadero si el árbol es un BST válido y falso de lo contrario. Lo que significa que siempre debe devolver verdadero o falso en un caso base, y también debe devolver los resultados de sus llamadas recursivas.
Joe
fuente
Con respecto al punto uno: este es un pseudocódigo, ¿verdad? Entonces, mientras se transmita la intención al lector, no hay razón para definir cosas así.
sepp2k
@ sepp2k es cierto, y mi comentario es probablemente un poco demasiado exigente para el pseudocódigo. Supongo que mi punto es que necesitamos entender lo que significa comparar dos nodos. Su punto es que ya deberíamos entender eso implícitamente.
Joe
Cierto, exactamente.
sepp2k