1 Estructuras de Datos ● Introducción y Heaps ● Docentes: ● Eugenia Simich ● Juan Manuel Rabasedas ● Sergio Giro
2 Cómo empezar?
3 ● Mi definición: ”Organización sistemática de los datos para permitir una administración eficiente de los mismos usando un conjunto de operaciones”
4 Cómo empezar? ● Mi definición: ”Organización sistemática de los datos para permitir una administración eficiente de los mismos usando un conjunto de operaciones” ● Wikipedia: ”Es una manera particular de guardar y organizar datos en una computadora para que puedan ser usados de manera eficiente”
5 Ejemplos ● (No necesariamente informáticos) ● Agenda (organización sistemática? operaciones? eficientes?) ● Guía telefónica ● Planilla ● Guía turística ● Mapa de la ciudad
6 Arreglos/vectores ● Usos: geometría cartesiana (entre otros) ● Operaciones necesarias: ● Poner el valor para una componente ● Tomar el valor de una componente ● Operaciones derivadas: ● Suma, producto interno...
7 Arreglos/vectores ● Organización sistemática:
8 Arreglos/vectores ● Organización sistemática: ● Organizo las componentes como factores primos de un número ● (4,1,8) → = 18750000 ● Leer la n-sima componente: descomponer el número y obtener la potencia correspondiente al n- simo primo ● Asignar la n-sima componente: dividir por p^{m} y multiplicar por p^{n}. Donde p es el n-simo primo, m es el valor anterior y n es el nuevo valor
9 Arreglos/vectores ● Organización sistemática: ● En un modelo de memoria lineal: ● Reservo espacio para n enteros a partir de la posicion b ● La n-sima componente es el entero en la posicion b+n ● Leer la n-sima componente: buscar el entero en la posicion b+n ● Asignar la n-sima componente: poner el nuevo valor en la posicion b+n
10 Arreglos/vectores ● Operaciones: ● Crear: Int → Arreglo ● Leer: Arreglo x Int → Int ● Asignar: Arreglo x Int x Int→ Arreglo ● Leer(a,i) se anota a[i] ● Asignar(a,i,j) es arreglo a despues de hacer a[i] = j ● Crear(n) en lenguaje C: malloc(n*sizeof(int))
11 Arreglos/vectores ● Operación derivada: producto interno ● prod: Arreglo x Arreglo → Int ● int prod (a,b,n) { ● int res = 0; ● for(int i = 0; i < n; i++) { ● res += a[i]*b[i]; ● } ● return res; ● }
12 (Min) Heaps ● Estructura de datos con las siguientes operaciones ● insertar : Heap x Int → Heap ● borrar_min : Heap → Heap ● obtener_minimo : Heap → Int ● Se utiliza para implementar colas de prioridad (priority queues)
13 (Min) Heaps ● Los heaps son colas de prioridad en las que los datos pueden recorrerse en forma de árbol ● En cada subárbol, la raíz es el elemento mínimo del subárbol 9 2 5 67 1010
14 (Min) Heaps ● Para explicitar la estructura de árbol podemos agregar una operación adicional: ● descomponer: Heap → Heap x Int x Heap 9 2 5 67 1010 5 67 5 67 5 67 2 9 1010
15 Representación ● Podemos representar un árbol (y por lo tanto un heap) utilizando arreglos 9 2 1313 1515 5 67 8 9090 8 9090 8 9090 1010 4040 8 9090 1414 2525 8 9090 2424 2323 0 12 3 46 5 7891011121314 2 5 9 6 7 13 15 8 90 10 40 14 25 24 23 0 01 2 3 4 5 6 7 8 9 10 11 12 13 14 ● Se puede probar por inducción: ● el hijo izquierdo del nodo n está en la posición 2n+1 ● el hijo derecho en la posición 2n+2 ● padre(n) = (n-1) div 2
16 Representación descomponer(h) = descomponer( (a,r,s) ) = ((a,2r+1,s), a[r], (a,2r+2,s)) 9 2 1313 1515 5 67 8 9090 8 9090 8 1010 0 12 3 4 789 0 5 6 ● Para implementar la función descomponer podemos tener, además del arreglo, un entero r indicando la raíz ● Para saber si un nodo tiene hijos, alcanza con guardar la ultima posicion ocupada ● h = (a,r,s)
17 Implementación de las operaciones 9 2 1313 1515 5 67 8 9090 8 9090 8 1010 0 12 3 4 789 0 5 6 ● insertar: puedo poner el nuevo elemento al final del arreglo 3
18 Implementación de insertar 9 2 1313 1515 5 67 8 9090 8 9090 8 1010 0 12 3 4 789 0 5 6 ● insertar: puedo poner el nuevo elemento al final del arreglo ● No cumple la propiedad de heap 3
19 Implementación de insertar 9 2 1313 1515 5 67 8 9090 8 9090 8 1010 0 12 3 4 789 0 5 6 ● insertar: puedo poner el nuevo elemento al final del arreglo ● No cumple la propiedad de heap ● Podemos hacer que el subarbol cumpla la propiedad intercambiando el valor con el nodo superior 3 7 8 1010 4 9 3 7 8 1010 4 9 3 3 8 1010 4 9 7
20 Implementación de insertar 2 5 63 8 9090 8 9090 8 1010 0 1 3 4 789 ● El nuevo árbol tampoco cumple la propiedad de heap 7...
21 Implementación de insertar 2 5 63 8 9090 8 9090 8 1010 0 1 3 4 789 ● El nuevo árbol tampoco cumple la propiedad de heap ● Pero puedo intercambiar nuevamente 7... 3 65 8 9090 8 9090 8 1010 1 3 4 7 7
22 Implementación de insertar 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 0 12 3 4 789 0 5 6 ● Finalmente cumple con la propiedad de heap 7
23 Implementación de insertar 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 0 12 3 4 789 0 5 6 ● insertar ( (a,r,s), n) es un heap (a',r',s') en el que: ● s' = s+1 ● r' = r ● a' es el arreglo luego de ejecutar: a[s+1] = n; flotar(a,s+1); 7 10
24 Implementación de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● void swap(int a[], int p, int q) { ● int aux = a[p]; ● a[p] = a[q]; ● a[q] = aux; ● } ● int padre(int i){ if(i ==0) return 0; return (i-1)/2; }
25 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Sea c el valor de la variable r al comenzar el bucle, d el valor de r al terminar, sea a' el valor del arreglo al terminar ● Debemos demostrar a'[u]
26 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Sea c el valor de la variable r al comenzar el bucle, d el valor de r al terminar, sea a' el valor del arreglo al terminar ● Debemos demostrar a'[u] d u
27 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Sea c el valor de la variable r al comenzar el bucle, d el valor de r al terminar, sea a' el valor del arreglo al terminar ● Debemos demostrar a'[u]
28 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Sea c el valor de la variable r al comenzar el bucle, d el valor de r al terminar, sea a' el valor del arreglo al terminar ● Debemos demostrar a'[u]
29 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Sea c el valor de la variable r al comenzar el bucle, d el valor de r al terminar, sea a' el valor del arreglo al terminar ● Debemos demostrar a'[u]
30 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Sea c el valor de la variable r al comenzar el bucle, d el valor de r al terminar, sea a' el valor del arreglo al terminar ● Debemos demostrar a'[u]
31 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Sea c el valor de la variable r al comenzar el bucle, d el valor de r al terminar, sea a' el valor del arreglo al terminar ● Debemos demostrar a'[u]
32 Corrección de insertar ● void flotar(int a[], int r) { ● while(padre(r) != r ● && a[r] < a[padre(r)]) { ● swap(a,r,padre(r)); ● r = padre(r); ● } ● Invariante: al finalizar el bucle, vale la propiedad de heap excepto que puede ser a[u] > a[r] si r desciende de u ● Una vez que el bucle sale, a[padre(r)]
33 Implementacion de borrar_min ● Sobreescribo la raíz con el último elemento 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 9 7 1313 1515 3 65 8 9090 8 9090 8 1010 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 7 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 7 ● De nuevo, no cumple con la propiedad de heap ● Puedo intercambiar la raíz con la raíz de un subárbol cuyo valor sea mínimo
34 Implementacion de borrar_min ● Sobreescribo la raíz con el último elemento 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 9 3 1313 1515 7 65 8 9090 8 9090 8 1010 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 9 7 1313 1515 3 65 8 9090 8 9090 8 1010 ● De nuevo, no cumple con la propiedad de heap ● Puedo intercambiar la raíz con la raíz de un subárbol cuyo valor sea mínimo
35 Implementacion de borrar_min ● Sobreescribo la raíz con el último elemento 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 9 3 1313 1515 5 67 8 9090 8 9090 8 1010 9 2 1313 1515 3 65 8 9090 8 9090 8 1010 9 3 1313 1515 7 65 8 9090 8 9090 8 1010 ● De nuevo, no cumple con la propiedad de heap ● Puedo intercambiar la raíz con la raíz de un subárbol cuyo valor sea mínimo (notar que si subía el 6, quedaba el 6 sobre el 5). En este caso el 7 ”se hunde”
36 Implementacion de borrar_min ● borrar(a,r,s) = (a', r', s') ● donde ● s'=s-1 ● r' = r ● a' es el arreglo a después de hacer: a[0] = a[s]; hundir(a,0,s-1); void hundir(int a[], int pos, int size) { int swapPos=pos; if(hijo_izq(pos)