Está en la página 1de 36

Apuntes de Estructuras de Datos

Captulo 4
Estructuras no lineales
4.1 Competencia especfica a desarrollar
Que el alumno conozca, identifique y aplique las estructuras no lineales en la solucin de
problemas del mundo real

4.2 Actividades de aprendizaje


Consultar con las fuentes bibliogrficas la terminologa sobre rboles. Comentar
informacin en plenaria
Prctica de ejercicios. Utilizando un lenguaje de programacin implemente las operaciones
bsicas (insertar, eliminar, buscar) en un rbol binario de bsqueda, as como los
recorridos en preorden, inorden y postorden

4.3 Definicin
Una de las estructuras de datos no lineales ms importantes en computacin es el rbol, a
travs del cual se pueden implementar una gran cantidad de algoritmos que trabajan con
mucha mayor rapidez que las estructuras lineales como las listas, por ejemplo.
Los rboles son llamados as porque si se trata de visualizar la estructura, luce como un rbol,
con raz, ramas y hojas. Los rboles son estructuras de datos basadas en nodos como las
listas encadenadas. Los nodos de los rboles tienen dos o mas nodos hijos.
Un rbol es una estructura de datos que empieza en un nodo raz. Cada nodo es, o un nodo
hoja o un nodo interno. Un nodo interno tiene uno o ms nodos hijos y es llamado el
padre de sus nodos hijos. Todos los hijos del mismo nodo son hermanos. Contrario a un
84

Apuntes de Estructuras de Datos

rbol fsico, la raz usualmente se representa en la parte superior de la estructura, y las


hojas al fondo, como se puede apreciar en el siguiente esquema grfico.
Raz

Padre

Hijo

Nodo

Hoja

El uso de esta estructura la podemos observar por ejemplo, en las relaciones de herencia entre
clases de un programa, en los sistemas operativos, donde los archivos se ordenan en
forma jerrquica en directorios o carpetas anidados, en los manejadores de bases de datos,
etc.
Los rboles son nombrados por el nmero de hijos que tienen sus nodos. Por ejemplo, si un
nodo del rbol tiene dos hijos se le llama rbol binario. Si tiene tres hijos se le llama rbol
ternario. Si tiene cuatro hijos, se le llama rbol cuaternario y as sucesivamente.
Afortunadamente para simplificar las cosas, con los rboles binarios se pueden simular
cualquier otro rbol, sin embargo se har una referencia breve a otros tipos de rboles.
Idealmente un rbol debe estar balanceado y la altura ser log n, donde n es el nmero de nodos
en el rbol. Si se quiere asegurar que la altura del rbol sea lo ms pequea posible y por
lo tanto proveer el mejor tiempo de corrida, debe usarse una estructura de rbol
balanceada como un rbol rojinegro, un rbol AVL, o un rbol B.
En trminos generales, un rbol balanceado es un rbol donde ninguna hoja est mucho ms
lejos de la raz que cualquier otra hoja.
Cuando se trabaja con una gran cantidad de datos, no es posible o deseable mantener la
estructura de datos completa en un almacenamiento primario (RAM). En lugar de esto, se
mantiene una porcin relativamente pequea de la estructura de datos en un
almacenamiento primario y los datos adicionales son ledos desde un almacenamiento

85

Apuntes de Estructuras de Datos

secundario segn se requieran. Desafortunadamente, un disco magntico, la forma ms


comn de almacenamiento secundario, es significativamente ms lento que el acceso a la
memoria RAM. De hecho el sistema emplea ms tiempo recuperando datos que
procesndolos.
Los rboles B mencionados, son rboles balanceados que estn optimizados para situaciones
cuando parte o todo el rbol debe mantenerse en almacenamiento secundario. Estos
rboles tratan de minimizar el nmero de accesos a disco, por ejemplo, un rbol B con
una altura de 2 y un factor de ramificacin de 1001 puede almacenar cerca de un billn de
llaves (claves de acceso) pero requiere a lo sumo dos accesos a disco para buscar
cualquier nodo (Cormen 384).
La altura para el peor caso es del orden O(log n); el tamao de las ramificaciones puede ser
grande comparado con muchas otras estructuras rbol balanceadas.
Un rbol rojinegro es un rbol de bsqueda binario con un atributo extra para cada nodo: el
color, con las siguientes propiedades:
1.
2.
3.
4.

Cada nodo es rojo o negro


Cada hoja es negra
Si un nodo es rojo, entonces sus hijos son negros
Cada camino simple desde un nodo a su hoja descendiente contiene el mismo nmero de
nodos negros.

Un rbol AVL es otro rbol de bsqueda binario, y como los rboles rojinegros, no estn
perfectamente balanceados, pero los pares de subrboles difieren en la altura cuando

86

Apuntes de Estructuras de Datos

mucho en 1, por lo que el tiempo de bsqueda se mantiene en O(log n). Tiene las
siguientes propiedades:
1. Los subrboles de cada nodo difieren en altura cuando mucho en 1
2. Cada subrbol es un rbol AVL

h-1

h-2

Hay otros rboles como el rbol B, que es un rbol de bsqueda balanceado en el cual cada
nodo tiene entre m/2 y m hijos, donde m > 1 es un entero fijo y m indica el orden. La
raz puede tener como mnimo dos hijos. Esta es una buena estructura si la mayor parte
del rbol est en disco, ya que la altura, y de aqu el nmero de accesos puede mantenerse
bajo, por decir uno o dos accesos. Tambin se conoce como un rbol multicamino
balanceado.
El rbol multicamino, es un rbol con cualquier nmero de hijos para cada nodo. Una
implementacin tpica para este tipo de rbol es tener una lista de nodos hijos de cada
nodo, ms que un nmero fijo de hijos. Una implementacin ms elaborada es tener un
nmero fijo de hijos, para manejar nodos ms eficientemente, y una lista encadenada para
aquellos nodos raros que tienen ms hijos.
El rbol B tiene algunas variantes, como el rbol 2-3-4 es un rbol B de orden 4, esto es, los
nodos internos tienen dos, tres o cuatro hijos; el rbol B* es tambin un rbol B en el cual
los nodos se mantienen completos a 2/3 por la redistribucin de las llaves para llenar dos
nodos hijos, y entonces dividirlos en tres nodos; el rbol 2-3, es un rbol B de orden 3,
esto es, que los nodos internos tienen dos o tres hijos y un rbol B+ en el cual las llaves
estn almacenadas en las hojas.

