Você está na página 1de 46

Arboles Binarios

Objetivo:
Dar una introducción a los árboles y mostrar la forma de implementar un árbol binario, ya sea en una
estructura estática (un vector) o dinámicamente (semejante a una lista ligada).

Descripción:
A lo largo de la siguiente práctica se mostrarán algunos conceptos útiles para comprender que es y para
que pudiera servir un árbol, además de que se mostrarán dos pequeños ejemplos que muestran algunos
ejemplos de utilidad para árboles binarios.

Nota:
El presente documento es una pequeña síntesis sobre árboles binarios. Se trata este
tema porque estos representan una de las estructuras de datos más importantes en
computación y estos pueden aplicarse para la solución de una gran variedad de
problemas, pero no muchas personas los ven como una buena solución puesto que los
consideran un tanto complejos. El objetivo del presente será dejar claro que es un
árbol binario y crear algunos ejemplos para tratar de tener una mayor comprensión.
Y así después cada uno de nosotros podamos definir si es conveniente aplicarlos a
la solución de algún problema o si alguna otra estructura de datos puede ser una
mejor opción. Puesto que solo veremos árboles las estructuras de datos alternativas
corren por su cuenta :-)

Desarrollo:
Conceptos y Terminología básica
Si vamos a trabajar con árboles, lo primero que tenemos que ver es que es un árbol. Aquí tenemos
algunas definiciones para árbol:
- Un árbol es una estructura de datos no lineal y homogénea en el que cada elemento puede
tener varios elementos posteriores, pero tan sólo puede tener un elemento anterior.
- Es una estructura jerárquica aplicada sobre una colección de elementos u objetos llamados
nodos; de los cuales uno es conocido como raíz. Además se crea una relación o parentesco entre
los nodos dando lugar a términos como padre, hijo, hermano, antecesor, sucesor, ancestro, etc.
- Un árbol es una estructura compuesta por un dato y varios árboles. Dado un nodo cualquiera de
la estructura, podemos considerarlo como una estructura independiente. Es decir, un nodo
cualquiera puede ser considerado como la raíz de un árbol completo.
- Son estructuras dinámicas no lineales. Dinámicas porque las estructuras de árbol pueden
cambiar durante la ejecución de un programa. No lineales, puesto que a cada elemento del árbol
pueden seguirle varios elementos. En las estructuras de datos lineales cada elemento tiene un
único elemento anterior y un único elemento posterior.

El tipo de estructura más general son los grafos. En un grafo cada elemento puede tener varios
elementos anteriores y varios elementos posteriores. Los árboles no son más que un tipo especial de
grafo en el que cada elemento puede tener varios elementos posteriores, pero tan sólo puede tener un
elemento anterior. Tanto grafos como árboles son estructuras no lineales.

Creo que la mayoría, sino es que todos, hemos utilizado los árboles cuando hemos hecho un árbol
genealógico o alguna estructura jerárquica de alguna organización. Y que me dicen de la forma en la
que organizamos la información en nuestras maquinas (se hace en una estructura de directorios y
subdirectorios en forma de árbol, para facilitar su búsqueda). Talvez sin darnos cuenta, pero hemos
manejado el concepto de árbol .
Además de la definición debemos conocer otros conceptos bastante importantes, pues estos pudieran
ser útiles a la hora de codificar un árbol:
En relación con otros nodos:
 Nodo padre. Nodo que contiene un puntero al nodo actual. En un árbol un nodo solo puede
tener un nodo padre. X es padre de Y sí y solo sí el nodo X apunta a Y. También se dice que X
es antecesor de Y. En la Figura 1: B es padre de E y F.
 Nodo hijo. Cualquiera de los nodos apuntados por uno de los nodos del árbol. Un nodo puede
tener varios hijos. X es hijo de Y, sí y solo sí el nodo X es apuntado por Y. También se dice que
X es descendiente directo de Y. En la Figura 1: E es hijo de B.
 Hermano. Dos nodos serán hermanos si son descendientes directos de un mismo nodo. En la
Figura 1: E y F son hermanos.
En cuanto a la posición dentro del árbol:
 Nodo raíz. Es el único nodo del árbol que no tiene padre. Este es el nodo que usaremos para
referirnos al árbol. En la Figura 1 A es el nodo raíz.
 Nodo hoja. Nodo que no tiene hijos. Se le llama hoja o terminal a aquellos nodos que no tienen
ramificaciones (hijos). En la Figura 1 N es un nodo hoja.
 Nodo interior. Es un nodo que no es raíz ni hoja. En la Figura 1 D es un nodo interior.
Existen otros conceptos que definen las características del árbol, en relación a su tamaño:
 Orden. Es el número potencial de hijos que puede tener cada elemento de árbol. De este modo,
diremos que un árbol en el que cada nodo puede apuntar a otros dos es de orden dos, si puede
apuntar a tres será de orden tres, etc. Podríamos decir que nuestro árbol de ejemplo (Figura 1)
es de orden tres.
 Grado. El número de hijos que tiene el elemento con más hijos dentro del árbol. En el árbol del
ejemplo, el grado es tres, ya que tanto A como D tienen tres hijos, y no existen elementos con
más de tres hijos.
 Nivel. Se define para cada elemento del árbol como la distancia a la raíz, medida en nodos. El
nivel de la raíz es cero, el de sus hijos uno y así sucesivamente. En el ejemplo, el nodo D tiene
nivel 1, el nodo G tiene nivel 2, y el nodo N nivel 3.
 Altura. La altura de un árbol se define como el nivel del nodo de mayor nivel. Como cada nodo
de un árbol puede considerarse a su vez como la raíz de un árbol, también podemos hablar de
altura de ramas; el máximo número de nodos que hay que recorrer para llegar de la raíz a una de
las hojas. El árbol de la Figura 1 tiene altura 3, la rama B tiene altura 2, la rama G tiene altura 1,
la N cero...
 Peso. Es el número de nodos del árbol sin contar la raíz.
 Camino. Es una secuencia de nodos, en el que dos nodos consecutivos cualesquiera son padre e
hijo. En el ejemplo A-D-H y A-C-G-M son caminos.
 Longitud de camino. Es el número de arcos que deben ser recorridos para llegar desde la raíz
al nodo X. Por definición la raíz tiene longitud de camino 1, y sus descendientes directos
longitud de camino 2 y así sucesivamente. En nuestro árbol de ejemplo G tiene longitud de
camino 3 y N tiene longitud de camino 4.
 Rama. Es el camino desde el nodo raíz a una hoja. En el ejemplo A-B-E-K y A-B-F son ramas.
A

B C D

E F G H I J

K L M N O

Figura 1. Árbol de grado tres

Los árboles tienen una gran variedad de aplicaciones. Por ejemplo, se pueden utilizar para representar
fórmulas matemáticas, para organizar adecuadamente la información, para construir un árbol
genealógico, para el análisis de circuitos eléctricos, para numerar los capítulos y secciones de un libro,
etc.

Bueno y como dijimos que lo que se vería aquí es árboles binarios pues yo creo que mejor nos vamos
centrando en estos…

Árboles binarios
Los árboles de orden dos son bastante especiales. Estos árboles se conocen también como árboles
binarios, ya que cada nodo del árbol no tendrá más de dos descendientes directos.

Algunas definiciones pueden ser:


1. Un Árbol binario es un árbol de grado 2
2. Un Árbol binario es aquel que:
a. Es vacío, ó
b. Esta formado por un nodo cuyos subárboles izquierdo y derecho son a su vez árboles
binarios.

E R

A G O V

U
Figura 2. Ejemplo de un árbol binario

En alguna de las definiciones del inicio del documento, la tercera para ser precisos, se utiliza la
recursión para definir un árbol porque representa la forma más apropiada y porque además es una
característica inherente de los mismos. En cualquier árbol, no sólo en los binarios, si eliminamos el
nodo raíz, obtenemos dos árboles; en el caso de que sea un árbol binario. Aquel que colgaba del enlace
izquierdo del nodo raíz se denomina subárbol izquierdo y aquel que colgaba del enlace derecho se
denomina subárbol derecho. Además, en un árbol binario, todos los subárboles son también árboles
binarios.
De hecho, a partir de cualquier nodo de un árbol podemos definir un nuevo árbol sin más que
considerarlo como su nodo raíz. Por tanto, cada nodo tiene asociados un subárbol derecho y uno
izquierdo.

Existen dos tipos de árboles binarios que se consideran especiales en función de ciertas propiedades.
Estos son los siguientes:
1. Árbol binario equilibrado
2. Árbol binario completo

Árbol binario equilibrado


Es un árbol en el que en todos sus nodos se cumple la siguiente propiedad:

