Вы находитесь на странице: 1из 87

Apuntes de Algoritmos y Estructuras de Datos

Programacin III
o
Facultad de Informtica - UNLP
a
Alejandro Santos
ale@ralo.com.ar
Curso 2013
(actualizado 29 de julio de 2013)

Prog. III


Indice general
1. Tiempo de Ejecucin
o
1.1. Tiempo de Ejecucin . . . . . . . . . . . . . . . .
o
1.2. Anlisis Asinttico BigOh . . . . . . . . . . . . .
a
o
1.2.1. Por denicin . . . . . . . . . . . . . . . .
o
1.2.2. Ejemplo 1, por denicin Big-Oh . . . . .
o
1.2.3. Por regla de los polinomios . . . . . . . . .
1.2.4. Por regla de la suma . . . . . . . . . . . .
1.2.5. Otras reglas . . . . . . . . . . . . . . . . .
1.2.6. Ejemplo 2, por denicin Big-Oh . . . . .
o
1.2.7. Ejemplo 3, por denicin Big-Oh en partes
o
1.2.8. Ejemplo 4, por denicin. . . . . . . . . .
o
1.3. Anlisis de Algoritmos y Recurrencias . . . . . . .
a
1.3.1. Expresin constante . . . . . . . . . . . .
o
1.3.2. Grupo de constantes . . . . . . . . . . . .
1.3.3. Secuencias . . . . . . . . . . . . . . . . . .
1.3.4. Condicionales . . . . . . . . . . . . . . . .
1.3.5. for y while . . . . . . . . . . . . . . . . . .
1.3.6. for y while, ejemplo . . . . . . . . . . . . .
1.3.7. Llamadas recursivas . . . . . . . . . . . .
1.3.8. Ejemplo 5, iterativo . . . . . . . . . . . . .
1.3.9. Ejemplo 6, recursivo . . . . . . . . . . . .
1.3.10. Ejemplo 7, recursivo . . . . . . . . . . . .
1.3.11. Ejemplo 8, Ejercicio 12.4 . . . . . . . . . .
1.4. Otros ejemplos . . . . . . . . . . . . . . . . . . .
1.4.1. Ejemplo 9 . . . . . . . . . . . . . . . . . .
Planteo de la recurrencia . . . . . . . . . .
Demostracin del orden O(f (n)) . . . . .
o
1.4.2. Ejemplo 10 . . . . . . . . . . . . . . . . .
Antes de empezar . . . . . . . . . . . . . .
Ejercicio . . . . . . . . . . . . . . . . . . .
3

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

7
8
8
9
9
10
10
11
11
12
13
15
15
15
15
15
15
15
17
17
18
19
21
23
23
23
25
26
26
27


INDICE GENERAL

Prog. III

Demostracin del orden O(f (n)) . . . . . . . . . . . . 29


o
1.5. Identidades de sumatorias y logaritmos . . . . . . . . . . . . . 30
2. Listas
2.1. Los Pares del Faran . . . . . . . . . . . . . . . . . . . . .
o
2.1.1. Explicacin del problema . . . . . . . . . . . . . . .
o
2.1.2. Implementacin Java . . . . . . . . . . . . . . . . .
o
2.2. PilaMin . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.1. Explicacin del problema . . . . . . . . . . . . . . .
o
2.2.2. Implementacin Java - Versin con Lista Enlazada .
o
o
2.2.3. PilaMin, menor elemento en tiempo O(1) . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

31
32
32
33
34
34
35
35

3. Arboles binarios y Generales


3.1. Evaluar arbol de expresin . . . . . . . . . . . . . . .

o
3.1.1. Explicacin del problema . . . . . . . . . . . .
o
3.1.2. Implementacin Java . . . . . . . . . . . . . .
o
3.2. M
nimo del Nivel . . . . . . . . . . . . . . . . . . . .
3.2.1. Explicacin del problema . . . . . . . . . . . .
o
3.2.2. Implementacin Java - Recursivo con Integer
o
3.2.3. Implementacin Java - Recursivo con int . . .
o
3.2.4. Implementacin Java - Iterativo . . . . . . . .
o
3.3. Trayectoria Pesada . . . . . . . . . . . . . . . . . . .
3.3.1. Forma de encarar el ejercicio . . . . . . . . . .
3.3.2. Recorrido del arbol . . . . . . . . . . . . . . .

3.3.3. Procesamiento de los nodos . . . . . . . . . .


3.3.4. Almacenamiento del resultado . . . . . . . . .
3.3.5. Detalles de implementacin . . . . . . . . . .
o
Modularizacin . . . . . . . . . . . . . . . . .
o
esHoja() . . . . . . . . . . . . . . . . . . . .
public y private . . . . . . . . . . . . . . .
Clases abstractas . . . . . . . . . . . . . . . .
3.3.6. Errores comunes en las entregas . . . . . . . .
3.4. Arboles Generales: Ancestro Comn ms Cercano . .
u
a
3.4.1. Explicacin General . . . . . . . . . . . . . .
o
3.4.2. Versin Recursiva . . . . . . . . . . . . . . . .
o

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

39
40
40
40
41
41
42
42
43
44
44
44
44
45
46
46
47
48
48
49
53
53
53

.
.
.
.

57
58
58
58
59

4. Arboles AVL
4.1. Perros y Perros y Gatos . . . .
4.1.1. Explicacin del problema
o
4.1.2. Implementacin Java . .
o
4.2. Pepe y Alicia . . . . . . . . . .
4

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.


INDICE GENERAL

Prog. III

4.2.1. Explicacin del problema . . . . . . . . . . . . . . . . . 59


o
4.2.2. Implementacin Java . . . . . . . . . . . . . . . . . . . 60
o
5. Grafos
5.1. Recorridos sobre grafos . . . . . . . .
5.2. El nmero Bacon . . . . . . . . . . .
u
5.2.1. Calcular el Nmero de Bacon
u
5.2.2. Explicacin del problema . . .
o
5.2.3. Implementacin Java - BFS .
o
5.2.4. Implementacin Java - DFS .
o
5.3. Juan Carlos Verticio . . . . . . . . .
5.3.1. Explicacin del problema . . .
o
5.3.2. Implementacin Java - DFS .
o
5.4. Virus de Computadora . . . . . . . .
5.4.1. Dibujo . . . . . . . . . . . . .
5.4.2. Caracter
sticas del grafo . . .
5.4.3. Explicacin del problema . . .
o
5.4.4. Implementacin Java - BFS .
o
5.5. Circuitos Electrnicos . . . . . . . . .
o
5.5.1. Dibujo . . . . . . . . . . . . .
5.5.2. Caracter
sticas del grafo . . .
5.5.3. Explicacin del problema . . .
o
5.5.4. Implementacin Java - BFS .
o
5.5.5. Implementacin Java - DFS .
o
5.6. Viajante de Encuestas . . . . . . . .
5.6.1. Explicacin del problema . . .
o
5.6.2. Implementacin Java . . . . .
o

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

63
64
67
68
68
68
70
72
72
73
75
75
75
75
76
78
78
78
79
79
80
83
83
83


INDICE GENERAL

Prog. III

Introduccin
o
El presente apunte es una recopilacin de explicaciones y exmenes paro
a
ciales dados como parte de los cursos 2012-2013 de Programacin III de
o
1
Ingenier en Computacin, UNLP . Actualmente se encuentra en estado de
a
o
borrador, y puede contener rro re s u omisiones, por lo que se recomienda
consultar a los docentes ante cualquier duda o ambiguedad.
Puede contactarse con el autor por e-mail a: ale@ralo.com.ar.

http://www.info.unlp.edu.ar/


INDICE GENERAL

Prog. III

Cap
tulo 1
Tiempo de Ejecucin
o


1.1. TIEMPO DE EJECUCION

1.1.

Prog. III

Tiempo de Ejecucin
o

Nos interesa analizar y comparar diferentes algoritmos para saber cul es


a
ms eciente.
a
Una forma de hacerlo es escribir el cdigo de cada algoritmo, ejecutarlos
o
con diferentes entradas y medir el tiempo de reloj que demora cada uno.
Por ejemplo, para ordenar una secuencia de nmeros existen diferentes
u
algoritmos: insercin, mergesort, etc. Se puede tomar cada uno de estos algoo
ritmos y ejecutarlos con una secuencia de nmeros, midiendo el tiempo que
u
tarda cada uno.
Sin embargo, existe otra forma de comparar algoritmos, que se conoce
como anlisis asinttico. Consiste en analizar el cdigo y convertirlo en una
a
o
o
expresin matemtica que nos diga el tiempo que demora cada uno en funcin
o
a
o
de la cantidad de elementos que se est procesando.
a
De esta forma, ya no es necesario ejecutarlos, y nos permite comparar
algoritmos en forma independiente de una plataforma en particular. En el
caso de los algoritmos de ordenacin, la funcin nos dar un valor en base a
o
o
a
la cantidad de elementos que se estn ordenando.
a

1.2.

Anlisis Asinttico BigOh


a
o

f (n) es de O(g(n)) si y solo si c tal que f (n) cg(n), n n0 .


La denicin de BigOh dice que una funcin f (n) es de orden g(n) si existe
o
o
una constante c tal que la funcin g(n) multiplicada por dicha constante acota
o
por arriba a la funcin f (n).
o
Cuando decimos acota queremos decir que todos los valores para los que
la funcin est denida son mayores. Esto es, la funcin g(n) acota a f (n)
o
a
o
si cada valor de g(n) es mayor o igual que f (n). Por ejemplo, la funcin
o
g(n) = n + 1 acota por arriba a la funcin f (n) = n ya que para todo
o
n 0, f (n) g(n).
Nuestro objetivo es encontrar un c que multiplicado por g(n) haga que se
cumpla la denicin. Si ese c existe, f (n) ser de orden g(n). Caso contrario,
o
a
si no es posible encontrar c, f (n) no es de orden g(n).
El problema entonces se reduce a encontrar el c, que algunas veces es
trivial y directo, y otras no lo es. Adems, el hecho de no poder encontrarlo
a
no quiere decir que no exista.
10


CAP
ITULO 1. TIEMPO DE EJECUCION

1.2.1.

Prog. III

Por denicin
o

Usando la denicin de BigOh, una forma de descubrir si una funcin


o
o
f (n) es de orden g(n) se traduce en realizar operaciones para despejar el
valor de c, y descubrirlo en caso que exista, o llegar a un absurdo en caso
que no exista.

1.2.2.

Ejemplo 1, por denicin Big-Oh


o

Ejemplo: 3n es de O(2n )? Primero escribimos la denicin de BigOh:


o
c, f (n) cg(n), n n0

(1.2.1)

Luego identicamos y reemplazamos las partes:


f (n) = 3n
g(n) = 2n

(1.2.2)
(1.2.3)

Una vez identicadas las partes podemos expresar la pregunta en forma


de BigOh, donde tenemos que encontrar un c que cumpla lo siguiente:
3n c2n , n n0

(1.2.4)

La segunda parte de la denicin, que dice para todo n mayor o igual a


o
n0 , es tan importante como la primera, y no hay que olvidarse de escribirlo. El valor de n0 puede ser cualquier que nos ayude a hacer verdadera la
desigualdad. Por ejemplo, en este caso se puede tomar n0 = 1.
n0 no puede ser negativo. Elegir un n0 negativo signica que estamos
calculando el tiempo de ejecucin con una cantidad negativa de elementos,
o
algo que no tiene sentido.
Eso tambin signica que las funciones siempre deben dar un valor poe
sitivo, dado que el valor de la funcin de la que se quiere calcular el orden
o
representa tiempo de ejecucin de un algoritmo. El tiempo debe ser siemo
pre positivo, y por ejemplo decir que una funcin demora 3 segundos en
o
procesar 10 elementos tampoco tiene sentido.
Se asume que 3n es de O(2n ), y se pasa el 2n dividiendo a la izquierda:
3n
c, n n0
2n
3
2

(1.2.5)

c, n n0

(1.2.6)

11

1.2. ANALISIS ASINTOTICO BIGOH

Prog. III

Se llega a un absurdo, ya que no es posible encontrar un nmero c para


u
que se cumpla la desigualdad para todo n mayor que algn n0 jo. Por lo
u
tanto es falso, 3n no es de O(2n ).
Un anlisis un poco ms cercano nos muestra que la funcion ( 3 )n es
a
a
2
siempre creciente hasta el innito, donde el l
mite:
l
m

3
2

(1.2.7)

toma el valor innito. Por ejemplo: para cualquier constante c ja, para
algunos valores de n se cumple, pero para otros no. Con c = 100,
3
2

100, n n0

(1.2.8)

Despejando el valor de n,
n

3
2

log 3
2

3
2

log 3 100, n n0

(1.2.9)

= n; log 3 100 11,35

(1.2.10)

n 11,35, n n0

log 3

(1.2.11)

n
2

No es posible que n sea menor a 11,35 y a la vez n sea mayor a algn n0 ,


u
ya que n debe ser mayor que n0 siempre.

1.2.3.

Por regla de los polinomios

En las teor se vieron algunas reglas para simplicar el trabajo:


as
Dado un polinomio P (n) de grado k, sabemos que el orden del polinomio
es O(nk ). Ejemplo: P (n) = 2n2 + 3n + 5. Como el grado del polinomio es 2,
el orden de P (n) es O(n2 ).
Esta regla solo se aplica a polinomios. Cualquier funcin que no lo
o
sea
1
habr que desarrollarla por algn otro mtodo. Ejemplo: T (n) = n 2 = n
a
u
e
no es un polinomio, y no puede aplicarse esta regla.

1.2.4.

Por regla de la suma

Si T1 (n) = O(f (n)) y T2 (n) = O(g(n)) entonces:


T1 (n) + T2 (n) = max(O(f (n)), O(g(n)))
12

(1.2.12)


CAP
ITULO 1. TIEMPO DE EJECUCION

1.2.5.

Prog. III

Otras reglas

T (n) = logk n es O(n).


T (n) = cte, donde cte es una expresin constante que no depende del
o
valor de n es O(1).
T (n) = cte f (n) es O(f (n)).

1.2.6.

Ejemplo 2, por denicin Big-Oh


o

Calcular y demostrar el orden de T (n) por denicin:


o
T (n) = 2n3 + 3n3 log(n)
Teniendo el T (n), para demostrar por denicin el BigOh se empieza
o
planteando cul puede ser, ya sea de forma intuitiva o por alguna de las
a
reglas conocidas. En este caso, el orden parecer ser: O(n3 log(n)).
a
Una vez planteado el Big-Oh posible, se aplica la denicin para vericar
o
si realmente el orden elegido corresponde con la funcin:
o
k R, T (n) kF (n), n n0

(1.2.13)

Se reemplazan T (n) y F (n) por sus correspondientes valores. La funcin


o
3
F (n) es el orden elegido, F (n) = n log(n):
2n3 + 3n3 log(n) kn3 log(n), n n0

(1.2.14)

Se pasa dividiendo n3 log(n) hacia la izquierda:


2n3 + 3n3 log(n)
k, n n0
n3 log(n)
2
+ 3 k, n n0 , n0 = 2
log(n)

(1.2.15)
(1.2.16)

Se puede ver que es posible encontrar un k jo que, sin importar el valor


tomado por n, la desigualdad sea siempre cierta.
Esto ocurre porque mientras n es ms grande, 2/ log(n) es cada vez ms
a
a
chico. Por lo tanto, la funcin nunca va a tomar un valor mayor que algn
o
u
k. En este ejemplo, k = 5 para que la desigualdad sea verdadera para todo
n n0 , con n0 = 2.
El valor de n0 no puede ser menor que 2 ya que con n = 1 se tendr una
a
divisin por cero.
o
13