87

Apuntes de Estructuras de Datos

Por otro lado, un rbol de bsqueda es un rbol donde cada subrbol de un nodo tiene llaves
menores que cualquier otro subrbol a la derecha del nodo, pero mayores que cualquier
llave en el subrbol a su izquierda.
En adelante se har referencia a los rboles binarios, que son los incluidos en este programa.

4.4 Definicin de rbol Binario


Un rbol binario es un conjunto finito de elementos que puede estar vaco o contener un
elemento denominado la raz del rbol y otros elementos divididos en dos subconjuntos
separados, cada uno de los cuales es en s un rbol binario. Estos dos subconjuntos son
denominados subrbol izquierdo y subrbol derecho. Cada elemento de un rbol binario se
denomina un nodo del rbol. A la raz tambin se le conoce con el nombre de padre, al
subrbol izquierdo como hijo izquierdo y al subrbol derecho como hijo derecho. Una hoja es
un nodo que no tiene hijos.
Raz (padre)
B

Subrbol izquierdo (hijo izquierdo)


Nodos hoja

C
E

Subrbol derecho (hijo derecho)

F
G

Bajo esta representacin, el nodo A, que es la raz, es el padre de B y C y es ancestro de los


mismos. De igual manera, A es ancestro de D, y B es descendiente izquierdo de A y C es
descendiente derecho de A. Dos nodos son hermanos si son hijo izquierdo y derecho del
mismo padre. El nivel de un nodo de un rbol binario empieza con el nivel cero (0)
correspondiente a la raz, y el nivel de los hijos se incrementa en uno con respecto al de su
padre.

4.5 Implementacin de un rbol binario

88

Apuntes de Estructuras de Datos

Primeramente, como en el caso de la implementacin de las listas, se puede definir la


