Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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.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
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
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
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
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.
C
E
F
G
88
InsLeft ( p, x );
InsRight ( p, x );
90
91
93
95
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
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
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
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
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
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
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
34
4
25
8
34
2
10
1
50
4
49
8
93
2
27
1
22
4
34
4
25
8
34
2
27
1
49
849
8
22
hijos):
4
9
103
25
8
50
4
34
2
93
2
50
4
93
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.
-2
4 -1
1
0
+1
8 -1
7 0
104
8 0
2
4 0
1
3
7 0
105
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;
..
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.
106
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
107
B
A
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
+2
1 D
0
D +1
C 0
+1
108
A
F 0
+1
2
+1
1 C
B
0
C 0
2 A
1 D
E 0
E 0
N
+1
1 B
0
1 B
0
D
A 0
109
0
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
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
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)}
112
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
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
g
<C,
<A,
B>
<A,
Lista de
Donde:
adyacencia
Nodo
cabecera:
(arcos)
Puntero al arco
info
E>
<D,
C>
<A,
D>
<A,
B>
E>
Nodo arco:
Puntero al arco
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.15 Implementacin
public class Nodo {
protected Object info;
115
116
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
119