Está en la página 1de 33

Finales de 9512 Algoritmos 2

Gordon “Alf” Shumway

Melmak, 20 de diciembre de 2016

Nota 1: La presente colección de finales resueltos que usted está consultando fueron preparados por un
alumno mientras preparaba el examen. Existe la posibilidad de que hayan cosas mal resueltas, así que no se
confíe!
Nota 2: Recomiendo visitar la siguiente página para “ver” los algoritmos: https://www.cs.usfca.edu/
~galles/visualization/Algorithms.html.

Índice
1. Preguntas que nos dio la última clase 3
Preguntas sobre TDA conjunto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Pregunta a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Pregunta b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Pregunta c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Pregunta d . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Verdadero o falso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2. Final del 10/02/2015 6


Problema 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Ítem 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Ítem 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Ítem 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Item 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Ítem 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Ítem 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Problema 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Ítem 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Ítem 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Ítem 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Ítem 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Problema 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Ítem 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Ítem 2 (no tengo idea) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Problema 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Ítem 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Ítem 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Ítem 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Ítem 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3. Final del 29/07/2014 18


Problema 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Problema 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Problema 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Pregunta 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Pregunta 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Problema 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

1
ÍNDICE ÍNDICE

4. Final 1 20
Problema 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Pregunta 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Pregunta 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Pregunta 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Pregunta 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Pregunta 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Problema 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Problema 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Pregunta 1 (incompleta) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Pregunta 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

5. Final 2 24
Problema 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Pregunta 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Pregunta 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Problema 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Problema 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Problema 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Preguntas 1 y 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Pregunta 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

6. Final 3 30
Problema 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Problema 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Pregunta 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Pregunta 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Pregunta 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Pregunta 4 (no tengo idea) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Pregunta 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Problema 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Preguntas 1 y 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Pregunta 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Problema 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Pregunta 1 (falta completar) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Pregunta 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

2 Los resueltos de Alf


1 PREGUNTAS QUE NOS DIO LA ÚLTIMA CLASE

1. Preguntas que nos dio la última clase


La última clase cayó con las siguientes preguntas y, si mal no le entendí, dijo que eran de final:
1. Considere el TDA conjunto.

a) Indique implementaciones que provean un alta O (1) para el peor caso y describa los algoritmos
correspondientes.
b) Indique implementaciones que provean baja O (n) para el peor caso y describa los algoritmos.
c) Indique implementaciones que provean la operación “pertenece”, para el peor caso:
1) O (1).
2) O (log n).
3) O (n).
d) Si se requiere que se provea la operación intersección entre conjuntos, describa el costo para distintas
implementaciones, justificando con la descripción del algoritmo correspondiente.

2. Coloque V o F según corresponda:

a) La programación dinámica se parece a la estrategia de Divide y Vencerás porque resuelve problemas


descomponiéndolos en subproblemas con la misma estructura que el original.
b) En la estrategia Greedy se trabaja con subproblemas solapados.
c) La programación dinámica tabula las soluciones parciales para no recalcularlas.
d) La programación dinámica usa un esquema bottom-up.
e) La programación dinámica suele preprocesar los candidatos que se eligen en cada etapa, o bien
organizarlos en una cola con prioridades.
f ) La estrategia Greedy (si se dan las condiciones para aplicarla) llega a una solución óptima cuando se
aseguran elecciones localmente óptimas.
g) La estrategia backtracking puede llegar a ser tan costosa como la de fuerza bruta.
h) La estrategia de fuerza bruta sólo se aplica a problemas de optimización.
i) Tanto en el backtracking como en la fuerza bruta el coste temporal depende, para el peor caso, de
m y s donde m es el número de nodos y s el número de opciones para cada nodo.

3 Los resueltos de Alf


Preguntas sobre TDA conjunto 1 PREGUNTAS QUE NOS DIO LA ÚLTIMA CLASE

Respuestas a las preguntas


Preguntas sobre TDA conjunto
Pregunta a
Lo que recuerdo que vimos en clase, a modo de un simple comentario, es que se puede implementar un
TDA Conjunto (medio limitado) usando los bits de un byte (o de muchos bytes). A cada bit se le asocia la
representación de uno de los elementos que pueden estar en el conjunto y se considera que el elemento está
en el conjunto cuando su bit vale 1 y que no está en el conjunto cuando vale 0. De esta forma el alta de un
elemento es una simple operación de OR en el bit apropiado. Lo mismo para la baja, es una AND con un 0. Estas
operaciones se pueden realizar en tiempo constante.
Esta implementación tiene la limitación de que la cantidad de elementos que puede contener el conjunto es
fija.

Pregunta b
Se me ocurre una implementación con una lista o con un arreglo no ordenado. En el peor caso el elemento
que se quiere dar de baja está al final de todo y entonces el tiempo hasta encontrarlo (y borrarlo) es O (n).

Pregunta c
La operación “pertenece” básicamente tiene que buscar el elemento en el conjunto para luego tomar la
decisión.
Una implementación que sea O (1) implica que el elemento es encontrado en tiempo constante. Esto es
posible únicamente en el caso en que ya se conozca de antemano a dónde ir a buscar el elemento. Es
decir que el conjunto almacena los elementos en un arreglo de acceso constante y tamaño definido. Una
implementación que permita lograr esto solo es aplicable a conjuntos de tamaño máximo finito, es decir
un conjunto que de antemano tiene definida la máxima cantidad de elementos que puede almacenar. Por
ejemplo un conjunto de colores primarios (rojo, azul y amarillo) se puede implementar con un arreglo de
tamaño 3 y asignarle a cada color una posición fija. La operación “pertenece” será O (1). En cambio, un
conjunto de “colores” donde los colores pueden ser cualesquiera definidos por una terna RGB no se podría
implementar así ya que son infinitos.
Una implementación que permita determinar si un elemento pertenece o no al conjunto con tiempo
O (log n) podría ser con un árbol de búsqueda. Este tipo de implementación solo es aplicable a conjuntos
cuyos elementos sean “comparables”, es decir para los cuales se pueda definir las operaciones “mayor” y
“menor” para así ir buscándolos en el árbol. Un ejemplo en el que esto no se puede aplicar es un conjunto
de colores.
Implementaciones que provean la operación “pertenece” en tiempo O (n) son las más sencillas: una lista
o un arreglo no ordenado. En este caso para saber si un elemento está o no en el conjunto hay que ir uno
por uno buscándolo.

Pregunta d
Implementación con bytes Si se realiza la implementación de bajo nivel hecha con bytes (ver respuesta a
la pregunta a) entonces la operación intersección entre dos conjuntos es de tiempo constante ya que se realiza
con una operación AND entre los dos bytes1 . Si se utiliza más de un byte para implementar el conjunto entonces
la complejidad comenzará a crecer ya que la operación de AND deberá realizarse para varios bytes.
l Simse utilizan
NB bytes y cada byte tiene Nb bits entonces un conjunto de n elementos requerirá de NB = Nnb bytes. Se
observa que para n → ∞ se obtiene NB ∝ n por lo tanto habrá que hacer una cantidad de operaciones AND
proporcional a n por lo que es O (n).

Implementación con arreglo no ordenado A la hora de construir el conjunto intersección habrá que
tomar uno de los conjuntos y recorrerlo elemento a elemento preguntándose, en cada uno, si está o no en el
otro conjunto. Este recorrido ya impone una cota Θ (n1 ) donde n1 es la cantidad de elementos del conjunto 1.
En el peor de los casos ambos conjuntos son iguales y la operación “pertenece al conjunto 2” es O (n2 ) lo cual
termina resultando en que el tiempo total de la intersección es O (n1 n2 ).
1 En clase lo vimos así, pero esto en realidad no es el análisis asintótico porque si se utilizó un único byte entonces n es constante...

4 Los resueltos de Alf