1.2. ANALISIS ASINTOTICO BIGOH

Prog. III

El objetivo de la demostracin es encontrar un k jo de forma que se


o
cumpla la desigualdad, para todo n n0 , y lo encontramos. Por lo tanto,
T (n) es de orden O(n3 log(n)).

1.2.7.

Ejemplo 3, por denicin Big-Oh en partes


o

Calcular y demostrar el orden de T (n) por denicin:


o
T (n) = 5n3 + 2n2 + n log(n)

(1.2.17)

Una segunda manera de demostrar el orden a partir de un T (n) es en


partes. Se toma cada trmino por separado, y se demuestra el orden de cada
e
uno. Si en todas las partes se cumple la desigualdad, quiere decir que se
cumple en la funcin original.
o
De la misma forma que siempre, hay que elegir un orden para luego
vericar si realmente se cumple. En este caso se elige:
O(n3 )
Es importante que todos los trminos se comparen con el mismo orden,
e
de otra forma la demostracin no ser correcta.
o
a
Planteando los tres trminos por separado se tiene:
e
T1 (n) = 5n3
T2 (n) = 2n2
T3 (n) = n log(n)

(1.2.18)
(1.2.19)
(1.2.20)

Demostrando cada uno por separado:


k1 , T1 (n) k1 F (n), n n1,0
k2 , T2 (n) k2 F (n), n n2,0
k3 , T3 (n) k3 F (n), n n3,0

(1.2.21)
(1.2.22)
(1.2.23)

Reemplazando las tres desigualdades se tiene:


k1 , 5n3 k1 n3 , n n1
5n3
k1 5 k1
n3
14

(1.2.24)
(1.2.25)


CAP
ITULO 1. TIEMPO DE EJECUCION

Prog. III

k2 , 2n2 k2 n3 , n n2
2n2
2
k2 k2
3
n
n

(1.2.26)

k3 , n log(n) k3 n3 , n n3
n log(n)
log(n)
k3
k3
3
n
n2

(1.2.28)

(1.2.27)

(1.2.29)

En los tres casos se llega a que la desigualdad se cumple, siendo posible


encontrar un ki R jo para cualquier valor positivo de n, mayor que algun
ni . Por lo tanto, T (n) es de orden O(n3 ).

1.2.8.

Ejemplo 4, por denicin.


o

Cul es el orden O() de la siguiente funcin?


a
o
F (n) = n1/2 + n1/2 log4 (n)
a) Es de orden O(n1/2 )? Para que F (n) sea O(n1/2 ) hace falta encontrar
un k constante tal que se cumpla la siguiente desigualdad para todo
n algn n0 .
u

n1/2 + n1/2 log4 (n) kn1/2

(1.2.30)

Pasando dividiendo el n1/2 hacia la izquierda queda:

n1/2 n1/2 log4 (n)


+
k
n1/2
n1/2
1 + log4 (n) k

(1.2.31)
(1.2.32)

Se concluye que es falso, porque a medida que n aumenta, la parte de


la izquierda de la desigualdad 1 + log4 (n) tambin aumenta, para todo
e
n cualquier n0 , y no es posible encontrar un k constante. No es de
orden O(n1/2 ).
15

1.2. ANALISIS ASINTOTICO BIGOH

Prog. III

b) Es de orden O(log4 (n))? Para que F (n) sea O(log4 (n)) hace falta
encontrar un k constante tal que se cumpla la siguiente desigualdad
para todo n algn n0 .
u

n1/2 + n1/2 log4 (n) k log4 (n)

(1.2.33)

Pasando dividiendo el log4 (n) hacia la izquierda queda:


n1/2 log4 (n)
n1/2
+
k
log4 (n)
log4 (n)
n1/2
+ n1/2 k
log4 (n)

(1.2.34)
(1.2.35)

Se concluye que es falso, porque a medida que n aumenta, la parte de


n1/2
la izquierda de la desigualdad log (n) +n1/2 tambin aumenta, para todo
e
4
n cualquier n0 , y no es posible encontrar un k constante. No es de
orden O(log4 (n)).
c) Es de orden O(n1/2 log4 (n))? Para que F (n) sea O(n1/2 log4 (n)) hace
falta encontrar un k constante tal que se cumpla la siguiente desigualdad para todo n algn n0 .
u

n1/2 + n1/2 log4 (n) kn1/2 log4 (n)

(1.2.36)

Pasando dividiendo el n1/2 log4 (n) hacia la izquierda queda:


n1/2 log (n)
n1/2
+ 1/2 4
k
n1/2 log4 (n) n log4 (n)
1
+1k
log4 (n)

(1.2.37)
(1.2.38)

Se concluye que es verdadero, porque a medida que n aumenta, la


parte de la izquierda de la desigualdad log 1(n) + 1 va tomando cada vez
4
valores ms pequeos para todo n 2, n0 = 2. Por lo tanto, es posible
a
n
encontrar el k constante y jo que haga que se cumpla la desigualdad
y F (n) es de orden O(n1/2 log4 (n)).
16


CAP
ITULO 1. TIEMPO DE EJECUCION

1.3.

Prog. III

Anlisis de Algoritmos y Recurrencias


a

El anlisis de algoritmos consiste en convertir un bloque de cdigo o


a
o
funcin escrita en algn lenguaje de programacin a su equivalente versin
o
u
o
o
que nos permita calcular su funcin de tiempo de ejecucin T (n).
o
o
Eso es, el objetivo es encontrar cual es el tiempo de ejecucin a partir de
o
la cantidad de elementos.
El objetivo de resolver una recurrencia es convertir la funcin en su forma
o
expl
cita equivalente sin contener sumatorias ni llamadas recursivas. Para ello
hace falta aplicar las deniciones previas de sumatorias, y el mecanismo de
resolucin de recurrencias.
o

1.3.1.

Expresin constante
o

Cualquier expresin que no dependa de la cantidad de elementos a proo


cesar se marcar como constante, c1 , ..., ck .
a

1.3.2.

Grupo de constantes

Cualquier grupo de constantes c1 , ..., ck se pueden agrupar en una unica

constante c, c = (c1 + ... + ck ).

1.3.3.

Secuencias

Dadas dos o ms sentencias una despus de la otra sus tiempos se suman.


a
e

1.3.4.

Condicionales

Dado un condicional if, el tiempo es el peor caso entre todos los caminos
posibles de ejecucin.
o

1.3.5.

for y while

Los bloques for y while se marcan como la cantidad de veces que se


ejecutan, expresado como una sumatoria desde 1 a esa cantidad. En el caso
del for se usar como variable de la sumatoria el mismo indice del for. En el
a
caso del while se usar una variable que no haya sido usada.
a

1.3.6.

for y while, ejemplo

Se tiene el siguiente cdigo:


o
17


1.3. ANALISIS DE ALGORITMOS Y RECURRENCIAS

Prog. III

1 void int f ( int N) {


2
int x = 1 ;
3
while ( x < N) {
4
/ * Algo de O ( 1 ) . . . * /
5
x = x * 2;
6
}
7
return x ;
8 }
Tal como se describi, hace falta escribir el tiempo T (n) como una sumao
toria de 1 hasta la cantidad de veces que se ejecuta el while. La cantidad de
veces que ste while se ejecuta se puede plantear de la siguiente forma:
e
1. El while se ejecuta mientras se cumple la condicin x < N .
o
2. La variable x comienza en 1, en cada paso se multiplica su valor por 2,
y la ejecucin termina cuando x llega a N .
o
3. La cantidad de veces que se ejecuta el while corresponde con la cantidad
de veces que la variable x se multiplica por 2.
Si decimos que la variable k representa la cantidad de veces que se multiplica la variable x por dos, la variable x entonces vale:
x = 1 2 2 ... 2 = 2k

(1.3.1)

Adems pedimos que x sea igual a N para que el while nalice:


a
x=N

(1.3.2)

Nos interesa entonces hallar el valor de k, a n de hallar la cantida de


veces que se ejecuta el while. Eso se logra facilmente aplicando propiedades
de logaritmos:
x = 2k
x=N
2k = N
log2 (2k ) = log2 N
k = log2 N
18

(1.3.3)
(1.3.4)
(1.3.5)
(1.3.6)
(1.3.7)


CAP
ITULO 1. TIEMPO DE EJECUCION

Prog. III

Por lo tanto, el T (n) del ejemplo anterior es:


log2 N

T (n) = c1 +

(c2 )

(1.3.8)

x=1

1.3.7.

Llamadas recursivas

Las llamadas recursivas se reemplazan por la denicion de tiempo recursiva. Las deniciones recursivas son funciones denidas por partes, teniendo
por un lado el caso base y por el otro el caso recursivo.

1.3.8.

Ejemplo 5, iterativo

1 void int sumar ( int [ ] d a t o s ) {


2
int acumulado = 0 ;
3
for ( int i =0; i <d a t o s . l e n g t h ; i ++) {
4
acumulado = acumulado + d a t o s [ i ] ;
5
}
6
return acumulado ;
7 }
Linea 2: c1 .
Linea 3:

n
i=1 .

Linea 4: c2 .
Linea 6: c3 .

Todo junto, el T (n) de la funcin sumar es:


o
n

T (n) = c1 + (

c2 ) + c3

(1.3.9)

i=1

El siguiente paso es convertir el T (n) a su versin expl


o
cita sin sumatorias.
Aplicando las identidades de la seccin anterior:
o
T (n) = c1 + nc2 + c3 = c4 + nc2 , c4 = c1 + c3

(1.3.10)

Por lo tanto, el T (n) de la funcin sumar es:


o
T (n) = c4 + nc2
Cul es su orden de ejecucin BigOh? T (n) es O(n).
a
o
19

(1.3.11)


1.3. ANALISIS DE ALGORITMOS Y RECURRENCIAS

1.3.9.

Prog. III

Ejemplo 6, recursivo

1 int r e c 1 ( int n ){
2
i f ( n <= 1 )
3
return 3 ;
4
else
5
return 1 + r e c 1 ( n1) * r e c 1 ( n 1);
6 }
Las funciones recursivas se denen por partes, donde cada parte se resuelve de forma similar a un codigo iterativo:
T (n) =

c1
2T (n 1) + c2

n1
n>1

El objetivo es encontrar una versin expl


o
cita de la recurrencias. Se comienza por escribir las primeras deniciones parciales:

T (n) = 2T (n 1) + c2 , n > 1
T (n 1) = 2T (n 2) + c2 , n 1 > 1
T (n 2) = 2T (n 3) + c2 , n 2 > 1

(1.3.12)
(1.3.13)
(1.3.14)

Segundo, se expande cada trmino tres o cuatro veces, las que sean necesae

rias para descubrir la forma en la que los diferentes trminos van cambiando:
e

T (n) = 2T (n 1) + c2
T (n) = 2(2T (n 2) + c2 ) + c2
T (n) = 2(2(2T (n 3) + c2 ) + c2 ) + c2
T (n) = 2(22 T (n 3) + 2c2 + c2 ) + c2
T (n) = 23 T (n 3) + 22 c2 + 2c2 + c2 , n 3 1

(1.3.15)
(1.3.16)
(1.3.17)
(1.3.18)
(1.3.19)

La denicin recursiva contina mientras el n > 1, por lo que en k pasos


o
u
tenemos la expresin general del paso k:
o
k1

T (n) = 2k T (n k) +

2i c2
i=0

La llamada recursiva naliza cuando n k = 1, por lo tanto:


20

(1.3.20)


CAP
ITULO 1. TIEMPO DE EJECUCION

Prog. III

nk =1
k =n1

(1.3.21)
(1.3.22)

n11

T (n) = 2n1 T (1) +

2i c2

(1.3.23)

2i c2

(1.3.24)

i=0
n2

T (n) = 2n1 c1 +

i=0
n2

T (n) = 2n1 c1 + c2

2i

(1.3.25)

T (n) = 2n1 c1 + c2 (2
1)
n1
n1
T (n) = 2 c1 + 2 c2 c2
T (n) = 2n1 (c1 + c2 ) c2

(1.3.26)
(1.3.27)
(1.3.28)

i=0
n2+1

Cul es su orden de ejecucin BigOh? T (n) es O(2n ).


a
o

1.3.10.

Ejemplo 7, recursivo

T (n) =

c
2n + T (n/2)

n=1
n2

Se asume que n es potencia entera de 2.


n

k=0

1
1
=2 n
k
2
2

Deniciones parciales:
T(
T(

T (n) = 2n + T (n/2)
2n
n
n
T( ) =
+T 2
2
2
2
Desarrollo:
21

n
n
2n
)= 2 +T 3
2
2
2
2
n
2n
n
)= 3 +T 4
3
2
2
2


1.3. ANALISIS DE ALGORITMOS Y RECURRENCIAS

T (n) = 2n + T (n/2)
2n
n
T (n) = 2n +
+T 2
2
2
2n
2n
n
T (n) = 2n +
+
+T 3
2
22
2
2n 2n
2n
n
T (n) = 2n +
+ 2 +
+T 4
2
2
23
2
3

T (n) =
i=0
3

Prog. III

(1.3.29)
(1.3.30)
(1.3.31)
(1.3.32)

n
2n
+T 4
i
2
2

(1.3.33)

1
n
+T 4
i
2
2

(1.3.34)

T (n) = 2n
i=0

Paso general k:

k1

T (n) = 2n
i=0

1
n
+T k
i
2
2

(1.3.35)

Caso base:
n
= 1 n = 2k log n = k
k
2

log n1

T (n) = 2n
i=0

1
n
+ T log n
i
2
2
log n1

T (n) = 2n
i=0

T (n) = 2n 2

1
+c
2i

+c
2log n1
2n
T (n) = 4n log n1 + c
2

(1.3.36)

(1.3.37)
(1.3.38)
(1.3.39)
(1.3.40)

22


CAP
ITULO 1. TIEMPO DE EJECUCION
T (n) = 4n

2n
(2log n )/2

Prog. III
+c

2n
+c
(n)/2
4n
T (n) = 4n
+c
n
T (n) = 4n 4 + c

T (n) = 4n

1.3.11.

(1.3.41)
(1.3.42)
(1.3.43)
(1.3.44)

Ejemplo 8, Ejercicio 12.4


T (n) =

1
n
8T ( ) + n3
2

n=1
n2

Soluciones parciales:
n
T (n) = 8T ( ) + n3
2
n
n
n
T ( ) = 8T ( ) + ( )3
2
4
2
n
n
n
T ( ) = 8T ( ) + ( )3
4
8
4
n
n
n
T ( ) = 8T ( ) + ( )3
8
16
8

(1.3.45)
(1.3.46)
(1.3.47)
(1.3.48)

Desarrollo:
n
T (n) = 8T ( ) + n3
2
n
n
T (n) = 8(8T ( ) + ( )3 ) + n3
4
2
n
n 3
n
T (n) = 8(8(8T ( ) + ( ) ) + ( )3 ) + n3
8
4
2
n
n 3
n 3
n
T (n) = 8(8(8(8T ( ) + ( ) ) + ( ) ) + ( )3 ) + n3
16
8
4
2
n
n 3
n 3
n 3
T (n) = 8(8(82 T ( ) + 8( ) + ( ) ) + ( ) ) + n3
16
8
4
2
n 3
n 3
n 3
n
T (n) = 8(83 T ( ) + 82 ( ) + 8( ) + ( ) ) + n3
16
8
4
2
n
n
n
n
T (n) = 84 T ( ) + 83 ( )3 + 82 ( )3 + 8( )3 + n3
16
8
4
2
n 3
n3 = 80 ( 0 )
2
23