| altura(subárbol_izquierdo) – altura(subárbol_derecho) | ≤ 1

E R N

A O V E

N Q A

Figura 3. Árbol equilibrado Figura 4. Árbol NO equilibrado

Árbol binario completo


Es un árbol en el que todos los nodos tienen dos hijos y todas las hojas están en el mismo nivel. Se
denomina completo porque cada nodo, excepto las hojas, tiene el máximo de hijos que puede tener.
N

E R

A G O V

Figura 5. Árbol binario completo

Recorridos por árboles


Una de las cosas que debemos poder hacer con un árbol es poder movernos a través de la información
que este contiene. El modo evidente de moverse a través de las ramas de un árbol es siguiendo las
referencias de los nodos que las componen. Los recorridos dependen en gran medida del tipo y
propósito del árbol, pero hay ciertos recorridos que usaremos frecuentemente. Se trata de aquellos
recorridos que incluyen todo el árbol.
Hay tres formas de recorrer un árbol completo, y las tres se suelen implementar mediante recursividad.
En los tres casos se sigue siempre a partir de cada nodo todas las ramas una por una.
Estos tres casos son:
- Pre-orden
- In-orden
- Post-orden

Recorrido Pre-orden (N – I – D)
En este recorrido lo primero que se obtiene o evalúa es el nodo, antes de recorrer las ramas;
posteriormente se recorre la rama izquierda y finalmente la rama derecha. El orden es Nodo – Izquierda
– Derecha (N – I – D).
El recorrido Pre-orden de nuestro árbol de la Figura 3 es: M – E – A – R – O – N – Q – V
Y el recorrido Pre-orden del árbol de la Figura 5 es: N – E – A – G – R – O – V

Recorrido In-orden (I – N – D)
En este recorrido se procesa la primera rama (rama izquierda), después el nodo y finalmente la ultima
rama (rama derecha). El orden es: Izquierda – Nodo – Derecha (I – N – D).
El recorrido In-orden de nuestro árbol de la Figura 3 es: A – E – M – N – O – Q – R – V
Y el recorrido In-orden del árbol de la Figura 5 es: A – E – G – N – O – R – V

Recorrido Post-orden (I – D – R)
En este recorrido se primero se procesan las ramas para finalmente procesar el nodo. El orden de este
recorrido es: Izquierda – Derecha – Nodo (I – D – N).
El recorrido Post-orden del árbol de la Figura 3 es: A – E – N – Q – O – V – R – M
Y el recorrido Post-orden del árbol de la Figura 5 es: A – G – E – O – V – R – N

Y como se que a la mayoría de nosotros no nos agrada mucho la teoría, aunque sea muy necesaria ,
pues vamos viendo algo práctico.

Implementaciones del Árbol binario


Para poder implementar un árbol binario primero debemos definir la estructura de los nodos. Cada
nodo del árbol estará formado por tres partes:
• La información contenida en el nodo, la cual puede ser un tipo primitivo o puede ser
cualquier tipo de objeto
• Un enlace al hijo derecho (raíz del subárbol derecho)
• Un enlace al hijo izquierdo (raíz del subárbol izquierdo)

Gráficamente:
NODO

Información

Izq Der

Figura 6. Nodo de un árbol binario

Frecuentemente, aunque tampoco es estrictamente necesario, para hacer más fácil moverse a través del
árbol, añadiremos un puntero a cada nodo que apunte al nodo padre. De este modo podremos avanzar
en dirección a la raíz, y no sólo hacia las hojas. Si implementamos este puntero nuestro nodo estaría
compuesto por cuatro partes:
• Un enlace al padre del nodo
• La información contenida en el nodo, la cual puede ser un tipo primitivo o puede ser
cualquier tipo de objeto
• Un enlace al hijo derecho (raíz del subárbol derecho)
• Un enlace al hijo izquierdo (raíz del subárbol izquierdo)

Gráficamente:
NODO

Padre

Información

Izq Der

Figura 7. Nodo de un árbol binario con referencia hacia su padre

Los árboles pueden ser construidos con estructuras estáticas y dinámicas. Las estáticas son arreglos,
registros y conjuntos, mientras que las dinámicas están representadas por listas.

Implementación estática de un árbol binario mediante un vector


Para realizar la implementación estática del árbol binario utilizamos una estrategia en la que simulamos
la memoria dinámica mediante el uso de un vector. Cada registro contendrá un nodo con los cuatro
campos especificados anteriormente, en la Figura 7, y todos los nodos se almacenan en un vector.
Para implementar los enlaces, en lugar de apuntadores, utilizaremos números enteros que serán los
índices en el vector donde se encuentran: el padre, el hijo izquierdo y el hijo derecho.

Una representación de nuestra clase Nodo sería la siguiente:

org.neos.arboles.binarios.estaticos.bean::Nodo
-informacion : Object
-padre : int
-derecho : int
-izquierdo : int

Figura 8. Clase Nodo

Y su definición en Java sería la siguiente:

package org.neos.arboles.binarios.estaticos.beans;

public class Nodo {


private int informacion;
private int padre;
private int derecho;
private int izquierdo;

public Nodo() {
this.informacion = -1;
this.derecho = -1;
this.izquierdo = -1;
}

public Nodo(int informacion) {


this.informacion = informacion;
this.derecho = -1;
this.izquierdo = -1;
}

public int getDerecho() {


return derecho;
}

public int getIzquierdo() {


return izquierdo;
}

public int getInformacion() {


return informacion;
}

public void setDerecho(int derecho) {


this.derecho = derecho;
}

public void setIzquierdo(int izquierdo) {


this.izquierdo = izquierdo;
}

public void setInformacion(int informacion) {


this.informacion = informacion;
}

public boolean equals(Object nodo) {


Nodo _nodo = (Nodo)nodo;
Object info_nodo = _nodo.getInformacion();

return this.informacion.equals(info_nodo);
}
}

Como vemos nuestro nodo es una clase muy simple la cual tiene cuatro variables de instancia y los
métodos get y set de estas. Adicional a los métodos get y set contiene un método que nos será útil para
saber si nuestro nodo es igual a otro nodo, este método es: “equals(Object)”, el cual estamos
sobrescribiendo del método que proporciona Object.

Ya tenemos la clase con la cual podemos crear instancias de nuestros nodos, así que ahora vamos a
implementar la clase que representará a nuestro árbol…

Ahh pero antes de eso… Además utilizaremos una especie de Wrapper para la información que
contendrá cada nodo del árbol. Pero los nodos no contendrán objetos del Wrapper este solo nos será útil
para cuando hagamos comparaciones entre la información de los nodos.
Perdón, pero fue la única forma que se me ocurrió para hacer independiente la información de la
estructura del árbol. Así no tenemos que modificar el código del árbol si queremos que este almacene
otro tipo de información… Aunque tendíamos que redefinir nuestro Wrapper 

Bueno pues pasemos a ver nuestro Wrapper para la información que almacenará nuestro árbol. Para
este ejemplo solo vamos a almacenar cadenas (pero solo para poder representar árboles como los que
vimos en los ejemplos de la teoría). De modo que quedaría de la siguiente forma.

org.neos.arboles.binarios.estaticos.bean::Informacion
-contenido : String
+equals(in info : Object) : Boolean
+compareTo(in info : Object) : int

Figura 9. Wrapper para la información que almacenará el árbol

Teóricamente, para poder trabajar con otro tipo de información lo único que debemos cambiar es el
tipo de la propiedad “contenido” y hacer que compareTo se haga la comparación de forma correcta y
en el constructor y el set de la información se haga el cast al tipo correcto, con respecto al tipo de la
variable “contenido”, ya lo único que no sabe el árbol es como comparar la información que
contendrán sus nodos, puesto que esta puede ser de cualquier tipo.

El árbol que implementaremos permitirá las siguientes operaciones:


- insertar elementos
- borrar elementos
- recorrer el árbol (preorden, inorden y postorden)
- Obtener el peso del árbol o una rama (mediante la posición del nodo, en el vector)
- Obtener el número de nodos del árbol o una rama (mediante la posición del nodo, en el vector)
- Obtener la altura de árbol o una rama (mediante la posición del nodo, en el vector)

Además será un árbol ordenado; es decir, el hijo izquierdo de un nodo debe ser menor y el hijo derecho
debe ser mayor al hijo izquierdo y al nodo.