Verdadero o falso 1 PREGUNTAS QUE NOS DIO LA ÚLTIMA CLASE

Implementación con árboles de búsqueda (balanceado) En este caso también habrá que recorrer al
primer conjunto elemento a elemento verificando si está o no en el segundo, por lo tanto nuevamente se tiene una
cota Θ (n1 ). Al tratarse de un árbol balanceado entonces la búsqueda de un elemento en el segundo conjunto
será de complejidad O (log n2 ) por lo tanto la operación de intersección termina siendo O (n1 log n2 ). En base a
esto se observa que siempre conviene poner en primer lugar (o sea en n1 ) al conjunto con la menor cantidad de
elementos.

Implementación con trie Cuando los elementos que contendrá el conjunto lo permiten, una implementación
con trie es conveniente ya que la búsqueda es muy rápida. Si se considera un conjunto de palabras del idioma
castellano entonces la búsqueda de una palabra de k caracteres es O (k) y no depende de n. Esto implica que
la intersección se podrá computar en tiempo Θ (n1 ) sin importar el tamaño de n2 (nota2 ).

Verdadero o falso
a) Verdadero. La programación dinámica reduce el problema a uno sencillo y luego comienza a avanzar de a
un paso por vez hasta resolver el problema original.

b) Falso. La programación dinámica es la que trabaja con subproblemas solapados.

c) Verdadero. Lo vi en el primer video de esta lista de reproducción: https://www.youtube.com/watch?


v=W2ote4jCuYw&list=PLyEvk8ZeQDMVbsg7CEfT0NV3s3GkMx1vN.

d) En base a lo que dice Wikipedia3 sobre top-down y bottom-up yo creo que la programación dinámica usa
el esquema bottom-up ya que resuelve problemas pequeños que cada vez son más grandes hasta resolver el
problema original.

e) No tengo idea.

f) Falso. Un ejemplo es el de este video: https://www.youtube.com/watch?v=_zE5z-KZGRw&list=PLyEvk8ZeQDMVbsg7CEfT


index=2. La estrategia greedy produce soluciones optimales (así lo dijo la profesora en clase). Según [3, cap.
16 (introduction)] “a greedy algorithm always makes the choice that looks best at the moment [...], it makes a
locally optimal choice in the hope that this choice will lead to a globally optimal solution. [...] Greedy algorithms
do not always yield optimal solutions...“.

g) Verdadero. En el peor de los casos existe la posibilidad de que la rama que resuelve el problema sea
explorada por el método backtracking al final de todo, haciendo que la resolución del problema sea tan costosa
como la de fuerza bruta.

h) Falso. La estrategia de fuerza bruta se aplica a todos los problemas. Simplemente consiste en probar todas
las soluciones una por una hasta encontrar la mejor.

i) No sé.

2 El corrector ortográfico del Word hace justamente esto. Toma el conjunto de palabras n que están en el texto y las busca en
1
el conjunto de palabras n2 del idioma configurado, que según algunos textos que leí suele estar implementado con una estructura
trie. Si tuviera alguna clase de dependencia con n2 entonces sería lentísimo, pero no.
3 https://en.wikipedia.org/wiki/Top-down_and_bottom-up_design .

5 Los resueltos de Alf


2 FINAL DEL 10/02/2015

2. Final del 10/02/2015


Consigna
Problema 1
1. Defina trie.
2. Un trie contiene palabras formadas con estas letras: a, b, c y el terminador λ. Se almacenan en el trie las
siguientes palabras: aa, aab, abc, abbc, bca, bcca, b. Mostrar cómo queda el trie si se lo implementa con
a) Nodos del trie con arrays de punteros y
b) con listas de punteros.
3. ¿Qué puede concluir acerca de la memoria requerida en cada caso?
4. Explique el algoritmo que determina si una clave determinada está o no en el trie, para cada una de las
dos implementaciones.
5. ¿De qué depende el tiempo consumido por ese algoritmo? ¿Cuál es su estimación?
6. ¿Para qué puede utilizarse un trie?

Problema 2
1. Defina árbol B.
2. Indique usos de la estructura.
3. Considere un árbol B de 5 vías (órden 5).
a) Muestre gráficamente la evolución del mismo, inicialmente vacío. Las claves que ingresan son: 200,
250, 150, 180, 200, 300, 40, 50, 60, 35, 10, 40.
b) Luego se borran estas claves: 200, 300, 150, 10, 35. Mostrar gráficamente el borrado.
4. ¿Cuántas claves tiene como máximo un árbol B de altura h?

Problema 3
1. Defina el árbol heap, indique coste temporal del alta y de la baja.
2. Explique qué relación hay entre la estructura heap y la estructura greedy de resolución de algoritmos (es
decir, para qué suele usarse un heap en esa estrategia y en qué beneficia a la resolución del problema).

Problema 4
1. Describa un algoritmo que permita determinar si un grafo tiene ciclos o no. ¿Cuál es el coste del algoritmo
que ha descripto? Justifique.
2. Explique qué es un recorrido topológico.
3. En el siguiente grafo, si es posible, realice un recorrido topológico e indique la salida.
4. Describa con detalle el algoritmo.

6 Los resueltos de Alf


Problema 1 2 FINAL DEL 10/02/2015