(1.3.49)
(1.3.50)
(1.3.51)
(1.3.52)
(1.3.53)
(1.3.54)
(1.3.55)
(1.3.56)


1.3. ANALISIS DE ALGORITMOS Y RECURRENCIAS
T (n) = 84 T (

n
n
n
n
n
) + 83 ( 3 )3 + 82 ( 2 )3 + 81 ( 1 )3 + 80 ( 0 )3
24
2
2
2
2
3

n
T (n) = 8 T ( 4 ) +
2
4

i=0

Prog. III
(1.3.57)

n
8i ( i )3
2

(1.3.58)

n
8i ( i )3
2

(1.3.59)

Caso k:
n
T (n) = 8 T ( k ) +
2

k1

i=0

Caso base:
n
= 1 n = 2k log n = log 2k = k
2k
k = log n

log n

T (n) = 8

T(

n
2log n

log n1

T (n) = 8

(1.3.61)

n3
)
(2i )3

(1.3.62)

(23 )i n3
)
23i

(1.3.63)

23i n3
)
23i

(1.3.64)

i=0
log n1

8i (
i=0
log n1

log n

n
8i ( i )3
2

)+

n
T (n) = 8log n T ( ) +
n
T (1) +

(
i=0
log n1

log n

T (n) = 8

T (1) +

(1.3.60)

(
i=0

log n1

T (n) = 8

log n

(n3 )

T (1) +

(1.3.65)

i=0

T (n) = 8log n + n3 log n

(1.3.66)

T (n) = (23 )log n + n3 log n

(1.3.67)

T (n) = 23log n + n3 log n

(1.3.68)

T (n) = (2(log n) )3 + n3 log n

(1.3.69)

T (n) = n3 + n3 log n

(1.3.70)

24


CAP
ITULO 1. TIEMPO DE EJECUCION

1.4.

Prog. III

Otros ejemplos

1.4.1.

Ejemplo 9

Sea el siguiente programa,


a. Calcular su T (n), detallando los pasos seguidos para llegar al resultado.
b. Calcular su O(f (n)) justicando usando la denicin de BigOh.
o
1 int uno ( int n ){
2
for ( int i = 1 ; i <= n ; i ++) {
3
for ( int j = 1 ; j <= i ; j ++) {
4
algo de O ( 1 ) ;
5
}
6
}
7
i f (n > 1)
8
for ( int i = 1 ; i <= 4 ; i ++)
9
uno ( n / 2 ) ;
10 }

Planteo de la recurrencia
c1

T (n) =

n
i=1

i
j=1 c2

n
+ 4T ( )
2

n=1
n2

Eliminando las sumatorias tenemos:


n

c2 =
i=1 j=1

ic2 = c2
i=1

i=
i=1

c2 n(n + 1)
c2
= (n2 + n)
2
2

c1
c2 2
n
(n + n) + 4T ( )
2
2
Desarrollo de la recurrencia:
T (n) =

T (n) =

c2 2
n
(n + n) + 4T ( )
2
2
25

(1.4.1)

n=1
n2

(1.4.2)

1.4. OTROS EJEMPLOS


=

Prog. III

c2 2
(n + n) + 4
2
=

c2
2

n
2

n
2

c2
c2 2
(n + n) + 4
2
2

n
2

n
22

+ 4T
n
2

+ 42 T

(1.4.3)
n
22

(1.4.4)

c2 2
c2
n 2
n
(n + n) + 4
+
+
2
2
2
2
c2
n 2
n
n
+ 4T 3
42
+ 2
2
2
2
2
2

(1.4.5)

c2 2
c2
n 2
n
(n + n) + 4
+
+
2
2
2
2
c2
n 2
n
n
42
+ 43 T 3
+ 2
2
2
2
2
2

(1.4.6)

c2
c2 2
n 2
n
(n + n) + 4
+
+
2
2
2
2
c2
n 2
n
42
+ 2
+
2
22
2
n 2
n
n
c2
43
+ 3
+ 4T 4
3
2
2
2
2

(1.4.7)

c2
n 2
c2 2
n
(n + n) + 4
+
+
2
2
2
2
c2
n 2
n
42
+ 2
+
2
2
2
2
n 2
n
n
c2
+ 3
+ 44 T 4
43
2
23
2
2

(1.4.8)

c2
=
2
=

c2
2

c2
=
2

4w
w=0

n
2w

n
2w

+ 44 T

n
24

(1.4.9)

4w

n2
(2w )2

+ 4w

n
2w

+ 44 T

n
24

(1.4.10)

22w

n2
22w

+ 22w

n
2w

+ 44 T

n
24

(1.4.11)

w=0
3

w=0

c2
2

n2 + 2w (n) + 44 T
w=0

26

n
24

(1.4.12)


CAP
ITULO 1. TIEMPO DE EJECUCION
c2
T (n) =
2

c2
n +
2
w=0

2w n + 44 T
w=0

Prog. III
n
24

(1.4.13)

Paso general k:

c2
T (n) =
2

k1

c2
n +
2
w=0

k1

2w n + 4k T
w=0

n
2k

(1.4.14)

Paso base:
n
=1
2k
n = 2k
log2 n = log2 (2k )
log2 n = k

log (n)1

(1.4.15)
(1.4.16)
(1.4.17)
(1.4.18)

log (n)1

c2 2
n
c2 2
n2 +
2w n + 4log2 n T log n
T (n) =
2 w=0
2 w=0
2 2
c2
n
c2
= n2 log2 (n) + (log2 (n) + 1)n + 4log2 n T log n
2
2
2 2
log2 n
2 log2 n
log2 n 2
4
= (2 )
= (2
) = n2
c2
c2
= n2 log2 (n) + (log2 (n) + 1)n + n2 T (1)
2
2
c2 2
c2
c2
T (n) = n log2 (n) + n log2 (n) + n + c1 n2
2
2
2

(1.4.19)
(1.4.20)
(1.4.21)
(1.4.22)
(1.4.23)

Demostracin del orden O(f (n))


o
Se elige el trmino de mayor orden: c22 n2 log2 (n)
e
f (n) es de O(g(n)) si y solo si w tal que f (n) wg(n), n n0 .
T (n) es de O(n2 log2 (n))?
c2 2
c2
c2
n log2 (n) + n log2 (n) + n + c1 n2 wn2 log2 (n)
2
2
2
c2 n2 log2 (n) c2 n log2 (n)
c2 n
c1 n 2
+ 2
+ 2
+ 2
w
2n2 log2 (n)
2n log2 (n) 2n log2 (n) n log2 (n)
c2
c2
c2
c1
+
+
+
w
2
2n 2n log2 (n) log2 (n)
27

(1.4.24)
(1.4.25)
(1.4.26)

1.4. OTROS EJEMPLOS

Prog. III

Dado que la parte izquierda de la desigualdad es una funcin decreciente,


o
con n0 = 2 se tiene que se cumple la denicin de BigOh, donde T (n)
o
wn2 log2 (n), n 2.
c2
c1
c2 c2 c2 c1
c2 c2
+ +
+
=
+ + +
= c2 + c1 = w(1.4.27)
2
4
4 log2 (2) log2 (2)
2
4
4
1
Por lo tanto se demuestra que el T (n) es efectivamente O(n2 log2 (n)).

1.4.2.

Ejemplo 10

Sea el siguiente programa,


a. Calcular su T (n), detallando los pasos seguidos para llegar al resultado.
b. Calcular su O(f (n)) justicando usando la denicin de BigOh.
o
1 public s t a t i c void f ( int n ) {
2
int sum=0, sum1=0, sum2=0;
3
for ( double i =1; i<= n ; i += 0 . 5 ) {
4
++sum1 ;
5
for ( double j =1; j <= sum1 ; j ++) {
6
sum2++;
7
for ( double k=j ; k <= sum1 ; k++) {
8
sum += sum1 + sum2 ;
9
}
10
}
11
}
12
return sum ;
13 }

Antes de empezar
Para plantear inicialmente este ejercicio hace falta analizar el programa.
Una estrategia interesante para resolver este tipo de ejercicios consiste en
reescribir los l
mites de los bucles, a n de conseguir un programa que se
ejecute la misma cantidad de veces que el original y calcule los mismos valores
con el mismo resultado, pero que sea ms fcil para analizar.
a a
Por ejemplo, los programas F1 y F2 son equivalentes en cuanto al valor
calculado y cantidad de veces que se ejecuta cada bucle, solo var en sus
an
constantes:
28


CAP
ITULO 1. TIEMPO DE EJECUCION

Prog. III

1 public s t a t i c void F1 ( int n ) {


2
int x = 0 ;
3
for ( int i = 1 0 ; i<= 10 * n ; i += 10 ) {
4
x += a l g o d e O 1 ( i ) ;
5
}
6
return x ;
7 }
1 public s t a t i c void F2 ( int n ) {
2
int x = 0 ;
3
for ( int i = 1 ; i <= n ; i ++) {
4
x += a l g o d e O 1 ( i * 1 0 ) ;
5
}
6
return x ;
7 }
Al momento de escribir el tiempo de ejecucin de los programas, se pueden
o
tener:
1. Sumatorias que sus variables tomen los mismos valores que los bucles
del programa.
2. Sumatorias que tengan la misma cantidad de trminos que la cantidad
e
de veces que se ejecuta cada bucle del programa.
Estas dos estrategias son equivalentes entre s Por esto, el tiempo de
.
ejecucin de ambas funciones F1 y F2 anteriores es:
o
F (n) = C1 + C2 n + C3

(1.4.28)

Donde C1 , C2 , C3 son constantes que su valor depende de la versin del


o
programa se est analizando, ya sea F1 o F2.
a
Ejercicio
A partir de esto podemos decir:
El for con
ndice i se ejecuta 2 * n - 1 veces.
La variable sum1 cuenta la cantidad de veces que se ejecuta el primer
for. Su valor comienza en 1 y termina en 2 * n - 1.
29

1.4. OTROS EJEMPLOS

Prog. III

El for con
ndice j se ejecuta tantas veces como vale la variable sum1.
Dado que el valor de la variable sum1 va cambiando a medida que se
ejecuta el programa, no es correcto decir que ste for se ejecuta 2 * n
e
veces, porque n siempre vale lo mismo pero sum1 no.
El for con
ndice k se ejecuta sum1 - j + 1 veces.
Dado que el
ndice i del primer for no se utiliza sino solo para contar
(y no aparece en ningn otro lugar del programa) podemos cambiar el
u
primer for (con
ndice i) por esto otro:
for ( int i =1; i <= ( 2 * n 1 ) ; i ++) { . . .
Con este cambio conseguimos que la variable i tome el mismo valor
que la variable sum1, manteniendo que ste for se ejecute la misma
e
cantidad de veces que el for original.
A partir de este anlisis, podemos denir la funcin de tiempo:
a
o
2n1

T (n) =

c1

(1.4.29)

(i j + 1)

(1.4.30)

i=1 j=1 k=j


2n1

T (n) = c1
i=1 j=1
2n1

T (n) = c1
i=1

j=1

i=1

(1.4.33)

T (n) = c1
i=1

i2 =

j=1

1
1
i2 i2 i + i
2
2
2n1

1
T (n) = c1
2

(1.4.31)

(1.4.32)

i=1

T (n) = c1

i(i + 1)
+i
2

i2
2n1

j+
j=1

2n1

T (n) = c1

i=0

1 2 1
i + i
2
2

2n1

2n1
2

i +
i=1

(1.4.34)

i
i=1

n(n + 1)(2n + 1)
6
30

(1.4.35)


CAP
ITULO 1. TIEMPO DE EJECUCION
2n1

2n

i2 =

i2

i=1

T (n) = c1

1
2

(2n)2

(1.4.36)

i=1

(2n)((2n) + 1)(2(2n) + 1)
(2n 1)(2n)
(2n)2 +
6
2

(1.4.37)

(4n2 + 2n)(4n + 1)
4n2 2n
4n2 +
6
2

(1.4.38)

16n3 + 4n2 + 8n2 + 2n


4n2 2n
2
4n +
6
2

(1.4.39)

16 3 4 2 8 2 2
4
2
n + n + n + n 4n2 + n2 n
6
6
6
6
2
2

(1.4.40)

T (n) = c1
1
T (n) = c1
2
T (n) = c1

Prog. III

1
2

1
2

1
2

16 3 2
n n
6
3

(1.4.41)

4
2
T (n) = c1 n3 c1 n
3
3

(1.4.42)

T (n) = c1

Demostracin del orden O(f (n))


o
Se elige el trmino de mayor orden: g(n) = n3
e
f (n) es de O(g(n)) si y solo si w tal que f (n) wg(n), n n0 .
T (n) es de O(n3 )?
4
2
c1 n3 c1 n wn3 n n0
(1.4.43)
3
3
4n3
2n
c1 3 c1 3 w
(1.4.44)
3n
3n
4
2
c1 c1 2 w
(1.4.45)
3
3n
Dado que la parte izquierda de la desigualdad es una funcin decreciente,
o
con n0 = 1 se tiene que se cumple la denicin de BigOh, donde T (n)
o
wn3 , n 1.
4
2
2
w = c1 c1 = c1
3
3
3
Por lo tanto se demuestra que el T (n) es efectivamente O(n3 ).

31

(1.4.46)

1.5. IDENTIDADES DE SUMATORIAS Y LOGARITMOS

1.5.

Prog. III

Identidades de sumatorias y logaritmos


n

c = nc

(1.5.1)

c = (n k + 1)c

(1.5.2)

i=1
n

i=k
n

n(n + 1)
2

(1.5.3)

n(n + 1)(2n + 1)
6

(1.5.4)

i=
i=1
n

i2 =
i=0
n

n(n + 1)
2

i3 =
i=0
n

i4 =
i=0

n(n + 1)(2n + 1)(3n2 + 3n 1)


30
n

i=k

(1.5.6)

k1

f (i)

f (i) =

(1.5.5)

f (i)

(1.5.7)

2i = 2n+1 1

(1.5.8)

1 an+1
1a

(1.5.9)

logc a
logc b

(1.5.10)

i=1
n

i=1

i=0
n

ai =
i=0

logb a =

a
= log a log b
b
log (a b) = log a + log b
logb (ba ) = blogb a = a
c
ab = abc = (ac )b
log

32

(1.5.11)
(1.5.12)
(1.5.13)
(1.5.14)

Cap
tulo 2
Listas

33


2.1. LOS PARES DEL FARAON

2.1.

Prog. III

Los Pares del Faran


o

Entre todas sus fascinaciones, al Faran le atrae la idea de ver qu grupos


o
e
de objetos a su alrededor se pueden dividir en dos grupos de igual tamao.
n
Como Ingeniero/a del Faran, es tu tarea escribir un programa en Java
o
que, a partir de una lista que representa la cantidad de elementos de cada
grupo, le permita al Faran descubrir cuntos grupos se pueden dividir en
o
a
dos sin tener un elemento sobrante (ni tener que dividir el elemento sobrante
por la mitad).
int c o n t a r ( L i s t a D e E n t e r o s L) { . . . }

2.1.1.

Explicacin del problema


o

Se tiene una lista de nmeros enteros, y hay que devolver la cantidad de


u
nmeros pares que aparecen en la lista.
u
El objetivo de este ejercicio es poner en prctica los conceptos de abstraa
cin, teniendo un tipo abstracto que representa una secuencia de elementos
o
sin importar de qu forma se representa internamente la estructura.
e
Es importante tener en cuenta que las listas se recorren con los mtodos
e
presentes en la prctica, sin acceder directamente al arreglo o nodos interioa
res.

34

CAP
ITULO 2. LISTAS

2.1.2.

Prog. III

Implementacin Java
o

1 int c o n t a r ( L i s t a D e E n t e r o s L) {
2
int c a n t i d a d = 0 ;
3
4
L . comenzar ( ) ;
5
while ( ! L . f i n ( ) ) {
6
i f ( ( L . elemento ( ) % 2 ) == 0 )
7
c a n t i d a d ++;
8
L . proximo ( ) ;
9
}
10
11
return c a n t i d a d ;
12 }

35

2.2. PILAMIN

2.2.

Prog. III

PilaMin

(a) Implementar una clase PilaDeEnteros donde todas las operaciones sean
de tiempo constante O(1).
(b) Implementar una subclase PilaMin que permita obtener el m
nimo elemento presente en la estructura, tambin en tiempo constante O(1).
e
1
2
3
4
5
6
7
8
9
10

class PilaDeEnteros {
public void poner ( int e ) { . . . }
public int s a c a r ( ) { . . . }
public int tope ( ) { . . . }
public boolean e s V a c i a ( ) { . . . }
}
c l a s s PilaMin extends P i l a D e E n t e r o s {
public int min ( ) { . . . }
}

2.2.1.

Explicacin del problema


o

La implementacin de PilaDeEnteros utiliza internamente una Listao


DeEnteros para almacenar los elementos de la estructura.
De esta forma, la lista se utiliza agregando y eliminando de la pila siempre
en el mismo extremo, ya sea siempre al comienzo de la estructura, o siempre
al nal.
Sim embargo, dependiendo de la implementacin concreta de ListaDeEno
teros elegida, la eciencia ser diferente. Utilizando una ListaDeEnterosCoa
nArreglos, para insertar al comienzo del arreglo es necesario hacer el corrimiento de todos los elementos de la estructura, teniendo orden lineal O(n),
con n en tamao de la estructua.
n
Algo similar ocurre en una ListaDeEnterosEnlazada, donde para insertar
al nal de la estructura es necesario recorrerla desde el comienzo para hacer
los enganches de los nodos al nal. Insertar un elemento a nal de una lista
enlazada simple es tambin una operacion de orden O(n).
e
De todas formas, es posible conseguir acceso de tiempo constante en una
lista. En un arreglo, insertar y eliminar un elemento al nal es una operacin
o
de tiempo constante, ya que sin importar la cantidad de elementos ni la
posicin a la que se accede, se puede acceder directamente y no hace falta
o
hacer ningn corrimiento de elementos.
u
36

CAP
ITULO 2. LISTAS

Prog. III

En una lista enlazada simple, insertar y eliminar en el comienzo es tambin de tiempo constante, ya que solo hace falta hacer los enganches del
e
primer nodo de la estructura junto con la referencia al primer elemento, sin
importar cuntos elementos contiene la estructura.
a

2.2.2.

Implementacin Java - Versin con Lista Enlao


o
zada

class PilaDeEnteros {
private L i s t a D e E n t e r o s E n l a z a d a L = new
ListaDeEnterosEnlazada ( ) ;
public void poner ( int e ) {
L . comenzar ( ) ;
L . a g r e g a r ( e ) ; / * Tiempo c t e . * /
}
public int s a c a r ( ) {
int x ;
L . comenzar ( ) ;
x = L . elemento ( ) ;
L . e l i m i n a r ( ) ; / * Tiempo c t e . * /
}
public int tope ( ) {
L . comenzar ( ) ;
return L . elemento ( ) ; / * Tiempo c t e . * /
}
public boolean e s V a c i a ( ) {
return (L . tamanio ( ) == 0 ) ; / * Tiempo c t e . * /
}
}

2.2.3.

PilaMin, menor elemento en tiempo O(1)

La operacin para obtener el m


o
nimo elemento de la estructura tambin
e
debe ser de tiempo constante, por lo que recorrer la lista cada vez que se
consulta el m
nimo no es correcto.
Tampoco es correcto agregar una variable que acumule el m
nimo entero
agregado, porque si bien funciona mientras se le agregan elementos a la pila,
37

2.2. PILAMIN

Prog. III

cuando se elimina un elemento de la pila ya pierde su valor. Por ejemplo:


c l a s s PilaMin extends P i l a D e E n t e r o s {
int minelem = I n t e g e r .MAX VALUE;
public void poner ( int e ) {
/ * VERSION INCORRECTA * /
i f ( e < minelem ) minelem = e ;
super . poner ( e ) ;
}
public int min ( ) {
/ * VERSION INCORRECTA * /
return minelem ;
}
}
Es posible obtener el m
nimo elemento en tiempo constante sin tener
que recorrer la lista. A medida que se van agregando elementos, el nuevo
menor es el menor elemento entre el menor actual y el nuevo elemento que
se est agregando. Pero cuando se eliminan elementos, el menor debe pasar
a
a ser el que estaba antes de agregarlo a la pila.
Por lo tanto, se puede crear una segunda pila que contenga la misma
cantidad de elementos y en la que se vaya apilando el menor en cada paso.
Entonces, el menor ser el que est en el tope de la segunda pila. Cuando se
a
e
elimina un elemento de la estructura tambien es necesario eliminarlo de la
pila de minimos.

38

CAP
ITULO 2. LISTAS

Prog. III

/ * Version f i n a l c o r r e c t a . * /
c l a s s PilaMin extends P i l a D e E n t e r o s {
private s t a t i c int MIN( int a , int b ) {
return ( a < b ) ? a : b ;
}
P i l a D e E n t e r o s minimos = new P i l a D e E n t e r o s ( ) ;
public void poner ( int e ) {
i f ( esVacia () )
minimos . poner ( e ) ;
else
minimos . poner (MIN( e , minimos . tope ( ) ) ) ;
super . poner ( e ) ;
}
public int s a c a r ( ) {
minimos . s a c a r ( ) ;
return super . s a c a r ( ) ;
}
public int min ( ) {
return minimos . tope ( ) ;
}
}

39

2.2. PILAMIN

Prog. III

40

Cap
tulo 3

Arboles binarios y Generales

41

3.1. EVALUAR ARBOL DE EXPRESION

3.1.

Prog. III

Evaluar rbol de expresin


a
o

Dado un arbol de expresin, implementar en Java una funcin que calcule

o
o
el valor de la expresin. El arbol ya se encuentra construido, es de String y
o

la funcin ser:
o
a
f l o a t e v a l u a r ( A r b o l B i n a r i o <S t r i n g > a ) { . . . }
Ejemplo: Dado el arbol 2 + 3 * 4, la funcin deber devolver el valor 14.

o
a

3.1.1.

Explicacin del problema


o

El arbol es de Strings porque hay diferentes tipos de nodos: operadores,

y valores. Los operadores pueden ser +, , , /, y los valores son nmeros.


u
Dado que cualquier subarbol de un arbol de expresin es tambin un rbol

o
e
a
de expresin, la forma ms fcil y directa de resolverlo es de forma recursiva.
o
a a
Si el rbol est vac se dispara un NullPointerException en la primer
a
a
o
llamada, pero dado que el rbol ya viene correctamente construido como un
a
arbol de expresin, todos los nodos operadores tendrn dos hijos, y los nodos

o
a
de valores sern hojas.
a

3.1.2.

Implementacin Java
o

1 f l o a t e v a l u a r ( A r b o l B i n a r i o <S t r i n g > A) {
2
S t r i n g d = A. getDatoRaiz ( ) ;
3
A r b o l B i n a r i o <S t r i n g > I = A. g e t H i j o I z q u i e r d o ( ) ;
4
A r b o l B i n a r i o <S t r i n g > D = A. g e t H i j o D e r e c h o ( ) ;
5
6
i f ( d . e q u a l s ( + ) )
7
return e v a l u a r ( I ) + e v a l u a r (D) ;
8
i f ( d . e q u a l s ( ) )
9
return e v a l u a r ( I ) e v a l u a r (D) ;
10
i f (d . equals ( * ) )
11
return e v a l u a r ( I ) * e v a l u a r (D) ;
12
i f ( d . e q u a l s ( / ) )
13
return e v a l u a r ( I ) / e v a l u a r (D) ;
14
15
return F l o a t . p a r s e F l o a t ( d ) ;
16 }

42


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES

3.2.

Prog. III

M
nimo del Nivel

Implementar en Java una funcin que dado un arbol binario de enteros


o

y un nmero de nivel, permita calcular el m


u
nimo valor del nivel indicado.
Considere la ra como nivel cero.
z
int c a l c u l a r M i n i m o ( A r b o l B i n a r i o <I n t e g e r > a , int
numeroNivel ) { . . . }

3.2.1.

Explicacin del problema


o

Hay dos formas diferentes de encarar este problema: de forma recursiva,


y de forma iterativa. Ambas son simples de entender y correctas, dando el
resultado esperado.
En la forma recursiva hace falta pasar por parmetro el arbol y el numero
a

de nivel actual. Cuando se llega al nivel esperado hay que devolver el valor
actual, y cuando se regresa de la recursin hay que comparar los minimos
o
entre los hijos izquierdo y derecho del nodo actual.
En la forma iterativa hace falta recorrer por niveles el arbol, contando el
nivel actual con la marca de null. Cuando se llega al nivel esperado hay que
buscar entre todos los valores desencolados el m
nimo de ellos. Una vez que
se terminaron los elementos o se lleg a un nivel ms que el buscado hay que
o
a
cortar la bsqueda.
u
En ambos casos, hace falta vericar que el nivel realmente exista. Una
forma de transmitirlo es devolviendo null cuando no se encontr el valor.
o
Esto solo se puede cuando se usa Integer como tipo de datos; usando int
como tipo de datos de retorno no es posible devolver null.
En el caso de usar int se puede usar la constante Integer.MAX VALUE
para indicar que el valor no se encontr.
o
En Java, el operador ternario signo de pregunta ?: es un if reducido.
La parte de la izquierda es la condicin, cuando es true se devuelve el valor
o
del medio, y cuando es false se devuelve lo que hay a la derecha del dos
puntos.
Por ejemplo, la siguiente funcin devuelve el mximo entre los valores
o
a
recibidos por parmetro:
a
1
2
3

int obtenerMaximo ( int A, int B) {


return (A > B) ? A : B ;
}

43

3.2. M
INIMO DEL NIVEL

3.2.2.

Prog. III

Implementacin Java - Recursivo con Integer


o

1 I n t e g e r c a l c u l a r M i n i m o ( A r b o l B i n a r i o <I n t e g e r > A, int


numeroNivel ) {
2
i f ( e s V a c i o (A) )
3
return null ;
4
i f ( numeroNivel == 0 )
5
return A. getDatoRaiz ( ) ;
6
7
I n t e g e r minI = c a l c u l a r M i n i m o (A. g e t H i j o I z q u i e r d o ( ) ,
numeroNivel 1 ) ;
8
I n t e g e r minD = c a l c u l a r M i n i m o (A. g e t H i j o D e r e c h o ( ) ,
numeroNivel 1 ) ;
9
10
/ * minI y minD pueden s e r n u l l * /
11
i f ( minI != null && minD != null )
12
return ( minI < minD) ? minI : minD ;
13
else
14
return ( minI == null ) ? minD : minI ;
15 }

3.2.3.

Implementacin Java - Recursivo con int


o

1 / * D e v u e l v e I n t e g e r .MAX VALUE cuando no s e e n c u e n t r a * /


2 int c a l c u l a r M i n i m o ( A r b o l B i n a r i o <I n t e g e r > A, int
numeroNivel ) {
3
i f ( e s V a c i o (A) )
4
return I n t e g e r .MAX VALUE;
5
i f ( numeroNivel == 0 )
6
return A. getDatoRaiz ( ) ;
7
8
int minI = c a l c u l a r M i n i m o (A. g e t H i j o I z q u i e r d o ( ) ,
numeroNivel 1 ) ;
9
int minD = c a l c u l a r M i n i m o (A. g e t H i j o D e r e c h o ( ) ,
numeroNivel 1 ) ;
10
11
return ( minI < minD) ? minI : minD ;
12 }

44


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES

3.2.4.

Prog. III

Implementacin Java - Iterativo


o

1 / * D e v u e l v e I n t e g e r .MAX VALUE cuando no s e e n c u e n t r a * /


2 int c a l c u l a r M i n i m o ( A r b o l B i n a r i o <I n t e g e r > A, int
numeroNivel ) {
3
i f ( e s V a c i o (A) )
4
return I n t e g e r .MAX VALUE;
5
6
ColaGenerica <A r b o l B i n a r i o <I n t e g e r >> c o l a = new
ColaGenerica <A r b o l B i n a r i o <I n t e g e r >>() ;
7
int n i v e l A c t u a l = 0 , minActual = I n t e g e r .MAX VALUE;
8
boolean s a l i r = f a l s e ;
9
10
/ * R e c o r r i d o por n i v e l e s en e l a r b o l * /
11
c o l a . poner (A) ;
12
c o l a . poner ( null ) ;
13
14
while ( ! c o l a . e s V a c i a ( ) && ! s a l i r ) {
15
A r b o l B i n a r i o <I n t e g e r > E = c o l a . s a c a r ( ) ;
16
i f (E == null ) {
17
i f ( n i v e l A c t u a l == numeroNivel ) {
18
s a l i r = true ;
19
} else {
20
n i v e l A c t u a l ++;
21
}
22
i f ( ! cola . esVacia () )
23
c o l a . poner ( null ) ;
24
} else {
25
i f ( n i v e l A c t u a l == numeroNivel && e . getDatoRaiz ( )
< minActual )
26
minActual = e . getDatoRaiz ( ) ;
27
i f ( t i e n e H i j o I z q u i e r d o (A) )
28
c o l a . poner (A. g e t H i j o I z q u i e r d o ( ) ) ;
29
i f ( t i e n e H i j o D e r e c h o (A) )
30
c o l a . poner (A. g e t H i j o D e r e c h o ( ) ) ;
31
}
32
}
33
34
return minActual ;
35 }

45

3.3. TRAYECTORIA PESADA

3.3.

Prog. III

Trayectoria Pesada

Se dene el valor de trayectoria pesada de una hoja de un rbol binario


a
como la suma del contenido de todos los nodos desde la ra hasta la hoja
z
multiplicado por el nivel en el que se encuentra. Implemente un mtodo que,
e
dado un rbol binario, devuelva el valor de la trayectoria pesada de cada una
a
de sus hojas.
Considere que el nivel de la ra es 1. Para el ejemplo de la gura: traz
yectoria pesada de la hoja 4 es: (4 3) + (1 2) + (7 1) = 21.

Figura 3.1: Ejercicio 5

3.3.1.

Forma de encarar el ejercicio

El ejercicio se puede dividir en tres partes:


1. Recorrido del arbol.

2. Procesamiento de los nodos.


3. Almacenamiento del resultado.

3.3.2.

Recorrido del rbol


a

Existen diferentes maneras de recorrer rboles, cada cual con sus ventajas
a
y desventajas. Un recorrido recursivo puede ser interesante, en especial el
preorden o inorden. Cualquiera de stos se puede aplicar al ejercicio. Por
e
ejemplo, a continuacin1 se puede ver el recorrido preorden.
o

3.3.3.

Procesamiento de los nodos

El el dibujo de ejemplo se pueden ver tres hojas. El valor de la trayectoria


de cada hoja es:
- Hoja 4: 7 1 + 1 2 + 4 3.
46


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES

Prog. III

Algorithm 1 versin en pseudocdigo de un recorrido preorden.


o
o
function R(A)
if A no es vac then
o
Procesar(A.dato)
if A tiene hijo izquierdo then
R(A.Izq)
end if
if A tiene hijo derecho then
R(A.Der)
end if
end if
end function
- Hoja 9: 7 1 + 1 2 + 9 3.
- Hoja 3: 7 1 + 3 2.
En todas las hojas, como parte del valor de la trayectoria se encuentra la
suma parcial de 7 1, y en todos los nodos hijos del nodo 1 se encuentra la
suma parcial de 1 2. En general, para un nodo cualquiera del rbol todos
a
sus hijos van a tener como parte de su trayectoria la suma parcial del camino
que existe desde la ra hasta ese nodo.
z
Esto signica que, recursivamente, podemos acumular el valor del nodo
actual multiplicado por el nmero de nivel y pasarlo por parmetro en el
u
a
llamado recursivo de ambos hijos.
Actualizando el pseudocdigo2 tenemos el recorrido preorden R con tres
o
parmetros: el rbol (A), el nmero de nivel actual (N ), y la suma acumulada
a
a
u
desde la ra hasta el nodo actual (V ):
z

3.3.4.

Almacenamiento del resultado

En ambas versiones de pseudocdigo anteriores se encuentra el llamao


do a la funcin Procesar. El enunciado pide devolver el valor de cada
o
una de las hojas del arbol, por lo que hace falta utilizar una estructura

que permite almacenar y devolver mltiples elementos. En nuestra mateu


ria, la estructura ms adecuada que tenemos se llama ListaGenerica<T>,
a
que nos permite almacenar objetos de cualquier tipo que corresponda con el
parmetro de comod de tipo. Otras opciones pueden ser ListaDeEnteros
a
n
o ColaDeEnteros.
Hace falta hacer un pequeo anlisis acerca de qu debe hacerse en
n
a
e
Procesar. El ejercicio pide almacenar el valor de la trayectoria, donde
47

3.3. TRAYECTORIA PESADA

Prog. III

Algorithm 2 versin en pseudocdigo del procesamiento de nodos.


o
o
function R(A, N, V )
if A no es vac then
o
Procesar(A.dato, N , V )
if A tiene hijo izquierdo then
R(A.Izq, N + 1, V + N A.dato)
end if
if A tiene hijo derecho then
R(A.Der, N + 1, V + N A.dato)
end if
end if
end function
ste es un valor que se calcula a partir del dato propio de cada nodo y del nie
vel, teniendo en cuenta que el dato de la hoja tambin debe ser almacenado.
e
Esto signica dos cosas, por un lado, al hacer el recorrido recursivo el dato
no puede almacenarse hasta no visitar una hoja, y por el otro el valor de la
hoja debe ser parte del resultado nal.
En Java, Procesar puede ser una llamada a un mtodo separado, o
e
puede ser simplemente el cdigo mismo. Por ejemplo:
o
1 private void P r o c e s a r ( A r b o l B i n a r i o <T> A, int N, int V,
L i s t a G e n e r i c a <I n t e g e r > l i s t a ) {
2
i f (A. esHoja ( ) ) {
3
4
l i s t a . a g r e g a r (V + ( I n t e g e r )A. getDatoRaiz ( ) * N) ;
5
}
6 }

3.3.5.

Detalles de implementacin
o

Modularizacin
o
Modularizar y separar el cdigo en diferentes funciones o mtodos suele
o
e
ser una buena idea. Hasta ahora vimos que el ejercicio se puede separar en
dos mtodos. Sin embargo, se pide devolver los valores de todas las hojas,
e
por lo que hace falta que el mtodo principal del ejercicio tenga como tipo de
e
retorno ListaGenerica<T> (o la estructura elegida). Esto puede complicar la
implementacin del recorrido preorden, por lo que puede ser de mucha ayuda
o
tener otro mtodo aparte que se encargue del recorrido recursivo. Una buena
e
forma de modularizar este ejercicio puede ser como el siguiente ejemplo:
48


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES

Prog. III

1 class EjercicioCinco {
2
public s t a t i c L i s t a G e n e r i c a <I n t e g e r >
C a l c u l a r T r a y e c t o r i a ( A r b o l B i n a r i o <T> A) {
3
L i s t a G e n e r i c a <I n t e g e r > l i s t a = new
L i s t a E n l a z a d a G e n e r i c a <I n t e g e r >() ;
4
5
R e c o r r i d o (A, 1 , 0 , l i s t a ) ;
6
7
return l i s t a ;
8
}
9
10
private s t a t i c void R e c o r r i d o ( A r b o l B i n a r i o <T> A,
int N, int V, L i s t a G e n e r i c a <I n t e g e r > l i s t a ) {
11
// Metodo d e l r e c o r r i d o R p r e o r d e n r e c u r s i v o .
12
i f ( ! A. e s V a c i o ( ) ) {
13
// . . .
14
}
15
}
16
17
private s t a t i c void P r o c e s a r ( A r b o l B i n a r i o <T> A,
int N, int V, L i s t a G e n e r i c a <I n t e g e r > l i s t a ) {
18
// . . .
19
}
20 }

esHoja()

La denicin del mtodo esHoja() de la clase ArbolBinario<T> puede


o
e
ser:
49

3.3. TRAYECTORIA PESADA

Prog. III

1 c l a s s A r b o l B i n a r i o <T> {
2
public boolean esHoja ( ) {
3
return ! t i e n e H i j o I z q u i e r d o ( ) && !
tieneHijoDerecho () ;
4
}
5
6
public boolean t i e n e H i j o I z q u i e r d o ( ) {
7
return g e t R a i z ( ) . g e t H i j o I z q u i e r d o ( ) != null ;
8
}
9
10
public boolean t i e n e H i j o D e r e c h o ( ) {
11
return g e t R a i z ( ) . g e t H i j o D e r e c h o ( ) != null ;
12
}
13 }
public y private
En Java, la forma que se tiene de abstraer y encapsular cdigo y datos
o
es mediante los especicadores de acceso. Dos de ellos son public y
private, que permiten denir desde qu parte del cdigo se puede invocar
e
o
un mtodo o acceder a una variable.
e
En el caso de este ejercicio, es importante que el mtodo principal del
e
recorrido sea public a n de que los usuarios del ArbolBinario lo puedan
utilizar. Por otro lado, tener los mtodos auxiliares como private permite
e
que los detalles de implementacin permanezcan ocultos, dejando la libertad
o
que en un futuro sea posible modicar el cdigo y los detalles de implemeno
tacin teniendo el menor impacto en el mantenimiento del resto del cdigo
o
o
donde se utilizan estas llamadas.
Clases abstractas
El operador new solo puede ser usado para instanciar clases que no
sean abstract. Por ejemplo, el siguiente programa no es correcto, ya que el
operador new se est usando para instanciar una clase abstract.
a
1 public abstract c l a s s X {
2
public X( ) {
3
// . . .
4
}
5
6
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
50


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES

Prog. III

7
X y = new X( ) ; // Error !
8
}
9 }
En particular, no es posible crear una instancia de ListaGenerica<T>,
ya que esta es una clase abstract. Es necesario instanciar una subclase que
no sea abstract. Por ejemplo, ListaEnlazadaGenerica<T>.
1 public void f ( ) {
2
L i s t a G e n e r i c a <I n t e g e r > L = new
L i s t a E n l a z a d a G e n e r i c a <I n t e g e r >() ;
3 }

3.3.6.

Errores comunes en las entregas

1. No devolver los valores. El enunciado pide devolver los valores, por


lo que imprimirlos mediante System.out.print(); no ser correcto.
a
Hace falta utilizar una estructura que permita almacenar una cantidad
variable de elementos para luego devolver la estructura completa.
2. No sumar el valor de la hoja como parte de la trayectoria. Al momento
de agregar el valor de la trayectoria hace falta incluir el valor de la hoja
multiplicado por su nivel. Manteniendo la idea explicada hasta ahora,
el siguiente resultado es incorrecto:
1 private s t a t i c void P r o c e s a r ( A r b o l B i n a r i o <T> A, int
N, int V, L i s t a G e n e r i c a <I n t e g e r > l i s t a ) {
2
i f (A. esHoja ( ) ) {
3
l i s t a . a g r e g a r (V) ; // I n c o r r e c t o , f a l t a c a l c u l a r
hoja
4
}
5 }

3. No preguntar inicialmente si el arbol es vac No solo hace falta pregun


o.
tar si el arbol tiene hijo izquierdo o hijo derecho, sino tambin si el arbol
e
original inicial tiene algn dato. Eso se logra preguntando si la raz de
u

ArbolBinario es null, o mediante el uso del mtodo esVacio().


e
51

3.3. TRAYECTORIA PESADA

Prog. III

1 private s t a t i c void R( A r b o l B i n a r i o <T> A) {


2
i f ( ! A. e s V a c i o ( ) ) {
3
// . . .
4
}
5 }
Donde el mtodo esVacio() se dene dentro de la clase ArbolBinario
e
como:
1 c l a s s A r b o l B i n a r i o <T> {
2
public boolean e s V a c i o ( ) {
3
return g e t R a i z ( ) == null ;
4
}
5 }

4. Uso del operador ++ a derecha para incrementar de nivel. El operador


++ a derecha, por ejemplo en el caso de n++, hace que el valor de
la variable n se modique, pero este incremento se realiza despus de
e
devolver el valor original. Si se usa n++ como incremento de nivel, y esta
expresin est directamente en el llamado recursivo, el efecto deseado
o
a
no se cumplir. En cada llamada recursiva, el parmetro n siempre
a
a
tomar el mismo valor, y no se estar incrementando el nivel.
a
a
1 public s t a t i c void R( A r b o l B i n a r i o <T> A, int N) {
2
i f (A. esHoja ( ) ) {
3
// El par metro N nunca l l e g a a l l l a m a d o
a
r e c u r s i v o con e l v a l o r incrementado .
4
} else {
5
i f (A. t i e n e H i j o I z q u i e r d o ( ) ) R(A.
g e t H i j o I z q u i e r d o ( ) , N++) ;
6
i f (A. t i e n e H i j o D e r e c h o ( ) ) R(A. g e t H i j o D e r e c h o ( ) ,
N++) ;
7
}
8 }

5. Pasar por parmetro el rbol en un mtodo no static de la clase


a
a
e
ArbolBinario genera que se esten recorriendo dos rboles a la vez.
a
Por un lado, el rbol que llega por parmetro, y por el otro el arbol de
a
a
this.
52


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES

Prog. III

1 c l a s s A r b o l B i n a r i o <T> {
2
public L i s t a G e n e r i c a <I n t e g e r > T r a y e c t o r i a (
A r b o l B i n a r i o <T> A) {
3
// Ac hay dos r b o l e s . t h i s y A .
a
a
4
}
5 }
La solucin a esto puede ser:
o
a) Indicar los mtodos de recorrido como static,
e
b) Quitar el parmetro y hacer el recorrido del arbol con this.
a
c) Implementar los mtodos en una clase diferente a ArbolBinario,
e
por ejemplo dentro de la clase EjercicioCinco.
6. Preguntar por la existencia del hijo izquierdo o derecho a partir del
valor devuelto por getHijoIzquierdo() de ArbolBinario.
1 c l a s s A r b o l B i n a r i o <T> {
2
public A r b o l B i n a r i o <T> g e t H i j o I z q u i e r d o ( ) {
3
return new A r b o l B i n a r i o <T>( g e t R a i z ( ) .
getHijoIzquierdo () ) ;
4
}
5 }
De acuerdo a la denicin vista en clase de ArbolBinario, los mtoo
e
dos getHijoIzquierdo y getHijoDerecho de ArbolBinario nunca devuelven null, y por lo tanto la siguiente pregunta nunca va a ser True,
porque el operador new nunca devuelve null.
1 i f ( a r b o l . g e t H i j o I z q u i e r d o ( )==null ) {
2
// Nunca puede s e r True .
3 }
Lo ideal en este caso es utilizar los mtodos tieneHijoIzquierdo y
e
tieneHijoDerecho, implementados dentro de la clase ArbolBinario
tal como se muestra en la seccin Detalles de implementacin.
o
o
7. Variables en null. El siguiente cdigo dispara siempre un error de
o
NullPointerException. Si la condicin del if da True entonces lista
o
es null, y de ninguna forma es posible utilizar la variable lista porque justamente su valor es null.
53