estructura para el nodo, la cual contiene un campo de informacin y dos campos de
apuntadores, uno para el hijo izquierdo y otro para el hijo derecho.
public class Nodo {
protected Object info;
protected Nodo left, right;
public Nodo ( ) {
info = null;
left = right = null;
}
public Nodo ( Object x ) {
info = x;
left = right = null;
}
public void SetLeft ( Nodo p ) {
left = p;
}
public void SetRight ( Nodo p ) {
right = p;
}
public void SetInfo ( Object x ) {
info = x;
}
public Nodo GetLeft ( ) {
return left;
}
public Nodo GetRight ( ) {
return right;
}
public Object GetInfo ( ) {
return info;
}
public String toString ( ) {
return "" + info;
}
89

Apuntes de Estructuras de Datos

4.6 Operaciones bsicas de los rboles binarios


1. Creacin de un nuevo rbol binario con un slo nodo. Esta operacin se implementa con el
mtodo constructor
2. Asignar un hijo izquierdo a un nodo p:
3. Asignar un hijo derecho a un nodo p:

InsLeft ( p, x );
InsRight ( p, x );

Para construir un rbol binario se requieren de estas tres operaciones bsicas.


Los rboles binarios son bastante complejos, y la mayora de las veces, se debe escribir una
implementacin nica para cada programa especfico. Los rboles tienen muchos diferentes
algoritmos asociados a ellos. Los ms bsicos son los algoritmos para atravesarlos o
recorrerlos, lo cual tiene diferentes formas de acuerdo al orden en el cual se observan sus
valores.
En esta operacin, los nodos del rbol se van visitando utilizando para ello diferentes mtodos.
Estos mtodos tienen una definicin recursiva de tal manera que el recorrido de un rbol
binario comprende la visita a la raz y a los subrboles izquierdo y derecho. Dependiendo del
orden en que se realice la visita a la raz, el mtodo recibe su nombre: preorden, inorden y
postorden. Estos diferentes tipos de recorridos significan diferentes cosas para diferentes
algoritmos y diferentes rboles. Por ejemplo, si es un rbol de bsqueda binario (se ver ms
adelante), entonces en el recorrido en inorden se imprimirn los elementos en orden
ascendente de acuerdo a su valor.
En el recorrido en PreOrden, la raz se visita primero:
1. Visitar la raz
2. Recorrer el subrbol izquierdo en PreOrden
3. Recorrer el subrbol derecho en PreOrden

90

Apuntes de Estructuras de Datos

En el recorrido en InOrden, la raz se visita en medio:


1. Recorrer el subrbol izquierdo en InOrden
2. Visitar la raz
3. Recorrer el subrbol derecho en InOrden
En el recorrido en PostOrden, la raz se visita al final:
1. Recorrer el subrbol izquierdo en PostOrden
2. Recorrer el subrbol derecho en PostOrden
3. Visitar la raz
El siguiente, es el cdigo para crear un rbol binario genrico:
public abstract class BinaryTree {
private Nodo r;
protected Nodo GetRaiz ( ) {
return r;
}
protected void SetRaiz ( Nodo raiz ) {
r = raiz;
}
public BinaryTree ( ) {
SetRaiz ( null );
}
public BinaryTree ( Object x ) {
SetRaiz ( new Nodo (x) );
}
public boolean IsEmpty ( ) {
return GetRaiz ( ) == null;
}
public Object GetInfo ( ) {
if ( !IsEmpty ( ) )
return GetRaiz ( ).GetInfo ( );
return null;
}

91

Apuntes de Estructuras de Datos

public Nodo GetLeft ( ) {


if ( !IsEmpty ( ) )
return GetRaiz ( ).GetLeft ( );
return null;
}
public Nodo GetRight ( ) {
if ( !IsEmpty ( ) )
return GetRaiz ( ).GetRight ( );
return null;
}
public void SetInfo ( Object x ) {
if ( !IsEmpty ( ) )
GetRaiz ( ).SetInfo ( x );
}
public void InsLeft ( Nodo p, Object x ) {
if ( ( p != null ) && ( p.GetLeft ( ) == null ) )
p.SetLeft ( new Nodo (x) );
}
public void InsRight ( Nodo p, Object x ) {
if ( ( p != null ) && ( p.GetRight ( ) == null ) )
p.SetRight ( new Nodo (x) );
}
public void Print ( int modo ) {
if ( modo == 1 )
PreOrden ( );
else if ( modo == 2 )
InOrden ( );
else if ( modo == 3 )
PostOrden ( );
}
public void PreOrden ( ) {
PreOrden ( GetRaiz ( ) );
}
protected void PreOrden ( Nodo p ) {
if ( p == null )
return;
System.out.print ( p.GetInfo ( ) + " " );
PreOrden ( p.GetLeft ( ) );
PreOrden ( p.GetRight ( ) );
}
92

Apuntes de Estructuras de Datos

public void InOrden ( ) {


InOrden ( GetRaiz ( ) );
}
protected void InOrden ( Nodo p ) {
if ( p == null )
return;
InOrden ( p.GetLeft ( ) );
System.out.print ( p.GetInfo ( ) + " " );
InOrden ( p.GetRight ( ) );
}
public void PostOrden ( ) {
PostOrden ( GetRaiz ( ) );
}
protected void PostOrden ( Nodo p ) {
if ( p == null )
return;
PostOrden ( p.GetLeft ( ) );
PostOrden ( p.GetRight ( ) );
System.out.print ( p.GetInfo ( ) + " " );
}

4.7 Creacin de rboles de bsqueda binarios (ABB)


Como se mencion anteriormente, un rbol de bsqueda es un rbol donde cada subrbol de
un nodo tiene llaves menores que cualquier otro subrbol a la derecha del nodo, pero mayores
que cualquier llave en el subrbol a su izquierda y esto se aplica tambin a los rboles de
bsqueda binarios, por lo que estos son rboles creados con sus valores ordenados.
Ya que un rbol de bsqueda binario es a final de cuentas un rbol binario, entonces se
heredar o extender del rbol binario genrico ya definido, y se adicionarn algunos mtodos
ms, como el mtodo CrearArbol, para insertar objetos al rbol de bsqueda binario.
public class BinarySearchTree extends BinaryTree {
public BinarySearchTree ( ) {
super ( );
}

93

Apuntes de Estructuras de Datos

public BinarySearchTree ( Object o ) {


super ( o );
}
public void Print ( ) {
Print (2);
}
public void CrearArbol ( Object o ) {
Nodo p, q;
for ( p = null, q = GetRaiz ( );
q != null && ((Integer)o).intValue ( ) != ((Integer)q.GetInfo ( ) ).intValue ( );
p = q, q = ((Integer)o).intValue ( ) < ((Integer)q.GetInfo ( ) ).intValue ( )
? q.GetLeft ( ) : q.GetRight ( ) );
if ( q != null )
return;
else
if ( p == null )
SetRaiz ( new Nodo (o) );
else
if ( ((Integer)o).intValue ( ) < ((Integer)q.GetInfo ( ) ).intValue ( ) )
InsLeft ( p, o );
else
InsRight ( p, o );
}
}
Como se puede ver, el mtodo CrearArbol ( ) es la clave de todo. Se empieza declarando dos
variables p y q para hacer el recorrido (como en las listas encadenadas). Un rbol de bsqueda
binario no permite inserciones de valores duplicados, por lo que esto es verificado en el
mtodo, para insertar un valor, se tiene que comparar con el valor en el nodo apuntado por q,
si es menor que ste, entonces se recorre a la izquierda, sino a la derecha, hasta encontrar un
nodo que no tenga subrbol izquierdo o derecho segn sea el caso.
Enseguida se muestra la prueba de estos mdulos para el rbol.
public class Main {
public static void main ( String [] args ) {
BinarySearchTree tree = new BinarySearchTree ( );
int n;
System.out.println ( "Numeros insertados:" );
94

Apuntes de Estructuras de Datos

for ( int i = 0 ; i < 10 ; i++ ) {


tree.CrearArbol ( n = (int) (Math.random ( ) * 1000) );
System.out.print ( n + " " );
}
System.out.println ( "\nPreorden:" );
tree.Print (1);
System.out.println ( "\nInorden" );
tree.Print ( );
System.out.println ( "\nPostorden:" );
tree.Print (3);
}
}
Al ejecutar este cdigo puede observarse que en el recorrido en In-orden se obtiene el listado
de los valores en forma ordenada.

4.8 Recorrido de un rbol


Hay dos algoritmos principales para atravesar un rbol: primero en profundidad y primero en
anchura.
Hasta el momento, solo se han visto los recorridos primero en profundidad, ya que los
recorridos en preorden, inorden y postorden son subconjuntos de este tipo de recorrido.
Los recorridos primero en anchura van por el rbol de arriba hacia abajo, de izquierda a
derecha, como por ejemplo, cuando se lee una pgina de un libro, es decir, que se visita cada
nodo en un nivel dado, antes de moverse al siguiente nivel y muchos algoritmos estn
centrados en este mtodo, como en el juego de ajedrez.
Para hacer un recorrido primero en anchura se har uso de la estructura cola, ya que a
diferencia del recorrido primero en profundidad que utiliza procesos recursivos basados en
una pila, en este caso se requiere de la estructura cola.
Aunque hay muchas variaciones a estos recorridos, por el momento se tratar de entender al
menos estas cuatro funciones bsicas.

95

Apuntes de Estructuras de Datos

Para el recorrido primero en anchura, se inserta el nodo raz en la cola y despus en el ciclo,
mientras la cola no est vaca, si se tiene un nodo izquierdo en el nodo que se est
examinando, se inserta en la cola, lo mismo para el nodo derecho. Eventualmente, los nodos
insertados en la cola, se remueven y subsecuentemente, tienen sus hijos izquierdos
examinados. El proceso continua hasta que se ha recorrido el rbol entero, desde la raz
principal hasta el ltimo nivel, de izquierda a derecha. Este proceso se agregar a los
recorridos primero en profundidad ya definidos:
public void BreadthFirst ( ) {
Cola q = new Cola ( 100 );
Nodo tmp;
q.Insert ( GetRaiz ( ) );
while ( !q.IsEmpty ( ) ) {
tmp = (Nodo) q.Remove ( );
if ( tmp.GetLeft ( ) != null )
q.Insert ( tmp.GetLeft ( ) );
if ( tmp.GetRight ( ) != null )
q.Insert ( tmp.GetRight ( ) );
System.out.print ( tmp.GetInfo ( ) + " " );
}
}
El cdigo de prueba es el mismo utilizado anteriormente, slo se aade el recorrido primero
en anchura.
public class Main {
public static void main ( String [ ] args ) {
BinarySearchTree tree = new BinarySearchTree ( );
int n;
System.out.println ( "Numeros insertados:" );
for ( int i = 0; i < 10; i++ ) {
tree.CrearArbol ( n= (int)( Math.random ( )*1000 ) );
System.out.print ( n + " " );
}
System.out.println ( "\nPreorden:" );
tree.Print ( 1 );
System.out.println ( "\nInorden:" );
tree.Print ( );
System.out.println ( "\nPostorden:" );
tree.Print ( 3 );
System.out.println ( "\nBreadthFirst:" );
96

Apuntes de Estructuras de Datos

tree.BreadthFirst ( );
}
}
Si en una supuesta corrida se insertaran los siguientes valores:
997 197 303 823 926 818 127 531 924 398, entonces el rbol lucira as:

99
7
19
7
30
3

12
7

82
3
81
8
53
1

92
6
92
4

39
8

4.9 Borrado de nodos de un rbol de bsqueda binario


Un rbol de bsqueda binario es una buena opcin para implementar una base de datos, ya que
permite realizar bsquedas rpidas, ordenaciones, inserciones, etc., aunque el tratar de borrar
un elemento del mismo resulta problemtico para el mantenimiento de la estructura rbol
mientras se realiza este proceso.
Hay muchos algoritmos para realizar un borrado y pueden variar en el grado de simplicidad y
eficiencia.
El ms simple es el que tiene una variable booleana dentro de un nodo, la cual indica si el
nodo es vlido o no, y para borrarlo, simplemente se cambia el valor de esta variable a falso,
97

Apuntes de Estructuras de Datos

es decir, es un borrado lgico, de tal manera que cuando se recorre el rbol simplemente se
brinca ese nodo. La eliminacin fsica del nodo se lleva a cabo cuando el rbol se reconstruye.
Esto puede verse como un desperdicio de espacio, pero unos cuantos bytes extras dentro de la
estructura no hacen mucha diferencia.
Por ejemplo, normalmente en una compaa existen miles de transacciones diarias como
borrados, inserciones, etc. Al final del da cuando el sistema est libre de transacciones, el
programa debe ejecutar una rutina de limpieza de esos datos borrados.
Otro enfoque es borrar el nodo fsicamente sin alterar la estructura del rbol. En este caso
pueden darse varias situaciones:
a) El nodo a borrar es un nodo hoja
b) El nodo a borrar no tiene subrbol izquierdo
c) El nodo a borrar no tiene subrbol derecho
d) El nodo a borrar tiene subrbol izquierdo y derecho
Los tres primeros casos no tienen problema. El problema del borrado viene cuando se trata de
borrar un nodo que tiene dos hijos vlidos, cul de los dos va a ser su sucesor?, es decir, cul
va a tomar el lugar del padre? ... Ninguno!!!
Cuando se trata de rboles de bsqueda binarios que tienen propiedades muy especficas, si se
necesita eliminar un nodo, una de las estrategias es reemplazar el elemento de ese nodo con el
menor elemento de su subrbol derecho, y eliminar despus el nodo del menor elemento,
modificando el enlace del hijo derecho del nodo padre del nodo con el menor valor igual al
hijo derecho del nodo con el menor valor.
Para que esto pueda ser entendido, primero se ver en forma grfica considerando el rbol
binario obtenido en el proceso anterior. Suponer que en este rbol se quiere eliminar el nodo
que tiene valor 823, el cual tiene dos hijos.