El campo que es importante conservar siempre es el nodo raíz, ya que es el nodo a partir del cual se
desarrolla el árbol y si perdemos este nodo perdemos el acceso a todo el árbol. En nuestro caso
tendremos dos variables más dentro de nuestro árbol: el vector en el que se almacenarán los nodos de
nuestro árbol y una arreglo (ArrayList) en el que almacenaremos los recorridos del árbol. De modo que
la clase que representa nuestro árbol quedará de la siguiente forma:
org.neos.arboles.binarios.estaticos::ArbolBinarioEstatico
-arbol : Nodo[]
-posicion_raiz : int
-recorrido : ArrayList
+ArbolBinarioEstatico(in num_elementos : int) : Nodo
+esArbolVacio() : Boolean
+obtenerPosicionRaiz() : int
-obtenerPosicionElemento(in info : String, in posicion _actual : int) : int
+obtenerPosicionElemento(in info : String) : int
+existeElemento(in info : String) : Boolean
+obtenerElemento(in info : String) : ArbolBinarioEstatico
+obtenerNumeroElementos(in posicion : int) : int
+obtenerPeso(in raiz : int) : int
+obtenerAltura(in raiz : int) : int
+obtenerCapacidadArbol() : int
-obtenerPosicionLibre() : int
-insertarElemento(in nuevo_nodo : ArbolBinarioEstatico, in posicion_actual : int) : Boolean
-insertarElemento(in info : String, in posicion _actual : int) : Boolean
+insertarElemento(in info : String) : Boolean
-borrarNodoHoja(in nodo : ArbolBinarioEstatico, in posicion _nodo : int) : Boolean
-borrarNodoInterior(in nodo : ArbolBinarioEstatico, in posicion _nodo : int) : Boolean
-borrarNodoRaiz(in nodo : ArbolBinarioEstatico, in posicion_nodo : int) : Boolean
+borrarElemento(in info : String) : Boolean
+recorridoPreorden(in posicion : int) : ArrayList
+recorridoInorden(in posicion : int) : ArrayList
+recorridoPostorden(in posicion : int) : ArrayList

Figura 9. Clase para el árbol contenido en un vector

Como podemos darnos cuenta nuestro árbol tiene varios métodos, donde algunos de estos son privados
y otros públicos. A continuación veremos una descripción de estos métodos:
Métodos públicos
public ArbolBinarioEstatico(int num_elementos) throws ExceptionDimensionInvalida
Constructor que permite crear un árbol contenido en un vector de un tamaño definido.
Parámetros:
- num_elementos: Número de elementos que podrá contener el vector.
Throws:
- ExceptionDimensionInvalida: En caso de que el tamaño del vector no sea valido.

public boolean esArbolVacio()


Método útil para preguntar es nuestro árbol esta vacío o no.
Return:
- true cuando el árbol no contiene elementos.
- false cuando el árbol contiene elementos.

public int obtenerPosicionRaiz()


Obtener la posición del nodo raíz, dentro del vector.
Return:
Posición del elemento que representa al nodo raíz. Se obtiene -1 (NULL) si el árbol esta vacío.

public int obtenerPosicionElemento(Object info) throws ArrayIndexOutOfBoundsException


Buscar un elemento en base a la información que contienen los elementos del árbol.
Parámetros:
- info: Información que contiene el elemento que se esta buscando.
Return:
Posición del elemento del vector que contiene la información indicada.
Throws:
- ArrayIndexOutOfBoundsException: Si la posición del elemento a buscar es -1. El nodo al que se apunta (ya sea izquierdo o
derecho) antes de llamar a esta función es nulo (NULL).

public boolean existeElemento(Object info)


Obtener respuesta a la pregunta ¿Existe el elemento en el árbol?
Parámetros:
- Información: a buscar dentro del árbol.
Return:
- true cuando un elemento en el árbol contiene la información especificada.
- false cuando ningún elemento en el árbol contiene la información especificada.
public Nodo obtenerElemento(Object info)
Obtener el elemento (Nodo) que contiene la información indicada.
Parámetros:
- info: Información que se busca dentro del árbol.
Return:
- elemento que contiene la infoemación, cuando la información existe dentro del árbol.
- null, cuando la información especificada no existe dentro del árbol.

public int obtenerNumeroElementos(int posicion)


Obtener el número de elementos que contiene una rama especificada.
Parámetros:
- posicion: Posición del elemento para el que se cuenta el número de nodos que cuelgan de él.
Return:
Número de nodos que cuelgan del nodo indicado.

public int obtenerPeso(int raiz)


Obtener el peso del árbol o una rama especificada. Que sería algo así como contar el número de nodos que cuelgan a la izquierda y
derecha del nodo.
Parámetros:
- raiz: Posición, dentro del vector, del nodo que se toma como raíz.
Return:
Peso del árbol o rama especificada.

public int obtenerAltura(int raiz)


Obtener la altura de un árbol o rama de un árbol binario. La altura de un árbol se define como el nivel del nodo de mayor nivel
Parámetros:
- raiz: Posición del elemento que es la raíz del árbol o subárbol.
Return:
Altura del árbol o rama.

public int obtenerCapacidadArbol()


Obtener el número de elementos que puede almacenar el árbol.
Return
Número de elementos que puede almacenar el árbol.

public boolean insertarElemento(Object info) throws ExceptionDesbordamientoVector, ExceptionElementoDuplicado


Comprobar si se puede crear e insertar un nuevo elemento con la información indicada.
Parámetros:
- info: Información que contendrá el nuevo elemento, si es que este puede ser insertado.
Return:
La respuesta a la pregunta ¿Se inserto el elemento?
Throws:
- ExceptionDesbordamientoVector: Si el vector que representa el árbol ya esta lleno.
- ExceptionElementoDuplicado: Si la información que se quiere insertar ya existe dentro del árbl.

public boolean borrarElemento(Object info) throws ExceptionNoEncontrado


Borrar un elemento del árbol.
Parámetros:
- info: Elemento que quiere eliminarse.
Return:
Respues ta a la pregunta ¿Se borro el elemento?
Throws:
- ExceptionNoEncontrado: Si el elemento especificado no existe dentro del árbol.

public String recorridoPreorden(int posicion)


Recorrido (Nodo, Izquierda, Derecha)
Parámetros:
- posicion: Posición a partir de la cual se comienza el recorrido.
Return:
Arreglo que contiene el recorrido del árbol.

public String recorridoInorden(int posicion)


Recorrido (Izquierda, Nodo, Derecha)
Parámetros:
- posicion: Posición apartir de la cual se comienza el recorrido.
Return:
Arreglo que contiene el recorrido del árbol.

public String recorridoPostorden(int posicion)


Recorrido (Izquierda, Derecha, Nodo)
Parámetros:
- posicion: Posición apartir de la cual se comienza el recorrido.
Return:
Arreglo que contiene el recorrido del árbol.

Métodos privados
private int obtenerPosicionElemento(Object info, int posicion_actual) throws ArrayIndexOutOfBoundsException
Buscar un elemento recorriendo los elementos del árbol.
Parámetros:
- info: Información que contiene el elemento que se esta buscando.
- posicion_actual: Posición del elemento actual, con el que se compara la información.
Return:
Posición del elemento del vector que contiene la información indicada.
Throws:
- ArrayIndexOutOfBoundsException: Si la posición del elemento a buscar es -1. El nodo al que se apunta (ya sea izquierdo o
derecho) antes de llamar a esta función es nulo (NULL).

private int obtenerPosicionLibre()


Buscar dentro del vector que representa el árbol una casilla que este libre o vacía.
Return:
Posición del vector que no contiene un nodo.

private boolean insertarElemento(Object info, int posicion_actual)


Método de apoyo a InsertarElemento(int). Este método se encarga de agregar el elemento dentro del vector que representa el árbol.
Parámetros:
- info: Información que contendrá el nuevo nodo.
- posicion_actual: Posición del nodo contra el que se compara la información.
Return:
La respuesta a la pregunta ¿Se inserto el elemento?

private boolean insertarElemento(Nodo nuevo_nodo, int posicion_actual)


Método de apoyo a InsertarElemento(int). Este método se encarga de agregar el elemento dentro del vector que representa el árbol.
Parámetros:
- nuevo_nodo: Infrmación que contendrá el nuevo nodo.
- posicion_actual: Posición del nodo contra el que se compara la información.
Return:
La respuesta a la pregunta ¿Se inserto el elemento?
private boolean borrarNodoInterior(Nodo nodo, int posicion_nodo)
Borrar un nodo interior, tiene por lo menos una rama y no es el noso raíz del árbol.
Parámetros:
- nodo: Nodo que se quiere borrar.
- posicion_nodo: Posición del nodo a borrar, en el vector.
Return:
Respuesta a la pregunta ¿Se borro el nodo?

private boolean borrarNodoHoja(Nodo nodo, int posicion_nodo)