3.3. TRAYECTORIA PESADA

Prog. III

1 i f ( l i s t a==null )
2 {
3
l i s t a . a g r e g a r ( sum ) ;
4 }
8. Preguntar si this es null. El lenguaje Java garantiza que this
jams pueda ser null, ya que al momento de invocar un mtodo de
a
e
instancia en una variable con referencia null, se dispara un error de
NullPointerException.

54


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES

3.4.

Prog. III

Arboles Generales: Ancestro Com n ms


u
a
Cercano

Sea un rbol general que representa un arbol genealgico de una familia,


a

o
donde la ra es el pariente inmigrante que lleg en barco desde un pa lejano,
z
o
s
y la relacin de hijos son los hijos de cada persona.
o
Escribir en Java un mtodo que, dado un arbol general de Strings y
e

dos nombres de personas, devuelva el nombre del ancestro en comn ms


u
a
cercano a ambas personas. Esto es, la ra siempre ser ancestro en comn
z
a
u
de cualquier par de nodos del rbol, pero no necesariamente es el ms cercano.
a
a
Asuma que no hay dos personas que se llamen igual en la familia (tradicin
o
familiar), y en caso que no exista relacin en comn entre ambas personas,
o
u
por ejemplo que una de las personas no gure en el arbol, devolver null.