98

Apuntes de Estructuras de Datos

99
7
19
7
30
3

12
7

82
3
81
8
53
1

92
6
92
4

39
8

Entonces se debe encontrar el nodo que tenga el menor valor de su subrbol derecho, que se
obtiene recorriendo primero al subrbol derecho, y despus hacia su izquierda hasta encontrar
el nodo que no tenga un hijo izquierdo, que en este caso sera el nodo con el valor 924.
Despus se sustituye el valor del nodo a borrar, por este valor:
99
7
19
7
30
3

12
7

92
4
81
8
53
1
39
8

92
6
92
4

99

Apuntes de Estructuras de Datos

Y por ltimo, se elimina el nodo con el valor menor:

99
7
19
7
30
3

12
7

92
4
81
8

92
6

53
1
39
8

Ahora suponer que se quiere eliminar el nodo con el valor 197, que tambin tiene dos hijos. El
proceso es el mismo. Se busca a su derecha el menor valor, que es el 303, ya que este nodo no
tiene hijo izquierdo. Luego se sustituye el valor 197 por el 303:
99
7
30
3
30
3

12
7

92
4
81
8
53
1
39
8

100

92
6

Apuntes de Estructuras de Datos

Y por ltimo se elimina el nodo con el valor menor. En este caso como el nodo que tiene el
menor valor tiene hijo derecho, entonces hay que actualizar la cadena derecha del padre del
nodo menor para que apunte al hijo derecho del nodo menor:
99
7
30
3
12
7