Resolución
Problema 1
Ítem 1
Un trie es una clase de árbol en la que la búsqueda se hace siguiendo una secuencia de caracteres de la clave
que se desea encontrar. Cada nodo de un trie tiene R punteros (donde R es el tamaño del alfabeto) y cada uno
de estos punteros se corresponde con un elemento del alfabeto. Cada nodo puede tener además un determinado
valor que es el asociado a la clave. Si dicho valor es NULL entonces la clave no está en el árbol. Esto está bien
detallado en [1, pag. 732].
A juzgar por lo que dice el ítem 2 (que tiene el terminador λ) me parece que en clase vimos la estructura
que presenta [2, sec. 7.2] (en donde usa como terminador el símbolo #).

Ítem 2
Subítem a. Si se lo implementa con arreglos de punteros entonces quedaría algo así:

a b c λ

a b c λ a b c λ
b

a b c λ a b c λ a b c λ
aa
a b c λ a b c λ a b c λ a b c λ a b c λ

aab
a b c λ abc bca a b c λ bcca

abbc

Ésta es la implementación que usa [2, sec. 7.2]. Los elementos indicados como a, b, c y λ son punteros y
cuando no apuntan a nada (en el dibujo) significa que son NULL.

Subítem b. Implementándolo con listas no sé bien cómo sería. Me parece que es la implementación que está
en [2, fig. 7.35], pero no estoy seguro. Asumiendo que sí, entonces sería:

a b

a b c λ

λ b c λ a c

c λ λ a

Todos los punteros que están sin indicar nada son NULL.

Ítem 3
El principal problema de los tries (implementados con arreglos de punteros) es la gran cantidad de espacio
que utilizan (y desperdician) [2, pag. 348]. Cuando el árbol no está completo quedan muchos punteros nulos, lo
cual se traduce en espacio desperdiciado.

7 Los resueltos de Alf


Problema 1 2 FINAL DEL 10/02/2015

La segunda implementación es mejor en este sentido: cuando el árbol está incompleto desperdicia menos
espacio. El problema que tiene es que cuando está completo ocupa más espacio (se guardan dos punteros por
cada elemento mientras que antes uno solo) y es más lento el acceso (esto afecta cuando se tiene un alfabeto
con muchos elementos).

Item 4
Caso de arreglo de punteros. Para determinar si una clave está o no en el trie se procede verificando el
puntero correspondiente a cada caracter en cada nivel. Por ejemplo supóngase que se quiere verificar si la clave
“abc” está en el trie. Entonces se entra en la raíz del árbol y se busca el puntero correspondiente al primer
elemento de la cadena, es decir el puntero “a”:

abc a b c λ

a b c λ a b c λ
Como dicho puntero no es nulo entonces seguimos avanzando. En el segundo nivel buscamos el puntero
correspondiente al segundo caracter de la cadena, es decir “b”:

abc a b c λ

abc a b c λ a b c λ
b

a b c λ a b c λ a b c λ
aa
A continuación buscamos el tercer elemento de la cadena, “c”, en el tercer nivel del árbol:

abc a b c λ

abc a b c λ a b c λ
abc
b c λ a b c λ a b c λ
aa
b c λ a b c λ a b c λ a b

Finalmente, como ya se alcanzó el final de la cadena, se busca si el terminador λ está referenciado a NULL o
no:

abc a b c λ

abc a b c λ a b c λ
abc
a b c λ a b c λ
aa
a b c λ a b c λ a

aab
a b c λ abc Está en el árbol

8 Los resueltos de Alf


Problema 1 2 FINAL DEL 10/02/2015

Como λ 6= NULL entonces se concluye que la clave está en el árbol.


El algoritmo puede escribirse en forma recursiva así:
1 // P s e u d o c ó d i g o.
2 E s t á _ e n _ t r i e( cadena , t ) { // I m p l e m e n t a c i ó n con a r r e g l o de p u n t e r o s.
3 i f ( length ( cadena ) == 0) { // Caso base cuando ya c h e q u e a m o s
todos los e l e m e n t o s de la cadena .
4 i f ( t . lambda == NULL )
5 return N O _ E S T Á;
6 else
7 return S Í _ E S T Á;
8 } else {
9 p u n t e r o = cadena [1]; // Este es el p u n t e r o en el que
tendré que buscar , el primer e l e m e n t o de la cadena .
10 i f ( t . p u n t e r o == NULL )
11 return N O _ E S T Á;
12 else
13 return E s t á _ e n _ t r i e ( cadena [2: end ] , t . p u n t e r o) ;
14 }
15 };

donde la notación cadena[2:end] es la notación de Octave/Matlab que significa: todos los elementos de la
cadena desde el 2 hasta el último.

Caso de lista de punteros. La idea básicamente es la misma pero cambia la forma en que se accede a la
información. El pseudocódigo sería más o menos así:
1 // P s e u d o c ó d i g o.
2 E s t á _ e n _ t r i e( cadena , t ) { // I m p l e m e n t a c i ó n con lista de p u n t e r o s.
3 i f ( length ( cadena ) == 0) { // Base case .
4 i f ( t . clave == lambda )
5 return S Í _ E S T Á;
6 else
7 return N O _ E S T Á;
8 } else {
9 e l q u e b u s c o = cadena [1]; // Este es el e l e m e n t o que
b u s c a m o s.
10 i f ( t . clave == e l q u e b u s c o) // Avanzo al s i g u i e n t e nivel
del árbol .
11 return E s t á _ e n _ t r i e ( cadena [2: end ] , t . izq ) ;
12 e l s e i f ( t . clave < e l q u e b u s c o) { // Tengo que a v a n z a r en
la lista de p u n t e r o s del nivel actual .
13 i f ( t . der != NULL )
14 return E s t á _ e n _ t r i e( cadena , t . der ) ;
15 else
16 return N O _ E S T Á;
17 } e l s e // if ( t . clave > e l q u e b u s c o)
18 return N O _ E S T Á;
19 }
20 };

Quizá me olvidé de algo, pero la idea es la misma salvo que con más precauciones por el tema de no meternos
en un puntero nulo y cosas así.

Ítem 5
El tiempo de este algoritmo es4
(
O (k) Implementación con arreglos de punteros
T (k) ∈
No sé Implementación con listas de punteros
4 Acá http://stackoverflow.com/questions/17891442/what-is-the-best-worst-average-case-big-o-runtime-of-a-trie-data-structure

respaldan mi idea de que sólo depende de la cantidad de letras de la clave.

9 Los resueltos de Alf


Problema 2 2 FINAL DEL 10/02/2015

donde k es la cantidad de caracteres de la clave que se desea encontrar (o sea la cantidad de elementos de la
cadena). Es independiente del tamaño del árbol, sólo depende de la clave.

Ítem 6
Los tries se usan para armar diccionarios en los que se quiere asegurar que la búsqueda sea rápida. Parece
que son bastante usados en los sistemas de autocompletado predictivo de texto de los celulares por ejemplo
debido a su elevada velocidad.
Según [2, sec. 7.4] se usan para los correctores ortográficos en los procesadores de texto.

Problema 2
Ítem 1
Nosotros en la clase vimos que un árbol B es un árbol multivía que verifica:
Todos los nodos, excepto la raíz, deben estar siempre completos (con claves) al menos hasta la mitad.
Si un nodo que no sea hoja tiene h claves entonces debe tener h + 1 nodos hijo no nulos.
La raíz: o es hoja o tiene al menos dos hijos.
Todas las hojas están al mismo nivel siempre.

Ítem 2
Según Wikipedia5 son muy usados por los sistemas de archivos modernos para permitir un acceso rápido a
un bloque arbitrario de memoria. Ejemplos de sistemas de archivos que los usan son: HFS+ de Apple, NTFS
de Microsoft y Ext4 de Linux.
En clase lo único que tengo anotado es que se usan para armar los TDA índice, que son un tipo de TDA
diccionario. Los índices se usan en los archivos para lo mismo que en un libro: evitan recorrer el archivo de
punta a punta hasta encontrar la información que se busca.
Aparentemente, según lo que leí, tienen las ventajas de los ABB pero minimizan el acceso a disco (que es
lento).

Ítem 3
Este ejercicio se resuelve en el siguiente link: https://www.cs.usfca.edu/~galles/visualization/BTree.
html. Yo igual voy a anotar los pasos acá:
1. El árbol comienza vacío:

Como es de orden 5 entonces


- - - - sus nodos tienen 4 claves y 5
punteros.

2. Llega la clave 200 y se almacena al principio de todo:

200 - - -

3. Ahora llega el 250 que se ubicará “después” del 200:

200 250 - -

4. Llega el 150 y como es menor que los otros los desplaza:

1  200 250 -

5. El 180 se mete entre medio del 150 y el 200:


5 https://en.wikipedia.org/wiki/B-tree#In_filesystems

10 Los resueltos de Alf


Problema 2 2 FINAL DEL 10/02/2015

150 180 200 250

6. Cuando llega el 200 se busca la posición adecuada (que será justo en el medio o en la siguiente, da igual):

200

150 180 200 250

pero como el nodo ya está lleno entonces hay que laburar un poco más. Como mínimo habría que agregar
una hoja, pero de acuerdo a la definición de árbol B es necesario que todos los nodos (salvo la raíz) tienen
que estar completos hasta la mitad (o más). La forma de resolver esto entonces es agregar dos hojas de la
siguiente forma:

200 - - -

150 180 - - 200 250 - -

7. El 300 se ubica fácilmente:

200 - - -

150 180 - - 200 250 3 -

8. El 40:

200 - - -

4 150 180 - 200 250  -

9. El 50:

200 - - -

 50 150 180 200 250  -

10. El 60 ya requiere un reacomodamiento de datos. Debería ubicarse acá:

200 - - -
6

 50 150 180 200 250


-

pero el nodo está lleno. Entonces se resuelve haciendo el siguiente reacomodamiento6:


6 No sé por qué no se hace esto:

180 - - -

5 150 2 2 25 
ya que te quedan menos nodos y aún satisface la definición de árbol B. En la clase tampoco lo hicimos así y no me avivé de
preguntar por qué...

11 Los resueltos de Alf


Problema 2 2 FINAL DEL 10/02/2015

 200 - -

40 50 - - 150 180 - - 200 250 300 -

Cuando pasa algo así parece que lo que se hace siempre es “el elemento del medio (60) se ubica en el nivel
superior y se parte el nodo en dos”.
11. Cuando llega el 35 se lo ubica al principio de todo:

60 200 - -

35 40 50 - 150 180 - - 200 250 300 -

12. Igual para el 10:

60 200 - -

10 35 40 50 150 180 - - 200 250 300 -

13. Finalmente llega el 40 que nuevamente encuentra un nodo lleno y hace lo mismo que hizo antes el 60: “el
elemento del medio se ubica en el nivel superior y se parte el nodo en dos”:

40 60 200 -

10 35 - - 40 50 - - 150 180 - - 200 250 300 -

14. Ahora se comienzan a eliminar elementos. Primero se elimina el 200. Para ello se navega por el árbol hasta
encontrarlo en su primera aparición:

40 60 200 -

10 35 - - 40 50 - - 150 180 - - 200 250 300 -

A continuación se “sube” el elemento más grande del nodo inferior, es decir el 180:

40 60 180 -

10 35 - - 40 50 - - 150 - - - 200 250 300 -

Nodo que no cumple reglas de árboles B

El problema ahora es que nos queda un nodo que no cumple las reglas de los árboles B ya que tiene menos
de la mitad de elementos. Entonces hay que hacer una rotación y el árbol queda así:

40 60 200 -

10 35 - - 40 50 - - 150 180 - - 250 300 - -

15. Al eliminar el 300 nuevamente queda un nodo que no satisface las reglas de los árboles B:

12 Los resueltos de Alf


Problema 2 2 FINAL DEL 10/02/2015

40 60 200 - N 

10 35 - - 40 50 - - 150 180 - - 250 - - -

por lo tanto habrá que hacer rotaciones hasta que todo quede bonito. La forma en que se resuelve es la
siguiente:

40 60 - -

10 35 - - 40 50 - - 150 180 200 250

16. La eliminación del 150 debería ser sencilla: simplemente se lo borra y chau. Sí, lo verifiqué con la página
que puse al principio ,. Queda así:

40 60 - -

10 35 - - 40 50 - - 180 200 250 -

17. Al eliminar el 10 nuevamente hay problemas. Creo que lo que se hace es similar a cuando se eliminó el
300: se elimina el nodo primero y se baja el 40 al nodo segundo:

40 60 - -

10 35 - - 40 50 - - 180 200 250 -

Al mover los elementos como indica la flecha roja queda

- 60 - -

- - - - 35 40 40 50 180 200 250 -

y finalmente se acomoda la raíz para que quede así:

60 - - -

35 40 40 50 180 200 250 -

18. Por último el 35 se elimina en forma directa y queda:

60 - - -

40 40 50 - 180 200 250 -

13 Los resueltos de Alf


Problema 3 2 FINAL DEL 10/02/2015

Ítem 4
Sea un árbol B de orden o (o sea de o punteros por nodo). Si el mismo tiene altura h y tiene la máxima
cantidad posible de elementos entonces todos los punteros están “ocupados”. Esto implica que tiene oh nodos. A
su vez, cada nodo tiene (o − 1) claves porque el árbol está lleno, por lo tanto la cantidad de claves de un árbol
B satisface
# de claves de árbol B de orden o y altura h ≤ oh (o − 1)
Si nos hubieran preguntado
 por la cantidad mínima la cosa sería así: por definición de árbol B los nodos no
pueden tener menos de o−1 elementos (salvo la raíz). La raíz sí puede tener 1 elemento. Entonces, definiendo

2
def  o−1 
q = 2 , el árbol estaría conformado así:

Nivel # de nodos # de claves


0 (raíz) ≥1 ≥1
1 ≥2 ≥ 2q
2 ≥ 2 (q + 1) ≥ 2 (q + 1) q
2 2
3 ≥ 2 (q + 1) ≥ 2 (q + 1) q
..
.
h−1 h−1
h ≥ 2 (q + 1) ≥ 2 (q + 1) q

Entonces, si k es el número de claves de todo el árbol, se satisface


h
X i−1
k ≥ 1+ 2q (q + 1)
i=1
h−1
X i
= 1 + 2q (q + 1)
i=0
h
(q + 1) − 1
Serie geométrica → = 1 + 2q
(q + 1) − 1
(q + 1)h − 1
= 1 + 2✁q
✁q
h
= 2 (q + 1) − 1

Finalmente uniendo las dos cosas (no lo pedía la consigna pero soy crack) la cantidad de claves k de un árbol
B de orden o (o sea con o punteros por nodo) y de altura h satisface
h
o−1
 
2 + 1 − 1 ≤ k ≤ oh (o − 1)
2

Problema 3
Ítem 1
De acuerdo con [2, sec. 6.9] y [3, sec. 6.1] un árbol heap máximo (mínimo) es un árbol binario que satisface
las siguientes propiedades:
El valor de cada nodo es mayor o igual (menor o igual) al valor de cada uno de sus hijos.
El árbol está perfectamente balanceado (salvo quizá en el último nivel) y las hojas del último nivel están
todas lo más a la izquierda posible.
El coste del alta y la baja en el heap es, según los apuntes del campus, O (log n) donde n es la cantidad de
elementos del heap. Esto se debe a la forma en que se realizan estas operaciones:

Operación de alta. El procedimiento es:


1. Se agrega el nuevo elemento en la última hoja (o sea a la derecha de la última que había) sin importar su
valor.
2. Se restaura el heap (para ubicarlo en la posición correcta).

14 Los resueltos de Alf


Problema 4 2 FINAL DEL 10/02/2015

Operación de baja. El proceder para el bajar de un elemento es


1. Reemplazar el elemento a bajar por la última hoja del heap.
2. Restaurar el heap.
En ambos casos se realizan dos operaciones, la primera de tiempo constante7 y la segunda (restaurar heap) es
de tiempo O (log n). Esto es fácil de probar, para cada caso, de la siguiente forma:
Para la operación de alta el peor caso es que se agregue un elemento que es más grande que la raíz (asumo
un heap máximo). Esto implica que en la última hoja quedará un elemento que en realidad debería estar
en la raíz, por lo tanto habrá que ir reacomodando hacia arriba hasta llevarlo a la raíz. Para lograr esto
simplemente se va intercambiando a éste elemento con sus padres hasta que quede ubicado en la raíz.
Como la altura del árbol es log2 n entonces cuanto mucho hay que hacer log2 n intercambios.
Para la operación de baja el peor caso es que es que se elimine la raíz del árbol ya que en su lugar
se colocará la última hoja (que es un elemento que debería estar en el último nivel). En este caso éste
elemento se va comparando con sus sucesivos hijos e intercambiando con el menor de ambos hasta que se
restaure la condición del heap. En el peor de los casos vuelve a ser una hoja por lo tanto es O (log n) ya
que la altura del árbol es log n.

Ítem 2 (no tengo idea)


La verdad que no lo encuentro por ningún lado.

Problema 4
Ítem 1
De acuerdo con el apunte sobre recorridos de grafos del campus, la detección de ciclos en grafos se puede
realizar con el algoritmo de “DFS de Tarjan”. Según Wikipedia8 el algoritmo corre en tiempo O (|V | + |E|)
donde |E| es el número de aristas (puentes) y |V | el número de vértices (nodos). Un análisis más completo se
puede encontrar en [2, sec. 8.4] que también usa el algoritmo de Tarzan de la siguiente forma:

A #aa v'# %v&  »


aa n nú  (
#a»"a  )( q  #
 sa » va .

E  un co  aas


q  !"a va#$.

Para gra  os


Lo marcamos #  va.
 i%&   vad

Para gra 

7 Esto es asumiendo que el heap se implementa en un array y que no hay que hacer un realloc porque nos quedamos sin lugar,

como asumen todos los libros y también el apunte.


8 https://en.wikipedia.org/wiki/Tarjan’s_strongly_connected_components_algorithm

15 Los resueltos de Alf


Problema 4 2 FINAL DEL 10/02/2015

Ítem 2
Un recorrido topológico (topological order según [4, sec. 12.4.1]) es un recorrido que permite ordenar los
vértices de un grafo dirigido y acíclico respetando su precedencia. La mejor forma de explicarlo sin entrar en
detalles del algoritmo es con un ejemplo como el siguiente [4, fig. 12.7]:

El ejemplo clásico que presentan todos los libros es el sistema de correlatividades de materias de la facultad.
Se trata de un grafo dirigido acíclico donde los vértices (nodos) son las materias y las aristas (flechitas) indican
las correlatividades.
Existen dos tipos de recorridos topológicos:
Depth-first o “en profundidad primero” [4, sec. 12.4.2]. Link9
Breadth-first o “en anchura primero” [4, sec. 12.4.3]. Link10

Ítem 3
Dos posibles recorridos en profundidad (depth-first) que comienzan en el nodo A son el rojo y el azul
mostrados a continuación:
E
C

B
J
I
*
D
G

F
En cada uno de los círculos de cada recorrido es cuando se procesa la información de cada nodo. El recorrido
preorder, inorder o postorder de un árbol es el recorrido en profundidad, obsérvese que es completamente
análogo.
Un recorrido en anchura es equivalente a un recorrido por niveles de un árbol (de hecho un recorrido por
niveles de un árbol es un caso particular de recorrido por anchura).

Ítem 4
Supongo que se habla del algoritmo de recorrido en profundidad. En clase lo vimos así:
1 R e c o r r e r en p r o f u n d i d a d ( G ) { // «G» es el grafo .
2 Marcar todos los nodos de G como no v i s i t a d o s;
3 Para cada nodo v de G {
4 i f ( v == no v i s i t a d o)
9 https://www.cs.usfca.edu/~galles/visualization/DFS.html
10 https://www.cs.usfca.edu/~galles/visualization/BFS.html

16 Los resueltos de Alf


Problema 4 2 FINAL DEL 10/02/2015

5 V i s i t a r( v ) ; // Esta f u n c i ó n está d e f i n i d a a
c o n t i n u a c i ó n.
6 }
7 }
8
9 V i s i t a r( v ) {
10 Marcar v como v i s i t a d o;
11 Para cada nodo u a d y a c e n t e a v {
12 i f ( u == no v i s i t a d o) {
13 V i s i t a r( u ) ;
14 }
15 }
16 }

17 Los resueltos de Alf


3 FINAL DEL 29/07/2014

3. Final del 29/07/2014


La consigna de este final la saqué de un post del foro, acá: http://www.foros-fiuba.com.ar/viewtopic.
php?t=3815.

Consigna
Problema 1
1. Defina hashing. Explique para qué se usa.
2. Explique los modos de resolver una colisión.
3. Indique qué implementaría mediante hashing y por qué.
4. Caracterice el hashing abierto y cerrado.
5. ¿Qué puede decir acerca del tiempo medio esperado hasta recuperar un registro en el hashing con enca-
denamiento?

Problema 2
1. Defina árbol B. Indique usos de la estructura.
2. Considere un árbol B 2-3 y muestre gráficamente la evolución del mismo cuando

a) Se cargan “tales” claves.


b) Se borran “tales” claves.

3. ¿Cuántas claves tiene como mínimo un árbol B de altura h?

Problema 3
1. Diseñe un algoritmo que permita probar si un grafo dirigido es o no acíclico.
2. ¿Cuál es el coste del algoritmo? Explique por qué.

Problema 4
Se quiere mantener las ciudades A, B, C, D, E y F conectadas al menor costo posible. Describa un algoritmo
que permita determinar cómo debe ser el grafo que logre este objetivo, desarróllelo y determine su complejidad.

18 Los resueltos de Alf


Problema 1 3 FINAL DEL 29/07/2014

Resolución
Problema 1
Ver la respuesta del ejercicio 1 del “final 1” en la página 21.

Problema 2
Ver la respuesta del ejercicio 2 del final del 10/02/2015 en la página 10.

Problema 3
Pregunta 1
Ver la respuesta del ejercicio 4 ítem 1 del final del 10/02/2015 en la página 15.

Pregunta 2
El coste del algoritmo es O (|V | + |E|). Esto se debe a que el algoritmo visita todos los nodos una única vez
cada uno y en cada visita recorre las aristas aún no recorridas, lo cual hace que finalmente haya recorrido todas
las aristas.

Problema 4
El problema de las ciudades se puede representar mediante un grafo en el que cada ciudad es un nodo y
las aristas tienen los costos. Lo que queremos encontrar es el árbol de expansión de coste mínimo (o mínimum
spanning tree). Según el apunte del campus “GrafosApunte3_7504.pdf” los algoritmos que permiten encontrar
este bello árbol son, entre otros, el algoritmo de Prim y el de Kruskal.
El siguiente video de Youtube lo explica en 2 minutos, está bueno: https://www.youtube.com/watch?
v=cplfcGZmX7I. Es un algoritmo que usa la estrategia greedy.
En [3, pag. 634] habla sobre este algoritmo. Dice que es greedy y también lo detalla en procedimiento. El
tiempo de ejecución del mismo depende de cómo se lo implementa. Parece que (ver en el libro) necesita una
cola de prioridades Q para funcionar y en función de cómo se la implementa el tiempo de ejecución es
(
O (|E| log |V |) Usando binary min-heap
T (n)Prim ∼
O (|E| + |V | log |V |) Usando Fibonacci heap

19 Los resueltos de Alf


4 FINAL 1

4. Final 1
Este es uno de los finales que andan dado vueltas sin fecha.

Consigna
Problema 1
1. Defina hashing, explique para qué se usa.
2. Explique diferentes modos de resolver una colisión.
3. Indique qué implementaría mediante hashing y por qué.
4. Caracterice el hashing abierto y cerrado.
5. ¿Qué puede decir acerca del tiempo medio esperado para hasta recuperar un registro en el hashing con
encadenamiento?.

Problema 2
1. Defina árbol B. Indique usos de la estructura.
2. Considere un árbol B de orden 5 y muestre gráficamente la evolución del mismo, inicialmente vacío, cuando

a) ingresan las claves 20, 25, 15, 18, 27, 30, 49, 59, 67, 35, 13 y 48.
b) Luego se borran las claves 15, 30, 49, 13, 48.