1 S t r i n g ancestroComun ( ArbolGeneral<S t r i n g > A, S t r i n g


Nombre1 , S t r i n g Nombre2 ) { . . . }

3.4.1.

Explicacin General
o

El problema se reduce a encontrar el nodo con mayor nmero de nivel


u
que los elementos indicados formen parte de los nodos de sus subrboles, sin
a
importar en qu parte del subrbol se encuentren.
e
a
Si alguno de los dos elementos indicados es la ra misma, dado que la
z
ra no tiene ancestro deber devolverse null. Adems, pueden existir varios
z
a
a
nodos que posean ambos elementos en sus subrboles, por ejemplo la ra es
a
z
ancestro comn de cualquier par de nodos del arbol, es por eso que se busca
u

el de mayor nivel.

3.4.2.

Versin Recursiva
o

1 class X {
2
public s t a t i c S t r i n g ancestroComun ( ArbolGeneral<
S t r i n g > A, S t r i n g Nom1, S t r i n g Nom2) {
3
i f (A. e s V a c i o ( ) )
4
return null ;
5
6
boolean t i e n e N 1 = f a l s e , t i e n e N 2 = f a l s e ;
7
L i s t a G e n e r i c a <ArbolGeneral<S t r i n g >> L = A. g e t H i j o s
() ;
8
S t r i n g a n c e s t r o = null ;
55

3.4. ARBOLES GENERALES: ANCESTRO COMUN MAS CERCANOIII


Prog.
9
10

// Primero me f i j o s i a l g u n sub r b o l t i e n e un nodo


a
ancestro .
L . comenzar ( ) ;
while ( ! L . f i n ( ) && a n c e s t r o == null ) {
a n c e s t r o = ancestroComun (L . elemento ( ) , Nom1, Nom2
);
L . proximo ( ) ;
}

11
12
13
14
15
16
17

// En caso de t e n e r l o , d e v u e l v o e s t e , ya que hay


que d e v o l v e r e l de mayor n i v e l .
i f ( a n c e s t r o != null )
return a n c e s t r o ;

18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

// S i no hay h i j o que s e a a n c e s t r o me f i j o s i e l
nodo a c t u a l e s a n c e s t r o ; donde N1 y N2 ambos
e s t e n en a l g u n o de mis sub r b o l e s .
a
L . comenzar ( ) ;
while ( ! L . f i n ( ) ) {
t i e n e N 1 = t i e n e N 1 | | e s H i j o (L . elemento ( ) , Nom1) ;
t i e n e N 2 = t i e n e N 2 | | e s H i j o (L . elemento ( ) , Nom2) ;
i f ( t i e n e N 1 && t i e n e N 2 )
break ;
L . proximo ( ) ;
}
i f ( t i e n e N 1 && t i e n e N 2 )
return A. getDatoRaiz ( ) ;
return null ;
}
private s t a t i c boolean e s H i j o ( ArbolGeneral<S t r i n g > A,
S t r i n g dato ) {
// D e v u e l v e True s i a l g u n nodo d e l s u b a r b o l t o t a l
p o s e e como d a t o e l parametro Dato
i f (A. e s V a c i o ( ) )
return f a l s e ;
i f (A. getDatoRaiz ( ) . e q u a l s ( dato ) )
return true ;

56


CAP
ITULO 3. ARBOLES BINARIOS Y GENERALES
42
43
44
45
46
47
48
49
50
51
}
52 }