92
4
81
8

92
6

53
1
39
8

Ahora se muestra la implementacin del mtodo para borrar un nodo, que se incluye en la
clase BinarySearchTree definida anteriormente.
public Object Borrar ( Object o ) {
Nodo p, q;
for ( p = null, q = GetRaiz ( );
q != null && ((Integer)o).intValue() != ((Integer)q.GetInfo( ) ).intValue();
p = q, q = ((Integer)o).intValue() < ((Integer)q.GetInfo( ) ).intValue()
? q.GetLeft ( ): q.GetRight ( ) );
if ( q == null )
return null;
Object x = q.GetInfo ( );
if ( q.GetLeft ( ) != null && q.GetRight ( ) != null) { // nodo con dos hijos
q.SetInfo ( BorrarMedio ( q ) );
return x;
}
if ( q.GetLeft ( ) == null && q.GetRight ( ) == null )
// nodo hoja
if ( p != null )
if ( p.GetLeft ( ) == q )
p.SetLeft ( null );
else
p.SetRight ( null );
else
101

Apuntes de Estructuras de Datos

SetRaiz ( null );
else
if ( q.GetLeft ( ) == null )
if ( q == GetRaiz ( ) )
SetRaiz ( q.GetRight ( ) );
else
if ( p.GetLeft ( ) == q )
p.SetLeft ( q.GetRight ( ) );
else
p.SetRight ( q.GetRight ( ) );
else
if ( q == GetRaiz ( ) )
SetRaiz ( q.GetLeft ( ) );
else
if ( p.GetLeft ( ) == q )
p.SetLeft ( q.GetLeft ( ) );
else
p.SetRight ( q.GetLeft ( ) );
return x;
}
public Object BorrarMedio ( Nodo q ) {
Object o;
Nodo aux = q, b = q;
q = q.GetRight ( );
while ( q.GetLeft ( ) != null ) {
aux = q;
q = q.GetLeft ( );
}
if ( aux == b )
aux.SetRight ( q.GetRight ( ) );
else
aux.SetLeft ( q.GetRight ( ) );
o = q.GetInfo ( );
return o;
}
El cdigo de prueba puede ser el siguiente:
import java.util.Scanner;
public class Main {
public static void main ( String [ ] args ) {
BinarySearchTree tree = new BinarySearchTree ( );
int n;
System.out.println ( "Numeros insertados:" );

102

// solo tiene hijo derecho

// solo tiene hijo izquierdo

Apuntes de Estructuras de Datos

for ( int i = 0 ; i < 10 ; i++ ) {


tree.CrearArbol ( n = (int) (Math.random ( ) * 1000) );
System.out.print ( n + " " );
}
System.out.println ( "\nInorden" );
tree.Print ( );
Scanner s = new Scanner ( System.in );
System.out.println ( "Numero a borrar:" );
int num = s.nextInt ( );
tree.Borrar ( num );
System.out.println ( "\nInorden" );
tree.Print ( );
System.out.println ( "Numero a borrar:" );
num = s.nextInt ( );
tree.Borrar ( num );
System.out.println ( "\nInorden" );
tree.Print ( );
System.out.println ( "Numero a borrar:" );
num = s.nextInt ( );
tree.Borrar ( num );
System.out.println ( "\nInorden" );
tree.Print ( );
}
}
Se muestra enseguida en forma grfica un ejemplo de rbol generado con este cdigo:
27
1
22
4

34
4
25
8

34
2

10
1

50
4
49
8

Despus de eliminar el valor 101 (nodo hoja):

93
2

27
1
22
4

34
4
25
8

34
2
27
1

Despus de eliminar el valor 344 (nodo con dos

49
849
8

22
hijos):
4
9

103

25
8

50
4

34
2

93
2

50
4
93

Apuntes de Estructuras de Datos

Finalmente, despus de eliminar el valor 244 (nodo con dos hijos):


27
1
25
8

49
8

34
2

50
4
93
2

Este tipo de eliminacin actualmente mejora la estructura del rbol hacindolo ms corto, (al
decrementar la profundidad del rbol) por lo que las bsquedas se hacen ms rpidas.

4.10 rboles AVL


Un rbol AVL es llamado as por las iniciales de sus inventores: Adelson-Velskii y Landis.
Bsicamente un rbol AVL es un rbol binario de bsqueda (ABB) al que se le aade una
condicin de equilibrio en la cual se establece que para todo nodo la altura de sus subrboles
izquierdo y derecho puede diferir a lo sumo en 1 y cada nodo tiene asignado un peso de
acuerdo a las alturas de sus subrboles de la siguiente manera:

Un nodo tiene un peso de +1 si su subrbol derecho es ms alto o profundo, 1 si su


subrbol izquierdo es ms alto o profundo y 0 si las alturas son las mismas.

No se trata de rboles perfectamente equilibrados, pero s son lo suficientemente equilibrados


como para que su comportamiento sea lo bastante bueno como para usarlos donde los ABB no
garantizan tiempos de bsqueda ptimos.
Un rbol AVL puede ser como el mostrado a la izquierda del siguiente grfico, pero no el de la
derecha, ya que ste ltimo viola la condicin de equilibrio en el nodo 6, ya que su subrbol
-1

-2

izquierdo tiene altura 3 6y su subrbol derecho tiene altura 1:


+1
0

4 -1

1
0

+1

8 -1

7 0

104

8 0

2
4 0

1
3

7 0

Apuntes de Estructuras de Datos

4.11 Factor de equilibrio