Borrar un nodo que es una hoja.
Parámetros:
- nodo: Nodo a borrar.
- posicion_nodo: Posición, en el vector, del nodo a borrar.
Return:
Respuesta a la pregunta ¿Se borro el nodo?

private boolean borrarNodoRaiz(Nodo nodo, int posicion_nodo)


Borrar el nodo que es la raíz del árbol.
Parámetros:
- nodo: Nodo a borrar.
- posicion_nodo: Posición del nodo dentro del vector.
Return:
Respuesta a la pregunta ¿Se borro el nodo?

Definición de la clase que representa el árbol:

package org.neos.arboles.binarios.estaticos;

import java.util.ArrayList;

import org.neos.arboles.binarios.estaticos.beans.Informacion;
import org.neos.arboles.binarios.estaticos.beans.Nodo;
import org.neos.arboles.binarios.estaticos.constantes.ConstantesArbolBinarioEstatico;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionDesbordamientoVector;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionDimensionInvalida;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionElementoDuplicado;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionNoEncontrado;

public class ArbolBinarioEstatico implements ConstantesArbolBinarioEstatico {


private Nodo[] arbol;
private int posicion_raiz;
private ArrayList recorrido;

public ArbolBinarioEstatico(int num_elementos) throws ExceptionDimensionInvalida {


if(num_elementos <= 0) {
String msg = "La dimension no puede ser cero o negativa!!";
throw new ExceptionDimensionInvalida(msg);
} else {
this.arbol = new Nodo[num_elementos];
}
this.posicion_raiz = NULL;
this.recorrido = null;
}

public boolean esArbolVacio() {


return (NULL == posicion_raiz) ? true : false;
}

public int obtenerPosicionRaiz() {


return posicion_raiz;
}

private int obtenerPosicionElemento(Object info, int posicion_actual) throws ArrayIndexOutOfBoundsException


{
Nodo nodo = arbol[posicion_actual];
Informacion obj_info = new Informacion(info);
Informacion obj_info_pa = new Informacion( nodo.getInformacion() );

if( obj_info_pa.equals(obj_info) ) {
return posicion_actual;
} else if( obj_info_pa.compareTo(obj_info) > 0 ) {
return obtenerPosicionElemento(info, nodo.getIzquierdo());
} else {
return obtenerPosicionElemento(info, nodo.getDerecho());
}
}

public int obtenerPosicionElemento(Object info) throws ArrayIndexOutOfBoundsException {


Nodo nodo_raiz = arbol[posicion_raiz];
Informacion obj_info = new Informacion(info);
Informacion obj_info_r = new Informacion( nodo_raiz.getInformacion() );

if( obj_info_r.equals(obj_info) ) {
return posicion_raiz;
} else if( obj_info_r.compareTo(obj_info) > 0 ) {
return obtenerPosicionElemento(info, nodo_raiz.getIzquierdo());
} else {
return obtenerPosicionElemento(info, nodo_raiz.getDerecho());
}
}

public boolean existeElemento(Object info) {


boolean respuesta = false;
int posicion_elemento = -1;

try {
posicion_elemento = obtenerPosicionElemento(info);
respuesta = true;
} catch(ArrayIndexOutOfBoundsException aioob_e) {
respuesta = false;
}
return respuesta;
}

public Nodo obtenerElemento(Object info) {


int posicion_elemento = -1;
Nodo elemento = null;

try {
posicion_elemento = obtenerPosicionElemento(info);
elemento = arbol[posicion_elemento];
} catch(ArrayIndexOutOfBoundsException aioob_e) {
;
}

return elemento;
}

public int obtenerNumeroElementos(int posicion) {


int num_elems = 0;

if(NULL != posicion) {
Nodo nodo = arbol[posicion];
num_elems += obtenerNumeroElementos(nodo.getIzquierdo());
num_elems++;
num_elems += obtenerNumeroElementos(nodo.getDerecho());
}

return (num_elems);
}

public int obtenerPeso(int raiz) {


int peso = 0;
Nodo nodo = null;
int num_h_izq = 0;
int num_h_der = 0;

if(NULL != raiz) {
nodo = arbol[raiz];

num_h_izq = obtenerNumeroElementos(nodo.getIzquierdo());
num_h_der = obtenerNumeroElementos(nodo.getDerecho());

peso = num_h_izq + num_h_der;


}

return peso;
}

public int obtenerAltura(int raiz) {


int altura = 0;
Nodo nodo = null;
int ref_h_izq;
int ref_h_der;
int altura_r_izq = 0;
int altura_r_der = 0;

if(NULL != raiz) {
altura = 1;

nodo = arbol[raiz];
ref_h_izq = nodo.getIzquierdo();
ref_h_der = nodo.getDerecho();

if( (ref_h_izq != NULL) && (ref_h_der != NULL) ) {


altura_r_izq = obtenerAltura(ref_h_izq);
altura_r_der = obtenerAltura(ref_h_der);
if(altura_r_izq >= altura_r_der) {
altura += altura_r_izq;
} else {
altura += altura_r_der;
}
} else if( (ref_h_izq != NULL) && (ref_h_der == NULL) ) {
altura += obtenerAltura(ref_h_izq);
} else if( (ref_h_izq == NULL) && (ref_h_der != NULL) ) {
altura += obtenerAltura(ref_h_der);
} else if( (ref_h_izq == NULL) && (ref_h_der == NULL) ) {
altura -= 1;
}
}

return altura;
}

public int obtenerCapacidadArbol() {


return arbol.length;
}

private int obtenerPosicionLibre() {


int i, longitud = arbol.length;

for(i = 0; i < longitud; i++) {


if(null == arbol[i]) {
return i;
}
}
return NULL;
}

private boolean insertarElemento(Object info, int posicion_actual) {


Nodo nodo = arbol[posicion_actual];
Nodo nuevo_nodo = null;
int posicion = NULL;
Informacion obj_info = new Informacion(info);
Informacion obj_info_pa = new Informacion( nodo.getInformacion() );

if( obj_info_pa.compareTo(obj_info) > 0 ) {


if(nodo.getIzquierdo() == NULL) {
posicion = obtenerPosicionLibre();
nodo.setIzquierdo(posicion);

nuevo_nodo = new Nodo(info);


nuevo_nodo.setPadre(posicion_actual);
arbol[posicion] = nuevo_nodo;

return true;
} else {
return insertarElemento(info, nodo.getIzquierdo());
}
} else {
if(nodo.getDerecho() == NULL) {
posicion = obtenerPosicionLibre();
nodo.setDerecho(posicion);

nuevo_nodo = new Nodo(info);


nuevo_nodo.setPadre(posicion_actual);
arbol[posicion] = nuevo_nodo;

return true;
} else {
return insertarElemento(info, nodo.getDerecho());
}
}
}

public boolean insertarElemento(Object info)


throws ExceptionDesbordamientoVector, ExceptionElementoDuplicado {

Nodo nuevo_nodo = null;


boolean se_inserto = false;

if( this.esArbolVacio() ) {
nuevo_nodo = new Nodo(info);
arbol[0] = nuevo_nodo;

this.posicion_raiz = 0;
se_inserto = true;
} else {
if(obtenerNumeroElementos(posicion_raiz) == arbol.length) {
throw new ExceptionDesbordamientoVector("El árbol esta lleno, no pueden agregarse
más elementos!!");
} else {
if( existeElemento(info) ) {
throw new ExceptionElementoDuplicado("El elemento ya existe dentro del
árbol!!");
} else {
se_inserto = insertarElemento(info, posicion_raiz);
}
}
}

return se_inserto;
}

public boolean borrarElemento(Object info) throws ExceptionNoEncontrado {


Nodo nodo = null;
int posicion_elem = NULL;
boolean se_borro = false;
Nodo nodo_padre = null;
Nodo nodo_aux = null;
int posicion_aux = NULL;

if( this.esArbolVacio() ) {
throw new ExceptionNoEncontrado("El árbol esta vacío!!");
} else {
if( existeElemento(info) ) {
posicion_elem = obtenerPosicionElemento(info);
nodo = arbol[posicion_elem];

if(nodo.getPadre() != NULL) { // Si no es el nodo raiz


if( (nodo.getIzquierdo() != NULL) || (nodo.getDerecho() != NULL) ) {
se_borro = borrarNodoInterior(nodo, posicion_elem);
} else {
se_borro = borrarNodoHoja(nodo, posicion_elem);
}
} else {
se_borro = borrarNodoRaiz(nodo, posicion_elem);
}
} else {
throw new ExceptionNoEncontrado("El elemento no existe dentro del árbol!!");
}
}

return se_borro;
}

private boolean insertarElemento(Nodo nuevo_nodo, int posicion_actual) {


Nodo nodo = arbol[posicion_actual];
int posicion = NULL;
Informacion obj_info = new Informacion( nuevo_nodo.getInformacion() );
Informacion obj_info_pa = new Informacion( nodo.getInformacion() );

if( obj_info_pa.compareTo(obj_info) > 0 ) {


if(nodo.getIzquierdo() == NULL) {
posicion = obtenerPosicionLibre();
nodo.setIzquierdo(posicion);

nuevo_nodo.setPadre(posicion_actual);
arbol[posicion] = nuevo_nodo;

return true;
} else {
return insertarElemento(nuevo_nodo, nodo.getIzquierdo());
}
} else {
if(nodo.getDerecho() == NULL) {
posicion = obtenerPosicionLibre();
nodo.setDerecho(posicion);

nuevo_nodo.setPadre(posicion_actual);
arbol[posicion] = nuevo_nodo;

return true;
} else {
return insertarElemento(nuevo_nodo, nodo.getDerecho());
}
}
}

private boolean borrarNodoInterior(Nodo nodo, int posicion_nodo) {


boolean respuesta = false;
Nodo nodo_padre = null;
Nodo nodo_aux = null;
int posicion_aux = NULL;
int posicion_ins = NULL;

if( (nodo.getIzquierdo() != NULL) && (nodo.getDerecho() != NULL) ) {


nodo_padre = arbol[nodo.getPadre()];
posicion_aux = nodo.getIzquierdo();
nodo_aux = arbol[posicion_aux];
posicion_ins = nodo.getDerecho();

if(nodo_padre.getIzquierdo() == posicion_nodo) {
nodo_padre.setIzquierdo( nodo.getDerecho() );
} else {
nodo_padre.setDerecho( nodo.getDerecho() );
}

nodo = new Nodo(nodo_aux.getInformacion());


nodo.setDerecho(nodo_aux.getDerecho());
nodo.setIzquierdo(nodo_aux.getIzquierdo());

arbol[posicion_nodo] = null;
arbol[posicion_aux] = null;
insertarElemento(nodo, posicion_ins);
respuesta = true;
} else if(nodo.getIzquierdo() != NULL) {
nodo_padre = arbol[nodo.getPadre()];

if(posicion_nodo == nodo_padre.getDerecho()) {
nodo_padre.setDerecho( nodo.getIzquierdo() );
} else {
nodo_padre.setIzquierdo( nodo.getIzquierdo() );
}

arbol[posicion_nodo] = null;
respuesta = true;
} else {
nodo_padre = arbol[nodo.getPadre()];

if(posicion_nodo == nodo_padre.getDerecho()) {
nodo_padre.setDerecho( nodo.getDerecho() );
} else {
nodo_padre.setIzquierdo( nodo.getDerecho() );
}

arbol[posicion_nodo] = null;
respuesta = true;
}
return respuesta;
}

private boolean borrarNodoHoja(Nodo nodo, int posicion_nodo) {


boolean se_borro = false;
Nodo nodo_padre = null;

nodo_padre = arbol[nodo.getPadre()];
if(posicion_nodo == nodo_padre.getDerecho()) {
nodo_padre.setDerecho(NULL);
} else {
nodo_padre.setIzquierdo(NULL);
}
arbol[posicion_nodo] = null;
se_borro = true;

return se_borro;
}

private boolean borrarNodoRaiz(Nodo nodo, int posicion_nodo) {


boolean se_borro = false;
Nodo nodo_aux = null;
int posicion_aux = NULL;

if( (nodo.getIzquierdo() != NULL) && (nodo.getDerecho() != NULL) ) {


this.posicion_raiz = nodo.getDerecho();
nodo_aux = arbol[posicion_raiz];
nodo_aux.setPadre(NULL);

posicion_aux = nodo.getIzquierdo();
nodo_aux = arbol[posicion_aux];

nodo = new Nodo(nodo_aux.getInformacion());


nodo.setDerecho(nodo_aux.getDerecho());
nodo.setIzquierdo(nodo_aux.getIzquierdo());

arbol[posicion_nodo] = null;
arbol[posicion_aux] = null;
insertarElemento(nodo, posicion_raiz);
se_borro = true;
} else if(nodo.getIzquierdo() != NULL) {
this.posicion_raiz = nodo.getIzquierdo();
nodo_aux = arbol[posicion_raiz];
nodo_aux.setPadre(NULL);
arbol[posicion_nodo] = null;
se_borro = true;
} else if(nodo.getDerecho() != NULL) {
this.posicion_raiz = nodo.getDerecho();
nodo_aux = arbol[posicion_raiz];
nodo_aux.setPadre(NULL);

arbol[posicion_nodo] = null;
se_borro = true;
} else { // no tiene ramas
arbol[posicion_raiz] = null;
posicion_raiz = NULL;
se_borro = true;
}
return se_borro;
}

public ArrayList recorridoPreorden(int posicion) {


if(posicion == posicion_raiz) {
recorrido = new ArrayList();
}

if(NULL != posicion) {
Nodo nodo = arbol[posicion];
recorrido.add(nodo.getInformacion());
recorridoPreorden(nodo.getIzquierdo());
recorridoPreorden(nodo.getDerecho());
}
return recorrido;
}

public ArrayList recorridoInorden(int posicion) {


if(posicion == posicion_raiz) {
recorrido = new ArrayList();
}

if(NULL != posicion) {
Nodo nodo = arbol[posicion];
recorridoInorden(nodo.getIzquierdo());
recorrido.add(nodo.getInformacion());
recorridoInorden(nodo.getDerecho());
}
return recorrido;
}

public ArrayList recorridoPostorden(int posicion) {


if(posicion == posicion_raiz) {
recorrido = new ArrayList();
}

if(NULL != posicion) {
Nodo nodo = arbol[posicion];
recorridoPostorden(nodo.getIzquierdo());
recorridoPostorden(nodo.getDerecho());
recorrido.add(nodo.getInformacion());
}
return recorrido;
}
}

Y un pequeño ejemplo en el que se muestra las funciones más importantes de nuestro árbol:

package org.neos.arboles.binarios.estaticos;

import java.util.ArrayList;

import org.neos.arboles.binarios.estaticos.exceptions.ExceptionDesbordamientoVector;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionDimensionInvalida;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionElementoDuplicado;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionNoEncontrado;

public class Ejemplo {

public static void main(String[] args) throws ExceptionDimensionInvalida {


ArbolBinarioEstatico arbol = new ArbolBinarioEstatico(27);
ArrayList recorrido = null;
int i, num_elems;

try {
arbol.insertarElemento("G");
arbol.insertarElemento("E");
arbol.insertarElemento("N");
arbol.insertarElemento("D");
arbol.insertarElemento("J");
arbol.insertarElemento("F");
arbol.insertarElemento("O");
arbol.insertarElemento("Q");

recorrido = arbol.recorridoPreorden(arbol.obtenerPosicionRaiz());
System.out.print("Recorrido Preorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print(recorrido.get(i) + " - ");
}
recorrido = arbol.recorridoInorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Inorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print(recorrido.get(i) + " - ");
}
recorrido = arbol.recorridoPostorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Postorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print(recorrido.get(i) + " - ");
}

System.err.println("\nNumero de elementos:
"+arbol.obtenerNumeroElementos(arbol.obtenerPosicionRaiz()));
System.err.println("Peso del árbol: "+arbol.obtenerPeso(arbol.obtenerPosicionRaiz()));
System.err.println("Altura del árbol: "+arbol.obtenerAltura(arbol.obtenerPosicionRaiz()));

// Borrar un elemento de nustro árbol (un nodo interior)


arbol.borrarElemento("N");

recorrido = arbol.recorridoPreorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Preorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print(recorrido.get(i) + " - ");
}
recorrido = arbol.recorridoInorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Inorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print(recorrido.get(i) + " - ");
}
recorrido = arbol.recorridoPostorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Postorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print(recorrido.get(i) + " - ");
}

System.err.println("\nNumero de elementos:
"+arbol.obtenerNumeroElementos(arbol.obtenerPosicionRaiz()));
System.err.println("Peso del árbol: "+arbol.obtenerPeso(arbol.obtenerPosicionRaiz()));
System.err.println("Altura del árbol: "+arbol.obtenerAltura(arbol.obtenerPosicionRaiz()));
} catch(ExceptionElementoDuplicado ed_e) {
System.err.println(ed_e.getMessage());
} catch(ExceptionDesbordamientoVector dv_e) {
System.err.println(dv_e.getMessage());
} catch(ExceptionNoEncontrado ne_e) {
System.err.println(ne_e.getMessage());
}
}
}