Prog. III

L i s t a G e n e r i c a <ArbolGeneral<S t r i n g >> L = A. g e t H i j o s
() ;
L . comenzar ( ) ;
while ( ! L . f i n ( ) ) {
i f ( e s H i j o (L . elemento ( ) , dato ) ) {
return true ;
}
L . proximo ( ) ;
}
return f a l s e ;

57

3.4. ARBOLES GENERALES: ANCESTRO COMUN MAS CERCANOIII


Prog.

58

Cap
tulo 4

Arboles AVL

59

4.1. PERROS Y PERROS Y GATOS

4.1.

Prog. III

Perros y Perros y Gatos

A partir de una lista de palabras, implemente en Java una funcin que,


o
de forma eciente y usando un AVL, determine la cantidad de palabras
diferentes en la secuencia.
Ejemplo: si la secuencia es: [Perro, Gato, Perro, Perro, Gato],
la funcin deber devolver 2 porque si bien hay 5 elementos, solo Perro y
o
a
Gato son las dos palabras diferentes que aparecen.

4.1.1.

Explicacin del problema


o

Hay que descartar las palabras repetidas y contar las diferentes. Eso se
logra recorriendo la lista de entrada; por cada palabra agregarla al arbol solo

si no se encuentra, contando la cantidad de palabras agregadas o devolviendo


el tamao nal del arbol.
n

4.1.2.

Implementacin Java
o

1 int c o n t a r ( L i s t a G e n e r i c a <S t r i n g > L) {


2
ArbolAVL<S t r i n g > A = new ArbolAVL<S t r i n g >() ;
3
int c a n t i d a d = 0 ;
4
5
L . comenzar ( ) ;
6
while ( ! L . f i n ( ) ) {
7
i f ( ! A. i n c l u y e (L . elemento ( ) ) {
8
A. i n s e r t a r (L . elemento ( ) ) ;
9
c a n t i d a d ++;
10
}
11
L . proximo ( ) ;
12
}
13
14
return c a n t i d a d ;
15 }

60


CAP
ITULO 4. ARBOLES AVL

4.2.

Prog. III

Pepe y Alicia

Pepe y Alicia quieren saber cules son sus intereses en comn. Para ello,
a
u
cada uno arm una lista de intereses, pero llegaron a tener miles cada uno.
o
Implemente en Java un programa que dadas dos listas de String devuelva
de forma eciente una lista con los intereses en comn, sin elementos repetiu
dos. Los intereses de cada uno pueden estar repetidos, pero el resultado debe
tener uno solo de cada tipo. No hace falta implementar las estructuras.
Ejemplo: A Pepe le gusta [A, B, C, A, B] y a Alicia [A, D, A]. Sus intereses en comn son: [A].
u
L i s t a G e n e r i c a <S t r i n g > interesesComun ( L i s t a G e n e r i c a <
S t r i n g > P , L i s t a G e n e r i c a <S t r i n g > A) { . . . }

4.2.1.

Explicacin del problema


o

Es una interseccin entre dos conjuntos, devolviendo los elementos que


o
pertenecen a ambas listas. Hace falta tener en cuenta que el enunciado pide
que sea de forma eciente. Es por ello que hace falta utilizar alguna estructura
que permita mejorar el tiempo de ejecucin.
o
Una primer solucin puede ser recorrer la primer lista, y por cada elemento
o
recorrer la segunda lista vericando si se encuentra en esta. Por ejemplo:
L i s t a G e n e r i c a <S t r i n g > interesesEnComun ( L i s t a G e n e r i c a <
S t r i n g > P , L i s t a G e n e r i c a <S t r i n g > A) {
/* S o l u c i o n i n e f i c i e n t e */
L i s t a G e n e r i c a <S t r i n g > r e s u l t a d o = new L i s t a G e n e r i c a <
S t r i n g >() ;
for (P . comenzar ( ) ; ! P . f i n ( ) ; P . s i g u i e n t e ( ) ) {
S t r i n g E = P . elemento ( ) ;
i f (A. i n c l u y e (E) && ! r e s u l t a d o . i n c l u y e (E) )
r e s u l t a d o . a g r e g a r (P . elemento ( ) ) ;
}
return r e s u l t a d o ;
}
Sin embargo, esta solucin no es eciente ya que por cada elemento de la
o
primer lista se recorre la segunda lista, dando un orden de ejecucin O(n2 ),
o
ya que la funcin incluye() de la clase ListaGenerica<T> debe recorrer
o
toda la estructura preguntando por cada elemento.
Para mejorar el tiempo de ejecucin, una mejor solucin puede ser utilizar
o
o
una estructura de datos intermedia, por ejemplo un rbol AVL.
a
61

4.2. PEPE Y ALICIA

Prog. III

Hace falta utilizar dos rboles AVL. El primer arbol contendr los elemena

a
tos de la primer lista, permitiendo conocer de forma eciente qu elemento
e
se encuentran en sta lista. Primero, hace falta guardar los elementos de la
e
primer lista en el primer arbol AVL sin repeticin. Luego, recorriendo la se
o
gunda lista se pregunta si cada elemento de esta se encuentra en el primer
arbol pero no en el segundo. En caso de que se cumpla la condicin, se inserta
o
en el segundo arbol AVL y adems en una lista temporal.

a
La lista temporal contendr los elementos que se encuentran en ambas
a
listas iniciales, sin repeticin, y ser construida en tiempo O(n log n), con
o
a
n siendo la suma de la cantidad de elementos de ambas listas iniciales. El
segundo arbol contendr los elementos que se encuentran en la lista temporal,

a
permitiendo una bsqueda eciente para vericar si ya fueron agregados a la
u
lista resultante.

4.2.2.

Implementacin Java
o

1 class I n t e r e s e s {
2
L i s t a G e n e r i c a <S t r i n g > interesesComun ( L i s t a G e n e r i c a <
S t r i n g > P , L i s t a G e n e r i c a <S t r i n g > A) {
3
L i s t a G e n e r i c a <S t r i n g > r e s u l t a d o = new
L i s t a E n l a z a d a G e n e r i c a <S t r i n g >() ;
4
ArbolAVL<S t r i n g > lasDePepe = new ArbolAVL<S t r i n g >()
;
5
ArbolAVL<S t r i n g > enComun = new ArbolAVL<S t r i n g >() ;
6
7
P . comenzar ( ) ;
8
while ( ! P . f i n ( ) ) {
9
i f ( ! lasDePepe . i n c l u y e (P . elemento ( ) ) ) {
10
lasDePepe . a g r e g a r (P . elemento ( ) ) ;
11
}
12
P. siguiente () ;
13
}
14
15
A. comenzar ( ) ;
16
while ( ! A. f i n ( ) ) {
17
i f ( lasDePepe . i n c l u y e (A. elemento ( ) ) && ! enComun .
i n c l u y e (A. elemento ( ) ) ) {
18
enComun . a g r e g a r (A. elemento ( ) ) ;
19
r e s u l t a d o . a g r e g a r (A. elemento ( ) ) ;
20
}
62


CAP
ITULO 4. ARBOLES AVL

Prog. III

21
A. s i g u i e n t e ( ) ;
22
}
23
24
return r e s u l t a d o ;
25
}
26 }

63

4.2. PEPE Y ALICIA

Prog. III

64

Cap
tulo 5
Grafos

65

5.1. RECORRIDOS SOBRE GRAFOS

5.1.

Prog. III

Recorridos sobre grafos

Existen dos tipos de recorridos sobre grafos: recorrido en profundidad


(DFS), y recorrido en amplitud (BFS).
El recorrido tipo DFS se ejecuta de forma recursiva sobre cada nodo del
grafo haciendo una bsqueda exhaustiva por cada posible camino que se
u
puede tomar entre un nodo inicial y el resto de los nodos del grafo.
La forma general de un recorrido DFS es:
1 void DFS( V e r t i c e V, int peso ) {
2
v i s i t a d o s [V. p o s i c i o n ( ) ] = true ;
3
4
L i s t a G e n e r i c a ADY = V. o b t e n e r A d y a c e n t e s ( ) ;
5
for (ADY. comenzar ( ) ; !ADY. f i n ( ) ; ADY. proximo ( ) ) {
6
A r i s t a E = ADY. elemento ( ) ;
7
i f ( ! v i s i t a d o s [E. verticeDestino () . posicion () ] )
8
DFS(E . v e r t i c e D e s t i n o ( ) , peso + E . peso ( ) ) ;
9
}
10
11
v i s i t a d o s [V. p o s i c i o n ( ) ] = f a l s e ;
12 }
Es importante tener en cuenta que para poder hacer la bsqueda de foru
ma exhaustiva, por cada adyacente de cada nodo hace falta iniciar de forma
recursiva el recorrido. De esta forma, se evalan todas las posibles combiu
naciones de nodos y aristas posibles del grafo, partiendo desde un vrtice
e
inicial.
Tambin es importante marcar los nodos como visitados al iniciar la ree
cursin, preguntar si el nodo no se encuentra actualmente visitado en el
o
recorrido de adyacentes, y por ultimo desmarcar el visitado al terminar la

recursin.
o
A diferencia del DFS, en un recorrido BFS no se evalan todas las posibles
u
combinaciones de caminos. En un DFS, cada vrtice puede ser visitado ms
e
a
de una vez, mientras que en un BFS cada vrtice puede ser visitado a lo sumo
e
una unica vez.

Por un lado, esto permite que en general los recorridos BFS sean ms
a
ecientes que un recorrido DFS. Por otro lado, no siempre es posible resolver
cualquier recorrido con un BFS, no dejando otra opcin que hacerlo aplicando
o
un recorrido DFS.
La forma general de un recorrido BFS es:
1 void BFS( V e r t i c e V) {
66

CAP
ITULO 5. GRAFOS
2

Prog. III

ColaGenerica <V e r t i c e > C = new ColaGenerica <V e r t i c e >()


;
C. poner (V) ;

3
4
5
6
7
8
9
10
11
12
13
14
15
16 }

while ( ! C. e s V a c i a ( ) ) {
V e r t i c e W = C. s a c a r ( ) ;
L i s t a G e n e r i c a ADY = W. o b t e n e r A d y a c e n t e s ( ) ;
for (ADY. comenzar ( ) ; !ADY. f i n ( ) ; ADY. proximo ( ) ) {
A r i s t a E = ADY. elemento ( ) ;
i f ( ! v i s i t a d o s [E. verticeDestino () . posicion () ] ) {
v i s i t a d o s [ E . v e r t i c e D e s t i n o ( ) . p o s i c i o n ( ) ] = true
;
C. poner (E . v e r t i c e D e s t i n o ( ) ) ;
}
}
}

Para calcular el camino m


nimo en un grafo no pesado es posible utilizar un recorrido BFS, modicando el anterior ejemplo general de forma que
adems de marcar como true el adyacente en visitado, tambin actualizar la
a
e
distancia. Hacer un recorrido BFS en un grafo no pesado garantiza que al
nalizar el recorrido la tabla de distancias tendr el camino m
a
nimo siempre.
Tambin es posible realizar un recorrido DFS para hacer el clculo del
e
a
camino m
nimo en un grafo no pesado, teniendo en cuenta la desventaja de
que el tiempo de ejecucin ser mayor con respecto a un recorrido BFS.
o
a
Sin embargo, en un grafo pesado, ya sea con aristas positivas o negativas,
en general no es posible calcular el camino m
nimo mediante el algoritmo
general de BFS.
En un grafo pesado con aristas todas no negativas, hace falta utilizar el
Algoritmo de Camino M
nimo de Dijkstra, reemplazando la ColaGenerica
por una cola de prioridad, y teniendo el peso del vrtice calculado hasta el
e
momento como valor de prioridad del nodo. Utilizar una Heap como estructura de cola de prioridad permite implementar el algoritmo de Dijkstra de la
forma ms eciente, O(n log n).
a
De otro modo, si el grafo es pesado con aristas negativas, es necesario
utilizar un recorrido DFS que verique exhaustivamente todas las posibles
combinaciones de caminos del grafo.
Por ejemplo, para calcular el camino m
nimo en un grafo pesado mediante
un DFS:
1 void DFS( V e r t i c e V, int peso ) {
67

5.1. RECORRIDOS SOBRE GRAFOS


2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 }

Prog. III

v i s i t a d o s [V. p o s i c i o n ( ) ] = true ;
i f ( d i s t a n c i a s [V. p o s i c i o n ( ) ] > peso ) {
d i s t a n c i a s [V. p o s i c i o n ( ) ] = peso ;
}
L i s t a G e n e r i c a ADY = V. o b t e n e r A d y a c e n t e s ( ) ;
for (ADY. comenzar ( ) ; !ADY. f i n ( ) ; ADY. proximo ( ) ) {
A r i s t a E = ADY. elemento ( ) ;
i f ( ! v i s i t a d o s [E. verticeDestino () . posicion () ] )
DFS(E . v e r t i c e D e s t i n o ( ) , peso + E . peso ( ) ) ;
}
v i s i t a d o s [V. p o s i c i o n ( ) ] = f a l s e ;

68

CAP
ITULO 5. GRAFOS

5.2.

Prog. III

El n mero Bacon
u

Kevin Norwood Bacon (n. el 8 de julio de 1958) es un actor de cine


y teatro estadounidense notable por sus papeles en National Lampoons Animal House, Diner, The Woodsman, Friday the 13th,
Hollow Man, Tremors y Frost/Nixon.
Bacon ha ganado un Premios Globo de Oro y Premios del Sindicato de Actores, estuvo nominado para un Premio Emmy, y fue
nombrado por The Guardian, como uno de los mejores actores

que no ha recibido una nominacin al Premio Oscar.1


o

Hace algunos aos en una entrevista, Kevin Bacon coment que l hab
n
o
e
a
actuado con la mayor de los actores de Hollywod, ya sea en la misma
a
pel
cula o indirectamente con otro actor.
Ah naci el Nmero de Bacon, con la idea de establecer cual era la

o
u
distancia entre Bacon y el resto de los actores de Hollywood2 . Los actores
que hab actuado en alguna pel
an
cula junto a Bacon tienen el nmero 1, los
u
que actuaron en alguna pel
cula junto a alguien que actu con Bacon tienen
o
el nmero 2, y as sucesivamente.
u

Por ejemplo, el actor Sean Penn tiene como Nmero de Bacon el 1 ya


u
que actuaron juntos en la pel
cula R M
o stico, mientras que Sean Connery
tiene como Nmero de Bacon el 2, ya que si bien no actuaron juntos en
u
ninguna pel
cula, ambos actuaron junto al actor John Lithgow en al menos
pel
cula (Sean Connery actu en A Good Man in Africa, y Kevin Bacon
o
en Footloose. John Lithgow actu en ambas pel
o
culas.)
1
2

Imagen y texto de Wikipedia, http://es.wikipedia.org/wiki/Kevin_Bacon


Bacon tom esta idea y fund una organizacin de caridad, www.sixdegrees.org
o
o
o

69


5.2. EL NUMERO BACON

5.2.1.

Prog. III

Calcular el N mero de Bacon


u

The Oracle Of Bacon3 es una pgina web que permite ver cul es la
a
a
relacin entre dos actores a partir de las pel
o
culas que ambos actuaron. Cmo
o
funciona?

5.2.2.

Explicacin del problema


o

Dado el nombre de un actor, indicar cual es su Nmero de Bacon. Eso es,


u
cul es la m
a
nima distancia que se puede establecer entre Kevin Bacon y el
segundo actor, relacionados por las pel
culas en las que actuaron.
El problema se puede modelar como un grafo, teniendo a cada actor de
Hollywood como un nodo del grafo, y cada pel
cula en la que actuaron juntos
como una arista del grafo.
Entonces el Nmero de Bacon se puede calcular con un recorrido sobre
u
el grafo, calculando la m
nima distancia entre los dos actores. Eso se logra
facilmente con un algoritmo de camino m
nimo en un grafo no pesado, ya
sea BFS o DFS.

5.2.3.

Implementacin Java - BFS


o

1 c l a s s BFS {
2
/ * Buscar e l v e r t i c e d e l a c t o r * /
3
private V e r t i c e <S t r i n g > b u s c a r A c t o r ( Grafo<S t r i n g > G,
String actor ) {
4
L i s t a G e n e r i c a <V e r t i c e <S t r i n g >> v e r t i c e s = G.
listaDeVertices () ;
5
v e r t i c e s . comenzar ( ) ;
6
while ( ! v e r t i c e s . f i n ( ) ) {
7
V e r t i c e <S t r i n g > v = v e r t i c e s . elemento ( ) .
verticeDestino () ;
8
i f ( v . dato ( ) . e q u a l s ( a c t o r ) )
9
return v ;
10
v e r t i c e s . proximo ( ) ;
11
}
12
return null ;
13
}
14
15
/ * Camino minimo en un g r a f o no pesado con BFS * /
3

http://oracleofbacon.org

70

CAP
ITULO 5. GRAFOS
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

int numeroDeBacon ( Grafo<S t r i n g > G, S t r i n g a c t o r ) {


V e r t i c e <S t r i n g > i n i c i a l = b u s c a r A c t o r (G, Kevin
Bacon ) ;
V e r t i c e <S t r i n g > d e s t i n o = b u s c a r A c t o r (G, a c t o r ) ;
int N = G. l i s t a D e V e r t i c e s ( ) . tamanio ( ) ;
ColaGenerica <V e r t i c e <S t r i n g >> c = new ColaGenerica <
V e r t i c e <S t r i n g >>() ;
boolean v i s i t a d o s [ ] = new boolean [N ] ;
int d i s t a n c i a s [ ] = new int [N ] ;
for ( int i = 0 ; i < N; ++i ) {
visitados [ i ] = false ;
d i s t a n c i a s [ i ] = I n t e g e r .MAX VALUE;
}
distancias [ i n i c i a l . posicion () ] = 0;
c o l a . poner ( i n i c i a l ) ;
while ( ! c o l a . e s V a c i a ( ) ) {
V e r t i c e <S t r i n g > v = c o l a . s a c a r ( ) ;
i f ( v == d e s t i n o ) / * Actor e n c o n t r a d o * /
return d i s t a n c i a s [ v . p o s i c i o n ( ) ] ;
L i s t a G e n e r i c a <A r i s t a <S t r i n g >> a d y a c e n t e s = v .
obtenerAdyacentes ( ) ;
a d y a c e n t e s . comenzar ( ) ;
while ( ! a d y a c e n t e s . f i n ( ) ) {
V e r t i c e <S t r i n g > w = a d y a c e n t e s . elemento ( ) .
verticeDestino () ;
int nuevaDist = d i s t a n c i a s [ v . p o s i c i o n ( ) ] + 1 ;

40
41
42
43
44
45
46
47
48
49
50
51
52

Prog. III

i f ( ! v i s i t a d o [w. p o s i c i o n ( ) ] ) {
v i s i t a d o [w. p o s i c i o n ( ) ] = true ;
d i s t a n c i a s [w. p o s i c i o n ( ) ] = nuevaDist ;
c o l a . poner (w) ;
}
a d y a c e n t e s . proximo ( ) ;
}
}
71


5.2. EL NUMERO BACON
53
54
55
56
}
57 }

Prog. III

/ * El a c t o r no e s t a r e l a c i o n a d o por ninguna
p e l i c u l a con Kevin Bacon * /
return I n t e g e r .MAX VALUE;

5.2.4.

Implementacin Java - DFS


o

1 c l a s s DFS {
2
private boolean v i s i t a d o s [ ] ;
3
private int d i s t a n c i a s [ ] ;
4
5
/ * Buscar e l v e r t i c e d e l a c t o r * /
6
private V e r t i c e <S t r i n g > b u s c a r A c t o r ( Grafo<S t r i n g > G,
String actor ) {
7
L i s t a G e n e r i c a <V e r t i c e <S t r i n g >> v e r t i c e s = G.
listaDeVertices () ;
8
v e r t i c e s . comenzar ( ) ;
9
while ( ! v e r t i c e s . f i n ( ) ) {
10
V e r t i c e <S t r i n g > v = v e r t i c e s . elemento ( ) .
verticeDestino () ;
11
i f ( v . dato ( ) . e q u a l s ( a c t o r ) )
12
return v ;
13
v e r t i c e s . proximo ( ) ;
14
}
15
return null ;
16
}
17
18
/ * Camino minimo en un g r a f o no pesado con DFS * /
19
public int numeroDeBacon ( Grafo<S t r i n g > G, S t r i n g
actor ) {
20
V e r t i c e <S t r i n g > i n i c i a l = b u s c a r A c t o r (G, Kevin
Bacon ) ;
21
V e r t i c e <S t r i n g > d e s t i n o = b u s c a r A c t o r (G, a c t o r ) ;
22
23
int N = G. l i s t a D e V e r t i c e s ( ) . tamanio ( ) ;
24
ColaGenerica <V e r t i c e <S t r i n g >> c = new ColaGenerica <
V e r t i c e <S t r i n g >>() ;
25
v i s i t a d o s [ ] = new boolean [N ] ;
26
d i s t a n c i a s [ ] = new int [N ] ;
72

CAP
ITULO 5. GRAFOS
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

Prog. III

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


visitados [ i ] = false ;
d i s t a n c i a s [ i ] = I n t e g e r .MAX VALUE;
}
numeroDeBaconDFS ( i n i c i o , 0 ) ;
return d i s t a n c i a s [ d e s t i n o . p o s i c i o n ( ) ] ;
}
private void numeroDeBaconDFS ( V e r t i c e <S t r i n g > v , int
peso ) {
v i s i t a d o s [ v . p o s i c i o n ( ) ] = true ;
i f ( peso < d i s t a n c i a s [ v . p o s i c i o n ( ) ] ) {
d i s t a n c i a s [ v . p o s i c i o n ( ) ] = peso ;
}
L i s t a G e n e r i c a <A r i s t a <S t r i n g >> a d y a c e n t e s = v .
obtenerAdyacentes ( ) ;
a d y a c e n t e s . comenzar ( ) ;
while ( ! a d y a c e n t e s . f i n ( ) ) {
V e r t i c e <S t r i n g > w = a d y a c e n t e s . elemento ( ) .
verticeDestino () ;

49
50
i f ( ! v i s i t a d o [w. p o s i c i o n ( ) ] )
51
numeroDeBaconDFS (w. v e r t i c e D e s t i n o ( ) , peso + 1 ) ;
52
53
a d y a c e n t e s . proximo ( ) ;
54
}
55
56
visitados [ v . posicion () ] = false ;
57
}
58 }