3. ¿Cuántas claves tiene como mínimo un árbol B de altura h?

Problema 3
1. Caracterice la estrategia greedy, indique cuándo la aplicaría (condiciones a verificarse en el problema).
2. Describa un problema de grafos que pueda resolverse aplicando estrategia greedy y el algoritmo que lo
resuelva aplicando dicha estrategia. Indique el coste del algoritmo especificando las estructuras que usaría
en la implementación del mismo.

20 Los resueltos de Alf


Problema 1 4 FINAL 1

Resolución
Problema 1
Pregunta 1
Hashing es una técnica utilizada para implementar tablas hash. Estas tablas son estructuras de datos muy
eficientes para implementar diccionarios que sólo soporten las operaciones de insertar, buscar y eliminar [3,
cap. 11]. La característica principal de las tablas hash es que el tiempo de búsqueda es
(
O (1) Average case
Tbúsqueda en tabla hash (n) ∼
Θ (n) Worst case

Las tablas hash son efectivas cuando el número de elementos que almacenan es pequeño en comparación al
número total de posibles claves. En [3, sec. 11.2] definen las cosas en base al siguiente esquema:

U=Universe of all keys Memory position

Hash table
k6 0 = h(k4)
k1 U Hash function 1 = h(k7)
k8
2 -
h(k)=memmory position for k 3 = h(ki)
k9 ki k2
K 4 -
k7 5 -
k4 ... ...
K=Actually stored keys
m-1 = h(k2)

De todo el universo de claves posibles U solo almacenamos un subconjunto K (cuanto más |K| ≪ |U | más
efectivo es el hashing) en un arreglo y la posición de memoria para la clave ki se calcula utilizando la función
hash haciendo
posición de ki = h (ki )
La función hash suele ser de la forma
h (k) = H (k) mód m
de modo tal que el valor de h (k) sea siempre un número contenido entre 0 y m − 1 (que es el tamaño de la
tabla). La función H (k) es, en principio, cualquier función.
Debido a que |U | > m con m el tamaño de la tabla hash entonces necesariamente habrán dos (o más) claves
ki y kj tales que h (ki ) = h (kj ), es decir que habrán dos (o más) claves que tendrán asignada la misma posición
de memoria. Esto se llama colisión y es algo que se quiere evitar, mas es imposible.
Pueden haber funciones hash malas o buenas. Lo que se busca es que la función hash distribuya las claves
de la manera más uniforme posible pero no siempre es fácil de lograr. El siguiente dibujito ilustra esto:

worst hash function intermediate hash function


catastrophic collision
collision

collision

nice hash function


collision

La función hash ideal será aquella que produzca la cantidad mínima de colisiones pero esto no es fácil de
lograr. La elección de la función hash dependerá de las claves que se quiera manejar.

21 Los resueltos de Alf


Problema 1 4 FINAL 1

Pregunta 2
Esto lo vimos en 15 minutos la última clase. Vimos los siguientes métodos:
1. Direccionamiento abierto (o hashing cerrado).

a) Sondeo lineal. Si la función hash nos manda a una posición que ya está ocupada entonces avanzamos
una posición y nos fijamos si está libre, si sí ya está y si no entonces avanzamos otra posición y
así. Este método produce lo que se conoce como “clusterización primaria” o “amontonamiento de
sinónimos”, que es algo que la función hash trata de evitar. Es por ello que no es un buen método
mas es simple.
b) Sondeo cuadrático. Es igual al sondeo lineal pero aumenta en forma cuadrática. Es decir que si la
posición p de la tabla está ocupada entonces se prueba en la p + 1, en la p + 2, en la p + 9, . . . , p + i2
y así sucesivamente. La idea es que los sinónimos queden dispersos a lo largo de la tabla.
c) Usar una segunda función hash. Si la posición dada por h1 (k) está ocupada entonces se intenta con
h1 (k)+h2 (k), si está ocupada entonces h1 (k)+2h2 (k) y así sucesivamente en la forma h1 (k)+ih2 (k).
d) Generalización de direccionamiento abierto. Esto no lo vimos en clase, lo vi en algún video de Youtube.
La función hash es, como dije más arriba, de la forma h (k) = H (k) mód m . Lo que se puede hacer
es modificarla así:
h (k) = (H (k) + f (i)) mód m
donde H (k) es la misma función de antes, i es un índice que empieza en 0 y lo incrementamos
cada vez que hay una colisión y f es una función cualquiera. Entonces todos los casos anteriores de
direccionamiento abierto son casos particulares de este.

