You are on page 1of 45

Tema 4 Árboles.

Árboles binarios. Algoritmos básicos


Algoritmos básicos con árboles binarios

 Recorrido completo.
 Creación.
 Terminación anticipada.
 Recorridos especiales.
 Manejo de varias estructuras.
Recorrido en Preorden.
Aplicado a objetos de la clase Arbol:
// Escribe las claves del árbol binario en preorden.
static void preOrden (NodoArbol arbol) {
if (arbol != null) {
System.out.print (arbol.clave+" ") ;

raiz
arbol nombre

preOrden (arbol.iz);
preOrden (arbol.de);
}
} 1
public void preorden () {
preorden (raiz);
} 2 3
 Orden de visita de nodos:
1, 2, 4, 9, 15, 5, 3, 8 y 7.
4 5 8 7
 Preferido para:
 Búsquedas. 9 15
Recorrido en Orden Central
arbol

raiz
 Aplicado a objetos de la clase Arbol: nombre

// Escribe las claves del árbol binario en orden central.


static void ordenCentral (NodoArbol arbol) {
if (arbol != null) {
ordenCentral (arbol.iz); 1
System.out.print (arbol.clave+" ");
ordenCentral (arbol.de);
}
2 3
}
public void ordenCentral () {
ordenCentral (raiz); 4 5 8 7
}

 Orden de visita de nodos: 9 15


9, 4, 15, 2, 5, 1, 8, 3 y 7.
 Preferido para:
 Recorrido de acuerdo al orden físico de los nodos.
 En árboles binarios de búsqueda recupera la secuencia.
Recorrido en Postorden
 Aplicado a objetos de la clase Arbol:
// Escribe las claves del árbol binario en postorden.

raiz
static void postOrden (NodoArbol arbol) { arbol nombre

if (arbol != null) {
postOrden (arbol.iz);
postOrden (arbol.de);
System.out.print (arbol.clave + " ") ; 1
}
}
public void postOrden () { 2 3
postOrden (raiz);
}
 Orden de visita de nodos: 4 5 8 7
9, 15, 4, 5, 2, 8, 7, 3 y 1.
 Preferido para:
9 15
 Liberar memoria.
 Nodos buscados en los niveles
más bajos del árbol.
Ejemplo: suma de claves
static int sumaClaves (NodoArbol nodoArbol) {
int resul = 0;

if (nodoArbol != null) {
resul = nodoArbol.clave + sumaClaves (nodoArbol.iz);
resul = resul + sumaClaves (nodoArbol.de);
}
return resul;
}
static int sumaClaves (Arbol a) {
return sumaClaves (a.raiz);
}
If (nodoArbol != null)
{
resul =sumaClaves(nodoArbol.iz)+nodoArbol.clave; 34 resul =15+24=39
resul =resul+sumaClaves(arbol.de); 17 resul =9+6
6
} 1 18
else resul=0

16 resul=5+4 = 9
33 resul =15+9=24
9 resul=2+3 25 resul =7+8
3 8
2 10 19
26

8 resul=2+0 = 2 24 resul =7+0 = 7


13 resul=0+4
5 resul=0+2 4 22 resul =0+7
2 7
3 11 20
6 14

null null null null null null


4 resul=0 7 resul=0 21 resul=0 23 resul=0
12 resul=0 15 resul=0
32 resul=9+0 = 9
29 resul=0+9
9

27 30

null null
28 resul=0 31 resul=0
Recorrido en Amplitud
arbol
static void probarAmplitud (Arbol arbol) {
NodoArbol referencia;
ColaArbol colaArbol = new ColaArbol (); Arbol

referencia = arbol.raiz;
if (referencia != null)
colaArbol.encolar (referencia);

raiz
while (! colaArbol.colaVacia ()) { nombre
referencia = colaArbol.desencolar ();
System.out.print (referencia.clave + " ");
if (referencia.iz != null)
colaArbol.encolar (referencia.iz); 1
if (referencia.de != null)
colaArbol.encolar (referencia.de);
} 2 3
}

 Orden de visita de nodos: 4 5 8 7


1, 2, 3, 4, 5, 8, 7, 9 y 15.
9 15
Obtención de la copia de un árbol
static Arbol copiarArbol (Arbol arbol) {
Arbol resul = new Arbol();
resul.nombre = "Arbol copia";
if (arbol.raiz != null)
resul.raiz = copiaArbol (arbol.raiz);
return resul;
}