73

5.3. JUAN CARLOS VERTICIO

5.3.

Prog. III

Juan Carlos Verticio

Juan Carlos Verticio es un artista que vive en la ciudad de Grafonia, y


entre sus pasiones personales est la de hacer dibujos de grafos4 , y recorrer
a
la ciudad en bicicleta. Verticio desea entrenar para la nal de ciclistas 2012,
patrocinada por la Divisin de Finanzas y Seguridad (DFS).
o
Entre sus planes de entrenamiento, Verticio desea armar una serie de
recorridos por la ciudad. A n de optimizar su tiempo en sus ratos libres en
la semana, l quiere poder salir y hacer un recorrido entre dos esquinas de la
e
ciudad.
Para ello necesita conocer cules caminos tienen exactamente una detera
minada longitud entre dos esquinas. El camino no puede terminar a mitad de
cuadra. Por ejemplo, todos los caminos de la ciudad que entre dos esquinas
miden 21 km.
A partir de un grafo dirigido5 que representa la ciudad de Grafonia, realizar una funcin en Java que le permita a Verticio obtener una lista de todos
o
los caminos de la ciudad con una longitud espec
ca. Las aristas del grafo tienen todas valores positivos, siendo la distancia de las diferentes calles
de la ciudad, medidas en Km como nmeros enteros. Los vrtices del grafo
u
e
contienen un String que representa la esquina de la ciudad.

5.3.1.

Explicacin del problema


o

Hace falta recorrer el grafo de forma de obtener todos los caminos posibles
con una determinada longitud. La forma ms directa de resolverlo es un con
a
un DFS, probando todas las posibles combinaciones de aristas del grafo.
4
5

La imagen es una de sus ms recientes obras.


a
Verticio no puede ir por las calles en contramano.

74

CAP
ITULO 5. GRAFOS

Prog. III

Para ello, por cada nodo del grafo hace falta iniciar un DFS, llevando la
lista de esquinas de la ciudad y el costo actual acumulado.

5.3.2.

Implementacin Java - DFS


o

1 c l a s s DFS {
2
boolean v i s i t a d o s [ ] ;
3
int d i s t a n c i a s [ ] ;
4
5
public L i s t a G e n e r i c a <L i s t a G e n e r i c a <S t r i n g >>
buscarCaminosDeCosto ( Grafo<S t r i n g > G, int c o s t o ) {
6
int N = G. l i s t a D e V e r t i c e s ( ) . tamanio ( ) ;
7
v i s i t a d o s [ ] = new boolean [N ] ;
8
d i s t a n c i a s [ ] = new int [N ] ;
9
L i s t a G e n e r i c a <L i s t a G e n e r i c a <S t r i n g >> r e s u l t a d o =
new L i s t a G e n e r i c a <L i s t a G e n e r i c a <S t r i n g >>() ;
10
11
for ( int i = 0 ; i < N; ++i ) {
12
visitados [ i ] = false ;
13
d i s t a n c i a s [ i ] = I n t e g e r .MAX VALUE;
14
}
15
16
for ( int i = 0 ; i < N; ++i ) {
17
L i s t a G e n e r i c a <S t r i n g > caminoActual = new
L i s t a G e n e r i c a <S t r i n g >() ;
18
V e r t i c e <S t r i n g > v = G. l i s t a D e V e r t i c e s ( ) . elemento (
i);
19
buscarCaminosDeCostoDFS ( v , r e s u l t a d o ,
caminoActual , c o s t o , 0 ) ;
20
}
21
22
return r e s u l t a d o ;
23
}
24
25
private void buscarCaminosDeCostoDFS ( V e r t i c e <S t r i n g >
v , L i s t a G e n e r i c a <L i s t a G e n e r i c a <S t r i n g >> r e s u l t a d o ,
L i s t a G e n e r i c a <S t r i n g > caminoActual , int c o s t o ,
int c o s t o A c t u a l ) {
26
v i s i t a d o s [ v . p o s i c i o n ( ) ] = true ;
27
caminoActual . a g r e g a r ( v . dato ( ) , caminoActual . tamanio
75

5.3. JUAN CARLOS VERTICIO

Prog. III

() ) ;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

i f ( c o s t o == c o s t o A c t u a l ) {
/ * Crear una c o p i a de l a l i s t a * /
L i s t a G e n e r i c a <S t r i n g > n u e v a L i s t a = new
L i s t a G e n e r i c a <S t r i n g >() ;
caminoActual . comenzar ( ) ;
while ( ! caminoActual . f i n ( ) ) {
n u e v a L i s t a . a g r e g a r ( caminoActual . elemento ( ) ,
n u e v a L i s t a . tamanio ( ) ) ;
caminoActual . proximo ( ) ;
}
resultado . agregar ( nuevaLista ) ;
}
L i s t a G e n e r i c a <A r i s t a <S t r i n g >> a d y a c e n t e s = v .
obtenerAdyacentes ( ) ;
a d y a c e n t e s . comenzar ( ) ;
while ( ! a d y a c e n t e s . f i n ( ) ) {
A r i s t a <S t r i n g > a = a d y a c e n t e s . elemento ( ) ;
V e r t i c e <S t r i n g > w = a . v e r t i c e D e s t i n o ( ) ;
i f ( ! v i s i t a d o s [w. p o s i c i o n ( ) ] ) {
buscarCaminosDeCostoDFS (w, r e s u l t a d o ,
caminoActual , c o s t o , c o s t o A c t u a l + a . peso ( ) )
;
}

48
49
50
a d y a c e n t e s . proximo ( ) ;
51
}
52
53
visitados [ v . posicion () ] = false ;
54
caminoActual . e l i m i n a r ( caminoActual . tamanio ( ) 1 ) ;
55
}
56 }

76

CAP
ITULO 5. GRAFOS

5.4.

Prog. III

Virus de Computadora

Un poderoso e inteligente virus de computadora infecta cualquier computadora en 1 minuto, logrando infectar toda la red de una empresa con cientos
de computadoras. Dado un grafo que representa las conexiones entre las
computadoras de la empresa, y una computadora ya infectada, escriba un
programa en Java que permita determinar el tiempo que demora el virus en
infectar el resto de las computadoras.
Asuma que todas las computadoras pueden ser infectadas, no todas las
computadoras tienen conexin directa entre s y un mismo virus puede ino
,
fectar un grupo de computadoras al mismo tiempo sin importar la cantidad.
Por ejemplo, si la computadora A se conecta con la B, y la B solo con la
C y D, el tiempo total desde la A ser de dos minutos: la A ya est infectada,
a
a
un minuto para la B, y un minuto para la C y D (ambas C y D al mismo
tiempo).

5.4.1.

Dibujo

5.4.2.

Caracter
sticas del grafo

Es un grafo no dirigido con aristas sin peso.

5.4.3.

Explicacin del problema


o

El problema empieza con una primer computadora infectada, y hay que


simular la infeccin en el resto de la red. Cada vez que el virus saltadesde
o
una computadora hacia la siguiente transcurre un minuto, por lo que si hace
77

5.4. VIRUS DE COMPUTADORA

Prog. III