2. Encadenamiento (o hashing abierto). Esto está explicado en [3, sec. 11.2]. Lo que se hace es simplemente
armar una lista enlazada en cada posición de la tabla hash entonces si hay una colisión se añaden las
claves en el mismo lugar y listo, problema resuelto. A continuación dejo una imagen del libro donde se
detalla la estructura

Pregunta 3
Las tablas hash son buenas cuando se requiere un acceso rápido a los datos. La verdad que no encuentro
una respuesta certera a esta pregunta en las fuentes típicas (libros, apuntes, internet), así que recurriré a mi
sentido común. Yo implementaría el TDA Conjunto usando una tabla hash cuando sé que mi conjunto no va a
tener “muchas claves” contenidas, es decir cuando veo que |U | ≫ |K|. De esta forma las operaciones agregar,
sacar y está? tendrían tiempo O (1) en el caso promedio.
Si mi aplicación requiere operaciones como encontrar máximo (o mínimo) o cosas así entonces la tabla hash
no sirve ya que es lineal, es como tener todo tirado en un arreglo desordenado [5, chap. 20].

Pregunta 4
Como se dijo antes cada tipo de hashing es
Hashing cerrado: Las colisiones se resuelven por sondeo.
Hashing abierto: Las colisiones se resuelven armando listas enlazadas.