static NodoArbol copiaArbol (NodoArbol nodoArbol) {


NodoArbol nuevoNodo = new NodoArbol();

nuevoNodo.clave = nodoArbol.clave;
if (nodoArbol.iz != null)
nuevoNodo.iz = copiaArbol (nodoArbol.iz);
if (nodoArbol.de != null)
nuevoNodo.de = copiaArbol(nodoArbol.de);
return nuevoNodo;
}
Insertar
 Criterio de inserción:
 Orden de los nodos.
 Operación juntar (join).
 Preguntar en qué rama se quiere insertar …
 No se permite insertar claves repetidas
 Búsqueda previa del dato que se quiere insertar para
evitar repeticiones.
 Se inserta cuando alcanzamos el nivel más
profundo (nodos hojas) de la rama seleccionada.
Inserción en árbol binario genérico
static public void juntar (Arbol arbol, int dato, Arbol a1, Arbol a2) {
if (a1.raiz == a2.raiz && a1.raiz != null)
System.out.println ("no se pueden juntar, a1 y a2 son iguales") ;
else {
arbol.raiz = new NodoArbol () ;
arbol.raiz.clave = dato;
arbol.raiz.iz = a1.raiz;
arbol.raiz.de = a2.raiz;
if (arbol != a1)
a1.raiz = null;
if (arbol != a2)
a2.raiz = null;
}
}
Inserción en árbol binario de búsqueda.
Método static que
static NodoArbol insertar (NodoArbol arbol, int dato) { recibe un NodoArbol
NodoArbol resul = arbol; y devuelve otro
como resultado
if (arbol != null)
if (arbol.clave < dato)
arbol.de = insertar (arbol.de, dato);
else if (arbol.clave > dato)
arbol.iz = insertar (arbol.iz, dato);
else System.out.println ("la clave ya existe");
else resul = new NodoArbol (dato);
return resul;
}

public void insertar (int dato) { Método de objeto


raiz = insertar (raiz, dato); de Arbol
}
Búsqueda en árbol binario.
boolean encontrar (int dato) {
return encuentra (raiz, dato);
}

static boolean encuentra (NodoArbol nodoArbol, int dato) {


boolean resul;
if (nodoArbol != null)
if (nodoArbol.clave == dato)
resul = true;
else {
resul = encuentra (nodoArbol.iz, dato);
if (!resul)
resul = encuentra (nodoArbol.de, dato);
}
else resul = false;
return resul;
}
Búsqueda en árbol binario de búsqueda.
boolean encontrar (int dato) {
return encuentra (raiz, dato);
}

public static boolean encuentra (NodoArbol nodoArbol, int dato) {


boolean resul = false;

if (nodoArbol != null)
if (nodoArbol.clave == dato)
resul = true;
else if (nodoArbol.clave > dato)
resul = encuentra (nodoArbol.iz, dato);
else resul = encuentra (nodoArbol.de, dato);
return resul;
}
Ejemplo: Verificar que un árbol Binario es
de Búsqueda
static class arbolBusqueda {
static int ant;
static boolean primero = true;
static boolean esBusqueda (NodoArbol arbol) {
boolean resul;
if (arbol == null)
resul = true;
else {
resul = esBusqueda (arbol.iz);
if (primero)
primero = false;
else if (arbol.clave <= ant)
resul = false;
if (resul) {
ant = arbol.clave;
resul = esBusqueda(arbol.de);
}
}
return resul;
static boolean esBusqueda (Arbol a) {
} return arbolBusqueda.esBusqueda (a.raiz);
} }
Eliminar una clave (I).
 Fase I. Se localiza la clave a eliminar (si existe).
 Fase II. Tres posibles situaciones:
 Clave está en un nodo hoja:
 Liberar memoria (innecesario en Java) y asignar el árbol a null.

 Clave tiene un único descendiente:


 Apuntar al subárbol no vacío y liberar memoria (innecesario en
Java).
 Clave tiene dos descendientes:
 [Orden topológico anterior] Se localiza el descendiente más a la