cuatro saltos transcurren cuatro minutos. En este caso, el tiempo total de


infeccin de la red es de cuatro minutos.
o
Hay que tener en cuenta que una misma computadora se puede infectar
por diferentes caminos, y dado que el virus infecta varias computadoras al
mismo tiempo, la infeccin termina siendo por la que est ms cerca.
o
a a
El problema se reduce a un camino m
nimo en un grafo no pesado, y
el tiempo total de infeccin de la red es el mayor costo de los nodos. La
o
solucin ms directa es un BFS, que representa la forma en la que el virus
o
a
se distribuye por la red.

5.4.4.

Implementacin Java - BFS


o

1 c l a s s BFS {
2
public int c a l c u l a r T i e m p o I n f e c c i o n ( Grafo G, V e r t i c e
inicial ) {
3
int N = G. l i s t a D e V e r t i c e s ( ) . tamanio ( ) ;
4
Cola c = new Cola ( ) ;
5
boolean v i s i t a d o s [ ] = new boolean [N ] ;
6
int d i s t a n c i a s [ ] = new int [N ] ;
7
int maxDist = 0 ;
8
9
for ( int i = 0 ; i < N; ++i ) {
10
visitados [ i ] = false ;
11
d i s t a n c i a s [ i ] = I n t e g e r .MAX VALUE;
12
}
13
14
distancias [ i n i c i a l . posicion () ] = 0;
15
c o l a . poner ( i n i c i a l ) ;
16
17
while ( ! c o l a . e s V a c i a ( ) ) {
18
Vertice v = cola . sacar () ;
19
L i s t a G e n e r i c a <A r i s t a > a d y a c e n t e s ;
20
21
adyacentes = v . obtenerAdyacentes ( ) ;
22
a d y a c e n t e s . comenzar ( ) ;
23
while ( ! a d y a c e n t e s . f i n ( ) ) {
24
V e r t i c e w = a d y a c e n t e s . elemento ( ) .
verticeDestino () ;
25
int nuevaDist = d i s t a n c i a s [ v . p o s i c i o n ( ) ] + 1 ;
26
78

CAP
ITULO 5. GRAFOS

Prog. III

27
i f ( ! v i s i t a d o [w. p o s i c i o n ( ) ] ) {
28
v i s i t a d o [w. p o s i c i o n ( ) ] = true ;
29
d i s t a n c i a s [w. p o s i c i o n ( ) ] = nuevaDist ;
30
i f ( nuevaDist > maxDist )
31
maxDist = nuevaDist ;
32
c o l a . poner (w) ;
33
}
34
a d y a c e n t e s . proximo ( ) ;
35
}
36
}
37
38
return maxDist ;
39
}
40 }

79


5.5. CIRCUITOS ELECTRONICOS

5.5.

Prog. III

Circuitos Electrnicos
o

En un circuito electrnico digital la electricidad viaja a la velocidad de


o
la luz. Sin embargo, las diferentes compuertas tienen un tiempo de demora
entre que la seal llega por sus entradas y se genera el resultado.
n
Si el circuito est mal diseado, cuando se conectan varias compuertas
a
n
entre s ocurre lo que se llama una condicin de carrera, donde las dos seales

o
n
que le llegan a una compuerta NAND lo hacen a diferente tiempo, y la salida
de la compuerta tardar en estabilizarse. Asuma que tiempo que demoran las
a
seales en viajar por los cables es cero, y el tiempo que demoran las seales
n
n
en atravezar una compuerta es el mismo para todas.
Se tiene un grafo que representa el circuito electrnico, y una unica eno

trada que generan una seal.


n

5.5.1.

Dibujo

El siguiente es un dibujo de un grafo que contenga una condicion de


carrera.

5.5.2.

Caracter
sticas del grafo

Es un grafo dirigido no pesado. Hay tres tipos de nodos: el nodo entrada


que genera las seales, los nodos de salida que reciben las seales, y los
n
n
nodos de compuertas, que siempre tienen dos aristas de entrada y una arista
de salida.
80

CAP
ITULO 5. GRAFOS

5.5.3.

Prog. III

Explicacin del problema


o

Hay una carrera cuando una seal le llega a una compuerta ms rpido
n
a a
por una de las entradas que por las otras.
Por ejemplo, en el dibujo anterior estn las compuertas 1 y 2, una entrada
a
por donde se disparan las seales, y una salida, por donde queda el resultado
n
de las compuertas. Las seales que le llegan a la compuerta 1 son todas al
n
mismo momento de tiempo, pero las seales que le llegan a la compuerta 2
n
lo hacen a diferente tiempo, porque una de las entradas es la salida de la
compuerta 1.
La idea del ejercicio es hacer el seguimiento de los diferentes caminos
que pueden tomar la seal. No es necesario hacer una lista de todos, sino
n
encontrar el caso de la primer carrea.
Una forma de ver la solucin es simular el paso del tiempo en el circuito,
o
haciendo el mismo recorrido que las seales a lo largo del tiempo. Eso se
n
puede hacer de dos formas: BFS o DFS.
En el caso del DFS, por cada nodo que se visita se va incrementando en
1 un contador a medida que se llama a la recursin, este valor se lo almacena
o
en una tabla, y si cuando se vuelve a visitar un nodo por segunda vez el valor
es diferente al primero, hay una carrera.
En el BFS es similar, pero hace falta diferenciar correctamente los nodos
visitados de los que no lo estn. Tambien, es necesario mantener una tabla;
a
si el nodo no fue visitado actualizr el valor como el valor del padre + 1, y si
el nodo ya fue visitado vericar que el valor es el mismo que el del padre +
1. Esto se hace dentro del while que recorre adyacentes.
En ambos casos, el contador sirve para ver en qu instante de tiempo le
e
estn llegando las seales a la compuerta. Si por un lado le llega un valor
a
n
diferente al que le llega por el otro, quiere decir que le llegan a diferente
tiempo y entonces hay una carrera.

5.5.4.

Implementacin Java - BFS


o

El siguiente programa Java determina si un circuito electrnico presenta


o
una codicin de carrea.
o
1 c l a s s BFS {
2
public boolean hayCarrera ( Grafo G, V e r t i c e i n i c i a l ) {
3
int N = G. l i s t a D e V e r t i c e s ( ) . tamanio ( ) ;
4
Cola c = new Cola ( ) ;
5
boolean v i s i t a d o s [ ] = new boolean [N ] ;
6
int d i s t a n c i a s [ ] = new int [N ] ;
7
81


5.5. CIRCUITOS ELECTRONICOS
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Prog. III

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


visitados [ i ] = false ;
d i s t a n c i a s [ i ] = I n t e g e r .MAX VALUE;
}
distancias [ i n i c i a l . posicion () ] = 0;
c o l a . poner ( i n i c i a l ) ;
while ( ! c o l a . e s V a c i a ( ) ) {
Vertice v = cola . sacar () ;
L i s t a G e n e r i c a <A r i s t a > a d y a c e n t e s ;
adyacentes = v . obtenerAdyacentes ( ) ;
a d y a c e n t e s . comenzar ( ) ;
while ( ! a d y a c e n t e s . f i n ( ) ) {
V e r t i c e w = a d y a c e n t e s . elemento ( ) .
verticeDestino () ;
int n u e v a D i s t a n c i a = d i s t a n c i a s [ v . p o s i c i o n ( ) ] +
1;

24
25
26
27
28
29
30
31

i f ( ! v i s i t a d o s [w. p o s i c i o n ( ) ] ) {
v i s i t a d o s [w. p o s i c i o n ( ) ] = true ;
d i s t a n c i a s [w. p o s i c i o n ( ) ] = n u e v a D i s t a n c i a ;
c o l a . poner (w) ;
} else {
i f ( d i s t a n c i a s [w. p o s i c i o n ( ) ] !=
nuevaDistancia )
return true ; / * Hay c a r r e r a * /
}
a d y a c e n t e s . proximo ( ) ;

32
33
34
35
}
36
}
37
return f a l s e ;
38
}
39 }

5.5.5.

Implementacin Java - DFS


o

1 c l a s s DFS {
2
boolean v i s i t a d o s [ ] ;
82

CAP
ITULO 5. GRAFOS
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

Prog. III

int d i s t a n c i a s [ ] ;
public boolean hayCarrera ( Grafo<I n t e g e r > G, V e r t i c e <
Integer> i n i c i a l ) {
int N = G. l i s t a D e V e r t i c e s ( ) . tamanio ( ) ;
v i s i t a d o s [ ] = new boolean [N ] ;
d i s t a n c i a s [ ] = new int [N ] ;
for ( int i = 0 ; i < N; ++i ) {
visitados [ i ] = false ;
d i s t a n c i a s [ i ] = I n t e g e r .MAX VALUE;
}
return hayCarreraDFS ( i n i c i a l , 0 ) ;
}
private boolean hayCarreraDFS ( V e r t i c e <I n t e g e r > v , int
tiempo ) {
v i s i t a d o s [ v . p o s i c i o n ( ) ] = true ;
i f ( d i s t a n c i a s [ v . p o s i c i o n ( ) ] != I n t e g e r .MAX VALUE)
{
i f ( d i s t a n c i a s [ v . p o s i c i o n ( ) ] != tiempo ) {
visitados [ v . posicion () ] = false ;
return true ;
}
} else {
d i s t a n c i a s [ v . p o s i c i o n ( ) ] = tiempo ;
}
L i s t a G e n e r i c a <V e r t i c e <I n t e g e r >> a d y a c e n t e s = v .
obtenerAdyacentes ( ) ;
a d y a c e n t e s . comenzar ( ) ;
while ( ! a d y a c e n t e s . f i n ( ) ) {
V e r t i c e <I n t e g e r > w = a d y a c e n t e s . elemento ( ) .
verticeDestino () ;
i f ( ! v i s i t a d o s [w. p o s i c i o n ( ) ] && hayCarreraDFS (w,
tiempo + 1 ) )
visitados [ v . posicion () ] = false ;
return true ;
}
83


5.5. CIRCUITOS ELECTRONICOS
38
a d y a c e n t e s . proximo ( ) ;
39
}
40
41
visitados [ v . posicion () ] = false ;
42
return f a l s e ;
43
}
44 }

84

Prog. III

CAP
ITULO 5. GRAFOS

5.6.

Prog. III

Viajante de Encuestas

La facultad de Ciencias Exactas tiene docentes dando clases en diferentes


Facultades: Informtica, Qu
a
mica, Astronom Medicina, etc. Mar es cona,
a
tratada por Exactas para realizar encuestas a los alumnos de las diferentes
comisiones de los diferentes cursos de todas las facultades, y quiere saber cul es la ruta optima que menos tiempo le lleve para visitar todos los
a

cursos de los diferentes edicios.


Dado un grafo con vrtices que representan los diferentes cursos de todas
e
las facultades, y con aristas representando cunto cuesta llegar desde un curso
a
a otro, haga un programa en Java que le ayude a Mar denir en qu orden
a
e
visitar los diferentes cursos para que le cueste lo menos posible. Asuma que
el primer curso lo puede elegir sin costo, y los cursos se pueden visitar en
cualquier momento.
Ayuda: Implemente un recorrido sobre el grafo que pruebe las diferentes
variantes de caminos.

5.6.1.

Explicacin del problema


o

Este problema se conoce como Viajante de Comercio, TSP en ingls. Es


e
un problema NPH, una categor de problemas6 de la que no se conoce un
a
algoritmo que funcione eciente en grafos con cientos de miles (o ms) de
a
nodos.
De todas formas, es muy fcil ver que se puede resolver con un DFS, inia
ciando un recorrido desde cada nodo del grafo y probando todas las posibles
combinaciones de caminos.
A medida que se va haciendo la recursin se va creando el camino, y una
o
vez cumplida la condicin de visitar todos los nodos se verica si el camino
o
actual tiene el menor costo entre los ya procesados.
Es importante tener en cuenta que el grafo debe ser conexo. De otra forma
nunca se cumplir la condicin de visitar todos los nodos del grafo.
a
o

5.6.2.

Implementacin Java
o

1 class ViajanteDeEncuestas {
2
int N;
3
boolean [ ] v i s i t a d o s ;
4
L i s t a G e n e r i c a <S t r i n g > mejorCamino ;
6

Categorizar problemas como P, NP, NPC, NPH es un tema que se encuentra fuera del
alcance de este curso.

85

5.6. VIAJANTE DE ENCUESTAS


5
6
7
8
9
10
11
12
13
14
15

int mejorCaminoCosto = I n t e g e r .MAX VALUE;


L i s t a G e n e r i c a <S t r i n g > c a l c u l a r M e j o r V i a j e ( Grafo<S t r i n g
> G) {
N = G. l i s t a D e V e r t i c e s ( ) . tamanio ( ) ;
v i s i t a d o s = new boolean [N ] ;
for ( int i = 0 ; i < N; ++i )
visitados [ i ] = false ;
for ( int i = 0 ; i < N; i ++) {
L i s t a G e n e r i c a <S t r i n g > caminoActual = new
L i s t a E n l a z a d a G e n e r i c a <S t r i n g >() ;
calcularCaminoMinimoDFS (G. l i s t a D e V e r t i c e s ( ) .
elemento ( i ) , caminoActual , 1 , 0 ) ;
}
return mejorCamino ;

16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

Prog. III

}
void calcularCaminoMinimoDFS ( V e r t i c e <S t r i n g > V,
L i s t a G e n e r i c a <S t r i n g > caminoActual , int c a n t V i s i t ,
int c o s t o A c t u a l ) {
v i s i t a d o s [V. p o s i c i o n ( ) ] = true ;
caminoActual . a g r e g a r (V. dato ( ) , caminoActual . tamanio
() ) ;
i f ( c a n t V i s i t == N && c o s t o A c t u a l <
mejorCaminoCosto )
{
/ * Copiar l a l i s t a * /
L i s t a G e n e r i c a <S t r i n g > n u e v a L i s t a = new
L i s t a E n l a z a d a G e n e r i c a <S t r i n g >() ;
caminoActual . comenzar ( ) ;
while ( ! caminoActual . f i n ( ) ) {
n u e v a L i s t a . a g r e g a r ( n uev aL ist a , n u e v a L i s t a .
tamanio ( ) ) ;
caminoActual . proximo ( ) ;
}
/ * A c t u a l i z a r e l camino minimo * /
mejorCamino = n u e v a L i s t a ;
mejorCaminoCosto = c o s t o A c t u a l ;
86

CAP
ITULO 5. GRAFOS
37
38
39
40
41
42
43
44
45
46
47

Prog. III

}
else
{
/ * S e g u i r DFS en l o s a d y a c e n t e s * /
L i s t a G e n e r i c a <S t r i n g > a d y a c e n t e s = V.
obtenerAdyacentes ( ) ;
a d y a c e n t e s . comenzar ( ) ;
while ( ! a d y a c e n t e s . f i n ( ) ) {
A r i s t a <S t r i n g > A = a d y a c e n t e s . elemento ( ) ;
V e r t i c e <S t r i n g > W = A. v e r t i c e D e s t i n o ( ) ;
i f ( ! v i s i t a d o s [W. p o s i c i o n ( ) ] ) {
calcularCaminoMinimoDFS (W, c a n t V i s i t + 1 ,
c o s t o A c t u a l + A. peso ( ) ) ;
}
a d y a c e n t e s . proximo ( ) ;
}
}

48
49
50
51
52
53
caminoActual . e l i m i n a r ( caminoActual . tamanio ( ) 1 ) ;
54
v i s i t a d o s [V. p o s i c i o n ( ) ] = f a l s e ;
55
}
56 }

87