22 Los resueltos de Alf


Problema 2 4 FINAL 1

Hashing abierto. Esto lo basé en lo que dice [3, sec. 11.2]. Para el peor caso (que nunca se va a dar, pero
bueno... Es el peor) el tiempo de búsqueda de un elemento es O (n) ya que podría pasar que la función hash es
pésima y todos los elementos colisionan en el mismo lugar, dando origen a una lista de longitud n.
Para el caso promedio tenemos los teoremas [3, teoremas 11.1 y 11.2] que dicen que el tiempo de búsqueda
en una tabla hash en la que las colisiones se resuelven por encadenamiento (o sea hashing abierto) es Θ (1 + α)
donde α es el load factor definido como
def n
α= → Load factor
m
con n la cantidad de elementos almacenados en la tabla y m el tamaño de la misma.
Para el mejor caso, obviamente, el elemento es encontrado de una así que es Ω (n).

Hashing cerrado. En la clase la profesora nos dio esto11 :

E [intentos en búsqueda exitosa] E [intentos en búsqueda no exitosa]


   
1 1 1 1
Sondeo lineal 2 1 + 1−α 2 1 + (1−α)2
1
Sondeo cuadrático 1 − ln (1 − α) − α
2 1−α − α − ln (1 − α)
 
1 1 1
Doble hashing α ln 1−α 1−α

Pregunta 5
Creí que era la pregunta anterior. En el hashing con encadenamiento (o abierto) el tiempo medio hasta
encontrar un elemento es Θ (1 + α) donde α = mn
es el load factor.

Problema 2
Ver respuesta al problema 2 del final del 10/02/2015, en página 10.

Problema 3
Pregunta 1 (incompleta)
La estrategia greedy (del inglés codicioso, ávido, goloso) consiste en dividir a un problema en subproblemas
elementales y resolver cada uno de estos problemas por separado, tomando como solución para cada problema
la que parece ser mejor en forma aislada.
Faltan las condiciones del problema para que se pueda resolver por estrategia greedy.

Pregunta 2
Según el apunte “GrafosApunte3_7504.pdf” del campus el algoritmo de Dijkstra para el cálculo de los
caminos mínimos con un mismo origen en un grafo ponderado (con ponderaciones no negativas) utiliza la
estrategia greedy. La mejor forma de aprender el algoritmo es en Youtube, por ejemplo acá: https://www.
youtube.com/watch?v=zXfDYaahsNA. También se puede ver el algoritmo en funcionamiento acá: https://
www.cs.usfca.edu/~galles/visualization/Dijkstra.html.

11 Las fórmulas de sondeo lineal se pueden encontrar en [5, theorem 20.3] mientras que las de doble hashing en [3, theorems 11.6

and 11.8].

23 Los resueltos de Alf


5 FINAL 2

5. Final 2
Este es uno de los finales que andan dado vueltas sin fecha.

Consigna
Problema 1
1. Determine el valor de los caminos mínimos de este grafo tomando como vértice de partida 1. Describa el
algoritmo usado.
2. Indique qué estructuras y de qué modo se podrían usar para almacenar datos y lograr un algoritmo más
eficiente que O n2 . Explique cuál es la estrategia usada y qué elementos la caracterizan.


Acá había un cartel indicando que debería haber una imagen del
grafo, pero la misma no estaba.

Problema 2
1. Defina hashing, indique qué implementaría mediante hashing y por qué.

2. Caracterice el hashing abierto y cerrado.


3. Indique modos de manejar las colisiones que no produzca clustering.

Problema 3
1. Defina árbol B. Indique usos de la estructura.
2. Considere un árbol B de 4 claves y 5 vías y muestre gráficamente la evolución del mismo, inicialmente
vacío, cuando