Cada nodo, adems de la informacin que se pretende almacenar, debe tener los dos punteros a
los rboles izquierdo y derecho, igual que los ABB, y adems un miembro nuevo: el factor de
equilibrio.
El factor de equilibrio es la diferencia entre las alturas del rbol derecho y el izquierdo:
FE(n) = altura subrbol derecho(n) altura subrbol izquierdo(n)
Un rbol binario es un AVL si y slo si cada uno de sus nodos tiene un equilibrio de 1 0. Si
alguno de los pesos de los nodos se modifica en un valor no vlido (2) debe seguirse un
esquema de rotacin para equilibrarlo.

4.12 Operaciones de los rboles AVL


La insercin y eliminacin en un rbol AVL es la misma que en el rbol ABB y slo se aaden
algunas otras operaciones como el balanceo para equilibrarlo:
1. Insercin
2. Balanceo
a. Rotacin simple a la izquierda RSI
b. Rotacin simple a la derecha RSD
c. Rotacin doble a la izquierda RDI
d. Rotacin doble a la derecha RDD
3. Supresin

105

Apuntes de Estructuras de Datos

Para la implementacin de los rboles AVL, primeramente hay que aadir en la clase Nodo, el
dato que permita almacenar el valor del equilibrio del nodo:
public class Nodo {
protected int bal;
// valor de equilibrio
protected Object info;
protected Nodo left, right;
..

4.12.1 Insercin de un nodo en un rbol AVL


En general, la insercin de nodos en un rbol AVL es igual que en un rbol ABB, la diferencia
est en que en un rbol AVL, despus de insertar un nodo se debe recorrer el rbol en sentido
hacia la raz, recalculando los valores de FE, hasta que se cumpla una de estas condiciones:

Que se llegue a la raz.

Que se encuentre un nodo con valor de FE de 2, o

Que se llegue a un nodo cuyo FE no cambie o decrezca en valor absoluto, es decir, que
cambie de 1 a 0 de 1 a 0.

En este sentido, se puede considerar que el algoritmo de insercin de nodos en rboles AVL es
una ampliacin de la insercin de nodos en un rbol ABB.

4.12.2 Equilibrio o balanceo de un rbol AVL


El algoritmo para mantener un rbol AVL equilibrado se basa en reequilibrados locales de
modo que, no es necesario explorar todo el rbol despus de cada insercin o borrado.

4.12.2.1 Rotacin simple a la izquierda (RSI)


Si el rbol est desequilibrado a la izquierda y su hijo derecho tiene el mismo signo (+),
entonces se hace rotacin simple izquierda. Esta rotacin se usar cuando el subrbol derecho
de un nodo sea 2 unidades ms alto que el izquierdo, es decir, cuando su FE sea de +2 y
adems, la raz del subrbol derecho tenga una FE de +1 0, es decir, que est cargado a la

106

Apuntes de Estructuras de Datos

derecha o est equilibrado. En esta rotacin el hijo derecho de A pasa a ser el padre de A y el
hijo izquierdo de B pasa a ser hijo derecho del padre A. De esta manera se sigue cumpliendo
con las caractersticas de un rbol ABB, es decir, sigue estando ordenado. Ejemplo:

+2

A
0

B +1

E
F 0

+1

B
C +1

A
F 0

D 0

4.12.2.2 Rotacin simple a la derecha (RSD)


Por otro lado, si el rbol est desequilibrado a la derecha y su hijo izquierdo tiene el mismo
signo () se hace rotacin sencilla a la derecha. Esta rotacin se usar cuando el subrbol
izquierdo de un nodo sea 2 unidades ms alto que el derecho, es decir, cuando su FE sea de 2.
Y adems, la raz del subrbol izquierdo tenga una FE de 1 0, es decir, que est cargado a la
izquierda o bien, equilibrado. En los siguientes ejemplos se muestra la rotacin cuando el
subrbol izquierdo tiene un FE de 1.
En esta rotacin el hijo izquierdo de A pasa a ser el padre de A y A pasa a ser el hijo derecho
de B. De igual manera que el caso anterior se sigue cumpliendo con las caractersticas de un
rbol ABB, es decir, sigue estando ordenado.
2
1
0

107

B
A

Apuntes de Estructuras de Datos

En este otro ejemplo el hijo izquierdo de A pasa a ser el padre de A y A pasa a ser el hijo
derecho de B. Como en este caso B tiene hijo derecho, este tiene que pasar a ser hijo izquierdo
de A, igualmente para seguir conservando las caractersticas de un rbol ABB:
0

2 A
1
1

1 C

E 0

A 0
0

0
4.12.2.3
D Rotacin doble a la izquierda (RDI)

Esta rotacin se usar cuando el subrbol derecho de un nodo sea 2 unidades ms alto que el
izquierdo, es decir, cuando su FE sea de 2. Y adems, la raz del subrbol derecho tenga una
FE de 1, es decir, que est cargado a la izquierda. En este caso se tienen que realizar dos
rotaciones. Primero se realiza una rotacin hacia la derecha y luego a la izquierda como se
muestra en el siguiente ejemplo:
+2

+2

A
B

+1
0
B

C
B

Aqu se muestra otro ejemplo:


+2
0

+2

1 D
0

D +1

C 0

+1

108

A
F 0

+1

Apuntes de Estructuras de Datos

4.12.2.4 Rotacin doble a la derecha (RDD)


Esta rotacin se usar cuando el subrbol izquierdo de un nodo sea 2 unidades ms alto que el
derecho, es decir, cuando su FE sea de 2. Y adems, la raz del subrbol izquierdo tenga una
FE de 1, es decir, que est cargado a la derecha, que a su vez puede tener una FE de 1, 0 1.
Enseguida se muestra un ejemplo:

2
+1

1 C

B
0

C 0

Otro ejemplo de este caso sera el siguiente:


2 A
+1

2 A
1 D

E 0

E 0
N

+1

1 B
0

1 B
0

D
A 0

109
0

Apuntes de Estructuras de Datos

4.12.3 Supresin de un nodo en un rbol AVL


La eliminacin de nodos sigue el mismo algoritmo que en los rboles ABB, solo que en un
rbol AVL se puede afectar el equilibrio de sus nodos, por lo que despus de eliminar el nodo
se debe recorrer el camino hacia la raz recalculando los valores de FE, y equilibrando el rbol
si es necesario:

Si el equilibrio del padre del nodo eliminado cambia de 0 a 1 el algoritmo concluye.

Si el padre del nodo eliminado cambio de 1 a 0, la altura del rbol ha cambiado y se


afecta el equilibrio de su abuelo.

Si el equilibrio del padre del nodo eliminado cambia de 1 a 2 hay que hacer una
rotacin, lo cual podra desequilibrar al nodo padre, por lo que a su vez, podra forzar a
realizar otros cambios (y probables rotaciones) en toda la ruta hacia arriba a medida
que se asciende hacia la raz.
Si encontramos en la ruta un nodo que cambie de 0 a 1 entonces se termina.

4.13 Grafos
Los rboles binarios proveen una forma muy til de representacin de relaciones en las que
existe una jerarqua. Es decir, un nodo es apuntado por, a lo sumo, otro nodo, y cada nodo
apunta, a lo sumo, a dos nodos ms:

110

Apuntes de Estructuras de Datos

Si quitamos la restriccin de que cada nodo pueda apuntar a lo sumo a otros dos nodos ms,
tenemos un rbol no binario:

Si quitamos la restriccin de que cada nodo pueda ser apuntado por a lo sumo otro nodo,
tenemos una estructura de datos llamada un grafo dirigido. Si reemplazamos las palabras
apuntado por est relacionado con, tenemos una estructura de datos llamada un grafo no
dirigido.
Un grafo est formado por un conjunto de nodos (vrtices) y un conjunto de lneas llamadas
aristas (o arcos) que conectan a los diferentes nodos. El conjunto de nodos se especifica
listando los nodos en notacin conjunto (dentro de parntesis o llaves). El conjunto de aristas
se especifica listando una secuencia de aristas. Cada arista se denota escribiendo los nombres
de los dos nodos que conecta entre parntesis, con una coma entre ellos.
Si el grafo es dirigido, la direccin de la lnea es indicada por el nodo que se lista primero. Si
el grafo es no dirigido, la relacin entre los dos nodos es desordenada.
Formalmente el grafo se define como:
G = (V,A)
donde:
V(G) es un conjunto finito, no vaco de vrtices y
A(G) es un conjunto de aristas (pares de vrtices)
Un rbol es un caso especial de un grafo dirigido. Un grafo no es por fuerza un rbol, pero un
rbol debe ser un grafo.

111

Apuntes de Estructuras de Datos

Existe una gran parte de matemtica formal asociada a los grafos, rea llamada Teora de
Grafos.
Aqu se presentan algunos ejemplos de grafos:

O
B
A

C
C
M
Q
U
8

Grafo no dirigido

Grafo dirigido

Grafo dirigido

V(G1)={A,B,C,D}

V(G2)={O,J,S,C,M,Q,U}

V(G3)={1,3,5,7,9,11}

A(G1)={(A,B),(A,D),

A(G2)={(O,J),(O,S),(J,C),

A(G3)={(1,3),(3,1),(5,7),

(B,C),(B,D)}

(J,M),(S,Q),(S,U)}

(5,9),(9,11),(9,9), (11,1)}

Si (Vi,Vj) es una arista en A(G), entonces Vi y Vj se dice que son adyacentes.


Un nodo n es incidente a una arista x si n es uno de los nodos en el par ordenado de nodos que
constituyen a x. El grado de un nodo es el nmero de aristas que inciden en l. El grado de
entrada de un nodo n es el nmero de aristas que llegan a n, y el grado de salida es el nmero
de aristas que salen de l.
Un grafo completo es aquel en el que todo nodo se conecta a todos los dems nodos:

112

Apuntes de Estructuras de Datos

Un grafo con peso es un grafo en el que cada arista transporta un valor y se usan para
representar situaciones en las que el valor de la conexin entre los vrtices es importante, no
slo la existencia de la conexin.
5

1
3

Se dice que hay un camino de a a b, si existe una secuencia de aristas de longitud k entre el
nodo a y el nodo b. Un camino de un nodo a s mismo se llama un ciclo.
Un grafo puede ser representado por una matriz de adyacencia o por listas de adyacencias. Si
hay N nodos en un grafo, la matriz de adyacencia ser una tabla con N filas y N columnas. El
valor en la posicin [i,j] de la tabla ser generalmente 1 si (Vi,Vj) es una arista de A, y 0 en
otro caso. Si el grafo es un grafo con peso, la celda [i,j] puede contener el peso sobre esta
arista si la arista est en A y 0 si no.

O
J
S
C
M
Q
U

O
0
0
0
0
0
0
0

J
1
0
0
0
0
0
0

S
1
0
0
0
0
0
0

C
0
1
0
0
0
0
0

Matriz de Adyacencia

113

M
0
1
0
0
0
0
0

Q
0
0
1
0
0
0
0

U
0
0
1
0
0
0
0

Apuntes de Estructuras de Datos

Nota: Esta matriz representa el primer grafo dirigido de la pgina anterior.


Las listas de adyacencia son listas enlazadas, una para cada nodo, conteniendo los nombres de
los nodos a los que el nodo est conectado. Las cabezas de cada una de estas listas se tienen en
un arreglo de punteros. Aqu tambin se considera el mismo grafo que para el ejemplo
anterior:
O

J
C
Q

S
C
M
Q
U

S
M
U

Lista de adyacencia

Una lista de adyacencia contiene todos los nodos que son adyacentes a un nodo dado, y un
nodo podra encontrarse en muchas listas de adyacencia diferentes. Pero esto requiere que
cada nodo asignado contenga un nmero variable de apuntadores dependiendo del nmero de
nodos a los cuales l es adyacente.
Una solucin: estructura multiligada, donde los nodos del grafo se representan en una lista
con nodos cabeza conteniendo informacin y 2 apuntadores; uno al siguiente nodo cabeza y el
otro a la lista de adyacencia, en la cual cada nodo representa un arco del grafo conteniendo 2
apuntadores, uno al nodo cabeza y el otro al siguiente nodo arco.
Por ejemplo, si se tiene el grafo:
B

114

Apuntes de Estructuras de Datos

La representacin en una estructura multiligada quedara como se muestra enseguida:


Nodos
cabeza
(nodos del
grafo)

g
<C,

<A,

B>

<A,

Lista de
Donde:
adyacencia
Nodo
cabecera:
(arcos)

Puntero al arco

info

E>

<D,

C>

<A,

D>

<A,

B>

E>

Puntero a nodo cabeza

Nodo arco:
Puntero al arco

Puntero a nodo cabeza

Nota: si es un arco con peso, se puede considerar el campo de informacin, de tal manera que
quedara como el nodo cabeza. Sin embargo, por simplicidad se considera que ambos nodos
tienen la misma estructura.

4.14 Operaciones bsicas


AddNode(x) - agrega un nodo del grafo
Join(a,b) agrega una arista que va del nodo a al b si an no existe ninguna que lo haga
JoinWt(a,b,x,) - agrega una arista que va del nodo a al b con peso x en un grafo con peso
Remv(a,b) elimina una arista de a a b si existe una
RemvWt(a,b,x) - elimina una arista de a a b si existe una y adems hace x igual a su peso
FindNode(x) retorna un apuntador a un nodo de encabezado, si existe
Adjacent(a,b) verdadero si b es adyacente a a y falso en caso contrario

4.15 Implementacin
public class Nodo {
protected Object info;
115

Apuntes de Estructuras de Datos

protected Nodo point, next;


public Nodo ( ) {
info = null;
point = next = null;
}
public Nodo ( Object x ) {
info = x;
point = next = null;
}
public void SetPoint ( Nodo p ) {
point = p;
}
public void SetNext ( Nodo p ) {
next = p;
}
public void SetInfo ( Object x ) {
info = x;
}
public Nodo GetPoint ( ) {
return point;
}
public Nodo GetNext ( ) {
return next;
}
public Object GetInfo ( ) {
return info;
}
public String toString ( ) {
return "" + info;
}
}
Para la clase Grafo:
public class Grafo {
protected Nodo g;

116

Apuntes de Estructuras de Datos

public Grafo ( ) {
g = null;
}
public Nodo GetG ( ) {
return g;
}
public boolean IsEmpty ( ) {
return g == null;
}
public void JoinWt ( Nodo p, Nodo q, Object wt ) { //la insercin se hace al final
// p y q son referencias a los nodos cabeza
// wt es el peso del arco
Nodo r2 = null, r = p.GetPoint ( );
// busca en la lista de adyacencia un arco de p a q
while ( r != null && r.GetPoint ( ) != q ) {
r2 = r;
r = r.GetNext ( );
}
if ( r == null ) {
// no existe el arco
r = new Nodo ( );
r.SetPoint ( q );
r.SetNext ( null );
}
r.SetInfo ( wt );
// se actualiza el peso
if ( r2 == null )
// no existe ningn arco
p.SetPoint ( r );
else
r2.SetNext ( r );
}
public void RemvWt ( Nodo p, Nodo q, Object x ) {
Nodo r2 = null, r = p.GetPoint ( );
// busca en la lista de adyacencia un arco de p a q
while ( r != null && r.GetPoint ( ) != q ) {
r2 = r;
r = r.GetNext ( );
}
if ( r != null ) {
// existe el arco entre p y q
x = r.GetInfo ( ); // toma el peso del arco
if ( r2 == null )
p.SetPoint ( r.GetNext ( ) );
// rompe el enlace con el arco
else
r2.SetNext ( r.GetNext ( ) );
}
}
117

Apuntes de Estructuras de Datos

public int Adjacent ( Nodo p, Nodo q ) {


Nodo r = p.GetPoint ( );
while ( r != null && r.GetPoint ( ) != q )
r = r.GetNext ( );
return ( r == null ? 0: 1);
}
public Nodo FindNode ( Object x ) {
Nodo p = g;
while ( p != null && p.GetInfo ( ) != x )
p = p.GetNext ( );
return p;
}
public void AddNode ( Object x ) { // las inserciones se hacen al inicio
Nodo p = new Nodo ( );
p.SetInfo ( x );
p.SetPoint ( null );
p.SetNext ( g );
g = p;
}
}
Para la clase aplicacin:
public class MiApp extends Grafo {
public void MuestraVertices ( ) {
Nodo q = g;
while ( q != null ) {
System.out.println ( q.GetInfo ( ) );
q = q.GetNext ( );
}
}
public void MuestraAristas ( Nodo q ) {
q = g.GetPoint ( );
while ( q != null ) {
System.out.println ( " Peso = " + q.GetInfo ( ) );
q = q.GetNext ( );
}
}
}
Un programa de muestra:
public class Main {
118

Apuntes de Estructuras de Datos

public static void main ( String [ ] args ) {


MiApp grafo = new MiApp ( );
char c;
int wt;
Nodo p, q;
int i, letra = 'F';
System.out.println ( "Crea vertices: " );
for ( i = 0; i < 5; i++ ) {
--letra;
c = (char) letra;
grafo.AddNode ( c );
System.out.println ( "Vertice del grafo: " + c );
}
System.out.println ( "Muestra la lista de nodos cabeza con los vrtices: ");
grafo.MuestraVertices ( );
letra = 'A';
q = p = grafo.GetG ( );
System.out.println ( "Adyacencias con A: " );
for ( i = 0; i < 4; i++ ) {
++letra;
q = q.GetNext ( );
wt = i+1;
grafo.JoinWt ( p, q, wt );
System.out.println ( "inserta adyacencia: " + (char) letra );
}
System.out.println ( "Muestra los pesos de las aristas: " );
grafo.MuestraAristas ( p );
}
}

119

También podría gustarte