Con las instrucciones del inicio de nuestro pequeño ejemplo (hasta antes de la marca de borrar un elemento) se crea un árbol como el
mostrado en la siguiente figura:

E N

D F J O

Figura 10. Árbol construido con el programa ejemplo.


Y las instrucciones “System.out.print” que están antes de la marca de borrar elemento nos ayudan a comprobar que lo que se muestra en la
Figura 11 es cierto. Lo que deben desplegar las instrucciones hasta antes de la marca de borrar elemento es:
Recorrido Preorden: G - E - D - F - N - J - O - Q -
Recorrido Inorden: D - E - F - G - J - N - O - Q -
Recorrido Postorden: D - F - E - J - Q - O - N - G –
Numero de elementos: 8
Peso del árbol: 7
Altura del árbol: 3
Después de la marca borrar elemento, la estructura de nuestro árbol cambia a la mostrada en la
siguiente figura:

E O

D F J Q

Figura 12. Árbol después de borrar el elemento “N”


del árbol de la Figura 11.

Y las instrucciones “System.out.print” que están despues de la marca de borrar elemento nos ayudan a
comprobar que lo que se muestra en la Figura 12 es cierto. Lo que deben desplegar las instrucciones
despues de la marca de borrar elemento es:
Borrar elemento "N":