a) ingresan las claves 23, 20, 25, 10, 15, 27, 38, 49, 59, 67, 65, 45, 5, 3, 6.
b) Luego se borran las claves 25, 15, 49, 59.

3. ¿Cuántas claves tiene como mínimo un árbol B de altura h?

Problema 4
1. Explique qué es un trie y para qué se utiliza.
2. Muestre gráficamente dos implementaciones distintas de trie en las que se hayan almacenado estos datos:
abbc, baac, baa, baab, abbb, a.
3. Explique los pasos de la baja en un trie en una de las implementaciones planteadas (no se pide codificar).
4. ¿Qué elementos determinan la complejidad temporal del alta y la baja de las claves en un trie?

24 Los resueltos de Alf


Problema 1 5 FINAL 2

Resolución
Problema 1
Pregunta 1
Ya que no está el dibujo del grafo lo que voy a hacer es asumir que es el siguiente:

Resultado del algoritmo de


los caminos mínimos de Dijkstra

que lo saqué de acá: https://www.cs.usfca.edu/~galles/visualization/Dijkstra.html y que se pide


empezar del nodo número cero.
El primer paso es inicializar todo, lo cual se hace de la siguiente manera:

0,-1 Vamos a arrancar de acá, le ponemos


un costo 0.

INF,-1 Ve,/ex 07879:; C<89 From


0 + 0 -1
INF,-1 1 F INF -1
INF,-1 2 F INF -1
INF,-1
3 F INF -1
4 F INF -1
INF,-1 5 F INF -1
INF,-1 6 F INF -1
7 F INF -1

INF,-1

Los valores en rojo al lado de cada nodo son primero el costo y segundo el “from” del cuadro que indica desde
qué nodo llegamos. Los pongo únicamente por claridad, con el cuadro solo ya alcanza. Primero se le coloca a
todos los nodos un costo infinito (o un número suficientemente grande que sabremos que jamás se alcanzará,
por ejemplo 1000 ya alcanzaría acá) y se los marca a todos como “no visitados”.
A continuación vamos al nodo que tiene el menor costo, en este caso el 0 ya que es por el cual vamos a
arrancar. Lo marcamos como visitado:

25 Los resueltos de Alf


Problema 1 5 FINAL 2

0,-1

INF,-1 Vertex Visited Cost From


0 T 0 -1
INF,-1 1 F INF -1
INF,-1 2 F INF -1
INF,-1
3 F INF -1
4 F INF -1
INF,-1 5 F INF -1
INF,-1 6 F INF -1
= F INF -1

INF,-1

Lo que sigue es fijarse quiénes son los vecinos y comparar su costo con ∞ (que es el costo actual). Como el
costo será menor, lo anotaremos. Esto es:

0,-1

6,0 Vertex Visited Cost From


0 > 0 -1
?DG 1 F 6 0
INF,-1 2 F ? 0
@DG 3 F INF -1
4 F @ 0
INF,-1 5 F INF -1
INF,-1 6 F INF -1
B F INF -1

INF,-1

Ahora el algoritmo ya empieza a repetirse: visitar aquel nodo que no haya sido visitado que tenga el menor
costo y hacer lo mismo. En este caso es el 1. Lo visitamos y lo marcamos como ya visitado:

0,-1

6,0 Vertex Visited Cost From


0 H 0 -1
IOP 1 H 6 0
INF,-1 2 F I 0
KOP
3 F INF -1
4 F K 0
INF,-1 5 F INF -1
INF,-1 6 F INF -1
M F INF -1

INF,-1

Ahora hay que registrar los costos que tienen todos sus vecinos realizando la siguiente operación:

26 Los resueltos de Alf


Problema 1 5 FINAL 2

1 i f ( cost (1) + edge (1 , i ) < cost ( i ) ) {


2 cost ( i ) = cost (1) + edge (1 , i ) ;
3 from ( i ) = 1;
4 }

donde cost(i) es el costo actual que tiene cada nodo (ver cuadro) y edge(1,i) es el costo de recorrer el
camino que une al nodo 1 con el i y from(i) es el valor de la columna from del cuadro. En dibujos sería así:

0,-1

6,0 Vertex Visited Cost From


