Story Transcript
Estructuras de Datos y Algoritmos
Tipos Abstractos De Datos
Introducci´ on Tipo de Datos(TD): conjunto de valores que una variable puede tomar. Ej: un n´ umero entero es de tipo entero). • Representaci´on interna: agrupaciones de bits. • Necesidad de usar TDs: – Elegir representaci´on interna ´ optima. – Aprovechar las caracter´ısticas de un TD. Ej: operaciones aritm´eticas. • TD simples, elementales o primitivos: enteros, reales, booleanos y caracteres. • Operaciones asociadas simples. Ej:multiplicaci´on.
1
Introducci´ on (II) Trabajar con matrices de n´ umeros −→ definir un TD matriz y operaciones asociadas (Ej: multiplicaci´on). Tipo Abstracto de Datos(TAD): modelo matem´atico con una serie de operaciones definidas (procedimientos). • TAD = generalizaci´ on de los TD simples. • Procedimientos = generalizaciones de operaciones primitivas. • La encapsulaci´ on o abstracci´ on permite: – Localizar la definici´on del TAD y sus operaciones en un lugar determinado. ⇒ Librer´ıas espec´ıficas para un TAD. – Cambiar la implementaci´on seg´un la adecuaci´ on al problema. – Facilidad en la depuraci´ on de c´ odigo. – Mayor orden y estructuraci´ on en los programas.
2
Introducci´ on (III) Ej: TAD matriz −→ implementaci´on tradicional o para matrices dispersas. • Implementaci´ on de un TAD: traducir en instrucciones de un lenguaje de programaci´ on la declaraci´on de ese tipo. Un procedimiento por cada operaci´ on definida en el TAD. • Una implementaci´on elige una estructura de datos (ED) para el TAD. • ED constru´ıda a partir de TDs simples + dispositivos de estructuraci´ on del lenguaje de programaci´ on (vectores o registros).
3
Ejemplo Definir TAD Matriz de Enteros: • Matriz de Enteros≡ agrupaci´ on de n´ umeros enteros ordenados por filas y por columnas. El n´ umero de elementos en cada fila es el mismo. Tambi´en el n´ umero de elementos en cada columna es el mismo. Un n´ umero entero que forme parte de la matriz se identifica por su fila y columna. Representaci´on l´ogica:
3 7 9 M = 4 5 6 2 3 5 M [1, 2] = 4.
4
Ejemplo (II) • Operaciones: – Suma Mat(A,B: Matriz) devuelve C: Matriz ≡ suma dos matrices que tienen igual n´ umero de filas y de columnas. La suma se realiza sumando elemento a elemento cada n´ umero entero de la matriz A con el correspondiente n´ umero entero de la matriz B que tenga igual n´ umero de fila e igual n´ umero de columna. – Multiplicar Mat(A,B: Matriz) devuelve C: Matriz ≡ multiplica dos matrices que cumplen la condici´ on . . . . – Inversa Mat(A: Matriz) devuelve C: Matriz ≡ en el caso de que la matriz A posea inversa, ´esta se calcula . . . .
5
Ejemplo: Implementaci´ on En lenguaje C: #define NUM_FILAS 10 #define NUM_COLUMNAS 10 /* Definimos el TAD */ typedef int t_matriz[NUM_FILAS][NUM_COLUMNAS]; /* Definimos una variable */ t_matriz M;
void Suma_Mat(t_matriz A, t_matriz B, t_matriz C) { int i,j; for (i=0;itope + 1 == maxP) /* Comprobamos si cabe el elemento. */ tratarPilaLlena(); /* Si no cabe hacemos un tratamiento de error. */ else { /* Si cabe, entonces */ p->tope = p->tope + 1; /* actualizamos el tope e */ p->v[p->tope] = e; /* insertamos el elemento. */ } return(p); /* Devolvemos un puntero a la pila modificada. */ } pila *desapilar(pila *p) { p->tope = p->tope - 1; /* Decrementamos el marcador al tope. */ return(p); /* Devolvemos un puntero a la pila modificada. */ } 4
int tope(pila *p) { return(p->v[p->tope]); /* Devolvemos el elemento senyalado por tope. */ }
int vaciap(pila *p) { return(p->tope < 0); /* Devolvemos 0 (falso) si la pila no esta vacia, */ } /* y 1 (cierto) en caso contrario. */
5
Representaci´ on enlazada de pilas con variable din´ amica -
Pn :
Pn−1 :
···
P2 :
P1 •
Definici´ on de tipo
typedef struct _pnodo { int e; /* Variable para almacenar un elemento de la pila. */ struct _pnodo *sig; /* Puntero al siguiente nodo que contiene un elemento } pnodo; /* Tipo nodo. Cada nodo contiene un elemento de la pila. */ typedef pnodo pila; pila *crearp() { return(NULL); /* Devolvemos un valor NULL para inicializar */ } /* el puntero de acceso a la pila. */ int tope(pila *p) { return(p->e); /* Devolvemos el elemento apuntado por p */ } 6
pila *apilar(pila *p, int e) { pnodo *paux; paux = (pnodo *) malloc(sizeof(pnodo)); /* Creamos un nodo. paux->e = e; /* Almacenamos el elemento e. paux->sig = p; /* El nuevo nodo pasa a ser tope de la pila. return(paux); /* Devolvemos un puntero al nuevo tope.
*/ */ */ */
} pila *desapilar(pila *p) { pnodo *paux;
paux = p; /* Guardamos un puntero al nodo a borrar. */ p = p->sig; /* El nuevo tope sera el nodo apuntado por el tope actual. */ free(paux); /* Liberamos la memoria ocupada por el tope actual. */ return(p); /* Devolvemos un puntero al nuevo tope. */ } int vaciap(pila *p) { return(p == NULL); /* Devolvemos 0 (falso) si la pila no esta vacia, */ } /* y 1 (cierto) en caso contrario. */ 7
Colas. Representaci´ on vectorial y enlazada Definici´ on. Una cola es una estructura de datos para almacenar objetos que se caracteriza por la manera de acceder a los datos: el primero que entra es el primero en salir (FIFO). Los elementos se introducen por la cola y se extraen por la cabeza. pcab
Q1
pcol
Q2
···
←
Qn
pcab
Q1
pcol
Q2
···
Qn
Qn+1
pcab
Q1
←
Qn+1
Q2
pcol
···
Qn
Qn+1
pcab
Q2
pcol
···
Qn
Qn+1
Aplicaciones • Cola de procesos de acceso a un recurso. • Cola de impresi´ on.
8
Operaciones sobre colas • crearq(): crea una cola vac´ıa. /
• encolar(q,e): a˜ nade el elemento e a la cola q. pcab
Q1
pcol
Q2
···
pcab /
Qn
pcol
Q1
···
Q2
Qn
e
• desencolar(q): elimina el elemento de la cabeza de q. pcab
Q1
pcol
Q2
···
pcab /
Qn
Q2
pcol
Q3
···
/
Q1
Qn
• cabeza(q): consulta el primer elemento de la cola q. pcab
Q1
pcol
Q2
···
Qn
• vaciaq(q): consulta si la cola q est´a vac´ıa. pcab
Q1
pcol
Q2
···
Qn
/
Verdadero si n = 0 Falso si n > 0
9
Representaci´ on vectorial de colas maxC−1
0
···
Q1
Q2
···
···
Qn
pcab
pcol
Posibles situaciones maxC−1
0
···
Q1
Q2
···
Qn
pcab
pcol
maxC−1
0
···
···
Qn pcol
Q1
Q2
···
pcab
10
'$ '$ '$ '$ maxC-1 0
maxC-1 0 B B
B B
XX HH pcab
· &% ··
XX pcab HH pcol
&%
&% &%
pcol
B B
¿Cola llena o cola vac´ıa?
Definici´ on de tipo #define maxC ...
/* Talla maxima del vector. */
typedef struct { int v[maxC]; /* Vector definido en tiempo de compilacion. */ int pcab, pcol; /* Marcador a la cabeza y a la cola. */ int talla; /* Numero de elementos. */ } cola;
11
cola *crearq() { cola *q;
q = (cola *) malloc(sizeof(cola)); /* Reservamos memoria para la cola. */ q->pcab = 0; /* Inicializamos el marcador a la cabeza. */ q->pcol = 0; /* Inicializamos el marcador a la cola. */ q->talla = 0; /* Inicializamos la talla. */ return(q); /* Devolvemos un puntero a la cola creada. */ } cola *encolar(cola *q, int e) { if (q->talla == maxC) /* Comprobamos si cabe el elemento. */ tratarColaLlena(); /* Si no cabe hacemos un tratamiento de error. */ else { /* Si cabe, entonces */ q->v[q->pcol] = e; /* guardamos el elemento, */ q->pcol = (q->pcol + 1) % maxC; /* incrementamos marcador de cola, */ q->talla = q->talla + 1; /* e incrementamos la talla. */ } return(q); /* Devolvemos un puntero a la cola modificada. */ }
12
cola *desencolar(cola *q) { q->pcab = (q->pcab + 1) % maxC; /* Avanzamos el marcador de cabeza. */ q->talla = q->talla - 1; /* Decrementamos la talla. */ return(q); /* Devolvemos un puntero a la cola modificada. */ } int cabeza(cola *q) { return(q->v[q->pcab]); /* Devolvemos el elemento que hay en cabeza. */ } int vaciaq(cola *p) { return(q->talla == 0); /* Devolvemos 0 (falso) si la cola */ /* no esta vacia, y 1 (cierto) en caso contrario.*/ }
13
Representaci´ on enlazada de colas con variable din´ amica pcab pcol
-
Q1 :
Q2
···
:
Qn−1 : *
Qn •
Definici´ on de tipo
typedef struct _cnodo { int e; /* Variable para almacenar un elemento de la cola. */ struct _cnodo *sig; /* Puntero al siguiente nodo que contiene un elemento } cnodo; /* Tipo nodo. Cada nodo contiene un elemento de la cola. */ typedef struct { cnodo *pcab, *pcol; /* Punteros a la cabeza y la cola. */ } cola;
14
cola *crearq() { cola *q; q = (cola*) malloc(sizeof(cola)); /* Creamos una cola. */ q->pcab = NULL; /* Inicializamos a NULL los punteros. */ q->pcol = NULL; return(q); /* Devolvemos un puntero a la cola creada.*/ } cola *encolar(cola *q, int e) { cnodo *qaux;
qaux = (cnodo *) malloc(sizeof(cnodo)); /* Creamos un nodo. */ qaux->e = e; /* Almacenamos el elemento e. */ qaux->sig = NULL; if (q->pcab == NULL) /* Si no hay nigun elemento, entonces */ q->pcab = qaux; /* pcab apunta al nuevo nodo creado, */ else /* y sino, */ q->pcol->seg = qaux; /* el nodo nuevo va despues del que apunta pcol. * q->pcol = qaux; /* El nuevo nodo pasa a estar apuntado por pcol. */ return(q); /* Devolvemos un puntero a la cola modificada. */ } 15
cola *desencolar(cola *q) { cnodo *qaux; qaux = q->pcab; /* Guardamos un puntero al nodo a borrar. q->pcab = q->pcab->sig; /* Actualizamos pcab. if (q->pcab == NULL) /* Si la cola se queda vacia, entonces q->pcol = NULL; /* actualizamos pcol. free(qaux); /* Liberamos la memoria ocupada por el nodo. return(q); /* Devolvemos un puntero a la cola modificada.
*/ */ */ */ */ */
} int cabeza(cola *q) { return(q->pcab->e); /* Devolvemos el elemento que hay en la cabeza. */ } int vaciaq(cola *q) { return(q->pcab == NULL); /* Devolvemos 0 (falso) si la cola */ } /* no esta vacia, y 1 (cierto) en caso contrario. */
16
Listas. Representaci´ on vectorial y enlazada Definici´ on. Una lista ´es una estructura de datos formada por una secuencia de objetos. Cada objeto se referencia mediante la posici´ on que ocupa en la secuencia. Operaciones sobre listas • crearl(): crea una lista vac´ıa. 1
n
/
···
• insertar(l,e,p): inserta e en la posici´ on p de la lista l. Los elementos que est´an a partir de la posici´ on p se desplazan una posici´ on. 1
p
···
L1
n
···
Lp
1 /
Ln
···
L1
p
p+1
e
Lp
n+1
···
Ln
• borrar(l,p): borra el elemento de la posici´ on p de la lista l. 1
L1
p
···
n
···
Lp
1 /
Ln
L1
n−1
p
···
Lp+1
···
Ln
• recuperar(l,p): devuelve el elemento de la posici´ on p de la lista l. 1
L1
p
···
Lp
n
···
Ln
/
Lp
17
• vacial(l): consulta si la lista l est´a vac´ıa. 1
L1
n
···
Verdadero si n = 0 /
Ln
Falso si n > 0
• fin(l): devuelve la posici´ on que sigue a la u ´ltima en la lista l. 1
n
···
L1
/
Ln
n+1
• principio(l): devuelve la primera posici´ on de la lista l. 1
L1
n
···
/
Ln
1
• siguiente(l,p): devuelve la posici´ on siguiente a p de la lista l. 1
L1
p
···
Lp
n
···
Ln
/
p+1
18
Representaci´ on vectorial de listas n−1
0
L1
L2
···
Ln
maxL−1
···
u ´ltimo
Definici´ on de tipo #define maxL ...
/* Talla maxima del vector. */
typedef int posicion; /* Cada posicion se referencia con un entero. */ typedef struct { int v[maxL]; /* Vector definido en tiempo de compilacion. */ posicion ultimo; /* Posicion del ultimo elemento. */ } lista;
19
lista *crearl() { lista *l l = (lista *) malloc(sizeof(lista)); /* Creamos la lista. */ l->ultimo = -1; /* Inicializamos el marcador al ultimo. */ return(l); /* Devolvemos un puntero a la lista creada. */ } lista *insertar(lista *l, int e, posicion p) { posicion i;
if (l->ultimo == maxL-1) /* Comprobamos si cabe el elemento. */ tratarListaLlena(); /* Si no cabe hacemos un tratamiento de error. */ else { /* Si cabe, entonces */ for (i=l->ultimo; i>=p; i--) /* hacemos un vacio en la posicion p, */ l->v[i+1] = l->v[i]; l->v[p] = e; /* guardamos el elemento, */ l->ultimo = l->ultimo + 1; /* e incrementamos el marcador al ultimo. */ return(l); /* Devolvemos un puntero a la lista modificada. */ } }
20
lista *borrar(lista *l, posicion p) { posicion i; for (i=p; iultimo; i++) /* Desplazamos los elementos del vector. */ l->v[i] = l->v[i+1]; l->ultimo = l->ultimo - 1; /* Decrementamos el marcador al ultimo. */ return(l); /* Devolvemos un puntero a la lista modificada. */ } int recuperar(lista *l, posicion p) { return(l->v[p]); /* Devolvemos el elemento que hay en la posicion p. */ } int vacial(lista *l) { return(l->ultimo < 0); /* Devolvemos 0 (falso) si la lista */ } /* no esta vacia, y 1 (cierto) en caso contrario. */
posicion fin(lista *l) { return(l->ultimo + 1); /* Devolvemos la posicion siguiente a la ultima. * } 21
posicion principio(lista *l) { return(0); /* Devolvemos la primera posicion. */ } posicion siguiente(lista *l, posicion p) { return(p+1); /* Devolvemos la posicion siguiente a la posicion p. */ }
22
Representaci´ on enlazada de listas con variable din´ amica Usamos un nodo centinela al principio de la lista para optimizar las operaciones de actualizaci´ on. primero
:
u ´ltimo
···
L1
Ln−1 : *
:
Ln •
• Primera posibilidad: dada una posici´ on p, el elemento Lp est´a en el nodo apuntado por p. p
-
···
Lp :
···
• Segunda posiblidad: dada una posici´ on p, el elemento Lp est´a en el nodo apuntado por p->sig. p
···
-
Lp−1 :
Lp :
···
23
Definici´ on de tipo
typedef struct _lnodo { int e; /* Variable para almacenar un elemento de la lista. */ struct _lnodo *sig; /* Puntero al siguiente nodo que contiene un elemento } lnodo typedef lnodo *posicion; /* Cada posicion se referencia con un puntero. */ typedef struct { /* Definimos el tipo lista con un puntero */ posicion primero, ultimo; /* al primero y ultimo nodos. */ } lista; lista *crearl() { lista *l; l = (lista *) malloc(sizeof(lista)); /* Creamos una lista. */ l->primero = (lnodo *) malloc(sizeof(lnodo)); /* Creamos el centinela */ l->primero->sig = NULL; l->ultimo = l->primero; return(l); /* Devolvemos un puntero a la lista creada. */ } 24
lista *insertar(lista *l, int e, posicion p) { posicion q; q = p->sig; /* Dejamos q apuntando al nodo que se desplaza. p->sig = (lnodo *) malloc(sizeof(lnodo)); /* Creamos un nodo. p->sig->e = e; /* Guardamos el elemento. p->sig->sig = q; /* El sucesor del nuevo nodo esta apuntado por q.
*/ */ */ */
if (p == l->ultimo) /* Si el nodo insertado ha pasaso a ser el ultimo, */ l->ultimo = p->sig; /* actualizamos ultimo. */ return(l); /* Devolvemos un puntero a la lista modificada. */ } lista *borrar(lista *l, posicion p) { posicion q; if (p->sig == l->ultimo) /* Si el nodo que borramos es el ultimo, l->ultimo = p; /* actualizamos ultimo. q = p->sig; /* Dejamos q apuntando al nodo a borrar. p->sig = p->sig->sig; /* p->sig apuntara a su sucesor. free(q); /* Liberamos la memoria ocupada por el nodo a borrar. return(l); /* Devolvemos un puntero a la lista modificada.
*/ */ */ */ */ */
} 25
int recuperar(lista *l, posicion p) { return(p->sig->e); /* Devolvemos el elemento que hay en la posicion p. */ } int vacial(lista *l) { return(l->primero->sig == NULL); /* Devolvemos 0 (falso) si la lista */ } /* no esta vacia, y 1 (cierto) en caso contrario. */ posicion fin(lista *l) { return(l->ultimo); } posicion principio(lista *l) { return(l->primero); }
/* Devolvemos la ultima posicion. */
/* Devolvemos la primera posicion. */
posicion siguiente(lista *l, posicion p) { return(p->sig); /* Devolvemos la posicion siguiente a la posicion p. */ } 26
Representaci´ on enlazada de listas con variable est´ atica 0
maxL−1
1
... p2
*
*
L1 p1
Lp
... *K
L2 *
Ln−1
...
Ln • *
u1
... • *
Definici´ on de tipo #define maxL ...
/* Talla maxima del vector. */
typedef posicion int; /* El tipo posicion se define como un entero. */ typedef struct { int v[maxL]; /* Vector definido en tiempo de compilacion. */ posicion p[maxL]; /* Vector de posiciones creado en tiempo de execucion. posicion p1, /* Marcador al principio de la lista. */ u1, /* Marcador al fin de la lista. */ p2; /* Marcador al principio de la lista de nodos vacios. */ } lista;
27
p
...
...
Lp
...
Lp = l.v[l.p[p]]
*
lista *crearl() { lista *l; int i; l = (lista *) malloc(sizeof(lista)); /* Creamos la lista. */ l->p1 = 0; /* El nodo 0 es el centinela. */ l->u1 = 0; l->p[0] = -1; l->p2 = 1; /* La lista de nodos vacios comienza en el node 1. */ for (i=1; ip[i] = i+1; l->p[maxL-1] = -1; /* El ultimo nodo vacio no senyala a ningun lugar. */ return(l); /* Devolvemos un puntero a lista construida. */ }
28
lista *insertar(lista *l, int e, posicion p) { posicion q;
if (l->p2 == -1) /* Si no quedan nodos vacios, */ tratarListaLlena(); /* hacemos un tratamiento de error. */ else { q = l->p2; /* Dejamos un marcador al primer nodo vacio. */ l->p2 = l->p[q]; /* El primer nodo vacio sera el sucesor de q. */ l->v[q] = e; /* Guardamos el elemento en el nodo reservado. */ l->p[q] = l->p[p]; /* Su sucesor pasa a ser el de la pos. p. */ l->p[p] = q; /* El sucesor del nodo apuntado por p pasa a ser q. */ if (p == l->u1) /* Si el nodo que hemos insertado pasa a ser el ultimo, l->u1 = q; /* actualizamos el marcador u1. */ return(l); /* Devolvemos un puntero a la lista modificada. */ }
29
lista *borrar(lista *l, posicion p) { posicion q;
if (l->p[p] == l->u1) /* Si el nodo que borramos es el ultimo, */ l->u1 = p; /* actualizamos u1. */ q = l->p[p]; /* Dejamos q senyalando al nodo a borrar. */ l->p[p] = l->p[q]; /* El sucesor del nodo senyalado por p pasa a ser el sucesor del nodo apuntado por q. */ l->p[q] = l->p2; /* El nodo que borramos sera el primero de los vacios. * l->p2 = q; /* El principio de la lista de nodos vacios comienza en q. */ return(l); }
30
Estructuras de Datos y Algoritmos
Divide y vencer´ as
Esquema general de “Divide y Vencer´ as” Dado un problema a resolver de talla n: 1. Dividir el problema en problemas de talla m´as peque˜ na (subproblemas), 2. Resolver independientemente los subproblemas (de manera recursiva), 3. Combinar las soluciones de los subproblemas para obtener la soluci´ on del problema original. Caracter´ısticas Esquema recursivo. Coste en la parte de divisi´ on del problema en subproblemas + Coste en la parte de combinaci´ on de resultados. Esquema eficiente cuando los subproblemas tienen una talla lo m´as parecida posible. 1
Algoritmos de ordenaci´ on Problema: Ordenar de manera no decreciente un conjunto de n enteros almacenado en un vector A.
Ordenaci´ on por Inserci´ on directa (InsertSort) Estrategia: 1. Dos partes en el vector: una ordenada y otra sin ordenar.
ordenado
no ordenado
2. Se selecciona el elemento que ocupa la primera posici´ on en la parte sin ordenar, y se inserta en la posici´ on que le corresponda por orden de la parte ordenada de tal forma que se mantenga la ordenaci´ on en dicha parte. i
ordenado
no ordenado
ordenado
no ordenado
2
Algoritmo: Argumentos:
Inserci´ on directa A: vector A[l, . . . , r], l: ´ındice de la primera posici´ on del vector, r: ´ındice de la u ´ltima posici´ on del vector
void insercion directa(int *A,int l,int r) { int i,j,aux; for(i=l+1;il) && (A[j-1]>aux)) { A[j]=A[j-1]; j--; } A[j]=aux; } }
3
llamada a la funci´ on:
insercion directa(A,0,4) 0
2
1
3
4
vector inicial A:
45 14 33 3 56
j=1
45 45 33 3 56
primera iteraci´ on (i=1,aux=14)
aux
j=0
14 45 33 3 56
segunda iteraci´ on (i=2,aux=33) 14 45 45 3 56
j=2
aux
j=1
14 33 45 3 56
4
tercera iteraci´ on (i=3,aux=3) j=3
14 33 45 45 56
j=2
14 33 33 45 56
j=1
14 14 33 45 56 aux
3 14 33 45 56
j=0
cuarta iteraci´ on (i=4,aux=56) aux
j=4
3
14 33 45 56
5
An´ alisis de la eficiencia Caso peor n X n(n − 1) 2 (i − 1) = ∈ Θ(n ) 2 i=2
Caso mejor n X
1 = n − 1 ∈ Θ(n)
i=2
Caso medio insertar en pos i:
i−2 X 1 ci = 2(i − 1) + k i
! =
k=1
n X i=2
donde Hn =
n X 1 i+1 − ci = 2 i i=2
!
(i − 1)(i + 2) i+1 1 = − 2i 2 i n2 + 3n 2 = − Hn ∈ Θ(n ) 4
Pn
1 i=1 i ∈ Θ(log n)
Coste temporal del algoritmo “inserci´ on directa” =⇒ Ω(n) O(n2 )
6
Ordenaci´ on por Selecci´ on directa (SelectSort) Funcionamiento: Dado un vector A[1, . . . , N ] 1. Inicialmente se selecciona el valor m´ınimo almacenado en el vector y se coloca en la primera posici´ on; es decir, asignamos el 1-´esimo menor elemento a la posici´ on 1. 2. A continuaci´ on, se selecciona el valor m´ınimo almacenado en la subsecuencia A[2, . . . , N ] y se coloca en la segunda posici´ on; es decir, asignamos el 2-´esimo menor elemento a la posici´ on 2. 3. Siguiendo este procedimiento se recorren todas las posiciones i del vector, asignando a cada una de ellas el elemento que le corresponde: el i-´esimo menor.
7
Algoritmo: Argumentos:
Selecci´ on directa A: vector A[l, . . . , r], l: ´ındice de la primera posici´ on del vector, r: ´ındice de la u ´ltima posici´ on del vector
void seleccion directa(int *A,int l,int r) { int i,j,min,aux; for(i=l;i>
/
30
?? ?? ?? ??
44 44 44
3
22 22 22
2
+
+
3
⇒
11 11 11
30
44 44 44
⇒
/
+
22 22 22
5
⇒
BB BB BB BB B
22 22 22
1
⇒
== == == ==
5
6
3
6
Representaci´ on de ´ arboles Representaci´ on mediante listas de hijos ⇒ formar una lista de los hijos de cada nodo.
Estructura de datos: 1. Vector v con la informaci´ on de cada nodo. 2. Cada elemento (nodo) del vector apunta a una lista enlazada de elementos (nodos) que indican cu´ales son sus nodos hijos. 3. Un ´ındice a la ra´ız.
7
Representaci´ on mediante listas de hijos: Ejemplo
7 3
12
5 1
8
11
4 2 6 9 13 10
1 2 3 4 5 6 raiz 7 8 9 10 11 12 13 14
4
2
6
/
1
8
3
5
12
/
9 11
13
10
/
/ / / /
/ / / / /
/
...
8
Representaci´ on mediante listas de hijos (III) Ventajas de esta representaci´ on: Es una representaci´on simple. Facilita las operaciones de acceso a los hijos. Inconvenientes de esta representaci´ on: Se desaprovecha memoria. Las operaciones para acceder al padre de un nodo son costosas.
9
Representaci´ on mediante listas de hijos (IV) Ejercicio: funci´ on recursiva que imprime los ´ındices de los nodos en preorden. #define N ... typedef struct snodo{ int e; struct snodo *sig; } nodo; typedef struct { int raiz; nodo *v[N]; } arbol;
void preorden(arbol *T, int n){ nodo *aux; aux = T->v[n]; printf(" %d ",n); while (aux != NULL){ preorden(T,aux->e); aux = aux->sig; } }
10
Representaci´ on de ´ arboles (II) Representaci´ on “hijo m´ as a la izquierda − hermano derecho” Para cada nodo, guardar la siguiente informaci´ on: 1. clave: valor de tipo base T almacenado en el nodo. 2. hijo izquierdo: hijo m´as a la izquierda del nodo. 3. hermano derecho: hermano derecho del nodo.
11
Representaci´ on “hijo m´ as a la izquierda − hermano derecho”: Ejemplo
A /
A B
B
C E
D F
C
D
/
/
E
J
F
J
/ /
/
GH I K LM G
H
/
/
I / /
K
L
M
/
/
/ /
12
Representaci´ on “hijo m´ as a la izquierda − hermano derecho” (III) Variante que facilita el acceso al padre desde un nodo hijo: enlazar el nodo hijo que ocupa la posici´on m´as a la derecha con su nodo padre. Especificar, en cada nodo, si el puntero al hermano derecho apunta a un hermano o al padre. Ejemplo:
A /
A B
B
C E
D F
C
D
/
E
J
F
J
/
GH I K LM G
H
/
/
I /
K
L
M
/
/
/ 13
Representaci´ on “hijo m´ as a la izquierda − hermano derecho” (IV) Ventajas de esta representaci´ on Facilita las operaciones de acceso a los hijos y al padre de un nodo. Uso eficiente de la memoria. Inconvenientes de esta representaci´ on El mantenimiento de la estructura es complejo.
14
Representaci´ on “hijo m´ as a la izquierda − hermano derecho” (V) Ejercicio: funci´ on que calcula de forma recursiva la altura de un ´arbol. int altura(arbol *T){ arbol *aux; int maxhsub=0, hsub; if (T == NULL) return(0); else if (T->hizq == NULL) return(0);
typedef struct snodo{ char clave[2]; struct snodo *hizq; struct snodo *der; } arbol;
else{ aux = T->hizq; while ( aux != NULL){ hsub = altura(aux); if (hsub > maxhsub) maxhsub = hsub; aux = aux->der; } return(maxhsub + 1);
} } 15
´ Arboles binarios ´ Arbol binario: conjunto finito de nodos tal que, o est´ a vac´ıo, o consiste en un nodo especial llamado ra´ız. El resto de nodos se agrupan en dos ´arboles binarios disjuntos llamados sub´arbol izquierdo y sub´arbol derecho. nodo raiz A B D
C E
F subarbol derecho
G subarbol izquierdo
Ejemplo de ´arboles binarios distintos: A B D
A B
C E
F G
D
C E
F
G 16
Representaci´ on de ´ arboles binarios Representaci´ on mediante vectores Estructura de datos: ´Indice al nodo ra´ız. Vector v para almacenar la informaci´ on de cada nodo. Cada elemento del vector (nodo), ser´ a una estructura con: 1. clave: valor de tipo base T almacenado en el nodo. 2. hijo izquierdo: ´ındice del nodo que es hijo izquierdo. 3. hijo derecho: ´ındice del nodo que es hijo derecho.
17
Representaci´ on mediante vectores: Ejemplo
A B D
C E
G
F
/ / / 5 / 4 / / 7
/ F / A D B C G E
/ / / 6 / 8 1 / /
/
/
/
...
0 1 2 3 raiz 4 5 6 7 8
Definici´on de tipos en C: #define N ... typedef ... tipo_baseT; typedef struct{ tipo_baseT e; int hizq, hder; } nodo; typedef struct{ int raiz; nodo v[N]; } arbol;
N−1
18
Representaci´ on de ´ arboles binarios (II) Representaci´ on mediante variables din´ amicas Para cada nodo, guardar la siguiente informaci´ on: 1. clave: valor de tipo base T almacenado en el nodo. 2. hijo izquierdo: puntero al hijo izquierdo. 3. hijo derecho: puntero al hijo derecho.
19
Representaci´ on mediante variables din´ amicas: Ejemplo A A B D
B
C E
C /
F
D
E
/ /
F /
/ /
G G / /
Definici´on de tipos en C: typedef ... tipo_baseT; typedef struct snodo{ tipo_baseT e; struct snodo *hizq, *hder; } arbol; 20
Representaci´ on mediante variables din´ amicas (III) ⇒ Variante que facilita el acceso al padre desde un hijo: en cada nodo, un puntero al padre.
A /
A B D
C E
C
B /
F
D
E
F
/ /
/
/ /
G G Definici´on de tipos en C:
/ /
typedef ... tipo_baseT; typedef struct snodo{ tipo_baseT e; struct snodo *hizq, *hder, *padre; } arbol; 21
Recorrido de ´ arboles binarios Al igual que para cualquier tipo de ´arbol, hay tres formas: recorrido en orden previo (preorden) recorrido en orden sim´etrico (inorden) recorrido en orden posterior (postorden) Preorden(x) si x 6= VACIO entonces AccionP(x) Preorden(hizquierdo(x)) Preorden(hderecho(x))
Inorden(x) si x 6= VACIO entonces Inorden(hizquierdo(x)) AccionP(x) Inorden(hderecho(x))
Postorden(x) si x 6= VACIO entonces Postorden(hizquierdo(x)) Postorden(hderecho(x)) AccionP(x)
Coste ∈ Θ(n), siendo n el n´ umero de nodos del ´arbol.
22
Recorrido de ´ arboles binarios: Implementaci´ on ⇒ Representaci´on mediante variables din´amicas. Acci´ on P ≡ escribir la clave del nodo. Definici´on de tipos en C: typedef int tipo_baseT; typedef struct snodo{ tipo_baseT e; struct snodo *hizq, *hder; } arbol;
void inorden(arbol *a){ if (a != NULL){ inorden(a->hizq); printf(‘‘ %d ’’,a->e); inorden(a->hder); } }
void preorden(arbol *a){ if (a != NULL){ printf(‘‘ %d ’’,a->e); preorden(a->hizq); preorden(a->hder); } } void postorden(arbol *a){ if (a != NULL){ postorden(a->hizq); postorden(a->hder); printf(‘‘ %d ’’,a->e); } } 23
´ Arboles binarios: Ejercicio Funci´ on en C que elimina todas las hojas del ´arbol binario, dejando exclusivamente los nodos del ´arbol que no lo son. arbol *elimina_hojas(arbol *T){ if (T == NULL) return(NULL); else if ( (T->hizq == NULL) && (T->hder == NULL) ){ free(T); return(NULL); }else{ T->hizq = elimina_hojas(T->hizq); T->hder = elimina_hojas(T->hder); return(T); } }
24
´ Arbol binario completo ´ Arbol binario completo: ´arbol binario en el cual todos los niveles tienen el m´aximo n´ umero posible de nodos excepto, puede ser, el u ´ltimo. En ese caso, las hojas del u ´ltimo nivel est´an tan a la izquierda como sea posible.
25
´ Arbol binario completo: Representaci´ on Los ´arboles binarios completos pueden ser representados mediante un vector: En la posici´on 1 se encuentra el nodo ra´ız del ´arbol. Dado un nodo que ocupa la posici´on i en el vector: • En la posici´on 2i se encuentra el nodo que es su hijo izquierdo. • En la posici´on 2i + 1 se encuentra el nodo que es su hijo derecho. • En la posici´on bi/2c se encuentra el nodo padre si i > 1. 1 7 3
2 16
10 4
5 3
8 4
7 5
11 9 10 2 13
6 1
1
2
3
4
5
6
7 10 16 3 11 5
7
8
9 10 11
1 4 13 2 19
11 19
26
´ Arbol binario completo: Ejercicio Tres funciones en C que, dado un nodo de un ´arbol binario completo representado mediante un vector, calculan la posici´ on del vector en la que se encuentra el nodo padre, el hijo izquierdo y el hijo derecho:
int hizq(int i) { return(2*i); }
int hder(int i) { return((2*i)+1); }
int padre(int i) { return(i/2); }
27
Propiedades de los ´ arboles binarios El m´aximo n´umero de nodos en el nivel i es 2i−1, i ≥ 1. En un ´arbol de i niveles hay como m´aximo 2i − 1 nodos, i ≥ 1. En un ´arbol binario no vac´ıo, si n0 es el n´ umero de hojas y n2 es el n´ umero de nodos de grado 2, se cumple que n0 = n2 + 1. La altura de un ´arbol binario completo que contiene n nodos es blog2 nc.
28
Propiedades de los ´ arboles binarios: Ejemplo Nivel 1 2 altura = 3 3
4
M´aximo nodos por nivel Nivel 1 20 = 1 Nivel 2 21 = 2 Nivel 3 22 = 4 Nivel 4 23 = 8 M´aximo nodos en el ´arbol 24 − 1 = 15 N´ umero de hojas (n0) n2 = 7 n0 = n2 + 1 = 7 + 1 = 8 Altura del ´arbol binario completo n = 15 blog2 nc = blog2 (15)c = 3
29
Estructuras de Datos y Algoritmos
Conjuntos
Conceptos generales Conjunto: colecci´on de elementos distintos; cada elemento puede ser un conjunto, o un ´atomo. Multiconjunto ≡ conjunto con elementos repetidos. Representaci´ on de conjuntos: Representaci´on expl´ıcita. C = {1, 4, 7, 11} Representaci´on mediante propiedades. C = {x ∈ N | x es par}
1
Notaci´ on Relaci´ on fundamental: pertenencia (∈) • x ∈ A, si x es un miembro del conjunto A. • x∈ / A, si x no es un miembro de A. Cardinalidad o talla: n´ umero de elementos que contiene un conjunto. Conjunto vac´ıo o nulo: no tiene miembros, ∅. A ⊆ B o B ⊇ A, si todo miembro de A tambi´en es miembro de B. Dos conjuntos son iguales sii A ⊆ B y B ⊆ A. Subconjunto propio: A 6= B y A ⊆ B.
2
Operaciones elementales sobre conjuntos Uni´ on de dos conjuntos: A ∪ B, es el conjunto de los elementos que son miembros de A, de B, o de ambos. Intersecci´on de dos conjuntos: A ∩ B, es el conjunto de los elementos que pertenecen, a la vez, tanto a A como a B. Diferencia de dos conjuntos: A − B, es el conjunto de los elementos que pertenecen a A y no pertenecen a B.
3
Conjuntos din´ amicos Conjunto din´ amico: sus elementos pueden variar a lo largo del tiempo.
Representaci´ on de conjuntos din´ amicos Sus elementos tendr´an: clave: valor que identifica al elemento. informaci´on sat´elite: informaci´ on asociada al elemento. Puede existir una relaci´on de orden total entre las claves. Ej: las claves son n´ umeros enteros, reales, palabras (orden alfab´etico). Si ∃ un orden total → definir m´ınimo y m´aximo, o predecesor o sucesor de un elemento.
4
Operaciones sobre conjuntos din´ amicos Dados S y x tal que clave(x) = k. Existen dos tipos de operaciones: Consultoras: • Buscar(S,k) → x ∈ S tal que clave(x) = k. → nulo si x ∈ / S. • Vac´ıo(S): si S es vac´ıo o no. • • • •
Operaciones posibles si en S existe una relaci´ on de orden total entre las claves: M´ınimo(S): → x con clave k m´ as peque˜ na del S. M´aximo(S): → x con clave k m´ as grande del S. Predecesor(S,x): → elemento de clave inmediatamente inferior a la de x. Sucesor(S,x): → elemento de clave inmediatamente superior a la de x.
Modificadoras: • Insertar(S,x): A˜nade x a S. • Borrar(S,x): Elimina x de S. • Crear(S): Crea S vac´ıo. 5
Tablas de dispersi´ on o tablas Hash Diccionario: conjunto que permite, principalmente, las operaciones insertar un elemento, borrar un elemento y determinar la pertenencia de un elemento.
Tablas de direccionamiento directo Direccionamiento directo: Aconsejable cuando el universo U de elementos posibles sea peque˜no. Cada elemento se identifica mediante una clave u ´nica. Representaci´on usando un vector de tama˜ no igual a la talla del universo |U |. T [0, . . . , | U | −1]: cada posici´on k del vector referencia al elemento x con clave k.
6
Representaci´ on de Tablas de direccionamiento directo Vector de punteros a estructuras donde se guarda la informaci´ on de los elementos. Operaciones triviales de coste O(1): insertar(T,x). buscar(T,k). borrar(T,x). Variante: almacenar la informaci´on en el propio vector. La posici´on del elemento indica cu´al es su clave. Necesidad de mecanismo para distinguir posiciones vac´ıas u ocupadas.
7
Representaci´ on de Tablas de direccionamiento directo (II) clave
informacion
clave
informacion
0 1
1
0
2
2
1 1 2 2
3 4
3
4
4 4
5 5
6 6
7
7
8
8
8
.. . M−1
8
.. . M−1
8
Tablas de dispersi´ on o tablas Hash Usando direccionamiento directo, si el universo U es grande: Imposibilidad de almacenar el vector. N´umero de elementos suele ser peque˜ no en comparaci´ on con la talla del universo U → espacio desaprovechado. ⇒ limitar tama˜no del vector T [0, . . . , M − 1]. Tabla de dispersi´ on (tabla hash): la posici´on de x con clave k, se obtiene al aplicar una funci´ on de dispersion o hashing h sobre la clave k: h(k). h : K −→ {0, 1, . . . , M − 1} Cubeta: cada posici´on del vector. x con clave k se dispersa en la cubeta h(k).
9
Tablas de dispersi´ on o tablas Hash (II) T 0 h(k2)
1 2
h(k1)
3
K k2 k1
L
4
k5
5 k7
6
h(k5)
7 8
h(k7)
.. . M−1
Colisi´ on: dos o m´as claves en la misma cubeta.
10
Resoluci´ on de colisiones por encadenamiento Estrategia: organizar los elementos de una cubeta mediante una lista enlazada. T 0 1
K
k9
k6
k1
k8
k2
2 k9
L
k6
3 k2 k1
4 k8
k5
5 k3
k4
k7
6
k5
7 8
k4
k7
k3
.. . M−1
11
Resoluci´ on de colisiones por encadenamiento (II) Dado T y x, tal que clave(x) = k, las operaciones son: Insertar(T,x): inserta el elemento x en la cabeza de la lista apuntada por T [h(clave(x))]. Buscar(T,k): busca un elemento con clave k en la lista T [h(k)]. Borrar(T,x): borra x de la lista encabezada por T [h(clave(x))].
12
An´ alisis del coste de las operaciones Dada T con m cubetas y que almacena n elementos ⇒ factor de carga: α =
n m.
Asunci´ on: h(k) puede ser calculado en O(1). Insertar → O(1). Buscar
→ O(n). → Θ(1 + α)
si tenemos que n = O(m) ⇒ O(1)
Borrar → Θ(1 + α) ⇒ O(1)
13
Funciones de dispersi´ on Funci´ on “ideal” → satisface la dispersi´ on uniforme simple: cada elemento con igual probabilidad de dispersarse en las m cubetas. Dificultad de encontrar funci´on con esta condici´ on. Usamos funciones que dispersen de forma aceptable los elementos entre las cubetas. Universo de claves ≡ conjunto de n´ umeros naturales N = {0, 1, 2, . . . }. Representaci´on de claves como n´ umeros naturales. Ej: cadena de caracteres → combinar la representaci´ on ASCII de sus caracteres.
14
Funciones de dispersi´ on: m´ etodo de la divisi´ on La clave k se transforma en un valor entre 0 y m − 1: h(k) = k mod m Ejemplo: Si k = 100 y m = 12 → h(100) = 100 mod 12 = 4 → cubeta 4. Si k = 16 y m = 12 → h(16) = 16 mod 12 = 4 → cubeta 4. Aspecto cr´ıtico: elecci´on de m. Buen comportamiento → m primo y no pr´ oximo a una potencia de 2. Ejemplo: Almacenar 2000 cadenas con α ≤ 3. M´ınimo n´umero de cubetas necesario: 2000/3 = 666,ˆ6. m pr´ oximo a 666, primo y no cercano a una potencia de 2: 701.
15
Funciones de dispersi´ on: m´ etodo de la multiplicaci´ on La clave k se transforma en un valor entre 0 y m − 1 en dos pasos: 1. Multiplicar k por una constante en el rango 0 < A < 1 y extraer u ´nicamente la parte decimal. (k · A) mod 1 2. Multiplicar el valor anterior por m, y truncar el resultado al entero m´ as pr´ oximo por debajo. h(k) = bm (( k · A) mod 1)c El valor de m no es cr´ıtico. Tomar m como potencia de 2 para facilitar c´omputo: m = 2p, p entero.
16
Funciones de dispersi´ on: Ejemplos sobre cadenas de caracteres Realizar conversi´on de la cadena a un numero natural. Cadena x de n caracteres (x = x0x1x2 . . . xn−1).
Ejemplos de funciones basadas en el m´ etodo de la divisi´ on. Funci´ on 1: sumar los c´ odigos ASCII de cada caracter. (aprovecha toda la clave) h(x) = (
n−1 X
xi) mod m
i=0
Inconvenientes: • m ↑↑, mala distribuci´ on de las claves. Ej: m = 10007, cadenas de longitud ≤ 10 → m´ aximo valor de x: 255 · 10 = 2550. Cubetas desde 2551 a 10006 vac´ıas. • El orden de los caracteres no se tiene en cuenta. h(“cosa”)=h(“saco”). 17
Ejemplos de funciones basadas en el m´ etodo de la divisi´ on. (II) Funci´ on 2: usar tres primeros caracteres como n´ umeros en una determinada base (256). h(x) = (
2 X
xi 256i) mod m
i=0
Inconvenientes: • Cadenas con tres primeros caracteres iguales → misma cubeta. h(“clase”)=h(“clarinete”)=h(“clan”). Funci´ on 3: Similar a funci´ on 2, considerando toda la cadena. h(x) = (
n−1 X
xi 256((n−1)−i)) mod m
i=0
Inconvenientes: • C´alculo de h costoso. 18
Ejemplos de funciones de dispersi´ on basadas en el m´ etodo de la multiplicaci´ on. Funcion 4: sumar los c´odigos ASCII de cada caracter considerando que son un n´ umero en base 2. n−1 X
h(x) = bm (((
xi 2((n−1)−i)) · A) mod 1) c
i=0
19
Ejemplos de funciones sobre cadenas de caracteres: Evaluaci´ on emp´ırica N´umero de elementos n = 3975. N´ umero de cubetas m = 2003. Funci´ on 1:
34 1000
32 30
alfa: 1.985; desv.tip: 4.33
28 26 100
24 numero de cubetas
elemento por cubeta
22 20 18 16 14
10
12 10 1
8 6 4 2 0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
0 1 2 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 numero de elementos
20
Ejemplos de funciones sobre cadenas de caracteres: Evaluaci´ on emp´ırica (II) Funci´ on 2:
1000
300 alfa: 1.985; desv.tip.: 8.97
250
200
numero de cubetas
elementos por cubeta
100
150
10
100 1 50
0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
0
25
50
75
100
125 150 175 numero de elementos
200
225
250
275
300
21
Ejemplos de funciones sobre cadenas de caracteres: Evaluaci´ on emp´ırica (III) Funci´ on 3:
10 1000 9
alfa: 1.985; desv.tip: 1.43
8 100
numero de cubetas
elementos por cubeta
7 6 5 4
10
3 1 2 1 0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
0
1
2
3
4 5 numero de elementos
6
7
8
9
22
Ejemplos de funciones sobre cadenas de caracteres: Evaluaci´ on emp´ırica (IV) Funci´ on 4:
15 1000 14
alfa: 1.985; desv.tip: 1.8
13 12 11
100
numero de cubetas
elementos por cubeta
10 9 8 7 6
10
5 4
1
3 2 1 0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
0
1
2
3
4
5
6 7 8 numero de elementos
9
10
11
12
13
14
23
Ejemplos de funciones sobre cadenas de caracteres: Variaci´ on del no de cubetas Funci´ on 3: m = 2003 10 1000 9
alfa: 1.985; desv.tip: 1.43
8 100
numero de cubetas
elementos por cubeta
7 6 5 4
10
3 1 2 1 0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
0
1
2
3
4 5 numero de elementos
6
7
8
9
m = 2000 50 alfa: 1.987; desv.tip: 7.83
1000
45 40 100 numero de cubetas
elementos por cubeta
35 30 25 20
10
15 1 10 5 0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
0
5
10
15
20 25 numero de elementos
30
35
40
45
24
Ejemplos de funciones sobre cadenas de caracteres: Variaci´ on del no de cubetas (II) Funci´ on 4: m = 2003 15 1000 14
alfa: 1.985; desv.tip: 1.8
13 12 11
100
numero de cubetas
elementos por cubeta
10 9 8 7 6
10
5 4
1
3 2 1 0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
0
1
0
1
2
3
4
5
6 7 8 numero de elementos
9
10
11
12
13
14
14
15
m = 2000 16 alfa: 1.987; desv.tip: 1.8
1000
14
12
numero de cubetas
elementos por cubeta
100 10
8
6
4
10
1
2
0 0
250
500
750
1000 cubeta
1250
1500
1750
2000
2
3
4
5
6 7 8 9 numero de elementos
10
11
12
13
25
Tablas de dispersi´ on: Ejercicio Definiciones: #define NCUB ... typedef struct snodo{ char *pal; struct snodo *sig; }nodo; typedef nodo *Tabla[NCUB]; Tabla T1, T2, T3; /* Inicializa una tabla vac´ ıa */ void crea_Tabla(Tabla T) /* Inserta la palabra w en la tabla T */ void inserta(Tabla T, char *pal) /* Devuelve un puntero al nodo que almacena la palabra pal, o NULL si no la encuentra */ nodo *buscar(Tabla T, char *pal)
26
Tablas de dispersi´ on: Ejercicio (II) Funci´ on que crea una tabla T 3 con los elementos pertenecientes a la intersecci´on de los conjuntos de las tablas T 1 y T 2. void interseccion(Tabla T1, Tabla T2, Tabla T3){ int i; nodo *aux; crea_Tabla(T3); for(i=0; ipal) != NULL ) inserta(T3,aux->pal); aux = aux->sig; } } }
27
´ Arboles binarios de b´ usqueda ´ Arbol binario de b´ usqueda: ´arbol binario que puede estar vac´ıo, o si no est´a vac´ıo cumple: Cada nodo contiene una clave u ´nica. Para cada nodo n, si su sub´arbol izquierdo no es vac´ıo, todas las claves almacenadas en su sub´arbol izquierdo son menores que la clave almacenada en n. Para cada nodo n, si su sub´arbol derecho no es vac´ıo, todas las claves almacenadas en su sub´arbol derecho son mayores que la clave almacenada en n. Usado para representar diccionarios, colas de prioridad, etc.
28
´ Arboles binarios de b´ usqueda: Ejemplos 4
15
7
7
18 10
3
21
11
9
9
SI
8
15
15 7
NO
8
7
18 16
3
14
21
3
18 8
14
21 19
29
Representaci´ on de ´ arboles binarios de b´ usqueda Usando variables din´amicas, cada nodo es una estructura: clave: clave del nodo. hijo izquierdo: puntero a la estructura del nodo que es hijo izquierdo. hijo derecho: puntero a la estructura del nodo que es hijo derecho. 15
15 7 3
11 9
7
18
18 /
21
3
11
/ /
/
21 / /
9
/ /
30
Representaci´ on de ´ arboles binarios de b´ usqueda (II) Definici´on de tipos en C: typedef ... tipo_baseT; typedef struct snodo{ tipo_baseT clave; struct snodo *hizq, *hder; }abb;
15 7
18 /
3
11
/ /
/
21 / /
9
/ / 31
Representaci´ on de ´ arboles binarios de b´ usqueda (III) Variante: almacenar puntero al nodo padre. typedef ... tipo_baseT; typedef struct snodo{ tipo_baseT clave; struct snodo *hizq, *hder, *padre; }abb;
15
/
7
18 /
11
3 / /
/
21
/ /
9 / / 32
Altura m´ axima y m´ınima de un abb Altura m´ınima: O(log n). Altura m´ axima: O(n).
logn
...
...
...
h
Altura m´ınima
.. Altura m´axima
Notaci´ on: Altura del ´arbol = h. Normalmente, altura de un abb aleatorio ∈ O(log n)
33
Recorrido de ´ arboles binarios de b´ usqueda 3 recorridos: preorden, inorden y postorden. Inorden: si la acci´on es imprimir la clave del nodo, las claves de un abb se imprimir´an de forma ordenada (menor a mayor). void inorden(abb *T){ if (T != NULL){ inorden(T->hizq); printf(" %d ",T->clave); inorden(T->hder); } } Ejercicio: realizar traza de “inorden” para el ´arbol ejemplo. 3, 7, 9, 11, 15, 18, 21 34
B´ usqueda de un elemento en un abb Operaci´ on m´as com´ un.
abb *abb_buscar(abb *T, tipo_baseT x){ while ( (T != NULL) && (x != T->clave) ) if (x < T->clave) T = T->hizq; else T = T->hder; return(T); } Coste: O(h).
35
B´ usqueda de un elemento en un abb (II) Ejercicio: versi´on recursiva de la anterior funci´ on de b´ usqueda en un abb. abb *abb_buscar(abb *T, tipo_baseT x){ if (T != NULL) if (x == T->clave) return(T); else if (x < T->clave) return(abb_buscar(T->hizq,x)); else return(abb_buscar(T->hder,x)); return(T); }
36
B´ usqueda de un elemento en un abb: Ejemplo 1 Buscar x = 11 → nodo = abb_buscar(T,11);
T
15
15
15
T
7
7
18 /
3
11
/ /
/
9
/ /
T=T−>hizq;
7
18
18
/
21 / /
3
11
/ /
/
9
/ /
T=T−>hder;
/
21 / /
3
21
11
/ /
/ /
/
9
T
/ /
return(T);
37
B´ usqueda de un elemento en un abb: Ejemplo 2 Buscar x = 19 → nodo = abb_buscar(T,19);
T
15
15
15
15
T
7
7
18
3
11
/ /
/
9
/ /
T=T−>hder;
7
18
/
18
/
21 / /
3
11
/ /
/
9
/ /
T=T−>hder;
/
21 / /
3
11
/ /
/
9
/ /
T=T−>hizq;
7
T
21 / /
18 /
3
21
11
/ /
/ /
/
9
T=NULL
/ /
return(T);
38
B´ usqueda del elemento m´ınimo y del elemento m´ aximo abb *minimo(abb *T){ if (T != NULL) while(T->hizq != NULL) T=T->hizq; return(T); } abb *maximo(abb *T){ if (T != NULL) while(T->hder != NULL) T=T->hder; return(T); } Coste: O(h). 39
B´ usqueda del m´ınimo y del m´ aximo: Ejemplo T
15
15
15
T
7
7
18
18
/
M´ınimo:
3
21
11
/ /
T
/
/ /
/
3
21
11
/ /
/ /
/
9
7
/
3
T
/ /
/
9
/ /
T=T−>hizq;
21
11
/ /
9
/ /
18
/ /
T=T−>hizq;
return(T);
15
15
15
T
7
7
18
M´ aximo:
3
11
/ /
/
9
/ /
T=T−>hder;
7
18
/
18
/
21 / /
3
11
/ /
/
9
/ /
T=T−>hder;
/
21 / /
3
11
/ /
/
T
21 / /
9
/ /
return(T);
40
Inserci´ on de un elemento en un abb → conservar la condici´on de abb. Estrategia: 1. Si el abb est´a vac´ıo → insertar nuevo nodo como ra´ız. 2. Si el abb no est´a vac´ıo: a) Buscar la posici´on que le corresponde en el abb al nuevo nodo. Para ello recorrer desde la ra´ız del ´arbol actuando como en la operaci´on de b´ usqueda. b) Una vez encontrada su posici´on, insertar en el ´arbol enlaz´andolo correctamente con el nodo que debe ser su padre. Coste: O(h).
41
Inserci´ on de un elemento en un abb (II) abb *abb_insertar(abb *T, abb *nodo){ abb *aux, *aux_padre=NULL; aux = T; while (aux != NULL){ aux_padre = aux; if (nodo->clave < aux->clave) aux = aux->hizq; else aux = aux->hder; } if (aux_padre == NULL) return(nodo); else{ if (nodo->clave < aux_padre->clave) aux_padre->hizq = nodo; else aux_padre->hder = nodo; return(T); }
} 42
Inserci´ on de un elemento en un abb: Ejemplo Insertar un nodo de clave 10. aux
aux_padre aux
T
15
7
18
7
/
3
7
18
21
3
/ /
/
11
/ /
9
/
aux 18 /
3
21
9
/ /
/ /
aux padre=aux; aux=aux->hizq;
aux padre=aux; aux=aux->hder;
aux padre=aux; aux=aux->hizq;
T
15
T
15 7
7
/ /
/
9
/ /
21
11
/ /
/ /
T
15
aux_padre
/
11
/ /
T
15
18 /
18 /
3
3
11
/ /
/
aux
21
aux_padre /
aux_padre / /
aux padre=aux; aux=aux->hder;
/ /
/
/ /
9
21
11
/ /
aux=null
9 10 / /
nodo
aux padre->hder=nodo; return(T); 43
Borrado de un elemento en un abb Estrategia: 1. Si el elemento a borrar no tiene hijos (es un hoja), se elimina. 2. Si el elemento a borrar es un nodo interior que tiene un hijo, se elimina, y su posici´on es ocupada por el hijo que tiene. 3. Si el elemento a borrar es un nodo interior que tiene dos hijos, su lugar es ocupado por el nodo de clave m´ınima del sub´arbol derecho (o por el de clave m´axima del sub´arbol izquierdo). Coste: O(h). Ejercicio: Escribir en C una funci´ on que realice el borrado de un elemento x en un abb.
44
Borrado de un elemento en un abb: Hoja T
/
/
Borrar x = 15. Hoja que no tiene padre.
aux
15
aux_padre=null T=NULL; free(aux); return(T);
Borrar x = 9. Hoja que es hijo izquierdo de un nodo. T T 15 7
18
7
/
3
11
/ /
aux_padre aux
/ /
18 /
21 /
/
9
15
20 / /
aux padre->hizq=NULL; free(aux);
3
/ /
21
11 /
/
/
20 / /
return(T);
45
Borrado de un elemento en un abb: Hoja (II) Borrar x = 21. Hoja que es hijo derecho de un nodo.
T
15
7
T
aux_padre 18
7
/
3
/ /
/ /
/
18
/ /
21
11
15
3
11
/ /
/
aux 9
/ /
aux padre->hder = NULL; free(aux);
9
/ /
return(T);
46
Borrado de un elemento en un abb: Nodo interno con un solo hijo Borrar x = 15. Nodo interno que es ra´ız del ´ arbol y tiene un u ´nico hijo derecho. 15
T
aux
/
aux_padre=null
18
T
18
/
/
21
21
/ /
/ /
T=aux->hder; free(aux);
return(T);
Borrar x = 7. Nodo interno que es hijo izquierdo y tiene un u ´nico hijo derecho. T aux
aux_padre
15
7
T
18
/
15
/
11
/
11
21
/
/ /
9
/ /
aux padre->hizq=aux->hder; free(aux);
18 /
21
9
/ /
/ /
return(T); 47
Borrado de un elemento en un abb: Nodo interno con un solo hijo (II) Borrar x = 21. Nodo interno que es hijo derecho y tiene un u ´nico hijo derecho. T
15
7
18
/
/
/
/
16
3
25
5
/ /
18
/
/
/
15
7
aux 21
16
3
T
aux_padre
/ /
25
/ /
5
/ /
/ /
aux padre->hder=aux->hder; free(aux);
return(T);
Borrar x = 15. Nodo interno que es ra´ız del ´ arbol y tiene un u ´nico hijo izquierdo. 15
T
/
aux aux_padre=null
7 3
11
/ /
/
9
/ /
T=aux->hizq; free(aux);
7
T 3
11
/ /
/
9
/ /
return(T); 48
Borrado de un elemento en un abb: Nodo interno con un solo hijo (III) Borrar x = 3. Nodo interno que es hijo izquierdo y tiene un u ´nico hijo izquierdo. T aux_padre
15
7
T
18
15
7
18
/
3
aux
11
/
/
/
21
1
/ /
/ /
/
9
9
1
/ /
/ /
/ /
21
11
/ /
aux padre->hizq=aux->hizq; free(aux);
return(T);
Borrar x = 19. Nodo interno que es hijo derecho y tiene un u ´nico hijo izquierdo. T 7
5
/ /
/
/
/
18 / /
aux padre->hder=aux->hizq; free(aux);
15
7
/
17
3
T
19
/
/
aux_padre aux
15
17 /
18
3
/ /
5
/ /
return(T); 49
Borrado de un elemento en un abb: Nodo interno con dos hijos Borrar x = 18. Nodo interno que tiene dos hijos y su hijo derecho es el nodo de clave m´ınima de su sub´ arbol derecho.
T
aux_padre
15
aux 7
18
/
/
17
3
/ /
T
15
p1
7
p2=null
25
25
/
/
5
31
/ /
/
26 / /
aux->clave=p1->clave; aux->hder=p1->hder; free(p1);
/
31
17
3
/
/ /
26
5
/ /
/ /
return(T);
50
Borrado de un elemento en un abb: Nodo interno con dos hijos (II) Borrar x = 10. Nodo interno que tiene dos hijos y el nodo de clave m´ınima de su sub´ arbol derecho no es su hijo derecho. T
aux
10
aux_padre=null 7 3
7
p2
18
/
25
16
/
p1
12
18
/
/
T
/
/
12
31
/
/
3
16
/
/
25 /
/
15 /
15
26
/
/ /
13 / /
aux->clave=p1->clave; p2->hizq=p1->hder; free(p1);
31
13
/
26
/ /
/ /
return(T);
51
´ Arboles binarios de b´ usqueda: Ejercicio Funci´ on recursiva que imprime por pantalla las claves de un ´arbol menores que una clave k.
typedef ... tipo_baseT; typedef struct snodo{ tipo_baseT clave; struct snodo *hizq, *hder; }abb; abb *T;
void menores(abb *T, tipo_baseT k){ if (T != NULL){ if (T->clave < k){ printf(" %d ",T->clave); menores(T->hizq,k); menores(T->hder,k);; } else menores(T->hizq,k); } }
→ Coste: O(n).
52
Mont´ıculos (Heaps). Colas de prioridad. ´ Arbol binario completo: ´arbol binario en el que todos los niveles tienen el m´ aximo n´ umero posible de nodos excepto, puede ser, el u ´ltimo. En ese caso, las hojas del u ´ltimo nivel est´an tan a la izquierda como sea posible. Mont´ıculo o heap: conjunto de n elementos representados en un ´arbol binario completo en el que se cumple: para todo nodo i, excepto el ra´ız, la clave del nodo i es menor o igual que la clave del padre de i (propiedad de mont´ıculo).
53
Representaci´ on de mont´ıculos → usando representaci´on vectorial de los ´arboles binarios completos: Posici´on 1 del vector → nodo ra´ız del ´arbol. Nodo en la posici´on i del vector: • Posici´on 2i → nodo que es su hijo izquierdo. • Posici´on 2i + 1 → nodo que es su hijo derecho. • Posici´on bi/2c → nodo padre si i > 1. Desde posici´on bn/2c + 1 hasta posici´on n → claves de los nodos que son hojas. (n es el n´ umero de nodos del ´arbol binario completo)
54
Representaci´ on de mont´ıculos: Ejemplo 1 16 3
2 10
14 4
5 8
8 2
6 9
7
1
3
2
5
6
7
16 14 10 8 7
9
3 2 4
7
3
4
8
9 10
1
9 10 4 1
N´umero de nodos del mont´ıculo (talla Heap): 1
A
2
3
4
5
6
7
16 14 10 8 7
9
3 2 4
8
9 10
1
...
tamanyo
talla_Heap
55
Mont´ıculos: Propiedades Usando representaci´on vectorial de un ´arbol binario completo: A[bi/2c] ≥ A[i], 1 < i ≤ n Nodo de clave m´axima → ra´ız del ´arbol. Cada rama est´a ordenada de mayor a menor desde la ra´ız hasta las hojas. Altura ∈ Θ(log n).
56
Manteniendo la propiedad de mont´ıculo Suponer A[i] < A[2i] y/o A[i] < A[2i + 1] 1 16 3
2 10
4 4
5 14
8 2
7
6
7 9
3
9 10 1 8
→ funci´ on heapify: mantiene propiedad de mont´ıculo. Transformar sub´arbol de un mont´ıculo. Sub´arboles izquierdo y derecho de i son mont´ıculos.
57
Manteniendo la propiedad de mont´ıculo: Heapify void heapify(tipo_baseT *M, int i){ tipo_baseT aux; int hizq, hder, mayor; hizq = 2*i; hder = 2*i+1; if ( (hizq M[i]) ) mayor = hizq; else mayor = i; if ( (hder M[mayor]) ) mayor = hder; if (mayor != i){ aux = M[i]; M[i] = M[mayor]; M[mayor] = aux; heapify(M,mayor); } } 58
Manteniendo la propiedad de mont´ıculo: Ejemplo → llamada inicial heapify(M,2) 1
16 3
2 4 i
10
4
5
hizq 14 8 2
6
hder 7
1
7 9
M
3
2
5
6
7
16 4 10 14 7
9
3 2 8
9 10 1 8
3
i
4
8
9 10
hizq hder
1
...
tamanyo
talla_Heap
1 16 2
3 4 i 5
4 mayor 14 8 2
10
7 9 10 1 8
6
1
7 9
3
M
2
5
6
7
16 4 10 14 7
9
3 2 8
i
3
4
mayor
8
9 10
1
...
tamanyo
talla_Heap
59
Manteniendo la propiedad de mont´ıculo: Ejemplo(II) 1 16 2
3 10
14 5
4 4 i
6 9
7
1
7 M
3
2
3
5
6
7
16 14 10 4 7
9
3 2 8 1
8
9 10 2 1 8 hizq hder
4
8
9 10
...
tamanyo
hizq hder talla_Heap
i
1 16 2
3 14
4 i 4 8
9 10 2 1 8 mayor
10 5 7
6
1
7 9
3
M
2
3
4
5
6
7
16 14 10 4
7
9
3 2 8
i
8
9 10
mayor
1
...
tamanyo
talla_Heap
60
Manteniendo la propiedad de mont´ıculo: Ejemplo (III) 1 16 2
3 14
4 8
10 5
8
7
6
1
9
3
M
2
5
6
7
16 14 10 8 7
9
3 2 4
7
9 10 2 4 i 1 mayor
3
4
8
9 10
i mayor
1
...
tamanyo
talla_Heap
Coste temporal de heapify: Caso mejor: O(1). Caso peor: O(h) = O(log n). Para un nodo i: O(h), h es la altura de i.
61
Construir un mont´ıculo: build Heap Problema: Dado un conjunto de n elementos representado en un ´arbol binario completo mediante un vector M , transformar el vector a un mont´ıculo. Estrategia: 1. Las hojas ya son mont´ıculos. 2. Aplicar heapify sobre el resto de nodos, comenzando por el de mayor ´ındice que no es hoja (M [bn/2c]). Descender hasta el ra´ız (M [1]). void build_Heap(tipo_baseT *M, int n){ int i; talla_Heap = n; for(i=n/2; i>0; i--) heapify(M,i); } 62
Construir un mont´ıculo: Ejemplo 1 4 3
2 1
3
4
5 2
8 14
M
1 4
2 1
6
7 9
16
10
9 10 7 8 3 3
4 2
5 16
6 9
7 10
8 14
9 8
10 7
build Heap(M,10);
63
1 4 3
2 1 4 2 8 14
M
1 4
3
5 mayor 16 i
6
7 9
10
9 10 7 8
2 1
3 3
4 2
5 6 16 9 i=mayor
7 10
8 14
9 8
10 7 talla_Heap
Heapify(M,5); 1 4 3
2 1
3
4
5 2
8 14
M
1 4
2 1
6
7 9
16
10
9 10 7 8 3 3
4 2
5 16
6 9
7 10
8 14
9 8
10 7 talla_Heap
Heapify(M,5); // 64
1 4 3
2 1
3
4
5 2 i
8 mayor 14
M
1 4
6
7 9
16
10
9 10 7 8
2 1
3 3
4 2 i
5 16
6 9
7 10
8 9 10 14 8 7 mayor talla_Heap
Heapify(M,4); 1 4 3
2 1
3
4
5 14 i
8 2
M
1 4
2 1
6
7 9
16
10
9 10 8 7 3 3
4 14
5 16
6 9
7 10
8 2
9 8
10 7 talla_Heap
Heapify(M,4); // 65
1 4 3
2 1
i 3
4
5 14
8 2
M
1 4
6 9
16
10 7 mayor
9 10 8 7
2 1
3 3 i
4 14
5 6 16 9
7 8 10 2 mayor
9 8
10 7 talla_Heap
Heapify(M,3); 1 4 3 10
2 1 4
5 14
8 2
M
1 4
2 1
16
6
7 9
3
9 10 7 8 3 4 10 14
5 6 16 9
7 3
8 2
9 8
10 7 talla_Heap
Heapify(M,3); // 66
1 4 3 10
2 i 1 4 14 8 2
M
1 4
6
5
7
9 16 mayor
3
9 10 8 7
2 1 i
3 4 10 14
5 6 16 9 mayor
7 3
8 2
9 8
10 7 talla_Heap
Heapify(M,2); 1 4 3 10
2 16 4 14
M
1 4
6
5
8 2
9 10 8 7
2 16
3 10
4 14
5 1
7 9
1
6 9
7 3
3
8 2
9 8
10 7 talla_Heap
heapify(M,2); → heapify(M,5); 67
1 4 3 10
2 16 4 8 2
M
1 4
6
5 14
9
i 1
3
7
9 10 7 mayor 8
2 16
3 10
4 14
5 1 i
7 3
6 9
talla_Heap 9 10 8 7 mayor
8 2
heapify(M,5); 1 4 3 10
2 16 4 14 8 2 M
1 4
6
5
2 16
7 9
7
3
9 10 1 8 3 10
4 14
5 7
6 9
7 3
8 2
9 8
10 1 talla_Heap
heapify(M,5); // → heapify(M,2); // 68
1 i 4 3 10
2 mayor 16 4
6
5 14
8 2
9
7
3
7
9 10 1 8
1 2 3 M 4 16 10 i mayor
4 14
5 7
7 3
6 9
8 2
9 8
10 1 talla_Heap
heapify(M,1); 1 16 3 10
2 4 4 14 8 2 1 M 16
2 4
6
5
9
7
3
7
9 10 8 1 3 10
4 14
5 7
6 9
7 3
8 2
9 8
10 1 talla_Heap
heapify(M,1); → Heapify(M,2); 69
1 16 3 10
2 4 i 4 8 2 1 M 16
6
5
mayor 14
7 9
7
3
9 10 8 1 3 10
2 4 i
4 5 14 7 mayor
7 3
6 9
8 2
9 8
10 1 talla_Heap
heapify(M,2); 1 16 3 10
2 14 4 4 8 2 1 M 16
6
5
2 14
7 9
7
3
9 10 8 1 3 10
4 4
5 7
6 9
7 3
8 2
9 8
10 1 talla_Heap
heapify(M,2); → Heapify(M,4); 70
1 16 3 10
2 14 4
6
5
i 4
7 9
7
3
8 2 1 M 16
10 9 8 1 mayor 3 4 5 2 14 10 4 7 i
7 3
6 9
talla_Heap 8 9 10 2 8 1 mayor
heapify(M,4); 1 16 3 10
2 14 4 8 2 1 M 16
6
5 8
2 14
7
4 3 10
9
1 4 8
7 9
3
10 5 7
6 9
7 3
8 2
9 4
10 1 talla_Heap
heapify(M,4); // → heapify(M,2); // → heapify(M,1); // 71
Coste temporal de build Heap 1a aproximaci´on: como heapify ∈ O(log n) ⇒ build heap ∈ O(n log n) 2a aproximaci´on: heapify sobre un nodo O(h) → altura h: NhO(h) Altura (h)
Nivel (i)
[log(n)] [log(n)]−1 [log(n)]−2
1
...
...
...
0
Nh ≤ 2blog2 nc−h
Num. maximo de nodos
1
2
2
2
3
2
[log(n)]
2
[log(n)]+1
2
0
1
2
[log(n)]−1
[log(n)]
2blog2 nc = 2h 72
Coste temporal de build Heap (II) Para cada altura, heapify sobre cada nodo.
blog2 nc
T (n) =
X
h=0
Como
P∞
h h=0 hx
blog2 nc
h log log 2n 2n X 2blog2 nc X X 1 log2 n h NhO(h) ≤ h ≤ 2 = n h 2h 2h 2 h=0
h=0
h=0
= x/(1 − x)2, si 0 < x < 1, ⇒
n
log 2n X h=0
h 1 h ≤ 2
1/2 n (1 − 1/2)2
= 2n ∈ O(n)
73
Algoritmo de ordenaci´ on Heapsort Estrategia: 1. build Heap: transformar el vector M en un mont´ıculo. 2. Clave mayor siempre en la ra´ız. → intercambiar ra´ız con el u ´ltimo nodo del mont´ıculo → el elemento mayor queda en su posici´on final. 1
2
M
...
n−2 n−1 n
tallaH 1
2
M
...
n−2 n−1 n
1; i--){ aux = M[i]; M[i] = M[1]; M[1] = aux; talla_Heap--; heapify(M,1); } }
76
Algoritmo de ordenaci´ on Heapsort: Ejemplo 1 1 3 10
2 14 4
5 8
8 2
7
4
9
1
1
16 14 7
4
i 8 3 4 1 2 5 6 7 9 10 16 14 10 8 7 9 3 2 4 1 tallaH
5 8
3
i 10 1
M[1]↔ M[i];
3 10
2
6 9
14
8 2
7
4
8
6
7 9
4 8 2
9 10 4 16 tallaH
heapify(M,1);
5 4
3
9
1 2 3 4 5 6 7 8 1 14 10 8 7 9 3 2
3 10
2
7
1
6
7 9
3
9
1 2 3 4 5 6 7 8 14 8 10 4 7 9 3 2
9 10 1 16 tallaH
fin iteraci´ on i = 10;
77
1
1 14 3 10
2 8 4 4 8 2
8 7
9
7
3 10
2
6
5
4
9
7
8
3
i 9 10 1 16 tallaH
fin iteraci´ on i = 9;
1 3 8 7 1
9
2
9
7
1
2
6 3
7
heapify(M,1); 3
5
3
1 2 3 4 5 6 7 8 9 10 10 8 9 4 7 1 3 2 14 16 tallaH
10 8
1
7
1 2 3 4 5 6 7 8 9 10 1 8 10 4 7 9 3 2 14 16 tallaH
1 2
6
5 4
8 2
M[1]↔ M[i];
4
9
4
7
8 2
1 2 3 4 5 6 7 8 14 8 10 4 7 9 3 2
3
2
6
5 4
3
9 1 i
4
1 10
1
5 4
8
9
4 7
6
7 1
3
2
3
3
4
5 4
7
6
7 1
2
8 i 2 i 1 2 3 4 5 6 7 8 9 10 10 8 9 4 7 1 3 2 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 2 8 9 4 7 1 3 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 9 8 3 4 7 1 2 10 14 16 tallaH
M[1]↔ M[i];
heapify(M,1);
fin iteraci´ on i = 8; 78
1
1 2
9 3
2 8 6
5 4
3 8
7
i 1
7
8
2
3
4
1
1
7
3
4
6
5 4
2
7
3
4
3
2
6
5 4
1
2
i 1 2 3 4 5 6 7 8 9 10 9 8 3 4 7 1 2 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 2 8 3 4 7 1 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 8 7 3 4 2 1 9 10 14 16 tallaH
M[1]↔ M[i];
heapify(M,1);
fin iteraci´ on i = 7;
1
1
1
8 3
2 7 5 4
2
6 1
i
7 3
2 7
3
4
1
5 4
4
3
4 2
3
2 3
4
5 1
2
i 1 2 3 4 5 6 7 8 9 10 8 7 3 4 2 1 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 1 7 3 4 2 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 7 4 3 1 2 8 9 10 14 16 tallaH
M[1]↔ M[i];
heapify(M,1);
fin iteraci´ on i = 6;
79
1
1
7
2 3
2 4 4 1
4 3
2 4
3 i
1
2
3
4
5
3
4 1
2
3
2
1
i 1 2 3 4 5 6 7 8 9 10 7 4 3 1 2 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 2 4 3 1 7 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 4 2 3 1 7 8 9 10 14 16 tallaH
M[1]↔ M[i];
heapify(M,1);
fin iteraci´ on i = 5;
1
1
4
4
3
1 3
2 2
1
3
3
2 2
3
3
2 2
1
i 1
i 1 2 3 4 5 6 7 8 9 10 4 2 3 1 7 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 1 2 3 4 7 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 3 2 1 4 7 8 9 10 14 16 tallaH
M[1]↔ M[i];
heapify(M,1);
fin iteraci´ on i = 4;
80
1
1
1
1
3 3
2
2
2
2 2
1
2 1 i i 1 2 3 4 5 6 7 8 9 10 3 2 1 4 7 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 1 2 3 4 7 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 2 1 3 4 7 8 9 10 14 16 tallaH
M[1]↔ M[i];
heapify(M,1);
fin iteraci´ on i = 3;
1
1 2
1
i i 1 2 3 4 5 6 7 8 9 10 2 1 3 4 7 8 9 10 14 16 tallaH
1 2 3 4 5 6 7 8 9 10 1 2 3 4 7 8 9 10 14 16 tallaH
M[1]↔ M[i];
heapify(M,1);
2 1
1 2 3 4 5 6 7 8 9 10 1 2 3 4 7 8 9 10 14 16
81
Coste temporal de heapsort Construir mont´ıculo: O(n). n − 1 llamadas a heapify (de coste O(log n)). ⇒ heapsort ∈ O(n log n). ´ Unicamente cuando todos los elementos fueran iguales el coste de heapsort ser´ıa O(n).
82
Colas de prioridad Aplicaci´ on usual de mont´ıculos. Cola de prioridad conjunto S de elementos, cada uno de los cuales tiene asociado una clave (prioridad). Operaciones asociadas: • Insert(S,x): inserta x en el conjunto S. • Extract Max(S): borra y devuelve el elemento de S con clave mayor. • Maximum(S): devuelve el elemento de S con clave mayor. ⇒ planificaci´ on de procesos en un sistema compartido.
83
Insertar un elemento en un mont´ıculo Estrategia: 1. Expandir el tama˜ no del mont´ıculo en 1 (talla Heap + 1). 2. Insertar el nuevo elemento en esa posici´on del vector (hoja m´ as a la izquierda posible). 3. Comparar clave del nuevo elemento con su padre: Si la clave del nuevo es mayor → el padre no cumple la propiedad de mont´ıculo: a) Intercambiar las claves. b) si el que ha pasado a ser padre del nuevo elemento no cumple la propiedad de mont´ıculo → repetir intercambio de claves hasta que el padre del nuevo elemento sea mayor o igual o hasta que el nuevo sea la ra´ız.
84
Insertar un elemento en un mont´ıculo (II) Funci´ on que inserta un nuevo elemento de clave x (antes de llamarla, debe comprobarse si el tama˜ no del vector permite la inserci´on): void insert(tipo_baseT *M, tipo_baseT x){ int i; talla_Heap++; i = talla_Heap; while ( (i > 1) && (M[i/2] < x) ){ M[i] = M[i/2]; i = i/2; } M[i] = x; }
85
Insertar un elemento en un mont´ıculo: Ejemplo insertar elemento de clave 15.
1
1
16
16
3 10
2 14 4
5 8
8 2
7
4
9
1
14
7
6 9
3 10
2 4 8
3
10
8 2
9
7 9 10 4 1
7
6
5
3
i
1 2 3 4 5 6 7 8 9 10 16 14 10 8 7 9 3 2 4 1 tallaH
i 8 1 2 3 4 5 6 7 9 10 11 16 14 10 8 7 9 3 2 4 1 tallaH
´arbol inicial
talla Heap++; i=talla Heap;
86
1
1
16
16 3 10
2 14 4 8 8 2
i 9 10 4 1
5
1 3 10
2 i 7
6 9
16
4 8
3
7
5
8 2
14 9 10 4 1
i 15 7
6 9
3 10
2 4 8
3
7
5
8 2
14 9 10 4 1
7
6 9
3
7
1 2 3 4 5 6 7 8 9 10 11 16 14 10 8 7 9 3 2 4 1 7 i tallaH
1 2 3 4 5 6 7 8 9 10 11 16 14 10 8 14 9 3 2 4 1 7 i tallaH
1 2 3 4 5 6 7 8 9 10 11 16 15 10 8 14 9 3 2 4 1 7 i tallaH
M[i]=M[i/2]; i=i/2;
M[i]=M[i/2]; i=i/2;
M[i]=x;
87
Coste temporal de insertar en un mont´ıculo Coste: n´ umero de comparaciones a realizar hasta encontrar la posici´on del nuevo elemento. Mont´ıculo de n elementos: • caso mejor: se inserta inicialmente en la posici´ on correspondiente: O(1). • caso peor: el nuevo elemento es mayor que cualquier otro elemento → llegar a la ra´ız: O(log n).
88
Extraer el m´ aximo de un mont´ıculo → ser´ a la ra´ız Estrategia: 1. obtener el valor de la ra´ız (M [1]). 2. borrar la ra´ız sustituy´endola por el valor de la u ´ltima posici´ on del mont´ıculo (M [1]=M [talla Heap]). Reducir talla del mont´ıculo en 1. 3. Aplicar heapify sobre la ra´ız para restablecer propiedad de mont´ıculo.
89
Extraer el m´ aximo de un mont´ıculo (II)
tipo_baseT extract_max(tipo_baseT *M){ tipo_baseT max; if (talla_Heap == 0){ fprintf(stderr,"Mont´ ıculo vac´ ıo"); exit(-1); } max = M[1]; M[1] = M[talla_Heap]; talla_Heap--; heapify(M,1); return(max); }
90
Extraer el m´ aximo de un mont´ıculo: Ejemplo 1
1
16
16 3 10
2 14 4
5 8
8 2
7
4
9
1
14 7
6 9
3 10
2 4 8
3
10
5
8 2
7
4
9
1
7
6 9
3
10
1 2 3 4 5 6 7 8 9 10 16 14 10 8 7 9 3 2 4 1 tallaH
1 2 3 4 5 6 7 8 9 10 16 14 10 8 7 9 3 2 4 1 tallaH
max=M[1];
M[1]↔ M[talla Heap];
91
1
1
1
14 3 10
2 14 4
5 8
8 2
7
4
8 7
6 9
3 10
2 4 4
3
9
1 2 3 4 5 6 7 8 9 1 14 10 8 7 9 3 2 4 tallaH
talla Heap–; heapify(M,1);
5
8 2
7
1
7
6 9
3
9
1 2 3 4 5 6 7 8 9 14 8 10 4 7 9 3 2 1 tallaH
return(max);
92
Coste temporal de extraer el m´ aximo de un mont´ıculo Realizar heapify sobre la ra´ız: O(log n). Extract Max ∈ O(log n).
⇒ mont´ıculo que representa una cola de prioridad: operaciones ∈ O(log n).
93
Estructura de datos para conjuntos disjuntos: MF-set Clase de equivalencia de a: subconjunto que contiene todos los elementos relacionados con a. Clases de equivalencia → partici´ on de C Todo miembro del conjunto aparece en una clase de equivalencia. Para saber si a tiene una relaci´ on de equivalencia con b ⇒ comprobar si a y b est´an en la misma clase de equivalencia.
94
MF-set (II) MF-set (Merge-Find set): estructura de n elementos (fijos). No se pueden borrar ni a˜ nadir elementos. Elementos organizados en clases de equivalencia. Subconjuntos identificados por representante: - elemento menor del subconjunto. - no importa el elemento. Si un subconjunto no es modificado → mismo representante. C
4 1 2
6
5 7
3
8 10
9 11
12
95
MF-set (III) Operaciones: Union(x,y) (Merge): x ∈ Sx y y ∈ Sy → uni´ on de Sx con Sy . El representante del nuevo subconjunto creado es alguno de sus miembros, normalmente se escoge al representante de Sx o al representante de Sy . Se eliminan los subconjuntos Sx y Sy . Buscar(x) (Find): devuelve el representante de la clase de equivalencia a la que pertenece x.
Aplicaciones: inferencia de gram´aticas, equivalencias de aut´ omatas finitos, c´alculo del ´arbol de expansi´ on de coste m´ınimo en un grafo no dirigido, etc.
96
Representaci´ on de MF-sets Cada subconjunto → un ´arbol: • Nodo: informaci´on del elemento. • La ra´ız es el representante. Representaci´on de ´arboles mediante apuntadores al padre: el que apunte a s´ı mismo ser´ a ra´ız. MF-set: colecci´on de ´arboles (bosque). Cada elemento → n´umero de 1 a n + vector M : posici´ on i ≡ ´ındice del padre de i. 1
2
4
5
12
10
6
8
3
9
7
11
1 2 3 4 5 6 7 8 9 10 11 12 M 1 1 2 4 4 4 9 10 8 10 9 12 97
Operaciones sobre MF-sets Operaci´ on Uni´ on Merge Union(x,y): hacer que la ra´ız de un ´arbol apunte al nodo ra´ız del otro. Suponiendo que x e y son ra´ıces (representantes) ⇒ modificar puntero al padre de uno de los representantes, O(1). 1
12
10
2
3
8
4
5
6
9
7
11
1 2 3 M 1 1 2
4 5 6 7 8 9 10 11 12 10 4 4 9 10 8 10 9 12
98
Operaciones sobre MF-sets Operaci´ on Buscar Find Buscar(x): usando el puntero al padre, recorrer el ´arbol desde x hasta la ra´ız. Los nodos visitados constituyen el camino de b´ usqueda. Coste proporcional a la profundidad del nodo, O(n). 10
8
4
5
6
1 2 3 M 1 1 2
9
7
4 5 6 7 8 9 10 11 12 10 4 4 9 10 8 10 9 12
11
99
An´ alisis del coste temporal MF-Set inicial: n subconjuntos de un elemento ⇒ peor secuencia de operaciones: 1. realizar n − 1 operaciones Uni´ on → u ´nico conjunto de n elementos, y 2. realizar m operaciones Buscar. Coste temporal: n − 1 operaciones Uni´ on ∈ O(n) → m operaciones Buscar ∈ O(mn). Coste determinado por c´ omo se hace la Uni´ on, tras k operaciones Uni´ on puede obtenerse un ´arbol de altura k. ⇒ T´ecnicas heur´ısticas para mejorar el coste (reduciendo altura del ´arbol).
100
Uni´ on por altura o rango Estrategia: unir de manera que la ra´ız del ´arbol m´as bajo apunte a la ra´ız del m´ as alto. Altura del ´arbol resultante: max(h1, h2). Necesario mantener altura de cada nodo. La altura de un ´arbol de n elementos ≤ blog nc. Coste de m operaciones de b´usqueda: O(m log n). 4
5
10 6
10
8
4 8
5
6
9
9
7
11
7
11
101
Compresi´ on de caminos Estrategia: Cuando se busca un elemento, hacer que todos los nodos del camino de b´ usqueda se enlacen directamente con la ra´ız. Combinando ambos heur´ısticos, coste de m operaciones de b´ usqueda: O(mα(m, n)), con α(m, n) ≡ inversa de la funci´ on de Ackerman, (crecimiento lento). Normalmente, α(m, n) ≤ 4. En la pr´actica, con ambos heur´ısticos ⇒ hacer m operaciones de b´ usqueda tiene un coste casi lineal en m. 10
10 8
9
8
11
9
7
11
7
102
Tries Tipo de ´arbol de b´usqueda. Aplicaci´ on: diccionarios. Palabras con prefijos comunes usan la misma memoria para dichos prefijos. Compactaci´ on de prefijos comunes ⇒ ahorro de espacio.
103
Tries (II) Ej: conjunto {pool, prize, preview, prepare, produce, progress}
104
Tries: B´ usqueda de un elemento Se comienza en el nodo ra´ız. Desde el principio al final de la palabra, se toma caracter a caracter. Elegir la arista etiquetada con el mismo caracter. Cada paso consume un caracter y desciende un nivel. Si se agota la palabra y se ha alcanzado una hoja ⇒ encontrado. Si en alg´ un momento no existe ninguna arista con el caracter actual o se ha agotado la palabra y estamos en un nodo interno ⇒ palabra no reconocida. Tiempo de b´ usqueda proporcional a la longitud de la palabra −→ estructura de datos muy eficiente.
105
Tries: Representaci´ on Trie ≡ un tipo de aut´ omata finito determinista (AFD). Representaci´on: matriz de transici´ on. • Filas = estados. • Columnas = etiquetas. Cada posici´on de la matriz almacena el siguiente estado al que transicionar. Coste temporal muy eficiente. La mayor´ıa de nodos tendr´an pocas aristas =⇒ gran cantidad de memoria desperdiciada.
106
´ Arboles Balanceados Estructuras de datos para almacenar elementos. Permiten una b´usqueda eficiente.
´ Arbol balanceado: ´arbol donde ninguna hoja est´ a m´as alejada de la ra´ız que cualquier otra hoja. Varios esquemas de balanceo (definici´on diferente de m´ as lejos). Diferentes algoritmos para actualizar el ´arbol.
107
´ Arboles AVL ´ Arbol AVL: ´arbol binario de b´ usqueda que cumple 1. Las alturas de los sub´arboles de cada nodo difieren como mucho en 1. 2. Cada sub´arbol es un ´arbol AVL. Inventores: Adelsson, Velskii y Landis. No est´an balanceados perfectamente. Buscar, insertar y borrar un elemento ∈ O(log n). 12
8
11
5
4
5
17
8
12
18
18
11
17
4
108
´ Arboles 2-3 ´ Arbol 2-3: ´arbol vac´ıo (si tiene 0 nodos) o un nodo simple (si tiene un u ´nico nodo) o un ´arbol con m´ultiples nodos que cumplen: 1. Cada nodo interior tiene 2 o tres hijos. 2. Cada camino desde la ra´ız a una hoja tiene la misma longitud. Nodos internos: p1 k1 p2 k2 p3 • • • • •
p1 : p2 : p3 : k1: k2:
puntero al primer hijo. puntero al segundo hijo. puntero al tercer hijo (si existe). clave m´as peque˜na descendiente del segundo hijo. clave m´as peque˜na descendiente del tercer hijo.
Hojas: informaci´on de la clave correspondiente.
109
´ Arboles 2-3 (II) Ejemplo:
110
´ Arboles 2-3: B´ usqueda Los valores en los nodos internos gu´ıan la b´ usqueda. Empezar en la ra´ız: k1 y k2 son los dos valores almacenados en la ra´ız. • Si x < k1, seguir la b´usqueda por el primer hijo. • Si x ≥ k1 y el nodo tiene solo 2 hijos, seguir la b´ usqueda por el segundo hijo. • Si x ≥ k1 y el nodo tiene 3 hijos, seguir la b´ usqueda por el segundo hijo si x < k2 y por el tercer hijo si x ≥ k2. Aplicar el esquema a cada nodo que forme parte del camino de b´ usqueda. Fin: se llega a una hoja con • la clave x =⇒ elemento encontrado. • una clave diferente a x =⇒ elemento no encontrado.
111
B-´ arboles Generalizaci´ on de los ´arboles 2-3. Aplicaciones: • Almacenamiento externo de datos. • Organizaci´ on de ´ındices en sistemas de bases de datos. • Permite minimizar los accesos a disco para consultas a bases de datos.
112
B-´ arboles (II) ´ Arbol B de orden n: ´arbol de b´ usqueda n-ario que cumple La ra´ız es una hoja o tiene por lo menos dos hijos. Cada nodo, excepto el ra´ız y las hojas. tiene entre d n2 e y n hijos. Cada camino desde la ra´ız a una hoja tiene la misma longitud. Cada nodo interno tiene hasta (n − 1) valores de claves y hasta m punteros a sus hijos. Los elementos se almacenan en los nodos internos y en las hojas. Un B-´ arbol puede verse como un ´ındice jer´arquico. La ra´ız ser´ıa el primer nivel de indizado.
113
B-´ arboles (III) Nodos internos:
p1 k1 p2 k2 . . . . . . kn−1 pn
• pi es un puntero al i-´esimo hijo, 1 ≤ i ≤ n. • ki son los valores de las claves, (k1 < k2 < . . . < kn−1 ) de manera que: ◦ todas las claves en el sub´arbol p1 son menores que k1. ◦ Para 2 ≤ i ≤ n − 1, todas las claves en el sub´arbol pi son mayores o iguales que ki−1 y menores que ki. ◦ Todas las claves en el sub´arbol pn son mayores o iguales que kn−1.
114
B-´ arboles + ´ Arbol B+: ´arbol B en el que las claves almacenadas en los nodos internos no son u ´tiles (s´ olo se usan para b´ usquedas). Todas las claves de nodos internos est´an duplicadas en las hojas. Ventaja: las hojas est´an enlazadas secuencialmente y se puede acceder a la informaci´ on de los elementos sin visitar nodos internos. Mejora la eficiencia de determinados m´etodos de b´ usqueda.
115
Estructuras de Datos y Algoritmos
Grafos
Definiciones Grafo→ modelo para representar relaciones entre elementos de un conjunto. Grafo: (V ,E), V es un conjunto de v´ertices o nodos, con una relaci´ on entre ellos; E es un conjunto de pares (u,v), u,v ∈ V , llamados aristas o arcos. Grafo dirigido: la relaci´ on sobre V no es sim´etrica. Arista ≡ par ordenado (u,v). Grafo no dirigido la relaci´ on sobre V s´ı es sim´etrica. Arista ≡ par no ordenado {u,v}, u,v ∈ V y u6=v 1
2
3
1
2 3
4
5
6
Grafo dirigido G(V , E). V = {1,2,3,4,5,6} E = {(1,2),(1,4),(2,5),(3,5),(3,6),(4,2), (5,4),(6,6)}
5
4
Grafo no dirigido G(V , E). V = {1,2,3,4,5} E = {{1,2},{1,5},{2,3},{2,4},{2,5}, {3,4},{4,5}} 1
Definiciones (II) Camino desde u ∈ V a v ∈ V : secuencia v1, v2, . . . , vk tal que u = v1, v = vk , y (vi−1,vi) ∈ E, para i =2,. . . ,k. Ej: camino desde 3 a 2 →. 1
2
3
4
5
6
Longitud de un camino: n´ umero de arcos del camino. Camino simple: camino en el que todos sus v´ertices, excepto, tal vez, el primero y el u ´ltimo, son distintos.
2
Definiciones (III) Ciclo: camino simple v1, v2, . . . , vk tal que v1 = vk . Ej: es un ciclo de longitud 3. 1
2
3
4
5
6
1
2
3
4
5
6
Bucle: ciclo de longitud 1.
Grafo ac´ıclico: grafo sin ciclos. 1
2
3
4
5
6
3
Definiciones (IV) v es adyacente a u si existe una arista (u,v) ∈ E. En un grafo no dirigido, (u,v) ∈ E incide en los nodos u, v. En un grafo dirigido, (u,v) ∈ E incide en v, y parte de u. Grado de un nodo: n´umero de arcos que inciden en ´el. En grafos dirigidos existen el grado de salida y el grado de entrada. El grado del v´ertice ser´a la suma de los grados de entrada y de salida. Grado de un grafo: m´aximo grado de sus v´ertices.
4
Definiciones (V) G0 = (V 0, E 0) es un subgrafo de G = (V , E) si V 0 ⊆ V y E 0 ⊆ E. Subgrafo inducido por V 0 ⊆ V : G0 = (V 0,E 0) tal que E 0 = {(u,v) ∈ E | u,v ∈ V 0}. Ejemplos de subgrafos del grafo de la transparencia 1:
1
2
1
2
4
5
4
5
V 0 = {1,2,4,5} E 0 = {(1,2),(1,4),(2,5),(5,4)}
Subgrafo inducido por V 0 = {1,2,4,5}
5
Definiciones (VI) v es alcanzable desde u, si existe un camino de u a v. Un grafo no dirigido es conexo si existe un camino desde cualquier v´ertice a cualquier otro. Un grafo dirigido con esta propiedad se denomina fuertemente conexo: 2 1
4
3
Si un grafo dirigido no es fuertemente conexo, pero el grafo subyacente (sin sentido en los arcos) es conexo, el grafo es d´ ebilmente conexo.
6
Definiciones (VII) En un grafo no dirigido, las componentes conexas son las clases de equivalencia seg´un la relaci´ on “ser alcanzable desde”. Un grafo no dirigido es no conexo si est´ a formado por varias componentes conexas. En un grafo dirigido, las componentes fuertemente conexas, son las clases de equivalencia seg´un la relaci´on “ser mutuamente alcanzable”. Un grafo dirigido es no fuertemente conexo si est´a formado por varias componentes fuertemente conexas. 1
2
3
4
5
6
7
Definiciones (VIII) Grafo ponderado o etiquetado: cada arco, o cada v´ertice, o los dos, tienen asociada una etiqueta.
1 8
4
10 12
2 7
9
5
3 −1
15
6
9
8
Introducci´ on a la teor´ıa de grafos problema → representaci´on con grafos → algoritmo → computador
Los puentes de K¨ onigsberg Una isla en el centro del r´ıo. Siete puentes que enlazan distintos barrios. Problema: planificar paseo de forma que saliendo de un punto se pueda volver a ´el, habiendo pasado cada uno de los puentes una vez y s´ olo una.
9
Los puentes de K¨ onigsberg (II)
10
Los puentes de K¨ onigsberg (III)
11
Los puentes de K¨ onigsberg (IV)
Traducci´ on a grafos: representar islas y orillas con puntos. transformar puentes en l´ıneas que enlacen los puntos. A C
D
B
Nuevo problema: ¿se puede dibujar la figura a partir de un punto y volver a ´el, sin repasar l´ıneas y sin levantar el l´apiz? 12
Los puentes de K¨ onigsberg (V) Euler descubri´ o las siguientes reglas: 1. Un grafo compuesto de v´ertices solo de grado par se puede recorrer en una sola pasada, partiendo de un v´ertice y regresando al mismo. 2. Un grafo con s´olo dos v´ertices de grado impar puede recorrerse en una sola pasada, pero sin volver al punto de partida. 3. Un grafo con un n´umero de v´ertices de grado impar superior a 2 no se puede recorrer en una sola pasada. Los puentes de K¨ onigsberg: ´ VERTICE A B C D
GRADO 3 3 5 3
⇒ ¡el problema no tiene soluci´ on! 13
Representaci´ on de grafos: Listas de adyacencia G = (V ,E): vector de tama˜ no |V |. Posici´on i → puntero a una lista enlazada de elementos (lista de adyacencia). Los elementos de la lista son los v´ertices adyacentes a i
2
1
3 4
5
1
4
2
5
3
6
1
2
5
2
1
5
3
2
4
4
2
5
3
5
1
2
4
1
2
2
5
3
5
4
2
5
4
6
6
3
4
4
6
14
Listas de adyacencia (II) Si G es dirigido, la suma de las longitudes de las listas de adyacencia ser´a |E|. Si G es no dirigido, la suma de las longitudes de las listas de adyacencia ser´a 2|E|. Coste espacial, sea dirigido o no: O(|V | + |E|). Representaci´on apropiada para grafos con |E| menor que |V |2. Desventaja: si se quiere comprobar si una arista (u,v) pertenece a E ⇒ buscar v en la lista de adyacencia de u. Coste O(Grado(G)) ⊆ O(|V |).
15
Listas de adyacencia (III) Representaci´on extensible a grafos ponderados. El peso de (u,v) se almacena en el nodo de v de la lista de adyacencia de u.
1 8
4
10 12
2 7
9
5
3 −1
15
6
9
1
2 10
2
5 7
3
5 −1
4
2 12
5
4 9
6
6 9
4 8
6 15
16
Listas de adyacencia (IV) Definici´on de tipos en C (grafos ponderados): #define MAXVERT ... typedef struct vertice{ int nodo, peso; struct vertice *sig; }vert_ady; typedef struct{ int talla; vert_ady *ady[MAXVERT]; }grafo;
17
Representaci´ on de grafos: Matriz de adyacencia G = (V ,E): matriz A de dimensi´on |V | × |V |. 1 si (i,j) ∈ E Valor aij de la matriz: aij = 0 en cualquier otro caso
2
1
3
1
4
4 5
4
5
2
5
1 2 3
3
6
1 2 3 4 5 6
1 0 1 0 0 1 1 0 0 0 0 0 0
2 1 0 1 1 1 2 1 0 0 1 0 0
3 0 1 0 1 0
4 0 1 1 0 1
5 1 1 0 1 0
3 0 0
4 1 0
5 0 1
6 0 0
0 0 0 0
0 0 1 0
1 0 0 0
1 0 0 1
18
Matriz de adyacencia (II) Coste espacial: O(|V |2). Representaci´on es u ´til para grafos con n´ umero de v´ertices peque˜ no, o grafos densos (|E| ≈ |V | × |V |). Comprobar si una arista (u,v) pertenece a E → consultar posici´on A[u][v]. Coste O(1).
19
Matriz de adyacencia (III) Representaci´on grafos ponderados: El peso de (i,j) se almacena en A[i, j]. aij =
1 8
4
10 12
2 7
9
5
w(i, j) si (i,j) ∈ E 0 o ∞ en cualquier otro caso
1 2 3
3 −1
15
6
9
4 5 6
1 0 0 0 0 0 0
2 10 0 0 12 0 0
3 0 0 0 0 0 0
4 8 0
5 6 0 0 0 7 0 −1 15 0 0 0 0 9 0 0 0 9
20
Matriz de adyacencia (IV) Definici´on de tipos en C: #define MAXVERT ... typedef struct{ int talla; int A[MAXVERT][MAXVERT]; }grafo;
21
Recorrido de grafos: recorrido primero en profundidad → Generalizaci´ on del orden previo (preorden) de un ´arbol. Estrategia: Partir de un v´ertice determinado v. Cuando se visita un nuevo v´ertice, explorar cada camino que salga de ´el. Hasta que no se ha finalizado de explorar uno de los caminos no se comienza con el siguiente. Un camino deja de explorarse cuando lleva a un v´ertice ya visitado. Si exist´ıan v´ertices no alcanzables desde v el recorrido queda incompleto: seleccionar alguno como nuevo v´ertice de partida, y repetir el proceso.
22
Recorrido primero en profundidad (II) Esquema recursivo: dado G = (V , E) 1. Marcar todos los v´ertices como no visitados. 2. Escoger v´ertice u como punto de partida. 3. Marcar u como visitado. 4. ∀v adyacente a u, (u,v) ∈ E, si v no ha sido visitado, repetir recursivamente (3) y (4) para v. Finalizar cuando se hayan visitado todos los nodos alcanzables desde u. Si desde u no fueran alcanzables todos los nodos del grafo: volver a (2), escoger un nuevo v´ertice de partida v no visitado, y repetir el proceso hasta que se hayan recorrido todos los v´ertices.
23
Recorrido primero en profundidad (III) → usar un vector color (talla |V |) para indicar si u ha sido visitado (color[u]=AMARILLO) o no (color[u]=BLANCO): Algoritmo Recorrido en profundidad(G){ para cada v´ertice u ∈ V color[u] = BLANCO fin para para cada v´ertice u ∈ V si (color[u] = BLANCO) Visita nodo(u) fin para } Algoritmo Visita nodo(u){ color[u] = AMARILLO para cada v´ertice v ∈ V adyacente a u si (color[v] = BLANCO) Visita nodo(v) fin para } 24
Recorrido primero en profundidad: Ejemplo
2
5 7
1
4
3
6
1
4
2
5
3
5
4
6
5
7
6
7
2
5
3
2
3
7
¡OJO!: el recorrido depende del orden en que aparecen los v´ertices en las listas de adyacencia.
25
Recorrido primero en profundidad: Ejemplo (II) 2
2
5
2
5
7 4
u
4
1
6
3
Visita nodo(1) 2
5
4
color[1]=AMARILLO 2
5
7 u
u
6
3
Visita nodo(4)
5
7
u
4
6
3
7 1
1
6
3
Rec en profund(G) 2
7
7 u
1
5
1
4
6
3
color[4]=AMARILLO
1
4
6
3
Visita nodo(6)
26
Recorrido primero en profundidad: Ejemplo (III) 2
2
5
5
7
2 u
5
7
u
7
u
4
1
4
1
6
3
1
6
3
color[6]=AMARILLO
4
6
3
Visita nodo(7)
color[7]=AMARILLO u
2
2
5
5
7 4
1
u
6
3
Visita nodo(3)
2
5
7 4
1
u
6
3
color[3]=AMARILLO
7 1
4
6
3
Visita nodo(5)
27
Recorrido primero en profundidad: Ejemplo (IV) 2
u
u
5
2
u
7 1
4
6
3
color[5]=AMARILLO
2
5
5
7 1
4
6
3
Visita nodo(2)
7 1
4
6
3
color[2]=AMARILLO
28
Recorrido primero en profundidad: coste temporal G = (V , E) se representa mediante listas de adyacencia. Visita nodo se aplica u ´nicamente sobre v´ertices no visitados → s´ olo una vez sobre cada v´ertice. Visita nodo depende del n´ umero de v´ertices adyacentes que tenga u (longitud de la lista de adyacencia). coste de todas las llamadas a Visita nodo: X
|ady(v)| = Θ(|E|)
v∈V
A˜ nadir coste asociado a los bucles de Recorrido en profundidad: O(|V |). ⇒ coste del recorrido en profundidad es O(|V | + |E|). 29
Recorrido de grafos: recorrido primero en anchura → Generalizaci´ on del recorrido por niveles de un ´arbol. Estrategia: Partir de alg´ un v´ertice u, visitar u y, despu´es, visitar cada uno de los v´ertices adyacentes a u. Repetir el proceso para cada nodo adyacente a u, siguiendo el orden en que fueron visitados. Coste: O(|V | + |E|). 2
5 7
u
1
4
6
3 30
Ordenaci´ on topol´ ogica Aplicaci´ on inmediata del recorrido en profundidad. G = (V , E) dirigido y ac´ıclico: la ordenaci´ on topol´ ogica es una permutaci´ on v1, v2, v3, . . . , v|V | de los v´ertices, tal que si (vi,vj ) ∈ E, vi 6= vj , entonces vi precede a vj en la permutaci´ on. Ordenaci´ on no posible si G es c´ıclico. La ordenaci´ on topol´ogica no es u ´nica. Una ordenaci´ on topol´ogica es como una ordenaci´ on de los v´ertices a lo largo de una l´ınea horizontal, con los arcos de izquierda a derecha. Algoritmo como el recorrido primero en profundidad. Utiliza una pila P en la que se almacena el orden topol´ogico de los v´ertices.
31
Ordenaci´ on topol´ ogica (II) Algoritmo Ordenaci´ on topol´ ogica(G){ para cada v´ertice u ∈ V color[u] = BLANCO fin para P =∅ para cada v´ertice u ∈ V si (color[u] = BLANCO) Visita nodo(u) fin para devolver(P ) } Algoritmo Visita nodo(u){ color[u] = AMARILLO para cada v´ertice v ∈ V adyacente a u si (color[v] = BLANCO) Visita nodo(v) fin para apilar(P ,u) } 32
Ordenaci´ on topol´ ogica (III) Finaliza Visita nodo(u) ↓ Se han visitado los v´ertices alcanzables desde u (los v´ertices que suceden a u en el orden topol´ ogico). ↓ Antes de acabar Visita nodo(u) apilamos u ↓ El v´ertice se apila despu´es de apilar todos los alcanzables desde ´el. ↓ Al final en P estar´an los v´ertices ordenados topol´ ogicamente.
Coste: equivalente a recorrido primero en profundidad, O(|V | + |E|).
33
Ordenaci´ on topol´ ogica: Ejemplo 2
2
5
2
5
7
7
7 u
1
4
1
6
3
u
4
1
6
Visita nodo(1) P = {} 2
5
4
6
3
3
Orden topologico(G)
2
5
5
7
Visita nodo(4) P = {} 2 u
5
7
u
7
u
1
4
6
3
Visita nodo(6) P = {}
1
4
6
3
Visita nodo(7) P = {}
1
4
6
3
apilar(P,7) P = {7} 34
Ordenaci´ on topol´ ogica: Ejemplo (II) u
2
5
2
4
u
6
2
5
7 1
u
5
7 1
3
4
7 1
6
3
Visita nodo(3) P = {7}
4
6
3
Visita nodo(5) P = {7}
apilar(P,5) P = {5,7} u
2
2
5
5
2
5
7
7
7
u
4
1
6
1
4
6
1
4
6
u
3
apilar(P,3) P = {3,5,7}
3
apilar(P,6) P = {6,3,5,7}
3
Visita nodo(2) P = {6,3,5,7} 35
Ordenaci´ on topol´ ogica: Ejemplo (III)
u
2
2
5
2
5 7
7
7 u
u
4
1
5
4
1
6
4
1
6
3
3
apilar(P,2) P = {2,6,3,5,7}
6
3
apilar(P,4) P = {4,2,6,3,5,7}
apilar(P,1) P = {1,4,2,6,3,5,7}
Ordenaci´ on de los v´ertices en una l´ınea horizontal, con todos los arcos de izquierda a derecha: 2
1
5
4
2
6
3
5
7
7 1
4
6
3
36
Caminos de m´ınimo peso G = (V , E) dirigido y ponderado con el peso de cada arista w(u,v). Peso de un camino p =< v0, v1, . . . , vk >: la suma de los pesos de las aristas que lo forman: w(p) =
k X
w(vi−1, vi)
i=1
Camino de m´ınimo peso desde u a v: camino que tiene un peso menor entre todos los caminos de u a v, o ∞ si no existe camino de u a v. Longitud de un camino de u a v: peso de un camino de u a v. Camino m´ as corto de u a v: camino de m´ınimo peso de u a v.
37
Caminos de m´ınimo peso (II) Caminos desde 1 a 2: 10
30
4
Camino
1
1
30
1
1
10
30
3 100
5
3 50
4 10
4 20
4
50
3
Longitud (peso o coste) 2
5
5
20 10
50
2
100
5
1
50
1
50 2
20
35 2
2 20
100 120
2
40 38
Caminos de m´ınimo peso (III) Usamos un grafo dirigido y ponderado para representar las comunicaciones entre ciudades: • v´ertice = ciudad. • arista (u,v) = carretera de u a v; el peso asociado a la arista es la distancia. Camino m´as corto = ruta m´ as r´apida. Variantes del c´alculo de caminos de menor longitud: • • • •
Obtenci´on Obtenci´on Obtenci´on Obtenci´on
de los caminos m´as cortos desde un v´ertice origen a todos los dem´as. de los caminos m´as cortos desde todos los v´ertices a uno destino. del camino m´as corto de un v´ertice u a un v´ertice v. de los caminos m´as cortos entre todos los pares de v´ertices.
39
Algoritmo de Dijkstra Problema: G = (V , E) grafo dirigido y ponderado con pesos no negativos; dado un v´ertice origen s, obtener los caminos m´ as cortos al resto de v´ertices de V . Si existen aristas con pesos negativos, la soluci´ on podr´ıa ser err´ onea. Otros algoritmos permiten pesos negativos, siempre que no existan ciclos de peso negativo. Idea: explotar la propiedad de que el camino m´as corto entre dos v´ertices contiene caminos m´ as cortos entre los v´ertices que forman el camino.
40
Algoritmo de Dijkstra (II) El algoritmo de Dijkstra mantiene estos conjuntos: Un conjunto de v´ertices S que contiene los v´ertices para los que la distancia m´as corta desde el origen ya es conocida. Inicialmente S = ∅. Un conjunto de v´ertices Q = V −S que mantiene, para cada v´ertice, la distancia m´as corta desde el origen pasando a trav´es de v´ertices que pertenecen a S (Distancia provisional). Para guardar las distancias provisionales usaremos un vector D[1..|V |], donde D[i] indicar´a la distancia provisional desde el origen s al v´ertice i. Inicialmente, D[u] = ∞ ∀u ∈ V − {s} y D[s]= 0. Adem´ as: Un vector P [1..|V |] para recuperar los caminos m´ınimos calculados. P [i] almacena el ´ındice del v´ertice que precede al v´ertice i en el camino m´as corto desde s hasta i.
41
Algoritmo de Dijkstra (III) Estrategia: 1. Extraer de Q el v´ertice u cuya distancia provisional D[u] sea menor. → esta distancia es la menor posible entre el v´ertice origen s y u. La distancia provisional se correspond´ıa con el camino m´as corto utilizando v´ertices de S. ⇒ ya no es posible encontrar un camino m´ as corto desde s hasta u utilizando alg´ un otro v´ertice del grafo 2. Insertar u, para el que se ha calculado el camino m´as corto desde s, en S (S = S ∪ {u}). Actualizar las distancias provisionales de los v´ertices de Q adyacentes a u que mejoren usando el nuevo camino. 3. Repetir 1 y 2 hasta que Q quede vac´ıo ⇒ en D se tendr´a, para cada v´ertice, la distancia m´as corta desde el origen.
42
Algoritmo Dijkstra(G, w, s) { para cada v´ertice v ∈ V hacer D[v] = ∞; P [v] = N U LO; fin para D[s] = 0; S = ∅; Q = V ; mientras Q 6= ∅ hacer u = extract min(Q); /∗ seg´un D ∗/ S = S ∪ {u}; para cada v´ertice v ∈ V adyacente a u hacer si D[v] > D[u] + w(u,v) entonces D[v] = D[u] + w(u,v); P [v] = u; fin si fin para fin mientras } 43
Algoritmo de Dijkstra: Ejemplo V´ertice origen 1. Aristas con trazo discont´ınuo = caminos provisionales desde el origen a los v´ertices. Aristas con trazo grueso = caminos m´ınimos ya calculados. Resto de aristas con trazo fino.
44
s
1
5
2
20
10
40
6
3
5
10 5 10
S {}
Q {1,2,3,4,5,6}
u −
5
1 0 N U LO
D P
4
20
2 ∞ N U LO
5
3 ∞ N U LO
4 ∞ N U LO
5 ∞ N U LO
6 ∞ N U LO
s
1
5
2
20
10
40
6
3
5
10 5 10
S {1}
Q {2,3,4,5,6}
u 1
5 D P
20
1 0 N U LO
4 2 ∞ N U LO
5
3 40 1
4 ∞ N U LO
5 10 1
6 5 1 45
s
1
5
2
20
10
40
6
3
5
10 5 10
S {1,6}
Q {2,3,4,5}
5
u 6
4
20
1 0 N U LO
D P
2 25 6
5
3 40 1
4 ∞ N U LO
5 10 1
6 5 1
s
1
5
2
20
10
40
6
3
5
10 5 10
S {1,6,5}
Q {2,3,4}
5 u 5
D P
20
1 0 N U LO
4 2 25 6
5
3 40 1
4 30 5
5 10 1
6 5 1 46
s
1
5
2
20
10
40
6
3
5
10 5 10
S {1,6,5,2}
Q {3,4}
5 u 2
D P
4
20
1 0 N U LO
2 25 6
5
3 40 1
4 30 5
5 10 1
6 5 1
4 30 5
5 10 1
6 5 1
s
1
5
2
20
10
40
6
3
5
10 5 10
S {1,6,5,2,4}
Q {3}
5 u 4
D P
20
1 0 N U LO
4 2 25 6
5
3 35 4
47
s
1
5
2
20
10
40
6
3
5
10 5 10
S {1,6,5,2,4,3}
Q {}
5 u 3
20
D P
1 0 N U LO
4 2 25 6
5
3 35 4
4 30 5
5 10 1
6 5 1
48
Coste temporal del algoritmo Consideraci´on: representaci´on mediante listas de adyacencia. El bucle mientras se repite hasta que el conjunto Q queda vac´ıo. Inicialmente Q tiene todos los v´ertices y en cada iteraci´ on se extrae uno de Q → |V | iteraciones. El bucle para interno al bucle mientras se repite tanto como v´ertices adyacentes tenga el v´ertice extra´ıdo de Q. El n´ umero total de iteraciones del bucle para ser´ a el n´ umero de v´ertices adyacentes de los v´ertices del grafo. → El n´ umero de arcos del grafo: |E|. La operaci´on extract min ∈ O(|V |) (conjunto Q = vector). extract min se realiza |V | veces. Coste total de extract min ∈ O(|V |2). Sumando costes del bucle para y extract min ⇒ Coste Dijkstra ∈ O(|V |2 + |E|) = O(|V |2) 49
Coste temporal del algoritmo (II) Optimizaci´ on: extract min ser´ıa m´as eficiente si Q fuese un mont´ıculo: Construir el mont´ıculo ∈ O(|V |). La operaci´on extract min ∈ O(log |V |) extract min se realiza |V | veces. Coste total de extract min ∈ O(|V | log |V |). El bucle para supone modificar la prioridad (distancia provisional) del v´ertice en el mont´ıculo y reorganizar el mont´ıculo ∈ O(log |V |). → cada iteraci´on del bucle para ∈ O(log |V |). El bucle para se realiza |E| veces → O(|E| log |V |). ⇒ Coste Dijkstra O(|V | + |V | log |V | + |E| log |V |) = O((|V | + |E|) log |V |).
50
´ Arbol de expansi´ on de coste m´ınimo ´ Arbol libre: grafo no dirigido, ac´ıclico y conexo. Propiedades: 1. Un ´arbol libre con n ≥ 1 nodos tiene n − 1 arcos. 2. Si se a˜nade una nueva arista, se crea un ciclo. 3. Cualquier par de de v´ertices est´an conectados por un u ´nico camino. 1 3
2 5
4 6
´ Arbol de expansi´ on de G = (V , E): ´arbol libre T = (V 0,E 0), tal que V 0 = V y E 0 ⊆ E. 1
1
3
2 5
6
3
2
4
´ → Arbol de expansi´ on →
5
4 6
51
´ Arbol de expansi´ on de coste m´ınimo (II) Coste de un ´ arbol de expansi´ on T : la suma de los costes de todos los arcos del ´arbol. w(T ) =
X
w(u, v)
(u,v)∈T
Un ´ arbol de expansi´ on ser´ a de coste m´ınimo, si se cumple que su coste es el menor posible respecto al coste de cada uno de los posibles ´arboles de expansi´ on del grafo. 1 5
6
3
6
5
5
3 4
6
6
4 2
1 5
6
1 5
2
1 1
1
3
2 6
5
4 4
6
Coste 22
5
2
3
4 4
3
5
2
6
Coste m´ınimo: 15
Problema: Sea G = (V , E) un grafo no dirigido, conexo y ponderado; obtener G0 = (V ,E 0) donde E 0 ⊆ E tal que G0 sea un ´arbol de expansi´ on de coste m´ınimo de G. 52
Algoritmo de Kruskal El algoritmo mantiene estas estructuras: Un conjunto A que mantiene las aristas que ya han sido seleccionadas como pertenecientes al ´arbol de expansi´on de coste m´ınimo. Al final A contendr´a el subconjunto de aristas que forman el ´arbol de expansi´ on decoste m´ınimo. Un MF-set que se usa para saber qu´e v´ertices est´an unidos entre s´ı en el bosque (conjunto de ´arboles) que se va obteniendo durante el proceso de construcci´on del ´arbol de expansi´ on de coste m´ınimo: • Conforme se a˜naden aristas al conjunto A, se unen v´ertices entre s´ı formando ´arboles. • Estos ´arboles se enlazar´an entre s´ı hasta obtener un u ´nico ´arbol que ser´a el ´arbol de expansi´ on de coste m´ınimo.
53
Algoritmo de Kruskal (II) Estrategia: 1. Inicialmente: A=∅ el MF-set tendr´a |V | subconjuntos cada uno de los cuales contendr´a uno de los v´ertices. 2. De entre todas las aristas (u,v), seleccionar como candidata para el AECM aqu´ella no seleccionada todav´ıa y con menor peso: Si u y v no est´an conectados entre s´ı en el AECM (provocar´an incremento de coste m´ınimo) → unir los v´ertices conectados con v con los v´ertices conectados con u y a˜ nadir (u,v) a A. Si u y v ya est´an conectados entre s´ı, no se realiza ninguna acci´ on. 3. Repetir (2) una vez para cada arista (u,v).
54
Algoritmo de Kruskal (III) Dado G = (V , E) no dirigido, conexo y ponderado: Algoritmo Kruskal(G) { A=∅ para cada v´ertice v ∈ V hacer Crear Subconjunto(v) fin para ordenar las aristas pertenecientes a E, seg´un su peso, en orden no decreciente para cada arista (u,v) ∈ E, siguiendo el orden no decreciente hacer si Buscar(u) 6= Buscar(v) entonces A = A ∪ {(u,v)} Union(Buscar(u),Buscar(v)) fin si fin para devuelve(A) } 55
Algoritmo de Kruskal: Ejemplo Para cada iteraci´on se muestran: El estado del conjunto A y del MF-set. El bosque que se va obteniendo durante la construcci´on del AECM. Qu´e arista ha sido seleccionada para formar parte del ´arbol y las acciones llevadas a cabo. 10
1 45
2 35
5
30
55
4
50
40
20
3
25 15
6
56
A
MF-set (componentes conexas)
´ Arbol de coste m´ınimo 1
2 5
{}
4
{{1},{2},{3},{4},{5},{6}}
1
10
3 6
2
Buscar(1) 6= Buscar(2) −→ A = A ∪ {(1,2)}; Union(Buscar(1),Buscar(2))
1
10
2
5
{(1,2)}
{{1,2},{3},{4},{5},{6}}
4
3 6
57
A
MF-set (componentes conexas)
´ Arbol de coste m´ınimo
3 6
15
Buscar(6) 6= Buscar(3) −→ A = A ∪ {(6,3)}; Union(Buscar(6),Buscar(3))
1
10
2
5
{(1,2),(6,3)}
{{1,2},{6,3},{4},{5}}
4
3 15
6
58
A
MF-set (componentes conexas) 4
20
´ Arbol de coste m´ınimo
6
Buscar(4) 6= Buscar(6) −→ A = A ∪ {(4,6)}; Union(Buscar(4),Buscar(6))
1
10
2
5
{(1,2),(6,3),(4,6)}
{{1,2},{4,6,3},{5}}
4
3 15
20
6
59
A
MF-set (componentes conexas)
´ Arbol de coste m´ınimo
2 25
6
Buscar(2) 6= Buscar(6) −→ A = A ∪ {(2,6)}; Union(Buscar(2),Buscar(6))
1
10
5
{(1,2),(6,3),(4,6),(2,6)}
{{1,2,4,6,3},{5}}
4
2 25
3 15
20
6
60
A
MF-set (componentes conexas)
´ Arbol de coste m´ınimo
1 30
4
Buscar(1) = Buscar(4)
5
35
3
Buscar(5) 6= Buscar(3) −→ A = A ∪ {(5,3)}; Union(Buscar(5),Buscar(3))
1
10
2 35
5
3
25
{(1,2),(6,3),(4,6),(2,6),(5,3)}
{{5,1,2,4,6,3}}
4
15 20
6
61
Coste temporal del algoritmo Asunci´ on: Las operaciones Uni´ on y Buscar aplican uni´ on por rango y compresi´ on de caminos. Construcci´on de |V | subconjuntos disjuntos ∈ O(|V |). Uni´ on y Buscar para cada arista del grafo ∈ O(|E|). Selecci´on de aristas en orden no decreciente. Organizar aristas mediante un mont´ıculo: • Extraer arista de m´ınimo peso ∈ O(log |E|). • |E| operaciones de extracci´on → O(|E| log |E|). • Construcci´on del mont´ıculo ∈ O(|E|). ⇒ Coste Kruskal ∈ O(|V | + |E| + |E| log |E| + |E|) = O(|E| log |E|).
62
Algoritmo de Prim El algoritmo de Prim utiliza las siguientes estructuras durante la ejecuci´on del algoritmo: Un conjunto A en el que se guardan aquellas aristas que ya forman parte del ´arbol de expansi´ on de coste m´ınimo. Un conjunto S inicialmente vac´ıo, y al que se ir´an a˜nadiendo los v´ertices de V conforme se vayan recorriendo para formar el ´arbol de expansi´ on de coste m´ınimo.
63
Algoritmo de Prim (II) Idea: Toma como ra´ız un v´ertice cualquiera, u, y a partir de ´el extenderse por todos los v´ertices. En cada paso seleccionar el arco de menor peso que une un v´ertice presente en S con uno de V que no lo est´e. Estrategia: empezando por cualquier v´ertice, construir incrementalmente un ´arbol seleccionando en cada paso una arista (u,v) ∈ A tal que: Si se a˜ nade (u,v) al conjunto A obtenido hasta ahora no se cree ning´ un ciclo. Produzca el menor incremento de peso posible. Los arcos del ´arbol de expansi´ on parcial formen un u ´nico ´arbol.
64
Algoritmo de Prim (III) Dado G = (V , E) no dirigido, conexo y ponderado: Algoritmo Prim(G) { A=∅ u = elemento arbitrario de V S = {u} mientras S 6= V hacer (u,v) = argmin(x,y)∈S×(V −S) peso(x, y) A = A ∪ {(u,v)} S = S ∪ {v} fin mientras devuelve(A) }
65
Algoritmo de Prim (IV) Donde la operaci´on de b´usqueda de la arista factible de menor peso, (u,v) = argmin(x,y)∈S×(V −S) peso(x, y) se calcula: m = +∞ para x ∈ S hacer para y ∈ V − S hacer si peso(x, y) < m entonces m = peso(x, y) (u,v) = (x,y) fin si fin para fin para
66
Algoritmo de Prim: Ejemplo Para cada iteraci´on se muestran: El estado del conjunto A y del conjunto S. Qu´e arista ha sido la seleccionada para formar parte del ´arbol y las acciones llevadas a cabo. 10
1 45
2 35
5
30
55
4
50
40
20
3
25 15
6
67
A
S
´ Arbol de coste m´ınimo 1
2 5
{}
{}
4
3 6
A = ∅; u = elemento arbitrario de V (Ej: u=1); S = {1}; 1
1
2 5
{}
{1}
4
3 6
68
A
´ Arbol de coste m´ınimo
S 1
10
2
argmin −→ (u,v) = (1,2); A = A∪ {(1,2)}; S = S∪ {2};
1
10
2
5
{(1,2)}
{1,2}
4
3 6
69
A
´ Arbol de coste m´ınimo
S 2
25
6
argmin −→ (u,v) = (2,6); A = A∪ {(2,6)}; S = S∪ {6};
1
10
5
{(1,2),(2,6)}
{1,2,6}
4
2 25
3
6
70
A
´ Arbol de coste m´ınimo
S 3 6
15
argmin −→ (u,v) = (6,3); A = A∪ {(6,3)}; S = S∪ {3};
1
10
5
{(1,2),(2,6),(6,3)}
{1,2,6,3}
4
2 25
3 15
6
71
A
´ Arbol de coste m´ınimo
S 4
20
6
argmin −→ (u,v) = (6,4); A = A∪ {(6,4)}; S = S∪ {4};
1
10
5
{(1,2),(2,6),(6,3),(6,4)}
{1,2,6,3,4}
4
2 25
3 15
20
6
72
A
S 5
35
´ Arbol de coste m´ınimo
3
argmin −→ (u,v) = (3,5); A = A∪ {(3,5)}; S = S∪ {5};
1
10
2 35
5
3
25
{(1,2),(2,6),(6,3),(6,4),(3,5)}
{1,2,6,3,4,5}
4
15 20
6
73
Coste temporal del algoritmo Coste de (u,v) = argmin(x,y)∈S×(V −S) peso(x, y): • Dos bucles para anidados que recorren los subconjuntos de v´ertices que crean ∈ O(|V |2). bucle mientras que engloba el c´alculo de la arista factible de menor peso. |V | − 1 iteraciones. ⇒ Coste ∈ O(|V |3). ∃ optimizaciones para (u,v) = argmin(x,y)∈S×(V −S) peso(x, y) con un coste ∈ O(|V |) ⇒ Coste ∈ O(|V |2).
74
Estructuras de Datos y Algoritmos
Algoritmos Voraces
Introducci´ on Objetivos: Estudiar el dise˜no de algoritmos voraces. Estudiar algunos problemas cl´asicos.
Aplicaci´ on: Problemas de optimizaci´ on −→ b´ usqueda del valor ´ optimo de una funci´ on objetivo.
1
Introducci´ on (II) Soluci´ on = secuencia de decisiones. Toma de decisiones: • En base a una medida local de optimizaci´ on. • Irreversibles. Soluci´ on sub´ optima. Coste computacional bajo. Estrategia voraz: realizar la elecci´on que es mejor en ese momento. Criterio de manejo de datos.
2
Ejemplo: Cajero autom´ atico Problema: suministrar cantidad de dinero solicitada usando s´olo los tipos de billetes especificados, de manera que el n´ umero de billetes sea m´ınimo.
Ejemplo: Cantidad solicitada M = 1100 Euros. Billetes disponibles: {100, 200, 500}. Posibles soluciones: • 11 × 100 (11 billetes). • 5 × 200 + 1 × 100 (6 billetes). • 2 × 500 + 1 × 100 (3 billetes).
3
Ejemplo: Cajero autom´ atico (II) Estrategia voraz: seleccionar siempre el billete de mayor valor, si quedan billetes de ese tipo y al a˜ nadirlo no nos pasamos de la cantidad solicitada. Posible casos: No hay soluci´on. Ej: M = 300 y no hay billetes de 100. La soluci´ on no se encuentra. Ej: M = 1100 y no hay billetes de 100. • Algoritmo −→ 2 × 500 −→ STOP. • Soluci´ on: 1 × 500 + 3 × 200. Encuentra una soluci´on no ´ optima. Ej: disponemos de billetes {10, 50, 110, 500} y M = 650. • Algoritmo −→ 1 × 500 + 1 × 110 + 4 × 10 (6 billetes). • Soluci´on ´optima: 1 × 500 + 3 × 50 (4 billetes).
4
Esquema general Voraz Notaci´ on: C : Conjunto de elementos o candidatos a elegir. S : Conjunto de elementos de la soluci´ on en curso. soluci´ on(S) : indica si S es soluci´ on o no. factible(S) : indica si S es factible o completable. selecciona(C) : selecciona un candidato de C conforme al criterio definido. f : funci´ on objetivo a optimizar.
5
Esquema general Voraz (II) Buscar subconjunto de C que optimice f : 1. Seleccionar en cada instante un candidato. 2. A˜nadir candidato a la soluci´on en curso si es completable, sino rechazarlo. 3. Repetir 1 y 2 hasta obtener la soluci´on. NOTA IMPORTANTE: no siempre se encuentra la soluci´ on ´ optima. decisiones irreversibles.
6
Esquema general Voraz (III) Algoritmo Voraz(C) { S = ∅; /* conjunto vacio */ while ((!soluci´on(S)) && (C 6= ∅)) { x = selecciona(C); C = C - {x} if (factible(S ∪ {x})) { S = S ∪ {x}; } } if (soluci´ on(S)) return(S); else return(No hay soluci´ on); } Ejemplo: cajero autom´atico: C ≡ conjunto de billetes disponibles, {24 de 100 Euros, 32 de 200 Euros, etc.}. factible(S) calcula si el nuevo billete se pasa de la cantidad total solicitada. etc. 7
El problema de la compresi´ on de ficheros Fichero con 100.000 caracteres (a, b, c, d, e, f ). Frecuencias de aparici´on: caracter Frecuencia ×103
a 45
b 13
c 12
d 16
e 9
f 5
Codificaci´ on fija: caracter C´odigo Fijo
a 000
b 001
c 010
d 011
e 100
b 101
c 100
d 111
e 1101
f 101
Tama˜ no del fichero −→ 300 Kbits. Codificaci´ on variable: caracter C´odigo Variable
a 0
f 1100
Tama˜ no del fichero −→ 224 Kbits. 8
Compresi´ on de ficheros (II) Algoritmo de compresi´ on: 1. Partir de la secuencia de bits original. 2. Obtener secuencia de caracteres asociada. 3. Codificar con c´odigos variables. Ejemplo: 000001010
f ijo
−→
abc
variable
−→
0101100
La codificaci´on variable cumple la propiedad de prefijo: ning´ un c´odigo es prefijo de otro. Cada caracter queda determinado por su secuencia de bits asociada.
9
Compresi´ on de ficheros (III) C´ odigo de Huffman: cumple propiedad de prefijo. Usado para compresi´on de ficheros. Pretende eliminar redundancia en la codificaci´ on de un mensaje. C´ odigo generado a partir del fichero a comprimir. Algoritmo voraz para crear el c´odigo de Huffman para un fichero. 0
1
a 0
0 c
1
1
0
1
b
d 0 f
1 e
10
Compresi´ on de ficheros (IV) Algoritmo para construir el ´arbol binario (c´ odigo de Huffman): C = conjunto de s´ımbolos. F = conjunto de frecuencias. Q = mont´ıculo que guardar´a el conjunto de frecuencias (MINHEAP). Estrategia: 1. Extraer las dos frecuencias menores del Q y colgarlas como hijo izquierdo e hijo derecho de un nuevo nodo. 2. Asociar como frecuencia al nuevo nodo creado la suma de las frecuencias de sus hijos. 3. Insertar la frecuencia del nuevo nodo en Q. 4. Repetir 1, 2 y 3 hasta que no queden m´as nodos que juntar. Finalizar´a cuando el ´arbol construido incluya a todos los s´ımbolos. 11
Algoritmo Huffman(C, F ) { for (x ∈ C, fx ∈ F ) { crear hoja(x,fx); } Q ←− F ; build heap(Q); for (i=1;i< |C|;i++) { z = crear nodo(); z.hizq = minimo(Q); extract min(Q); z.hder = minimo(Q); extract min(Q); z.frec = z.hizq.frec + z.hder.frec; insertar(Q, z); } return(minimo(Q)); } Coste ∈ O(n log n), n ≡ talla del vocabulario. 12
Compresi´ on de ficheros: Ejemplo Traza para el ejemplo del fichero anterior: 5
12
9
f
5
e
9
c
12
b
13
d
16
a
45
13
45
16
12
c
12
b
14
13 0 f
5
d
16
a
13
45
16
1 e
9
45
14
13
Compresi´ on de ficheros: Ejemplo (II) 14
14 0 f
d
25
16
1
5
e
0 9
c
25 0 c
12
12
a
b
0 13
b
f
5
45
13
a
45
1
14 0
16
1
30 1
25
45
d
16
25
1 e
9
45
30
14
Compresi´ on de ficheros: Ejemplo (III) a
45
55 0
1
25 0 c
12
30 1 b
0 13
14 0
f
1
5
d
16 45
1 e
9
55
15
Compresi´ on de ficheros: Ejemplo (IV) 100 0 a
1
45
55 0
1
25 0 c
12
30 1 b
0 13
14 0
f
1
5
d
16
1 e
9
100
16
El problema de la mochila con fraccionamiento Tenemos una mochila de capacidad M (en peso), y N objetos para incluir en ella. Cada objeto tiene un peso pi y un beneficio bi, 1 ≤ i ≤ N . Los objetos se pueden fraccionar. No se puede sobrepasar la capacidad de la mochila. Problema: ¿C´omo llenar la mochila de forma que el beneficio total sea m´aximo? Planteamiento del problema: Buscamos secuencia de N valores (x1, . . . , xN ), donde 0 ≤ xi ≤ 1, para 1 ≤ i ≤ N . (xi ≡ parte fraccional tomada del objeto i). Objetivo: maximizar el beneficio PN es decir, i=1 pixi ≤ M
PN
i=1 bi xi
con la restricci´on de que los objetos quepan,
17
El problema de la mochila con fraccionamiento (II) Ejemplo: M = 20, pesos de los objetos = (18, 15, 10), beneficios asociados = (25, 24, 15). Soluci´ on (1/2, 1/3, 1/4) (1, 2/15, 0)∗ (0, 2/3, 1)∗∗ (0, 1, 1/2)∗∗∗
Peso total 16.5 20.0 20.0 20.0
Beneficio total 24.25 28.20 31.00 31.50
La soluci´ on ´optima deber´a llenar la mochila al completo. Estrategia voraz: aplicamos criterio de optimizaci´ on local. Podemos definir tres criterios de selecci´on de objetos: • Escoger el elemento de mayor beneficio∗. • Escoger el elemento de menor peso∗∗. • Escoger el elemento de mayor relaci´ on beneficio/peso∗∗∗.
18
El problema de la mochila con fraccionamiento (III) Estrategia: coger elementos completos mientras no sobrepasemos el peso M de la mochila. Cuando no quepan elementos completos, del siguiente objeto que cumpla el criterio de selecci´on, coger la parte fraccional correspondiente para llenar la mochila. p = vector de pesos. b = vector de beneficios. M = tama˜no de la mochila. N = n´ umero de objetos. C = conjunto que representa los objetos para elegir asign´andoles identificadores. solucion = vector que almacena la soluci´ on.
19
Algoritmo Mochila-fraccional(p, b, M, N ) { C = {1, 2, . . . , N}; for (i = 1; i 0)) { /* i = elemento de C con m´aximo beneficio b[i]; */ /* i = elemento de C con m´ınimo peso p[i]; */ i = elemento de C con m´axima relaci´ on b[i]/p[i]; C = C - {i}; if (p[i] ≤ M ) { solucion[i] = 1; M = M − p[i]; } else { solucion[i] = M/p[i]; M = 0; } } return(solucion); } 20
El problema de la mochila con fraccionamiento: Ejemplo M = 50. p = (30, 18, 15, 10). b = (25, 13, 12, 10). 1. Criterio: seleccionar objeto de mayor beneficio bi. Iteraci´ on 1 2 3
M 50 20 2 0
C {1,2,3,4} {2,3,4} {3,4} {4}
0 1 1 1
Soluci´ on 0 0 0 0 1 0 1 2/15
0 0 0 0
Beneficio = 25 + 13 + (2/15) · 12 = 39.6
21
2. Criterio: seleccionar objeto de menor peso pi. Iteraci´ on 1 2 3 4
M 50 40 25 7 0
C {1,2,3,4} {1,2,3} {1,2} {1} ∅
Soluci´ on 0 0 0 0 0 0 0 0 1 0 1 1 7/30 1 1
0 1 1 1 1
Beneficio = (7/30) · 25 + 13 + 12 + 10 = 40.83 3. Criterio: seleccionar objeto de mayor beneficio unitario bi/pi = (5/30, 13/18, 12/15, 10/10). Iteraci´ on 1 2 3
M 50 40 10 0
C {1,2,3,4} {1,2,3} {2,3} {2}
0 0 1 1
Soluci´ on 0 0 0 0 0 0 0 10/15
0 1 1 1
Beneficio = 25 + (10/15) · 12 +10 = 42.99 22
Coste temporal de Mochila-fraccional Bucle for que inicializa el vector solucion ∈ O(N ). Bucle mientras selecciona uno de los N objetos a cada iteraci´ on → num. de iteraciones ∈ O(N ). • Selecci´on del elemento de C (representado en un vector) ∈ O(N ). Total bucle mientras ∈ O(N 2). ⇒ Coste de Mochila-fraccional ∈ O(N + N 2) ∈ O(N 2). Implementaci´ on m´ as eficiente: Ordenar previamente los objetos conforme a la relaci´on b/p: Selecci´on ∈ O(1) + Ordenaci´on ∈ O(N log N ). ⇒ Coste de Mochila-fraccional ∈ O(N + N + N log N ) ∈ O(N log N ). 23