Recorrido Preorden: G - E - D - F - O - J - Q -
Recorrido Inorden: D - E - F - G - J - O - Q -
Recorrido Postorden: D - F - E - J - Q - O - G –
Numero de elementos: 7
Peso del árbol: 6
Altura del árbol: 2

Y como fue a mí a quien se le ocurrió la brillante idea de poner el Wrapper para la información que
contienen los nodos pues vamos a ver un ejemplo en donde nuestro nodo va a contener un objeto
Persona, el cual vamos a definir aquí. La definición de nuestra clase persona sería la que se muestra en
la Figura 13.

org.neos.beans::Persona
-nombre : Nombre
-direccion : Direccion
-telefono : String

Figura 13. Clase Persona

Esta clase esta compuesta por un nombre (clase org.beans.Nombre), una dirección (clase
org.neos.beans.Direccion) y un atributo cadena llamado telefono.

El código de estas clases es:

Clase org.neos.beans.Nombre:
package org.neos.beans;

public class Nombre {


private String nombre;
private String paterno;
private String materno;

public Nombre(String nombre, String paterno, String materno) {


this.nombre = nombre;
this.paterno = paterno;
this.materno = materno;
}
public String getMaterno() {
return materno;
}
public String getNombre() {
return nombre;
}
public String getPaterno() {
return paterno;
}
public void setMaterno(String string) {
materno = string;
}
public void setNombre(String string) {
nombre = string;
}
public void setPaterno(String string) {
paterno = string;
}

public boolean equals(Object nombre) {


Nombre _nombre = (Nombre)nombre;

if( this.nombre.equals(_nombre.getNombre() )
&& paterno.equals(_nombre.getPaterno() )
&& materno.equals(_nombre.getMaterno() ) ) {
return true;
} else {
return false;
}
}

public int compareTo(Object nombre) {


Nombre _nombre = (Nombre)nombre;
String nom = _nombre.getNombre();
String a_pat = _nombre.getPaterno();
String a_mat = _nombre.getMaterno();

int com_ap = paterno.compareTo(a_pat);

if(com_ap == 0) {
com_ap = materno.compareTo(a_mat);
if(com_ap == 0) {
return this.nombre.compareTo(nom);
} else {
return com_ap;
}
} else {
return com_ap;
}
}
}
Clase org.neos.beans.Direccion:
package org.neos.beans;

public class Direccion {


private String calle;
private int numero;
private String interior;
private String colonia;
private String poblacion;
private String cp;

public Direccion(String calle, int num, String interior,


String col, String pob, String cp) {
this.calle = calle;
this.numero = num;
this.interior = interior;
this.colonia = col;
this.poblacion = pob;
this.cp = cp;
}
public String getColonia() {
return colonia;
}
public String getCp() {
return cp;
}
public String getInterior() {
return interior;
}
public int getNumero() {
return numero;
}
public String getPoblacion() {
return poblacion;
}
public void setColonia(String string) {
colonia = string;
}
public void setCp(String string) {
cp = string;
}
public void setInterior(String string) {
interior = string;
}
public void setNumero(int i) {
numero = i;
}
public void setPoblacion(String string) {
poblacion = string;
}

Clase org.neos.beans.Direccion:
package org.neos.beans;

public class Persona {


private Nombre nombre;
private Direccion direccion;
private String telefono;

public Persona(Nombre nombre, Direccion direccion, String telefono) {


this.nombre = nombre;
this.direccion = direccion;
this.telefono = telefono;
}
public Direccion getDireccion() {
return direccion;
}
public Nombre getNombre() {
return nombre;
}
public String getTelefono() {
return telefono;
}
public void setDireccion(Direccion direccion) {
this.direccion = direccion;
}
public void setNombre(Nombre nombre) {
this.nombre = nombre;
}
public void setTelefono(String string) {
telefono = string;
}

public boolean equals(Object persona) {


Persona _persona = (Persona)persona;

return nombre.equals(_persona.getNombre() );
}

public int compareTo(Object persona) {


Persona _persona = (Persona)persona;

return nombre.compareTo(_persona.getNombre() );
}

public String toString() {


String respuesta = nombre.getPaterno() + " " + nombre.getMaterno() + ",
" + nombre.getNombre();

return respuesta;
}
}

Ahora lo único que tenemos que hacer es adaptar nuestra Clase


org.neos.arboles.binarios.estaticos.beans.Informacion para que su contenido sea un objeto de tipo
org.neos.beans.Persona:

package org.neos.arboles.binarios.estaticos.beans;

import org.neos.beans.Persona;

public class Informacion {


private Persona contenido;
public Informacion(Object contenido) {
this.contenido = (Persona)contenido;
}

public Object getContenido() {


return contenido;
}

public void setContenido(Object contenido) {


this.contenido = (Persona)contenido;
}

public boolean equals(Object info) {


Informacion _info = (Informacion)info;

if( contenido.equals(_info.getContenido()) ) {
return true;
} else {
return false;
}
}

public int compareTo(Object info) {


Informacion _info = (Informacion)info;

return contenido.compareTo(_info.getContenido());
}
}

Como podemos darnos cuenta lo que tenemos que hacer si queremos cambiar el contenido de nuestro
árbol es definir la clase que nos dará la estructura de la información que queremos contener en nuestro
árbol, junto con sus métodos equals(Object) y compareTo(Object) (así como se hizo aquí con persona).
Y ya que se tenga definida la clase de nuestra información se debe importar esta en la clase
org.neos.arboles.binarios.estaticos.beans.Informacion y cambiar el tipo de nuestro contenido y el cast
de este (Lo que se encuentra marcado con amarillo en el codigo de nuestra clase Informacion) y nuestro
árbol debe seguir funcionando.

Ya que se ha definido la clase que define la información que va a contener el árbol y como se compara
esta información (en org.neos.arboles.binarios.estaticos.beans.Informacion) podemos comenzar a usar
nuestro árbol. A continuación se muestra un pequeño ejemplo:

package org.neos.arboles.binarios.estaticos;

import java.util.ArrayList;

import
org.neos.arboles.binarios.estaticos.exceptions.ExceptionDesbordamientoVector;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionDimensionInvalida;
import org.neos.arboles.binarios.estaticos.exceptions.ExceptionElementoDuplicado;
import org.neos.beans.Direccion;
import org.neos.beans.Nombre;
import org.neos.beans.Persona;

public class Ejemplo {


public static void main(String[] args) throws ExceptionDimensionInvalida {
ArbolBinarioEstatico arbol = new ArbolBinarioEstatico(27);
ArrayList recorrido = null;
int i, num_elems;
Direccion direccion = null;
Nombre nombre = null;
Persona persona = null;

try {
direccion = new Direccion("AAA", 1, "B-1", "CCC", "DDD", "EEE");
nombre = new Nombre("Eugenio", "Flores", "Barrera");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Nayeli", "Vazquez", "Vasquez");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Hector", "Hidalgo", "Martinez");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Mario", "Hidalgo", "Matinez");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Miguel", "Flores", "Barrera");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);

recorrido = arbol.recorridoPreorden(arbol.obtenerPosicionRaiz());
System.out.print("Recorrido Preorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print((Persona)recorrido.get(i) + " - ");
}
recorrido = arbol.recorridoInorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Inorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print((Persona)recorrido.get(i) + " - ");
}
recorrido =
arbol.recorridoPostorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Postorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print((Persona)recorrido.get(i) + " - ");
}