0 Q 0 -1
[\]
SXY 1 Q 6 0
INF,-1 [\^ 2 F S 0
UXY
[\[ 3 F INF -1
4 F U 0
12,1 5 F W 1
WXZ 6 F 12 1
W F INF -1

INF,-1

Obsérvese que el costo de 2 ya era 8 por lo tanto no se actualiza.


Ahora nuevamente se elige, de los no visitados, aquel con menor costo. Es el 5. Vamos y lo marcamos como
visitado:

0,-1

6,0 Vertex Visited Cost From


0 _ 0 -1
`fg 1 _ 6 0
INF,-1 2 F ` 0
bfg 3 F INF -1
4 F b 0
12,1 5 _ c 1
cfh 6 F 12 1
c F INF -1

INF,-1

Ahora, como ya no quedan vecinos no visitados en el 5 entonces terminamos. Repetimos lo mismo que antes:
de los nodos no visitados vamos a aquel con menor costo, ahora es el 2:

27 Los resueltos de Alf


Problema 1 5 FINAL 2

0,-1

6,0 Vertex Visited Cost From


0 j 0 -1
knp 1 j 6 0
INF,-1 2 j k 0
lnp 3 F INF -1
4 F l 0
12,1 5 j m 1
mnr 6 F 12 1
m F INF -1

INF,-1

A continuación verificamos si hay que actualizar el peso hacia los vecinos no visitados. En este caso el único
es el 4 y resulta que no hay que actualizarlo:

0,-1

6,0 Vertex Visited Cost From


}~ 0 t 0 -1
 t
uz{ 1 6 0
INF,-1 2 t u 0
wz{ 3 F INF -1
4 F w 0
12,1 5 t y 1
yz| 6 F 12 1
y F INF -1

INF,-1

Lo que falta es repetir lo mismo hasta terminar. Primero vamos a aquel nodo (de los no visitados) que tiene
el menor costo, ahora será el 4:

0,-1

6,0 Vertex Visited Cost From


0 € 0 -1
„… 1 € 6 0
INF,-1 2 €  0
‚„…
3 F INF -1
4 € ‚ 0
12,1 5 € ƒ 1
ƒ„† 6 F 12 1
ƒ F INF -1

INF,-1

No hay nada que hacer. Repetimos lo mismo y vamos al 6:

28 Los resueltos de Alf


Problema 2 5 FINAL 2

0,-1

6,0 Vertex Visited Cost From


0 ‡ 0 -1
ˆ‹Œ 1 ‡ 6 0
INF,-1 2 ‡ ˆ 0
‰‹Œ 3 F INF -1
4 ‡ ‰ 0
12,1 5 ‡ Š 1
Š‹ 6 ‡ 12 1
Š F INF -1

INF,-1

Ahora cuando nos volvemos a preguntar ¿cuál de los no visitados tiene el menor costo? lo que ocurre es que
los que quedan son infinitos, entonces no los vamos a visitar (justamente porque están desconectados). Entonces
el algoritmo termina acá.
Ahora si queremos el menor camino al nodo i lo que hacemos es ir a dicho nodo (en la tabla) y ver desde
dónde vinimos. A continuación vamos a aquel nodo desde el cual vinimos y repetimos en forma recursiva hasta
llegar al 0 y habremos obtenido el camino mínimo al nodo i.

Pregunta 2
 
2
Según Wikipedia12 el algoritmo de Dijkstra es O |V | donde |V | es la cantidad de nodos (del inglés
vertices). Hay una modificación del algoritmo (Wikipedia) que utiliza una implementación basada en una cola de
prioridades (min-priority queue) implementada con un Fibonacci heap que corre en tiempo O (|E| + |V | log |V |)
donde |E| es la cantidad de aristas o caminitos (del inglés edges).

Problema 2
Ver la respuesta al problema 1 del “Final 1” en la página 21.

Problema 3
Ver la respuesta al problema 2 del final del 10/02/2015 en la página 10.

Problema 4
Preguntas 1 y 2
Ver respuesta al problema 1 del final del 10/02/2015 en la página 7.

Pregunta 3
Las dos implementaciones que conozco de trie son las que presenté en la página 7 en la respuesta de la
pregunta 1 ítem 2. Éstas son:
Implementación con arreglo de punteros.
Implementación con listas.
El procedimiento para dar de baja una clave se puede hacer así:
1. Buscar la clave en el árbol.
2. Si la encontramos entonces la sacamos, si no la encontramos no hacemos nada.
La parte “complicada” es buscar la clave en el árbol, el paso 2 es simplemente un if que borra un puntero. La
búsqueda en un trie la describí en la respuesta de la pregunta 1 ítem 4 del final del 10/02/2015, en la página 8.
12 https://en.wikipedia.org/wiki/Dijkstra’s_algorithm.

29 Los resueltos de Alf


6 FINAL 3

6. Final 3
Este es uno de los finales que andan dado vueltas sin fecha.

Consigna
Problema 1
Defina O, Ω y Θ. Ejemplifique.

Problema 2
1. Explique en qué consiste la estrategia “divide y vencerás”.
2. ¿Cómo se puede determinar el umbral para continuar o no la partición en subproblemas?
3. ¿Siempre es más eficiente un algoritmo que usa esta estrategia?
4. ¿Qué relación hay (si existe tal relación) entre esta estrategia y la modularización?
5. Mencione algoritmos que la apliquen.

Problema 3
1. Defina árbol multivías y árbol B.
2. Considere un árbol B 2-3 inicialmente vacío. Muestre gráficamente su evolución cuando

a) se ingresan tales claves.


b) Se borran tales claves.

3. Describa detalladamente el algoritmo de alta.

Problema 4
1. ¿Qué es un TDA Conjunto? ¿Qué es un TDA Diccionario? ¿Cuáles son las operaciones básicas de cada
uno? ¿Para qué puede utilizarse cada uno?.

2. Indique tres implementaciones distintas para un diccionario. De las que ha elegido, ¿cuál le parece la más
conveniente? ¿Por qué?.

30 Los resueltos de Alf


Problema 1 6 FINAL 3

Resolución
Problema 1
Las definiciones de cada uno de estos conjuntos de funciones son, de acuerdo a como lo vimos en la clase
práctica,
def
O (f (n)) = {g (n) : ∃n0 , c > 0 tq 0 ≤ g (n) ≤ cf (n) ∀n > n0 }
def
Ω (f (n)) = {g (n) : ∃n0 , c > 0 tq 0 ≤ cf (n) ≤ g (n) ∀n > n0 }
def
Θ (f (n)) = {g (n) : ∃n0 , c1 , c2 > 0 tq 0 ≤ c1 f (n) ≤ g (n) ≤ c2 f (n) ∀n > n0 }

Problema 2
Pregunta 1
La estrategia “divide and conquer” consiste en tomar un problema y partirlo en n subproblemas de naturaleza
idéntica en forma recursiva, hasta llegar a un caso base en el que la solución del problema es trivial.

Pregunta 2
No sé exactamente a qué se refiere. Supongo que habla de cómo se sabe si se llegó al caso base... Bueno, uno
define una condición de caso base y en cada llamada recursiva la testea con un if.

Pregunta 3
En el apunte13 del campus llamado “dvide and conquer.pdf” dice que no, que los algoritmos que se obtienen
utilizando la técnica de dividir y conquistar son recursivos y son muy buenos, pero se los puede mejorar llevándo-
los a su versión iterativa. Básicamente dice que lo bueno de método de dividir y conquistar es que generalmente
quedan algoritmos que se pueden pasar a la versión iterativa de forma muy fácil mejorando así su performance
(mejora el tiempo de ejecución y, si el algoritmo recursivo se apila en el stack, también la complejidad espacial),
pero atentando en contra de la legibilidad del código y el mantenimiento.

Pregunta 4 (no tengo idea)


Pregunta 5
Algoritmos que aplican la estrategia de dividir y conquistar son:
Cálculo de la FFT.
Cálculo de la potencia de una matriz.
Búsqueda de un elemento en un arreglo desordenado.

Problema 3
Preguntas 1 y 2
Ver respuesta al problema 2 del fina del 10/02/2015 en la página 10.

Pregunta 3
El algoritmo de alta se puede expresar de la siguiente manera:
1. Ubicar el nodo en el que debería estar el nuevo elemento.
2. Si el nodo tiene al menos un lugar libre hacer a), si no hacer b):

a) Colocar el nuevo elemento en la posición correspondiente dentro del nodo y acomodar todos los
demás, junto con los punteros. Listo, terminó el alta.
b) Si el nodo está lleno entonces hacer “el elemento del medio se coloca en el nivel superior y se parte
el nodo en dos”. Esto significa: armar un arreglo ordenado con todos los elementos el nodo más
el elemento nuevo. Buscar el elemento “del medio” y mandarlo al nivel superior. La mitad inferior
mandarla al nodo existente y la mitad superior a un nodo nuevo ubicado en el mismo nivel.
13 En este caso en realidad creo que es un capítulo robado de un libro, pero no dice cuál.

31 Los resueltos de Alf


Problema 4 6 FINAL 3

1) Si el “nodo superior” también está lleno entonces repetir el paso b) en forma recursiva hasta
hasta que todo quede ordenado. En el peor de los casos se llega a la raíz.

Para entender mejor todo esto puede ser conveniente mirar la respuesta que puse en el problema 2 ítem 3 del
final del 10/02/2015 en la página 10.

Problema 4
Pregunta 1 (falta completar)
Según vimos en clase:

TDA Conjunto Es un TDA contenedor14 que almacena un grupo de datos sin que esté permitido que haya
repeticiones. La idea es la misma que la del conjunto matemático.
Sus operaciones básicas son
Crear.
Destruir.
Alta de un elemento.
Baja de un elemento.

Está (o pertenece).

TDA Diccionario Es un TDA contenedor de pares ordenados (clave, valor) donde las claves no pueden estar
repetidas.

Pregunta 2
Implementaciones que se me ocurren para un diccionario son
1. Array. Esta es la implementación más básica de todas, un arreglo que contenga en cada posición una clave
y su valor asociado. No es una implementación eficiente desde ningún punto de vista.
2. Tabla hash. Las tablas hash son buenas cuando la cantidad de claves almacenadas en el diccionario es
mucho menor al conjunto total de claves posibles ya que permiten tiempos de búsqueda, en promedio,
O (1).
3. Trie. Los tries son ideales para implementar diccionarios, cuando las claves lo permiten (por ejemplo de
palabras donde las claves son cadenas).
Yo elegiría una tabla hash o un trie dependiendo de la aplicación. Si quisiera implementar el diccionario de la
Real Academia Española usaría un trie ya que es una estructura más flexible y que garantiza tiempos de acceso
constantes (al menos independientes de n, la cantidad de palabras). Además en esta aplicación la cantidad de
palabras almacenadas en el diccionario sería (idealmente) el 100 % de las claves posibles lo cual haría que una
tabla hash comience a perder eficiencia.

14 Significa que la cantidad de elementos que posee es variable.

32 Los resueltos de Alf


REFERENCIAS REFERENCIAS

Referencias
[1] Algorithms, Sedgewick & Wayne.
[2] Data structures and algorithms in C++, Drozdek.
[3] Introduction to algorithms, Cormen.
[4] Data structures and program design in C++, Kruse.
[5] Data Structures and Problem Solving with C++, Weiss.

33 Los resueltos de Alf