derecha del hijo izquierdo, se sustituye la clave a borrar por la
clave encontrada, se “cortocircuita” el subárbol izquierdo y se
borra (innecesario en Java) el nodo que contenía la clave
sustituida.
Eliminar una clave (II).
 Peculiaridades del lenguaje Java.
 Causa: Las referencias (punteros) se pasan por valor.
 Efecto: Los cambios realizados en la instancia actual
sobre el puntero (NodoArbol) que se recibe como
argumento no surten efecto cuando se retorna a la
instancia de llamada.
 Técnica posible: Construir métodos que devuelvan
actualizado el puntero (referencia) que se recibe como
argumento.
 Ejemplo:
static NodoArbol elimina (NodoArbol nodoArbol, int dato) {
// cuerpo del método que modifica nodoArbol;
return nodoArbol;
}
static void eliminarClave (Arbol arbol, int dato) {
arbol.raiz = eliminarElemento (arbol.raiz, dato);
}
static NodoArbol eliminarElemento (NodoArbol arbol, int elem) {
NodoArbol p;
if (arbol != null)
if (arbol.clave > elem)
arbol.iz = eliminarElemento (arbol.iz, elem);
else if (arbol.clave < elem)
arbol.de = eliminarElemento (arbol.de, elem);
else {
p = arbol;
if (arbol.iz == null)
arbol= arbol.de;
else if (arbol.de == null)
arbol = arbol.iz;
else arbol.iz = eliminar2Hijos (arbol.iz, p);
}
else System.out.println (" la clave buscada no existe");
return arbol;
}
static NodoArbol eliminar2Hijos (NodoArbol arbol, NodoArbol p) {
NodoArbol resul;
if (arbol.de != null) {
resul = arbol;
arbol.de = eliminar2Hijos (arbol.de, p);
}
else {
p.clave = arbol.clave;
resul = arbol.iz;
}
return resul;
}
Eliminar un árbol.
static void eliminarArbol (Arbol arbol) {
System.out.println ("NOTA: Este algoritmo es innecesario en Java ");
System.out.println (“Lo que no sucede en todos los lenguajes");
if (arbol.raiz == null)
System.out.println ("Arbol vacio");
else {
arbol.raiz =eliminaArbol (arbol.raiz);
System.out.println (“Faltaría 'destruir' la referencia principal (arbol)");
}
}

static NodoArbol eliminaArbol (NodoArbol nodoArbol) {


NodoArbol resul;
if (nodoArbol != null) {
nodoArbol.iz = eliminaArbol (nodoArbol.iz);
nodoArbol.de = eliminaArbol (nodoArbol.de);
System.out.println ("Clave antes de ser eliminada: “+nodoArbol.clave);
nodoArbol = null;
resul = nodoArbol;
}
else resul = null;
return resul;
}
Tratamiento de hojas. Ejemplo.
 Condición de hoja:
(nodoArbol.iz == null) && (nodoArbol.de == null)

 Ejemplo: Devuelve el número de hojas de un árbol


static int cuentaHojas (Arbol arbol) {
return contarHojas (arbol.raiz);
}

static int contarHojas (NodoArbol nodoArbol) {


int resul = 0;
if (nodoArbol != null)
if ((nodoArbol.iz == null) && (nodoArbol.de == null))
resul = 1;
else resul = contarHojas (nodoArbol.iz) + contarHojas (nodoArbol.de);
return resul;
}
Constancia de nivel. Recorrido en
profundidad (I)
 Mecanismo:
Se pasa un argumento entero (inicializado a 1) que se incrementa
en cada llamada.

 Ejemplo:
static void clavesNiveles (Arbol arbol) {
clavesNiveles (arbol.raiz,1);
}

static void clavesNiveles (NodoArbol nodoArbol, int n) {


if (nodoArbol != null) {
System.out.println ("Clave: “ + nodoArbol.clave + " en el nivel: “ + n);
clavesNiveles (nodoArbol.iz, n+1);
clavesNiveles (nodoArbol.de, n+1);
}
}
Constancia de nivel. Recorrido en
profundidad (II)
Ejemplo: Método que devuelve la altura de un árbol.
static int pruebaAltura (Arbol arbol) {
int resul = 0;
if (arbol.raiz != null)
resul = Altura.altura (arbol.raiz,1);
return resul;
}
static int altura (NodoArbol nodoArbol, int n, int altura) {
int resulIz, resulDe;
if (nodoArbol != null) {
if (n > altura)
altura = n;
resulIz = altura (nodoArbol.iz, n+1, altura);
resulDe = altura (nodoArbol.de, n+1, altura);
altura = Math.max (resulIz, resulDe);
}
return altura;
}
Constancia de nivel. Recorrido en amplitud (I).
 Dos opciones:
 Iteración anidada en dos niveles.
 Modificar la cola de referencias a nodos del árbol para
que incluya una variable con el nivel.
Constancia de nivel. Recorrido en amplitud (II)
 Sin modificar la cola de referencias.
 Dos iteraciones:
 Externa que recorre el árbol en niveles
 Interna que visita los nodos en amplitud
 Variables:
 contador: controla el recorrido del nivel
 actual: amplitud del nivel
 siguiente: número de hijos del siguiente nivel
static void ListarAmplitud (Arbol arbol) {
Cola c = new tad_cola ();
int actual, siguiente, contador, altura;
NodoArbol p p = arbol.raiz; Arbol.raiz
altura = 0; 1
siguiente = 1;
p
p = arbol;
if (p != null ) 2 3
c.encolar (p);
while (!c.colaVacia()) { 4 5 8
actual = siguiente;
siguiente = 0; ↑1
contador = 1;
altura++; altura = 0
while (contador <= actual) { siguiente =1;
p = c.desencolar ();
System.out.println ("clave: "+p.clave+" nivel: "+altura);
contador++;
if (p.iz != null) {
c.encolar (p.iz);
siguiente++;
}
if (p.de != null) {
c.encolar (p.de);
siguiente++;
}
}
}
}
altura = 0
static void ListarAmplitud (Arbol arbol) { siguiente = 1;
Cola c = new tad_cola (); Arbol.raiz
int actual, siguiente, contador, altura;
1
c.inicializarCola ();
NodoArbol p = arbol.raiz; p

altura = 0; 2 3
siguiente = 1;
p = arbol; 4 5 8
if (p != null )
c.encolar (p);
while (!c.colaVacia()) { ↑1
actual = siguiente;
siguiente = 0; actual = 1
contador = 1; siguiente = 0
altura++; contador = 0
while (contador <= actual) { altura = 1
p = c.desencolar ();
System.out.println ("clave: "+p.clave+" nivel: "+altura);
contador++;
if (p.iz != null) {
c.encolar (p.iz);
siguiente++;
}
if (p.de != null) {
c.encolar (p.de);
siguiente++;
}
}
}
}
static void ListarAmplitud (Arbol arbol) { Iteración interna:
NodoArbol p p = arbol.raiz; while (contador <= actual) actual = 1
Cola c = new tad_cola (); contador = 0
int actual, siguiente, contador, altura; altura = 1
altura = 0; Arbol.raiz

siguiente = 1; 1
c.inicializarCola (); p
p = arbol;
2 3
if (p != null )
c.encolar (p);
while (!c.colaVacia()) { 4 5 8
actual = siguiente;
siguiente = 0; ↑2 ↑3
contador = 1;
altura++;
while (contador <= actual) { ↑1 clave es 1, altura 1
p = c.desencolar (); contador = 1
System.out.println ("clave: "+p.clave+" nivel: "+altura);
contador++; siguiente = 1
if (p.iz != null) {
siguiente = 2
c.encolar (p.iz);
siguiente++;
}
if (p.de != null) { salimos de la iteración interna
c.encolar (p.de);
siguiente++;
}
}
}
}
Arbol.raiz

static void ListarAmplitud (Arbol arbol) { 1


NodoArbol p p = arbol.raiz;
Cola c = new tad_cola (); siguiente =2 P

altura =1 2 3
int actual, siguiente, contador, altura;
altura = 0;
siguiente = 1; 4 5 8
c.inicializarCola ();
p = arbol;
if (p != null ) ↑2 ↑3
c.encolar (p);
while (!c.colaVacia()) { actual = 2
actual = siguiente; siguiente = 0
siguiente = 0; contador = 0
altura = 2
contador = 1;
altura++;
while (contador <= actual) {
p = c.desencolar (); while (contador <= actual)
System.out.println ("clave: "+p.clave+" nivel: "+altura); 1
contador++; P
if (p.iz != null) {
c.encolar (p.iz); ↑3 ↑4 ↑5 2 3
siguiente++;
} 4 5 8
if (p.de != null) {
c.encolar (p.de);
siguiente++; ↑2 siguiente = 1
}
} clave es 2 y altura 2
siguiente = 2
} contador = 1
}
static void ListarAmplitud (Arbol arbol) { iteración interna:
NodoArbol p p = arbol.raiz; while (contador <= actual) 1 P
Cola c = new tad_cola ();
int actual, siguiente, contador, altura;
↑4 ↑5 ↑8 2 3
altura = 0;
siguiente = 1;
c.inicializarCola (); 4 5 8
p = arbol;
if (p != null ) ↑3
c.encolar (p); siguiente = 3
while (!c.colaVacia()) { clave es 3 y altura 2
actual = siguiente;
siguiente = 0; contador = 2
contador = 1;
altura++;
---- salimos de la iteración interna ----
while (contador <= actual) {
p = c.desencolar ();
System.out.println ("clave: "+p.clave+" nivel: "+altura);
contador++; 1 P

if (p.iz != null) {
c.encolar (p.iz);
siguiente++; ↑4 ↑5 ↑8 2 3
}
if (p.de != null) { 4 5 8
c.encolar (p.de);
siguiente++; actual = 3
} siguiente = 0
} contador = 0
} altura = 3
}
clave es 3 y altura 2
static void ListarAmplitud (Arbol arbol) { iteración interna:
NodoArbol p p = arbol.raiz; while (contador < =actual)
Cola c = new tad_cola (); P 1
int actual, siguiente, contador, altura;
altura = 0;
siguiente = 1; ↑5 ↑8 2 3
c.inicializarCola ();
p = arbol; 4 5 8
↑4
if (p != null )
c.encolar (p); actual = 3
while (!c.colaVacia()) { contador = 1
actual = siguiente; clave es 4 y altura 3
siguiente = 0;
contador = 1;
altura++;
while (contador <= actual) { ↑8 actual = 3
contador = 2
p = c.desencolar ();
↑5"+altura);
System.out.println ("clave: "+p.clave+" nivel: clave es 5 y altura 3
contador++;
if (p.iz != null) {
c.encolar (p.iz);
siguiente++;
} actual = 3
if (p.de != null) { contador = 3
c.encolar (p.de);
siguiente++; ↑8 clave es 8 y altura 3

}
}
}
}
Constancia de nivel. Recorrido en amplitud (III)
 Modificando la cola de referencias.
class tElemento { class NodoColaArbolModificada {
NodoColaArbolModificada siguiente;
int nivel; tElemento elem;
NodoArbol nodoArbol;
NodoColaArbolModificada () {
tElemento (NodoArbol a, int n) { elem = null;
nivel = n; siguiente = null;
nodoArbol = a; }
} }
}

public class ColaArbolModificada {


private NodoColaArbolModificada principio;
private NodoColaArbolModificada fin;

public ColaArbolModificada () {
principio = null;
fin = null;
}
}
Constancia de nivel. Recorrido en amplitud (IV)
static void listarAmplitud (Arbol arbol) {
NodoArbol p;
int nivel;
ColaArbolModificada cola = new ColaArbolModificada();
p = arbol.raiz;
if (p != null) {
tElemento elem = new tElemento (referencia, 1);
cola.encolar(elem);
while (! cola.colaVacia()) {
elem = cola.desencolar ();
p = elem.nodoArbol;
nivel = elem.nivel;
System.out.println("nivel: "+nivel+" "+p.clave+" ");
if (p.iz != null) {
elem = new tElemento(p.iz,nivel+1);
cola.encolar(elem);
}
if (p.de != null) {
elem = new tElemento (p.de, nivel+1);
cola.encolar(elem);
}
}
}
} Ver codigo
nodoArbol

1
p
static void listarAmplitud(Arbol arbol) { 3
2
NodoArbol referencia;
int nivel; 4 5 8
ColaArbolModificada cola = new ColaArbolModificada();
p = arbol.raiz;
if (p != null) { ↑1 1
tElemento elem = new tElemento (p, 1);
cola.encolar(elem);
while (! cola.colaVacia()) { nodoArbol

elem = cola.desencolar(); 1
p = elem.nodoArbol; p

nivel = elem.nivel; 2 3
System.out.println("nivel: "+nivel+" "+p.clave+" ");
if (p.iz != null) { 4 5 8
elem = new tElemento (p.iz, nivel+1);
cola.encolar (elem);
↑2 2 ↑3 2
}
if (p.de != null) { p = ↑1
elem = new tElemento(p.de, nivel+1); ↑1 1 nivel =1;
cola.encolar(elem); clave 1, Nivel 1
}
}
}
}
nodoArbol

1
p

2 3

4 5 8

↑3 2 ↑4 3 ↑5 3
while (! cola.colaVacia()) {
elem = cola.desencolar(); ↑2 2
referencia = elem.nodoArbol; p = ↑2
nivel = elem.nivel; nivel =2;
System.out.println("nivel: "+nivel+" "+p.clave+" "); clave 1, nivel 1
if (p.iz != null) { clave 2, nivel 2
elem = new tElemento (p.iz,nivel+1); nodoArbol
cola.encolar(elem); 1
p

}
if (p.de != null) {
elem = new tElemento (p.de,nivel+1); 2 3
cola.encolar(elem);
} 4 5 8
}
↑4 3 ↑5 3 ↑8 3

↑3 2 p = ↑3
nivel =3;
clave 1, nivel 1
clave 2, nivel 2
nodoArbol

1
p

while (! cola.colaVacia()) { 2 3
elem = cola.desencolar();
4 5 8
referencia = elem.nodoArbol;
nivel = elem.nivel;
System.out.println("nivel: "+nivel+" "+p.clave+" "); ↑5 3 ↑8 3
if (p.iz != null) {
elem = new tElemento (p.iz,nivel+1); p = ↑4; n =2;
↑4 3
clave 1, nivel 1
cola.encolar(elem); clave 2, nivel 2
} clave 3, nivel 2
if (p.de != null) { clave 4, nivel 3
elem = new tElemento (p.de,nivel+1);
nodoArbol
cola.encolar(elem);
1
}
}
2 3

4 5 8
p

p = ↑5; n =3;
clave 1, nivel 1 ↑8 3
clave 2, nivel 2
clave 3, nivel 2
clave 4, nivel 3 ↑5 3
clave 5, nivel 3
nodoArbol

2 3
p

4 5 8

while (! cola.colaVacia()) {
elem = cola.desencolar();
referencia = elem.nodoArbol; p = ↑8; n =3;
↑8 3
nivel = elem.nivel; clave 1, nivel 1
System.out.println("nivel: "+nivel+" "+p.clave+" "); clave 2, nivel 2
if (p.iz != null) { clave 3, nivel 2
clave 4, nivel 3
elem = new tElemento (p.iz,nivel+1);
clave 5, nivel 3
cola.encolar(elem); Clave 8, nivel 3
}
if (p.de != null) {
elem = new tElemento (p.de,nivel+1);
cola.encolar(elem);
}
}
Verificar si dos árboles son iguales
static boolean pruebaIguales (Arbol arboA, Arbol arbolB) {
return sonIguales (arbol1.raiz, arbol2.raiz);
}

static boolean sonIguales (NodoArbol a, NodoArbol b) {


boolean resul ;

if ((a == null) && (b == null))


resul = true;
else if ((a == null) || (b == null))
resul = false;
else if (a.clave == b.clave)
resul = sonIguales (a.iz, b.iz) && sonIguales (a.de, b.de);
else resul = false;
return resul;
}
Arbol Binario de Búsqueda contenido en
lista
static boolean estaContenido (NodoArbol nodoArbol, NodoLista nodoLista) {
boolean seguir, saltar;
if (nodoArbol == null)
seguir = true;
else {
seguir = estaContenido (nodoArbol.iz, nodoLista);
if (seguir && (nodoLista != null))
if (nodoArbol.clave < nodoLista.clave)
seguir = false;
else {
saltar = true;
while ((nodoLista != null) && saltar)
if (nodoArbol.clave == nodoLista.clave)
saltar = false;
else nodoLista = nodoLista.sig;
if (!saltar)
seguir = estaContenido (nodoArbol.de, nodoLista.sig);
else seguir = false;
}
else seguir = false;
}
return seguir;
}
static boolean estaContenido (Arbol arbol, Lista lista) {
return estaContenido (arbol.raiz, lista.inicio);
}