System.err.println("\nNumero de elementos:
"+arbol.obtenerNumeroElementos(arbol.obtenerPosicionRaiz()));
System.err.println("Peso del árbol:
"+arbol.obtenerPeso(arbol.obtenerPosicionRaiz()));
System.err.println("Altura del árbol:
"+arbol.obtenerAltura(arbol.obtenerPosicionRaiz()));
} catch(ExceptionElementoDuplicado ed_e) {
System.err.println(ed_e.getMessage());
} catch(ExceptionDesbordamientoVector dv_e) {
System.err.println(dv_e.getMessage());
}
}
}
Con las instrucciones del inicio de nuestro pequeño ejemplo se crea un árbol como el mostrado en la
siguiente figura:

Flores Barrera,
Eugenio

Vazquez
Vasquez,
Nayeli

Hidalgo
Martinez,
Hugo

Hidalgo
Flores Barrera,
Martinez,
Miguel
Mario

Figura 14. Árbol construido con el ejemplo anterior

Y para demostrar que lo que muestra la figura es cierto. Lo que debe desplegar nuestro pequeño
ejemplo es lo siguiente:

Recorrido Preorden: Flores Barrera, Eugenio - Vazquez Vasquez, Nayeli - Hidalgo


Martinez, Hector - Flores Barrera, Miguel - Hidalgo Matinez, Mario -
Recorrido Inorden: Flores Barrera, Eugenio - Flores Barrera, Miguel - Hidalgo
Martinez, Hector - Hidalgo Matinez, Mario - Vazquez Vasquez, Nayeli -
Recorrido Postorden: Flores Barrera, Miguel - Hidalgo Matinez, Mario - Hidalgo
Martinez, Hector - Vazquez Vasquez, Nayeli - Flores Barrera, Eugenio -
Numero de elementos: 5
Peso del árbol: 4
Altura del árbol: 3

Y con esto terminamos la implementación de nuestro árbol dentro de un vector…


Ya solo resta que ustedes vean si les es útil e implementarlo.

Implementación dinámica de un árbol binario

Si añadimos a los árboles la restricción de que cada elemento puede tener un solo posterior, llegamos a
las estructuras lineales, y más concretamente a las listas. Así pues, las listas no son más que un caso
particular de los árboles. En este punto, si añadimos ciertas restricciones de acceso a las listas llegamos
a las colas o a las pilas. Por lo tanto, colas pilas, son tipos particulares de listas. Así que se han
implementado una lista dinámica lo que vamos a ver debe ser fácil. Y como vamos a reciclar el código
que generamos para el árbol dentro del vector será más parecido a una lista doblemente ligada.

La estructura de nuestro nodo sigue siendo la misma que la de la Figura 7 y el mostrado en la siguiente
figura:

NODO

Padre

Información

Izq Der

Figura 15. Nodo de un árbol binario con referencia hacia su padre

Solo que en este caso las referencias hacia el padre, el hijo izquierdo y el hijo derecho son apuntadores
hacia objetos (objetos nodo iguales al mostrado en la figura), de modo que al formar nuestro árbol
dinámico se armaría una estructura más o menos parecida a la mostrada en la figura:

Padre

Información

Izq Der

Padre Padre

Información Información

Izq Der Izq Der

Figura 16. Estructura del árbol dinámico

Bueno, pues entonces vamos a ver la definición en Java de la clase que representará nuestro árbol. En
esta ocasión no voy a describir los métodos ya que estamos reciclando el código que se genero para el
árbol en el vector, por lo tanto es muy parecido y creo que lo entenderán para que sirve cada método si
lo relacionan con los métodos del árbol visto anteriormente :-)
package org.neos.arboles.binarios.dinamicos;

import java.util.ArrayList;

import org.neos.arboles.binarios.dinamicos.beans.Informacion;
import org.neos.arboles.binarios.dinamicos.beans.Nodo;
import org.neos.arboles.binarios.dinamicos.exceptions.ExceptionElementoDuplicado;
import org.neos.arboles.binarios.dinamicos.exceptions.ExceptionNoEncontrado;

public class ArbolBinario {


private Nodo raiz;
private ArrayList recorrido;

public ArbolBinario() {
this.raiz = null;
this.recorrido = null;
}

public boolean esArbolVacio() {


return (null == raiz) ? true : false;
}

public Nodo obtenerPosicionRaiz() {


return raiz;
}

public boolean existeElemento(Object info) {


Nodo nodo = obtenerElemento(info, raiz);

if(null != nodo) {
return true;
} else {
return false;
}
}

public Nodo obtenerElemento(Object info, Nodo raiz) {


if(null == raiz) {
return raiz;
} else {
Informacion obj_info = new Informacion(info);
Informacion obj_info_pa = new Informacion( raiz.getInformacion() );
if( obj_info_pa.equals(obj_info) ) {
return raiz;
} else if( obj_info.compareTo(obj_info_pa) < 0 ) {
return obtenerElemento(info, raiz.getIzquierdo());
} else {
return obtenerElemento(info, raiz.getDerecho());
}
}
}

public int obtenerNumeroElementos(Nodo nodo) {


int num_elems = 0;

if(null != nodo) {
num_elems += obtenerNumeroElementos(nodo.getIzquierdo());
num_elems++; // contabilizar el nodo visitado
num_elems += obtenerNumeroElementos(nodo.getDerecho());
}

return num_elems;
}

public int obtenerPeso(Nodo nodo) {


int peso = 0;
int num_h_izq = 0;
int num_h_der = 0;

if(null != nodo) {
num_h_izq = obtenerNumeroElementos(nodo.getIzquierdo());
num_h_der = obtenerNumeroElementos(nodo.getDerecho());

peso = num_h_izq + num_h_der;


}

return peso;
}

public int obtenerAltura(Nodo nodo) {


int altura = 0;
Nodo ref_h_izq;
Nodo ref_h_der;
int altura_r_izq = 0;
int altura_r_der = 0;

if(null != nodo) {
altura = 1;

ref_h_izq = nodo.getIzquierdo();
ref_h_der = nodo.getDerecho();

if( (ref_h_izq != null) && (ref_h_der != null) ) {


altura_r_izq = obtenerAltura(ref_h_izq);
altura_r_der = obtenerAltura(ref_h_der);
if(altura_r_izq >= altura_r_der) {
altura += altura_r_izq;
} else {
altura += altura_r_der;
}
} else if( (ref_h_izq != null) && (ref_h_der == null) ) {
altura += obtenerAltura(ref_h_izq);
} else if( (ref_h_izq == null) && (ref_h_der != null) ) {
altura += obtenerAltura(ref_h_der);
} else if( (ref_h_izq == null) && (ref_h_der == null) ) {
altura -= 1;
}
}

return altura;
}

private boolean insertarElemento(Object info, Nodo nodo) {


Nodo nuevo_nodo = null;
Informacion obj_info = new Informacion(info);
Informacion obj_info_pa = new Informacion( nodo.getInformacion() );

if( obj_info.compareTo(obj_info_pa) < 0 ) {


if(nodo.getIzquierdo() == null) {
nuevo_nodo = new Nodo(info);
nuevo_nodo.setPadre(nodo);
nodo.setIzquierdo(nuevo_nodo);

return true;
} else {
return insertarElemento(info, nodo.getIzquierdo());
}
} else {
if(nodo.getDerecho() == null) {
nuevo_nodo = new Nodo(info);
nuevo_nodo.setPadre(nodo);
nodo.setDerecho(nuevo_nodo);

return true;
} else {
return insertarElemento(info, nodo.getDerecho());
}
}
}

public boolean insertarElemento(Object info) throws ExceptionElementoDuplicado {


boolean se_inserto = false;

if( this.esArbolVacio() ) {
this.raiz = new Nodo(info);

se_inserto = true;
} else {
if( existeElemento(info) ) {
throw new ExceptionElementoDuplicado("El elemento ya existe dentro del árbol!!");
} else {
se_inserto = insertarElemento(info, raiz);
}
}

return se_inserto;
}

public boolean borrarElemento(Object info) throws ExceptionNoEncontrado {


Nodo nodo = null;
boolean se_borro = false;

if( this.esArbolVacio() ) {
throw new ExceptionNoEncontrado("El árbol esta vacío!!");
} else {
if( existeElemento(info) ) {
nodo = obtenerElemento(info, raiz);

if(nodo.getPadre() != null) {
if( (nodo.getIzquierdo() != null) || (nodo.getDerecho() != null) ) {
se_borro = borrarNodoInterior(nodo);
} else {
se_borro = borrarNodoHoja(nodo);
}
} else {
se_borro = borrarNodoRaiz(nodo);
}
} else {
throw new ExceptionNoEncontrado("El elemento no existe dentro del árbol!!");
}
}

return se_borro;
}

private boolean borrarNodoInterior(Nodo nodo) {


boolean respuesta = false;
Nodo nodo_padre = nodo.getPadre();
Nodo nodo_izq = nodo.getIzquierdo();
Nodo nodo_der = nodo.getDerecho();
Nodo nodo_c = null;

if( (nodo.getIzquierdo() != null) && (nodo.getDerecho() != null) ) {


if(nodo_padre.getIzquierdo() == nodo) {
nodo_padre.setIzquierdo(nodo_der);
} else {
nodo_padre.setDerecho(nodo_der);
}

nodo_der.setPadre(nodo_padre);

nodo_c = new Nodo(nodo_izq.getInformacion());


nodo_c.setDerecho(nodo_izq.getDerecho());
nodo_c.setIzquierdo(nodo_izq.getIzquierdo());

nodo = null;
nodo_izq = null;
insertarElemento(nodo_c.getInformacion(), nodo_der);
respuesta = true;
} else if(nodo.getIzquierdo() != null) {
nodo_izq.setPadre(nodo_padre);

if(nodo_padre.getDerecho() == nodo) {
nodo_padre.setDerecho(nodo_izq);
} else {
nodo_padre.setIzquierdo(nodo_izq);
}
nodo = null;
respuesta = true;
} else {
nodo_der.setPadre(nodo_padre);

if(nodo_padre.getDerecho() == nodo) {
nodo_padre.setDerecho(nodo_der);
} else {
nodo_padre.setIzquierdo(nodo_der);
}
nodo = null;
respuesta = true;
}
return respuesta;
}

private boolean borrarNodoHoja(Nodo nodo) {


boolean se_borro = false;
Nodo nodo_padre = nodo.getPadre();

if(nodo_padre.getDerecho() == nodo) {
nodo_padre.setDerecho(null);
} else {
nodo_padre.setIzquierdo(null);
}
nodo = null;
se_borro = true;

return se_borro;
}

private boolean borrarNodoRaiz(Nodo nodo) {


boolean se_borro = false;
Nodo nodo_izq = nodo.getIzquierdo();
Nodo nodo_der = nodo.getDerecho();
Nodo nodo_c = null;

if( (nodo.getIzquierdo() != null) && (nodo.getDerecho() != null) ) {


this.raiz = nodo_der;
this.raiz.setPadre(null);

nodo_c = new Nodo(nodo_izq.getInformacion());


nodo_c.setDerecho(nodo_izq.getDerecho());
nodo_c.setIzquierdo(nodo_izq.getIzquierdo());

nodo = null;
nodo_izq = null;
insertarElemento(nodo_c, raiz);
se_borro = true;
} else if(nodo.getIzquierdo() != null) {
this.raiz = nodo_izq;
this.raiz.setPadre(null);

nodo = null;
se_borro = true;
} else if(nodo.getDerecho() != null) {
this.raiz = nodo_der;
this.raiz.setPadre(null);

nodo = null;
se_borro = true;
} else {
this.raiz = null;
se_borro = true;
}
return se_borro;
}

public ArrayList recorridoPreorden(Nodo nodo) {


if(nodo == raiz) {
recorrido = new ArrayList();
}

if(null != nodo) {
recorrido.add(nodo.getInformacion());
recorridoPreorden(nodo.getIzquierdo());
recorridoPreorden(nodo.getDerecho());
}
return recorrido;
}

public ArrayList recorridoInorden(Nodo nodo) {


if(nodo == raiz) {
recorrido = new ArrayList();
}

if(null != nodo) {
recorridoInorden(nodo.getIzquierdo());
recorrido.add(nodo.getInformacion());
recorridoInorden(nodo.getDerecho());
}
return recorrido;
}

public ArrayList recorridoPostorden(Nodo nodo) {


if(nodo == raiz) {
recorrido = new ArrayList();
}

if(null != nodo) {
recorridoPostorden(nodo.getIzquierdo());
recorridoPostorden(nodo.getDerecho());
recorrido.add(nodo.getInformacion());
}
return recorrido;
}
}

Y bueno, pues el ya tan tradicional pequeño ejemplo que utiliza nuestro árbol, para demostrar que este funciona, o que por lo menos lo
funciona lo esencial.
El código de nuestro ejemplo es el código del último ejemplo del árbol contenido en el vector, solo que utilizando nuestro árbol dinámico,
por lo que utiliza las mismas clases que se usaron en el ejemplo anterior para almacenar la información.

package org.neos.arboles.binarios.dinamicos;

import java.util.ArrayList;

import org.neos.arboles.binarios.dinamicos.exceptions.ExceptionElementoDuplicado;
import org.neos.beans.Direccion;
import org.neos.beans.Nombre;
import org.neos.beans.Persona;

public class Ejemplo {


public static void main(String[] args) {
ArbolBinario arbol = new ArbolBinario();
ArrayList recorrido = null;
int i, num_elems;
Direccion direccion = null;
Nombre nombre = null;
Persona persona = null;

try {
direccion = new Direccion("AAA", 1, "B-1", "CCC", "DDD", "EEE");
nombre = new Nombre("Eugenio", "Flores", "Barrera");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Nayeli", "Vazquez", "Vasquez");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Hector", "Hidalgo", "Martinez");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Mario", "Hidalgo", "Matinez");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);
nombre = new Nombre("Miguel", "Flores", "Barrera");
persona = new Persona(nombre, direccion, null);
arbol.insertarElemento(persona);

recorrido = arbol.recorridoPreorden(arbol.obtenerPosicionRaiz());
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print((Persona)recorrido.get(i) + " - ");
}
recorrido = arbol.recorridoInorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Inorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print((Persona)recorrido.get(i) + " - ");
}
recorrido = arbol.recorridoPostorden(arbol.obtenerPosicionRaiz());
System.out.print("\nRecorrido Postorden: ");
num_elems = recorrido.size();
for(i = 0; i < num_elems; i++) {
System.out.print((Persona)recorrido.get(i) + " - ");
}

System.err.println("\nNumero de elementos:
"+arbol.obtenerNumeroElementos(arbol.obtenerPosicionRaiz()));
System.err.println("Peso del árbol: "+arbol.obtenerPeso(arbol.obtenerPosicionRaiz()));
System.err.println("Altura del árbol: "+arbol.obtenerAltura(arbol.obtenerPosicionRaiz()));
} catch(ExceptionElementoDuplicado ed_e) {
System.err.println(ed_e.getMessage());
}
}
}

Con nuestro pequeño ejemplo se crea un árbol como el mostrado en la Figura 14. Y para demostrar que lo que muestra la figura es cierto. Lo
que debe desplegar nuestro pequeño ejemplo es lo siguiente:

Recorrido Preorden: Flores Barrera, Eugenio - Vazquez Vasquez, Nayeli - Hidalgo Martinez, Hector - Flores
Barrera, Miguel - Hidalgo Matinez, Mario -
Recorrido Inorden: Flores Barrera, Eugenio - Flores Barrera, Miguel - Hidalgo Martinez, Hector - Hidalgo
Matinez, Mario - Vazquez Vasquez, Nayeli -
Recorrido Postorden: Flores Barrera, Miguel - Hidalgo Matinez, Mario - Hidalgo Martinez, Hector - Vazquez
Vasquez, Nayeli - Flores Barrera, Eugenio -
Numero de elementos: 5
Peso del árbol: 4
Altura del árbol: 3

Algo que podríamos hacer para mejorar nuestro árbol sería implementar un método que permita balancear nuestro árbol para que no
tengamos una rama más larga que otra y de esta forma tratar de optimizar búsquedas e inserciones. Algo que se me ocurre para implementar
este método es vaciar todos los elementos del árbol a un vector (en el vector los elementos estarían ordenados de menor a mayor) y después
volver a reconstruir todo el árbol tomando como raíz el elemento que se encuentra a la mitad del vector; al tomar el elemento del centro
nuestro vector se dividiría en dos partes (una izquierda, con los elementos más pequeños; y una derecha, con los elementos más grandes) por
lo tanto debemos hacer lo mismo para la parte izquierda y la parte derecha (tomar el elemento del centro como raíz) de manera recursiva
hasta terminar con ambas partes del vector. Otra opción, aun mejor, sería implementar los algoritmos para árboles equilibrados (AVL).

Otra muy buena mejora a nuestro árbol sería hacerlo independiente de la información que almacena y que siguiera teniendo la información
ordenada, para eliminar las modificaciones que tenemos que hacer a la clase “Informacion” cada ves que queremos que el árbol almacene
otro tipo de información.

Você também pode gostar