You are on page 1of 39

1

Captulo 5.

Conjuntos dinmicos. Listas, stacks, colas.


Se estudian estructuras abstractas de datos para representar el concepto matemtico de conjuntos, considerando que el nmero de los elementos del conjunto puede variar en el tiempo.

5.1. Nodos.
Cada elemento o nodo se representa por una estructura, cuyos campos pueden ser ledos y escritos a travs de un puntero a la estructura. Suele existir un campo que se denomina clave, que identifica unvocamente al nodo; otros campos suelen contener punteros a otros nodos de la estructura. La clave puede ser numrica o alfanumrica.

5.2. Operaciones.
Las principales operaciones que suelen implementarse pueden clasificarse en consultas, y modificaciones. 5.2.1. Consultas: Buscar un nodo de la estructura que tenga igual valor de clave, que un valor que se pasa como argumento; retornando un puntero al nodo encontrado o NULL si no est presente. Seleccionar un nodo de la estructura que tenga el menor o mayor valor de la clave. Hay otras consultas que pueden hacerse, como buscar el sucesor o antecesor de un nodo. 5.2.2. Modificaciones. Insertar un nodo con determinados valores en la estructura. Debe establecerse la forma en que ser insertado, de tal modo de preservar la organizacin de la estructura. Normalmente esto implica primero conseguir el espacio para el nuevo nodo, y la inicializacin de sus campos; tambin es usual retornar un puntero al nodo recin creado.

Profesor Leopoldo Silva Bijit

20-01-2010

Estructuras de Datos y Algoritmos

Descartar o remover un nodo de la estructura. Asumiendo que se pasa como argumento un puntero al nodo que ser descartado, o al nodo anterior. La operacin debe mantener la organizacin de la estructura. Algunos algoritmos no requieren implementar todas las operaciones. Por ejemplo los que tienen slo las operaciones de buscar, insertar y descartar suelen denominarse diccionarios. Los algoritmos en que slo se busque e inserte se denominan arreglos asociativos, o tablas de smbolos. En un diccionario puro slo se implementa buscar. La complejidad de estas operaciones suele cuantificarse de acuerdo al nmero de nodos de la estructura. Los principales conjuntos dinmicos que estudiaremos son: listas, stacks, colas, rboles binarios de bsqueda, tablas de hash y colas de prioridad.

5.3. Listas.
Existe una gran variedad de estructuras denominas listas. 5.3.1. Lista simplemente enlazada. La lista ms bsica es la simplemente enlazada, la que puede definirse como la secuencia de cero (lista vaca) o ms elementos de un determinado tipo. Los elementos quedan ordenados linealmente por su posicin en la secuencia. Se requiere slo un enlace entre un elemento y su sucesor. Los elementos de un arreglo ocupan posiciones contiguas o adyacentes en la memoria. En las listas debe asumirse que el espacio de un nodo no es contiguo con otro; por esta razn, no basta incrementar en uno el puntero a un nodo, para obtener la direccin de inicio del nodo siguiente. Cada nodo est conectado con el siguiente mediante un puntero que es un campo del nodo. Los elementos del arreglo se direccionan en tiempo constante, O(1). Los elementos de las listas tienen un costo de acceso O(n), en peor caso. Las operaciones sobre listas deben considerar que sta puede estar vaca, lo cual requiere un tratamiento especial; as tambin los elementos ubicados al inicio y al final de la lista deben considerarse especialmente. Los siguientes diagramas ilustran una lista vaca y una lista con tres elementos. Si los nodos se crean en el heap, la variable lista, de la Figura 5.1, debe estar definida en el stack, o en la zona esttica, con el tipo puntero a nodo. Note que el programador no dispone de nombres de variables para los nodos, stos slo pueden ser accesados va puntero (esto debido a que en el momento de la compilacin no se conocen las direcciones de los nodos; estas direcciones sern retornadas por malloc en tiempo de ejecucin). Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.


lista
lista 1 nodo1 2 nodo2 3 nodo3

Figura 5.1. Lista vaca y con tres nodos. Se denominan listas con cabecera (header) o centinela aquellas que tienen un primer nodo al inicio de la lista. Con esta definicin algunas de las operaciones sobre listas resultan ms simples, que el caso anterior.
lista c

lista c 1 nodo1 2 nodo2 3 nodo3

Figura 5.2. Lista con encabezado vaca y con tres nodos. El caso de lista vaca y las acciones con el primer o ltimo elemento de la lista han intentado ser resueltas agregando un nodo de encabezado o un centinela al fin de la lista. Estos elementos facilitan que las funciones diseadas traten en forma homognea a todos los elementos de la lista; por ejemplo, la insercin al inicio se trata de igual forma que la insercin en otra posicin; el costo del mayor tamao es despreciable comparado con los beneficios. Se definen los tipos: typedef struct moldenodo { int clave; struct moldenodo *proximo; } nodo, *pnodo; 5.3.1.1. Crea Nodo La siguiente funcin retorna un puntero al nodo inicializado: pnodo CreaNodo(int dato) { pnodo pn=NULL; if ( (pn= (pnodo) malloc(sizeof(nodo))) ==NULL) exit(1); else { pn->clave=dato; pn->proximo=NULL; } return(pn); }

Profesor Leopoldo Silva Bijit

20-01-2010

4
pn dato

Estructuras de Datos y Algoritmos

Figura 5.3. Espacio antes de salir de CreaNodo. El diagrama de la Figura 5.3, ilustra la situacin justo antes de salir de la funcin. Despus de salir no existe la variable pn, ya que es automtica. Ejemplos de definicin de listas: pnodo lista=NULL; //Creacin de lista vaca sin centinela
lista

Figura 5.4. Creacin de lista vaca sin centinela. //Creacin de lista vaca con encabezado. pnodo listaC = CreaNodo(0);
listaC 0

Figura 5.5. Creacin de lista vaca con encabezado. Se ha considerado valor de clave 0 en el encabezado, pero podra ser otro valor; por ejemplo, uno que no sea usado por los valores que se almacenarn en la lista. Debe liberarse, el espacio adquirido mediante malloc, cuando deje de usarse, y dentro del alcance de lista, y siempre que la lista no est vaca. Esto se logra con: free(lista); Si lista est definida dentro de una funcin, debe liberarse el espacio, antes de salir de sta, ya que luego ser imposible liberar el espacio, debido a que las variables locales dejan de existir al salir de la funcin. El ejemplo anterior libera el espacio del nodo que est al inicio de la lista; el borrado de la lista completa requiere liberar el espacio de cada uno de los nodos. 5.3.1.2. Operaciones de consultas en listas. a) Recorrer la lista. Recorrer una lista es un tipo de operacin frecuente. Veamos por ejemplo una funcin que cuente los nodos de la lista. Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. /* Dada la direccin de un nodo de la lista Retornar el nmero de nodos desde el apuntado hasta el final de la lista. */ int LargoLista(pnodo p) { int numeroelementos = 0; while (p != NULL) { numeroelementos ++; p = p ->proximo; //recorre la lista } return (numeroelementos); }
lista 1 p 2
p->proximo

3 numeroelementos

Figura 5.6. Variables en LargoLista. Una alternativa de diseo es empleando un lazo for. int LargoLista(pnodo p) { int numeroelementos = 0; for( ; p != NULL; p=p->proximo) numeroelementos ++; return (numeroelementos); } Otras operaciones que demandan recorrer la lista son el despliegue de los elementos de la lista o buscar un nodo que tenga un determinado valor de clave.

b) Buscar elemento. Se da una lista y un valor de la clave: se retorna un puntero al nodo de la lista que tiene igual valor de clave, que el valor pasado como argumento; retorna NULL, si no encuentra dicho valor en la lista.

Profesor Leopoldo Silva Bijit

20-01-2010

6 pnodo Buscar(pnodo p, int valor) { while (p != NULL) { if (p->clave== valor) return (p); //lo encontr else p = p ->proximo; //recorre la lista. O(n) } return (p); //retorna NULL si no lo encontr. } El costo de la operacin es O(n). Ejemplo de uso. pnodo q;

Estructuras de Datos y Algoritmos

if ( (q= Buscar(lista, 5)) == NULL) { /* no encontr nodo con clave igual a 5*/ } else { /* lo encontr. ..*/ } Si la lista es con centinela: if ( (q= Buscar(listaC->proximo, 5)) == NULL) { /* no encontr nodo con clave igual a 5*/ } else { /* lo encontr. ..*/ } c) Seleccionar un valor extremo. Se da una lista y se desea encontrar un puntero al nodo que cumple la propiedad de tener el mnimo valor de clave. Si la lista es vaca retorna NULL. Ntese que en seleccionar slo se dan los datos de la lista; buscar requiere un argumento adicional. Debido a la organizacin de la estructura las operaciones de consulta tienen costo O(n). Veremos que existen estructuras y algoritmos ms eficientes para buscar y seleccionar. pnodo SeleccionarMinimo(pnodo p) { int min; pnodo t; if (p==NULL) return (NULL); else {min=p->clave; //Inicia min t=p; p=p->proximo; } while (p != NULL) { if (p->clave <min ) {min=p->clave; t=p;} p = p ->proximo; //recorre la lista. O(n) } return (t); } Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.

Si se inicializa la variable min con el mayor valor de su tipo, se simplifica el tratamiento en el borde. pnodo SelMin(pnodo p) { int min= INT_MAX; //requiere incluir limits.h pnodo t=NULL; while (p != NULL) { if (p->clave < min ) {min=p->clave; t=p;} p = p ->proximo; //recorre la lista. O(n) } return (t); } d) Buscar el ltimo nodo. pnodo ApuntarAlFinal(pnodo p) { pnodo t; if (p==NULL) return (NULL); else while (p != NULL) { t=p; p = p ->proximo; //recorre la lista. O(n) } return (t); } 5.3.1.3. Operaciones de modificacin de listas. a) Anlisis de insercin. Si consideramos pasar como argumentos punteros a nodos, de tal forma de no efectuar copias de los nodos en el stack, en la insercin, se requiere escribir direcciones en los campos prximos de dos nodos, y en determinada secuencia. Esto se requiere para mantener la lista ligada. Supongamos que tenemos dos variables de tipo puntero a nodo: p apunta a un nodo de una lista y n apunta a un nodo correctamente inicializado (por ejemplo, el retorno de CreaNodo). La situacin se ilustra en la Figura 5.7 a la izquierda, donde las variables n y p, se han diagramado por pequeos rectngulos. Los nodos se han representado por crculos, con una casilla para la clave, y otra para el puntero al nodo siguiente. El nodo n puede ser insertado despus del nodo apuntado por p. La primera escritura en un campo de la estructura puede describirse por: n->proximo = p->proximo; Despus de esta accin, la situacin puede verse en el diagrama a la derecha de la Figura 5.7.

Profesor Leopoldo Silva Bijit

20-01-2010

8
p
p->proximo

Estructuras de Datos y Algoritmos


p
p->proximo

n 3

n 3

n->proximo

n->proximo

Figura 5.7. Insercin en listas. Primer enlace. La segunda escritura, que termina de encadenar la lista, y que necesariamente debe realizarse despus de la primera, puede describirse por: p->proximo = n; La situacin y el estado de las variables, despus de la asignacin, puede describirse segn:
p
p->proximo

n 3

n->proximo

Figura 5.8. Insercin en listas. Segundo enlace. Los valores que toman las variables de tipo puntero son direcciones de memoria, y no son de inters para el programador. Es de fundamental importancia apoyarse en un diagrama para escribir correctamente expresiones en que estn involucrados punteros. Debe considerarse que si en el diseo se elige que las variables n y p sean los argumentos de la funcin que inserta un nodo, despus de ejecutada la funcin, automticamente ellas dejan de existir.

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.

Se puede emplear el siguiente cdigo, si se desea insertar antes de la posicin p; se requiere una variable entera, de igual tipo que la clave del nodo, para efectuar el intercambio. Si el nodo tiene ms informacin perifrica asociada, tambin debe ser intercambiada entre los nodos. int temp; n->proximo = p->proximo; p->proximo = n; temp=p->clave; p->clave=n->clave; n->clave=temp; //importa el orden de la secuencia. Despus de ejecutado el segmento anterior, se ilustra el estado final de las variables y un esquema de la situacin, en el diagrama siguiente.
p
p->proximo

3 temp 1

n 1

n->proximo

Figura 5.9. Insertar antes. Si la lista es sin cabecera, la insercin al inicio, debe codificarse en forma especial, ya que no existe en este caso la variable p->proximo. El inicio de la lista sin cabecera es una variable de tipo puntero a nodo, no es de tipo nodo, y por lo tanto no tiene el campo prximo. b) Anlisis de la operacin descarte. En el descarte de un nodo, si consideramos pasar como argumento un puntero a la posicin del nodo anterior al que se desea descartar, se requiere escribir una direccin y mantener una referencia al nodo que se desea liberar a travs de free. Entonces la variable p apunta al nodo anterior al que se desea descartar, y t apunta al nodo que se desea desligar de la lista. Se ilustra en la Figura 5.10, la situacin de las variables, despus de ejecutada la accin: t=p->proximo;

Profesor Leopoldo Silva Bijit

20-01-2010

10
p
p->proximo

Estructuras de Datos y Algoritmos

1 t

t->proximo

Figura 5.10. Fijacin de t. Fijar la posicin de t es necesario, ya que el siguiente paso es escribir en p->proximo, lo cual hara perder la referencia al nodo que se desea liberar. La variable t es necesaria, ya que tampoco se puede efectuar la liberacin del nodo mediante: free(p->proximo) ya que esto hara perder la referencia al siguiente nodo de la lista (el nodo con clave 3 en el diagrama). La siguiente accin es la escritura en un campo, para mantener la lista ligada. Esto se logra con: p->proximo = t->proximo;
p
p->proximo

1 t

t->proximo

Figura 5.11. Mantencin de lista ligada. Ahora puede liberarse el espacio, del nodo que ser descartado, mediante: free(t); Lo cual se ilustra en la Figura 5.12. Tambin puede descartarse el nodo apuntado por el argumento, pero se requiere copiar los valores del nodo siguiente, enlazar con el subsiguiente y liberar el espacio del nodo siguiente. Tambin debe notarse que descartar el primer nodo requiere un tratamiento especial, ya que se requiere escribir en el puntero a un nodo, que define el inicio, y en ste no existe el campo prximo. Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.


p
p->proximo

11

?
t
t->proximo

Figura 5.12. Espacio despus de liberar el nodo. Es un error serio, normalmente fatal, escribir expresiones formadas por: *t, t->clave, o t->proximo, ya que stas dejaron de existir, despus de la ejecucin de free(t). Si no se libera el espacio, queda un fragmento de la memoria dinmica inutilizable. No siempre es necesario liberar el espacio, por ejemplo se desea sacar un elemento de una lista e insertarlo en otra, no debe invocarse a free. Aparentemente las operaciones de modificacin de listas son sencillas, pero como veremos a continuacin an hay detalles que analizar. c) Anlisis adicionales en operacin Insertar despus. Considerando lo analizado anteriormente un primer diseo de la funcin es el siguiente: pnodo InsertarDespues( pnodo posicin, pnodo nuevo) { nuevo->proximo=posicion->proximo; posicion->proximo=nuevo; return(nuevo); } Se decide retornar la direccin del nodo recin incorporado a la lista. Pero el diseo puede originar problemas, si el nuevo nodo se obtiene invocando a la funcin CreaNodo2 y ste no pudo ser creado por malloc, ya que en este caso tendr valor NULL. pnodo CreaNodo2(int dato) { pnodo pn=NULL; if ( (pn= (pnodo) malloc(sizeof(nodo))) !=NULL) ; { pn->clave=dato; pn->proximo=NULL; } return(pn); }

Profesor Leopoldo Silva Bijit

20-01-2010

12

Estructuras de Datos y Algoritmos

En este caso, en la funcin InsertarDespues, no existe nuevo->proximo, lo cual producira un error fatal en ejecucin. Una forma de resolver lo anterior es agregando una lnea para tratar la excepcin. pnodo InsertarDespues( pnodo posicin, pnodo nuevo) { if (nuevo == NULL) return (NULL); nuevo->proximo=posicion->proximo; posicion->proximo=nuevo; return(nuevo); } El diseo considera que si la funcin retorna NULL, implica que la insercin fall. La funcin funciona bien si la posicin apunta al primer nodo, a uno intermedio o al ltimo; ya que todos stos tienen el campo prximo. Pero si el argumento posicin toma valor NULL, se producir un serio error, ya que posicin->proximo apunta a cualquier parte, lo cual podra suceder si se intenta insertar en una lista vaca sin header. Esto lleva a agregar otra alternativa en el cuerpo de la funcin: pnodo InsertarDespues( pnodo posicin, pnodo nuevo) { if (nuevo == NULL) return (NULL); if (posicion != NULL) { nuevo->proximo=posicion->proximo; posicion->proximo=nuevo; } return(nuevo); } Se analiza a continuacin la insercin en una lista vaca. pnodo listaS=NULL; //lista sin header pnodo listaC= CreaNodo(0); //lista con header listaS = InsertarDespues(listaS, CreaNodo(1)); Es necesaria la asignacin del retorno de la funcin a la variable listaS, para mantener vinculada la lista. En el caso de lista con header, el argumento listaC, no ser NULL, en caso de lista vaca. El llamado: InsertarDespues(listaC, CreaNodo(1)); inserta correctamente el nuevo nodo al inicio de la lista. El valor de retorno apunta al recin agregado a la lista. 5.3.2. Listas doblemente enlazadas. Una definicin de tipos:

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. typedef struct moldecelda { int clave; struct moldecelda *nx; //next struct modecelda *pr; // previo } nodo, *pnodo;

13

pr clave

nx

Figura 5.13. Lista doblemente enlazada. Los diagramas describen el estado de las variables, antes y despus de la operacin de insertar el nodo apuntado por q, despus del nodo apuntado por p:

Figura 5.14. Insercin de nodo en lista doblemente enlazada. La secuencia de asignaciones describe la insercin. q->nx = p->nx; q->pr = p; p->nx = q ; q->nx->pr = q ; Descartar el nodo apuntado por q: q->pr->nx = q->nx; q->nx->pr = q->pr ; free(q) ; Las operaciones de insertar, buscar y descartar deben considerar las condiciones en los bordes, y que la lista pueda estar vaca.

Profesor Leopoldo Silva Bijit

20-01-2010

14

Estructuras de Datos y Algoritmos

Una forma usual de tratar simplificadamente las condiciones de borde, es definir un nodo vaco, denominado cabecera o centinela. La Figura 5.15 superior muestra una lista doblemente enlazada vaca, la inferior una con dos elementos: Las listas circulares doblemente enlazadas con cabecera son ms sencillas de implementar y manipular. Las listas circulares simplemente enlazadas ocupan menos espacio pero su codificacin debe incluir varios casos especiales, lo cual aumenta el cdigo necesario para implementarlas y el tiempo para ejecutar las acciones.

lista h

lista h

Figura 5.15. Lista doblemente enlazada circular con centinela. Tarea: Desarrollar las operaciones: Insertar, descartar y buscar en una lista doblemente enlazada circular. 5.3.3. Lista circular. En listas simplemente enlazadas, sin o con cabecera, puede escogerse que el ltimo nodo apunte al primero, con esto se logra que el primer nodo pueda ser cualquier nodo de la lista.
lista 1 2 3 4

Figura 5.16. Lista simplemente enlazada circular. La insercin al inicio, en el caso de la Figura 5.16, debe tratarse de manera especial, con costo O(n), para que el ltimo nodo apunte al nuevo primero. Si la lista es con cabecera, y si el ltimo apunta a la cabecera, no es necesario introducir cdigo adicional. 5.3.4. Lista auto organizada. La operacin buscar mueve a la primera posicin el elemento encontrado. De esta manera los elementos ms buscados van quedando ms cerca del inicio de la lista.

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. 5.3.5. Lista ordenada.

15

Se mantiene, segn el orden de la lista, los valores ordenados de las claves. La insercin requiere primero buscar la posicin para intercalar el nuevo nodo. 5.3.6. Listas en base a cursores. En algunas aplicaciones se limita el nmero de nodos de la estructura por adelantado. En estos casos tiene ventajas tratar listas en base a arreglos. Pudiendo ser stos: arreglos de nodos, en los cuales se emplean punteros; o bien arreglos que contienen la informacin de vnculos en base a cursores que almacenan ndices.

5.4. Ejemplos de operaciones en listas sin centinela.


Ejemplo 5.1 Insercin de un nodo. a) Insertar antes. Para el diseo de la funcin suponemos que disponemos del valor nuevo, un puntero que apunta a un nodo inicializado.
nuevo dato

Figura 5.17. Nuevo nodo que ser insertado. Tambin disponemos del valor posicin, un puntero que apunta al nodo sucesor del que ser insertado. Se ilustran dos posibles escenarios, cuando existe lista y el caso de lista vaca.
lista posicin 1 2 3
lista posicin

Figura 5.18. Escenarios para insercin. En el diseo de la funcin consideramos que se retorne un puntero al nodo recin insertado. Para entender las operaciones sobre listas o estructuras que empleen punteros es recomendable emplear diagramas. Observamos que en caso de lista no vaca, debe escribirse en el campo nuevo->proximo el valor del argumento posicin, y retornar el valor de nuevo. Si la lista, estaba originalmente vaca no es preciso escribir el puntero nulo en el campo nuevo->posicin, si es que estaba correctamente inicializado.

Profesor Leopoldo Silva Bijit

20-01-2010

16

Estructuras de Datos y Algoritmos

lista
nuevo

1
posicin nuevo dato

dato

Figura 5.19. Variables en InsertaNodo. pnodo InsertaNodo(pnodo posicion, pnodo nuevo) { if (nuevo == NULL) return (NULL); if (posicion!=NULL) nuevo->proximo=posicion; return nuevo; }

//O(1)

Para una lista no vaca, un ejemplo de uso, se logra con: lista->proximo=InsertaNodo(lista->proximo, CreaNodo(8));
lista 1

Figura 5.20. Inserta nodo con valor 8 en Figura 5.18. Originalmente el primer argumento de InsertaNodo apuntaba al nodo dos. Dentro de la funcin se escribe en el campo prximo del nodo recin creado, de este modo se apunta al sucesor. Luego de la asignacin, se escribe en el campo de enlace la direccin del nodo agregado. Un ejemplo de insercin al inicio: lista =InsertaNodo(lista, CreaNodo(7));
lista 7

Figura 5.21. Insercin al inicio de nodo con valor 7 en Figura 5.18. La operacin diseada inserta antes de la posicin indicada por el argumento.

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. b) Insertar despus. Una variante es insertar despus de la posicin. pnodo InsertaNodoDespues(pnodo posicion, pnodo nuevo) { if (nuevo == NULL) return (NULL); if (posicion!=NULL) { nuevo->proximo=posicion->proximo; //enlaza con el resto de la lista posicion->proximo=nuevo; //termina de enlazar el nuevo nodo return (posicion); } return nuevo; }
lista 1 posicin

17

nuevo

Figura 5.22. Insercin del nodo con valor 4, despus del nodo 2 en Figura 5.18. Es importante el orden de las asignaciones. c) Insertar al final. La siguiente funcin implementa la operacin de insertar un nodo, con determinado valor, al final de la lista. pnodo InsertaNodoalFinal(pnodo posicion, int dato) { pnodo temp=posicion; if (temp != NULL) { while (temp->proximo !=NULL) temp=temp->proximo; //O(n) temp->proximo=CreaNodo(dato); return (temp->proximo); //retorna NULL si no se pudo crear el nodo } else return (CreaNodo(dato)); } Si frecuentemente se realizarn las operaciones de insertar al inicio o insertar al final, es preferible modificar la definicin de la estructura de datos, agregando otra variable para apuntar al ltimo de la lista, que suele denominarse centinela.

Profesor Leopoldo Silva Bijit

20-01-2010

18 d) Insertar al inicio y al final.

Estructuras de Datos y Algoritmos

Asumiendo variables globales, se simplifica el paso de argumentos. Sin embargo las operaciones slo son vlidas para la lista asociada a dichas variables globales: static pnodo cabeza=NULL; static pnodo cola=NULL;

cabeza 1 cola 2 3 4

Figura 5.23. Inserciones al inicio y al final. pnodo insertainicio(int clave) { pnodo t=CreaNodo(clave); if(cabeza==NULL) cola=t; t->proximo=cabeza; cabeza=t; //O(1) return(t); } pnodo insertafinal(int clave) { pnodo t =CreaNodo(clave); if(cola==NULL) { cola=cabeza=t;} else { cola->proximo=t; cola=t;} //O(1) return(t); } Tarea: Disear descartar al inicio y descartar al final. Cuando slo se desea insertar y descartar en un extremo la estructura se denomina stack. Cuando se inserta en un extremo y se descarta en el otro se denomina cola (en ingls queue). Cuando la estructura posibilita insertar y descartar en ambos extremos se la denomina doble cola (dequeue o buffer de anillo). e) Procedimiento de insercin. Es posible disear una funcin que no tenga retorno, en este caso uno de los argumentos debe ser pasado por referencia, ya que para mantener la lista ligada debe escribirse en dos campos. La operacin puede aplicarse a varias listas, a diferencia del diseo con globales visto anteriormente.

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. void insertanodo_ref(pnodo *p, pnodo t) { if (*p==NULL) *p=t; //inserta en lista vaca. else { t->proximo=*p; //lee variable externa. *p=t; //escribe en variable externa. } } Ejemplos de uso. Insertanodo_ref(&lista1, CreaNodo(5)); //Paso por referencia. Aparece &. Insertanodo_ref(&lista2, CreaNodo(3)); // Se inserta en lista2.

19

lista1 p

1 t

Figura 5.23a. Espacio luego de ingresar a la funcin Insertanodo_ref. En el diseo anterior, se pasa como argumento un puntero a un puntero a nodo. Lo cual permite pasar la direccin de la variable que define la lista. En caso de no emplear definicin de tipos, en la definicin de la funcin aparece ms de un asterisco: void insertanodo_ref(struct moldenodo ** p, pnodo t) Complicando ms an la interpretacin del cdigo de la funcin. f) Error comn en pasos por referencia. No es posible escribir fuera de la funcin sin emplear indireccin. void Push(pnodo p, int valor) { pnodo NuevoNodo = malloc(sizeof(struct node)); NuevoNodo->clave = valor; NuevoNodo->proximo = p; p = NuevoNodo; // No escribe en variable externa. } Push(lista, 1); //no se modifica la variable lista p pertenece al frame. Desaparece despus de ejecutada la funcin.

Profesor Leopoldo Silva Bijit

20-01-2010

20 Ejemplo 5.2. Descartar o Borrar nodo.

Estructuras de Datos y Algoritmos

Debido a que descartar un nodo implica mantener la estructura de la lista, resulta sencilla la operacin de borrar el siguiente a la posicin pasada como argumento. Se tienen tres escenarios posibles: Que la lista est vaca, que la posicin dada apunte al ltimo de la lista, y finalmente, que la posicin apunte a un nodo que tiene sucesor.

pnodo Descartar(pnodo p) { pnodo t = p; if (p==NULL) return (p); // Lista vaca if ( p->proximo==NULL) { free(p); return(NULL); // ltimo de la lista } else { t=p->proximo; free(p); return (t); //Retorna enlace si borr el nodo. } } Los diagramas ilustran las variables luego de ingresar a la funcin.
p lista t
t lista 5 p

lista 1 p t 2

p->proximo

Figura 5.24. Tres escenarios en descarte de nodo. Es responsabilidad de la funcin que llama a Descarte mantener ligada la lista, mediante el retorno. Tarea: Confeccionar ejemplos de invocacin a Descartar, manteniendo ligada la lista. Borrar el nodo apuntado por p, requiere recorrer la lista, para encontrar el nodo anterior al que se desea borrar; contemplando el caso que el nodo ha ser borrado sea el primero de la lista. Esta operacin es O(n).

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.

21

Para lograr un algoritmo de costo constante, debe modificarse la estructura de datos de la lista, por ejemplo agregando un puntero al anterior. Similar situacin se tiene si se desea implementar la operacin predecesor.

5.5. Stack. Pila. Estructura LIFO (last-in, first-out),


5.5.1. Definicin. La utilidad de esta estructura es muy amplia, y se la ha usado tradicionalmente incorporada al hardware de los procesadores: para organizar el retorno desde las subrutinas, para implementar el uso de variables automticas, permitiendo el diseo de funciones recursivas, para salvar el estado de registros, en el paso de parmetros y argumentos. Generalmente los traductores de lenguajes, ensambladores y compiladores, emplean esta estructura para la evaluacin y conversin de expresiones y para la determinacin del balance de parntesis; tambin existen arquitecturas virtuales denominadas mquinas de stack, para traducir a lenguajes de nivel intermedio las sentencias de lenguajes de alto nivel. Describiremos ahora lo que suele denominarse stack de usuario, como una estructura de datos que permite implementar el proceso de componentes con la poltica de atencin: la ltima que entr, es la primera en ser atendida. El stack es una lista restringida, en cuanto a operaciones, ya que slo permite inserciones y descartes en un extremo, el cual se denomina tope del stack. Debido a esta restriccin suelen darse nombres especializados a las operaciones. Se denomina push (o empujar en la pila) a la insercin; y pop (o sacar de la pila) al descarte. No suele implementarse la operacin buscar, ya que en esta estructura la complejidad de esta operacin es O(n); en algunas aplicaciones se dispone de la operacin leer el primer elemento del stack, sin extraerlo. En general la implementacin de las operaciones generales de insercin y descarte usando arreglos son costosas, en comparacin con nodos enlazados va punteros, debido a que es necesario desplazar el resto de las componentes despus de una insercin o descarte; adems de que el tamao del arreglo debe ser declarado en el cdigo, no pudiendo crecer dinmicamente durante la ejecucin. Sin embargo la primera dificultad no existe en un stack, la segunda se ve atenuada ya que no se requiere almacenar punteros lo cual disminuye el tamao del espacio de almacenamiento; la nica limitacin es la declaracin del tamao del arreglo. Cuando es posible predecir por adelantado la profundidad mxima del stack, se suele implementar mediante arreglos. 5.5.2. Diagrama de un stack. Variables. La representacin grfica siguiente, muestra el arreglo y dos variables para administrar el espacio del stack. La variable stack es un puntero al inicio del arreglo.

Profesor Leopoldo Silva Bijit

20-01-2010

22

Estructuras de Datos y Algoritmos

stack 0 1
NumeroDeElementos

Base del stack

2 3 4 5
MAXN-1

ltimo ocupado

Parte vaca del stack

Figura 5.25. Variables en un stack La variable NumeroDeElementos, contiene el nmero de elementos almacenados en el stack, el cual en la grfica crece hacia abajo. Usualmente suele representarse al revs, para mostrar que es una estructura en que se van apilando las componentes; slo se ve la primera componente, la del tope. El uso de la variable NumeroDeElementos, facilita el diseo de las funciones que prueban si el stack est lleno o vaco. 5.5.3. Archivo de encabezado ( *.h). Si se desea utilizar en alguna implementacin la estructura de datos stack, es una prctica usual definir un archivo con extensin h (por header o encabezado), en el que se describen los prototipos de las funciones asociadas al stack. Esto permite conocer las operaciones implementadas y sus argumentos, acompaando a este archivo est el del mismo nombre, pero con extensin .c, que contiene las definiciones de las operaciones; en ste, se suele incluir al principio el archivo con extensin h, de tal modo que si existen funciones que invoquen a otras del mismo paquete, no importe el orden en que son definidas, ya que se conocen los prototipos. En el archivo siguiente, con extensin h, se ha empleado la compilacin condicional, mediante la deteccin de la definicin de un identificador. En el caso que se analiza, si no est definido el smbolo __STACK_H__ (note los underscores, para evitar alcances de nombres) se lo define y se compila. En caso contrario, si ya est definido no se compila; esto permite compilar una sola vez este archivo, a pesar de que se lo puede incluir en diferentes archivos que usen el stack. En el texto se incluye un archivo datos.h que permite, usando la misma tcnica, definir focalizadamente los tipos de datos que emplee la aplicacin que use la herramienta stack. En este caso en particular debe definirse el tipo de datos ElementoStack, que describe la estructura de una componente del arreglo.

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. /*stack.h> */ #ifndef __STACK_H__ #define __STACK_H__ #include "datos.h" #define push2(A, B) StackPush((B)); StackPush((A)); void StackInit(int); int StackEmpty(void); int StackFull(void); void StackPush(ElementoStack); ElementoStack StackPop(void); void StackDestroy(void); #endif /* __STACK_H__ */

23

El ejemplo tambin ilustra la definicin de una macro: push2, que se implementa mediante el reemplazo del macro por dos invocaciones a funciones del paquete. Note que los argumentos se definen entre parntesis. 5.5.4. Implementacin de operaciones. El diseo de las funciones contempla tres variables globales asociadas al stack. Tope y NumeroDeElementos, que ya han sido definidas; adems emplea la global MAXN, para almacenar el mximo nmero de elementos, ya que el tamao del stack, se solicita dinmicamente, y no est restringido a ser una constante. Las variables globales simplifican el paso de argumentos de las operaciones; sin embargo restringen las operaciones a un solo stack. Si la aplicacin empleara varios stacks diferentes, las funciones tendran que ser redefinidas.

/*stack.c Implementacin basada en arreglos dinmicos. */ #include <stdlib.h> #include <stdio.h> #include "datos.h" #include "stack.h" static ElementoStack * stack; //puntero al inicio de la zona de la pila static int NumeroDeElementos; //elementos almacenados en el stack static int MAXN; //Mxima capacidad del stack void StackInit(int max) {stack = malloc(max*sizeof(ElementoStack) ); //se solicita el arreglo. if (stack == NULL) exit(1); NumeroDeElementos = 0; MAXN=max; }

Profesor Leopoldo Silva Bijit

20-01-2010

24

Estructuras de Datos y Algoritmos

int StackEmpty(void) { return(NumeroDeElementos == 0) ; //Retorna verdadero si stack vaco } int StackFull(void) { return(NumeroDeElementos == MAXN) ; //Retorna verdadero si stack lleno } //se puede empujar algo al stack si no est lleno. void StackPush(ElementoStack cursor) { if (!StackFull() ) stack[NumeroDeElementos ++]= cursor; } //se puede sacar algo del stack si no est vaco ElementoStack StackPop(void) { if( StackEmpty() ) {printf("error. Extraccin de stack vacio\n"); exit(1); return; } else return ( stack[--NumeroDeElementos] ) ; } void StackDestroy(void) { free(stack); } Es buena prctica que las funciones StackInit y StackDestroy se invoquen en una misma funcin, para asegurar la liberacin del espacio. Los programadores evitan la invocacin de funciones innecesariamente, cuando las acciones de stas sean simples; esto debido al costo de la creacin del frame, de la copia de valores de argumentos y de la posterior destruccin del frame. En esta aplicacin, podra haberse definido como macros los test de stack vaco o lleno, segn: #define StackEmpty( ) (NumeroDeElementos == 0) #define StackFull( ) (NumeroDeElementos == MAXN) Ejemplo 5.3. Uso de stack. Balance de parntesis. a) Especificacin del algoritmo: Se dispone de un archivo de texto, que contiene expresiones que usan parntesis. Se desea verificar que los parntesis estn balanceados. Es preciso identificar los pares que deben estar balanceados. Ejemplo: (, ), [, ], {, }, etc. Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.

25

Se asume que se dispone de funciones para leer caracteres desde un archivo de texto, y para discriminar si el carcter es uno de los smbolos que deben ser balanceados o no. La secuencia siguiente no est balanceada: a+(b-c) * [(d+e])/f, al final estn intercambiados dos tipos de parntesis. b) Descripcin inicial. Crear el stack. Mientras no se ha llegado al final del archivo de entrada: Descartar smbolos que no necesiten ser balanceados. Si es un parntesis de apertura: empujar al stack. Si es un parntesis de cierre, efectuar un pop y comparar. Si son de igual tipo continuar Si son de diferente tipo: avisar el error. Si se llega al fin de archivo, y el stack no esta vaco: avisar el error. Destruir el stack. El siguiente paso en el desarrollo es la descripcin por seudo cdigo, en la cual se establecen las variables y el nombre de las funciones. Ejemplo 5.4. Evaluacin de expresiones en notacin polaca inversa. Las expresiones aritmticas que generalmente escribimos estn en notacin in situ o fija. En esta notacin los operadores se presentan entre dos operandos; por ejemplo: 2 + 3 * 4. Esta notacin no explica el orden de precedencia de los operadores; debido a esto los lenguajes de programacin tienen reglas de que establecen cuales operadores reciben primero sus operandos. En el lenguaje C, la multiplicacin tiene mayor precedencia que el operador suma; entonces, en el caso del ejemplo, se realizar primero la multiplicacin y luego la suma. La relacin entre operadores y operandos puede hacerse explcita mediante el uso de parntesis. La escritura de ( 2 + 3) *4 y 2 + (3 * 4) asocia operadores y operandos mediante parntesis. En C, adems existen reglas de asociatividad para especificar los operandos de un operador, en caso de que existan varios de igual precedencia, por ejemplo: 3*4*5. Si la asociatividad es de izquierda a derecha: se interpreta: ((3 * 4) * 5); si es de derecha a izquierda: (3* (4*5)) La notacin inversa desarrollada por Jan Lukasiewicz (1878 - 1956) y empleada por los ingenieros de Hewlett-Packard para simplificar el diseo electrnico de las primeras calculadoras, permite escribir expresiones sin emplear parntesis y definiendo prioridades para los operadores. En esta notacin el operador sigue a los operandos. La expresin infija 3 + 4 tiene su equivalente en notacin inversa como: 3 4 +. Y el ejemplo inicial: 2 + 3 * 4, se representa, en notacin inversa, segn: 2 3 4 * +. Una generalizacin es agregar el nombre de funciones a los operadores. Normalmente las funciones son operadores mondicos: sin[123 + 45 ln(27 - 6)] a) Ejemplo de evaluacin. La expresin: (3 + 5) * (7 - 2) puede escribirse: 3 5 + 7 2 - *

Profesor Leopoldo Silva Bijit

20-01-2010

26

Estructuras de Datos y Algoritmos

Leyendo la expresin en notacin inversa, de izquierda a derecha, se realizan las siguientes operaciones: Push 3 en el stack. Push 5 en el stack. ste contiene ahora (3, 5). El 5 est en el tope, el ltimo en entrar. Se aplica la operacin + : la cual saca los dos nmeros en el tope del stack, los suma y coloca el resultado en el tope del stack. Ahora el stack contiene el nmero 8. Push 7 en el stack. Push 2 en el stack. ste contiene ahora (8, 7, 2). El 2 est en el tope. Se efecta la operacin con los dos nmeros ubicados en el tope. ste contiene ahora (8, 5) Se efecta la operacin * con los dos nmeros ubicados en el tope. ste contiene ahora (40) La clave es entender que las operaciones se realizan sobre los dos primeros nmeros almacenados en el stack, y que se empujan los operandos. b) Especificacin. Se dispone de un archivo de texto que contiene expresiones aritmticas en notacin inversa. Se dispone de funciones que permiten: leer un nmero como una secuencia de dgitos; reconocer los siguientes smbolos como operadores: +, -, * y /. descartar separadores, que pueden ser los smbolos: espacio, tab, nueva lnea. reconocer el smbolo fin de archivo. c) Seudo cdigo. While ( no se haya ledo el smbolo fin de archivo EOF) { leer un smbolo; Si es nmero: empujar el valor del smbolo en el stack Si es un operador: { Efectuar dos pop en el stack; Operar los nmeros, de acuerdo al operador; Empujar el resultado en el stack; } } Retornar el contenido del tope del stack, mediante pop. Ejemplo 5.5. Conversin de notacin in situ a inversa. Se emplea para convertir las expresiones infijas y evaluarlas en un stack. Para especificar el algoritmo es preciso establecer las reglas de precedencia de operadores. La ms alta prioridad est asociada a los parntesis, los cuales se tratan como smbolos; prioridad media tienen la operaciones de multiplicacin y divisin; la ms baja la suma y resta. Se asume solamente la presencia de parntesis redondos en expresiones. Como la notacin polaca inversa no requiere de parntesis, stos no se sacarn hacia la salida. Notar que el orden en que aparecen los nmeros son iguales en ambas representaciones, slo difieren en el orden y el lugar en que aparecen los operadores. Se emplear el stack para almacenar los operadores y el smbolo de apertura de parntesis. Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. Seudo cdigo. While ( no se haya ledo el smbolo fin de archivo EOF) { leer un smbolo; Si es nmero: enviar hacia la salida; Si es el smbolo ): sacar del stack hacia la salida, hasta encontrar (, el cual no debe copiarse hacia la salida. Si es operador o el smbolo (: Si la prioridad del recin ledo es menor o igual que la prioridad del operado ubicado en el tope del stack: { if( tope==( ) empujar el operador recin ledo; else { efectuar pop del operador y sacarlo hacia la salida hasta que la prioridad del operador recin ledo sea mayor que la prioridad del operador del tope. Empujar el recin ledo en el tope del stack. } } } Si se llega a fin de archivo: vaciar el stack, hacia la salida. Se trata un stack con el smbolo ( en el tope como un stack vaco.

27

5.6. Cola. Buffer circular. Estructura FIFO (first-in, first-out).


5.6.1. Definicin de estructura. Una cola es una lista con restricciones. En sta las inserciones ocurren en un extremo y los descartes en el otro. La atencin a los clientes en un banco, el pago de peaje en autopistas, son ejemplos cotidianos de filas o colas de atencin. Si se conoce el mximo nmero de componentes que tendrn que esperar en la cola, se suele implementar en base a arreglos. Se requieren ahora dos variables para administrar los ndices de la posicin del elemento que ser insertado o encolado (cola, tail en ingls); y tambin el ndice de la posicin de la componente que ser descartada o desencolada en la parte frontal (cabeza. head).

out cabeza cola in

cabeza

cola
Figura 5.26. Diagrama de una cola.

Profesor Leopoldo Silva Bijit

20-01-2010

28

Estructuras de Datos y Algoritmos

El diagrama ilustra la situacin luego: de la insercin de los elementos: 0, 1, 2, 3, y 4 y del descarte del electo 0. La cabeza (head) apunta al elemento a desencolar. La cola (tail) apunta a la posicin para encolar. Apunta a un elemento disponible. Se observa que a medida que se consumen o desencolan componentes, van quedando espacios disponibles en las primeras posiciones del arreglo. Tambin a medida que se encolan elementos va disminuyendo el espacio para agregar nuevos elementos, en la zona alta del arreglo. Una mejor utilizacin del espacio se logra con un buffer circular, en el cual la posicin siguiente a la ltima del arreglo es la primera del arreglo. 5.6.2. Buffer circular. Esto es sencillo de implementar aplicando aritmtica modular, si el anillo tiene N posiciones, la operacin: cola = (cola+1) % N, mantiene el valor de la variable cola entre 0 y N-1. Operacin similar puede efectuarse para la variable cabeza cuando deba ser incrementada en uno. La variable cola puede variar entre 0 y N-1. Si cola tiene valor N-1, al ser incrementada en uno (mdulo N), tomar valor cero.

cabeza
N-1

0 1 2 3 4 5 cola

Figura 5.27. Buffer circular. Los nmeros, del diagrama, muestran los valores del ndice de cada casilla del arreglo circular. La grfica anterior ilustra la misma situacin planteada con un arreglo lineal. 5.6.3. Cola vaca y llena. El diagrama a la izquierda ilustra una cola vaca; la de la derecha una cola con un espacio disponible. En esta ltima situacin, el cursor cola (tail) dio la vuelta completa y est marcando como posicin disponible para encolar la posicin anterior a la que tocara consumir. Si se encola un nuevo elemento, se producir la condicin de cola llena; pero esta situacin es indistinguible de la de cola vaca.

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.


cabeza cola
N -1

29

0 1 2 3 4 5

N -1

0 1 2 3 4 5
cabeza cola

Figura 5.28. Cola vaca y casi llena. De esta forma no es posible distinguir entre las dos situaciones: cola llena o vaca. Una de las mltiples soluciones a este problema, es registrar en una variable adicional la cuenta de los elementos encolados; esto adems facilita el diseo de las funciones que determinan cola vaca o llena. Si la variable la denominamos encolados. Entonces con cola vaca, encolados toma valor cero. La cola llena se detecta cuando encolados toma valor N. El algoritmo se basa en las funciones que operan sobre una cola circular basada en arreglos. Con operaciones de colocar en la cola (put), sacar de la cola (get) y verificar si la cola est vaca o llena. 5.6.4. Operaciones en colas. /* QUEUE.c en base a arreglo circular dinmico */ #include <stdlib.h> #include "QUEUE.h" static Item *q; // Puntero al arreglo de Items static int N, cabeza, cola, encolados; //Administran el anillo Debe estar definido el tipo de datos Item. void QUEUEinit(int maxN) //maxN es el valor N-1 de la Figura 5.27. { q = malloc((maxN+1)*sizeof(Item)); //Se pide espacio para N celdas. N = maxN+1; cabeza = 0; cola = 0; encolados=0; } La deteccin de cola vaca se logra con: int QUEUEempty() { return encolados == 0; }

Profesor Leopoldo Silva Bijit

20-01-2010

30 Si la cola no est vaca se puede consumir un elemento: Item QUEUEget() { Item consumido= q[cabeza]; cabeza = (cabeza + 1) % N ; encolados--; return (consumido); } Se emplea aritmtica mdulo N. La deteccin de cola llena se logra con: int QUEUEfull() {return( encolados == N); }

Estructuras de Datos y Algoritmos

Si la cola no est llena se puede encolar un elemento: void QUEUEput(Item item) { q[cola] = item; cola = (cola +1) % N; encolados++;} Para recuperar el espacio: void QUEUEdestroy(void) { free ( q ); } En un caso prctico las funciones cola llena y vaca se implementan con macros. #define QUEUEempty() (encolados == 0) #define QUEUEfull() (encolados == N) Las dos aplicaciones, el stack de usuario y la cola, se emplearn en algoritmos para construir rboles en grafos. Ejemplo 5.6. Diseo de buffer circular esttico de caracteres. Para insensibilizarse de las diferentes velocidades que pueden tener un consumidor y un productor de caracteres, se suele emplear un buffer. En el caso de un computador alimentando a una impresora, la velocidad de produccin de caracteres del procesador es mucho mayor que la que tiene la impresora para liberar los caracteres hacia el medio de impresin; el disponer de un buffer de impresora, permite al procesador escribir en el buffer y no tener que esperar que la impresora escriba un carcter. Lo mismo ocurre cuando un usuario escribe caracteres desde un teclado; su velocidad de digitacin es bastante menor que la velocidad con que el procesador utiliza los caracteres. Se emplea la variable cnt para llevar la cuenta de los elementos almacenados en el buffer. #define SIZE 16 #define LLENO (cnt==SIZE) #define VACIO (cnt==0) unsigned char Buffer[SIZE]; int rd=0, wr=0, cnt=0; //buffer esttico //administran el espacio

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.

31

El cursor rd apunta al elemento a leer. El cursor wr al elemento que est disponible para ser escrito. La rutina put, coloca elementos en el buffer. void put(unsigned char c) { Buffer[wr]=c; wr=(wr+1)%SIZE; cnt++; } La rutina get consume elementos del buffer. unsigned char get(void) { unsigned char ch; ch=Buffer[rd]; rd=(rd+1)%SIZE; cnt--; return(ch); }
SIZE-1

0 1 2

cnt 2

wr

rd

Figura 5.29. Buffer de caracteres. Las siguientes sentencias ilustran el uso de las funciones: if ( !VACIO ) ch=get(); else printf("vaco\n"); while( !LLENO ) put('1'); //lo llena if ( !LLENO ) put('2'); else printf("lleno\n"); while( !VACIO ) putchar(get()); //lo vacia if ( !VACIO ) putchar(get()); else printf("\nvacio\n"); Usualmente una de las rutinas opera por interrupciones. La rutina que no es de interrupcin debe modificar la variable comn cnt deshabilitando el tipo de interrupcin.

Profesor Leopoldo Silva Bijit

20-01-2010

32

Estructuras de Datos y Algoritmos

Problemas resueltos.
P5.1 Se tienen los diagramas de una lista circular vaca, y luego de haber insertado uno, dos y tres elementos. Notar que el puntero a la lista referencia el ltimo nodo insertado en la estructura.

Figura P5.1. Buffer de caracteres. Definir tipos de datos: nodo es el tipo de datos del nodo, y pnodo es el nombre del tipo puntero a nodo. El valor almacenado en el nodo es de tipo entero. En cada caso ilustrar un ejemplo de uso, mostrando las variables que sean necesarias, con diagramas que ilustren la relacin entre los datos. a) Disear funcin insertar con prototipo: pnodo insertar(int); El argumento es el valor que debe almacenarse en el nodo que se inserta. Retorna puntero al recin insertado, nulo en caso que no se haya podido crear el nodo. Asumir que se tiene variable global de nombre lista, de tipo pnodo. b) Disear funcin sumar con prototipo: int sumar(pnodo); El argumento es un puntero a un nodo cualquiera de la lista. Retorna la suma de los valores almacenados en todos los nodos de la lista; 0 en caso de lista vaca. c) Asumir que se tienen varias listas circulares, cada una de ellas referenciadas por un puntero almacenado en una variable de tipo pnodo. Se tiene la siguiente funcin, en la cual el argumento sirve para referenciar a una de las listas. pnodo funcion(pnodo *p) { pnodo t=*p; if(*p==NULL) return (NULL); *p = (*p)->proximo; return (t); } Determinar que realiza la funcin. Solucin. typedef struct moldenodo { int clave; struct moldenodo *proximo; Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. } nodo, *pnodo; a) pnodo Insertar(int valor) { pnodo pn=NULL; if ( (pn = (pnodo) malloc(sizeof(nodo))) == NULL) return NULL; pn->clave = valor; if (listac == NULL){pn->proximo = pn;} else {pn->proximo = listac->proximo; listac->proximo = pn;} listac = pn; return (pn); }

33

La siguiente definicin, debe estar fuera de las funciones, y ubicada antes de la definicin de la funcin Insertar: pnodo listac=NULL; La sentencia siguiente forma la lista cuyo diagrama se muestra ms a la izquierda, en la definicin del problema. for(i=1; i<4; i++) if ( Insertar( i ) == NULL) break; b) int Sumar(pnodo p) { pnodo t = p; int sum = 0; if(p == NULL) {printf(" Lista vaca. "); return(0);} sum += t->clave; for(t = p->proximo; t != p; t = t->proximo) sum += t->clave; return (sum); } printf("La suma de los elementos de la lista circular es %d\n", Sumar(listac)); c) La accin que realiza funcion(&Lista1), es apuntar al siguiente de la lista referenciada por la variable Lista1. Retorna puntero al que antes era el primero de la lista, nulo en caso de lista vaca. En el caso de la lista con tres elementos, dada al inicio, despus de invocar a la funcin, en esa lista, debe retornar un puntero al nodo con valor 3, y la lista apunta al elemento con valor 1, segn se ilustra en el siguiente diagrama.

Profesor Leopoldo Silva Bijit

20-01-2010

34
Lista1

Estructuras de Datos y Algoritmos

Figura P5.2. Si antes de invocar se tiene la situacin dada al inicio, el siguiente segmento: pnodo t=NULL; if( (t=avanzar(&Lista1))!=NULL) printf("el anterior era %d\n", t->clave); Imprime el valor 3.

Ejercicios propuestos.
E5.1. Verificar que para la siguiente entrada: a+b*c+(d*e+f)*g La salida, en notacin polaca inversa, se genera en el siguiente orden: abc abc*+ abc*+ abc*+d abc*+de abc*+de* abc*+de*f a b c * + d e* f + a b c * + d e* f + a b c * + d e* f + g a b c * + d e* f + g * + Efectuar una traza del contenido del stack, a medida que se van procesando los smbolos de entrada. E5.2. Se tienen los siguientes tipos de datos: typedef struct moldenodo { int clave; struct moldenodo *proximo; } nodo, *pnodo; Para la estructura de la Figura E5.1: a) Declarar las variables inicial y final. Profesor Leopoldo Silva Bijit 20-01-2010

Conjuntos dinmicos. Listas, stacks, colas. b) Disear funcin que inserte nodo, con un valor pasado como argumento, al inicio. c) Disear funcin que inserte nodo, con valor pasado como argumento, al final. d) Disear funcin que intercambie el nodo inicial con el nodo final. Las funciones de insercin deben considerar la posibilidad de insertar en una cola vaca.

35

inicial

final

Figura E5.1. Cola. E5.3. Bsqueda autoorganizada en listas. El proceso de reorganizar una lista por transposicin, tiene por objetivo mejorar el tiempo promedio de acceso para futuras bsquedas, moviendo los nodos ms accesados hacia el comienzo de la lista. Disear una rutina, en C, que busque un elemento en una lista en base a punteros. Y tal que cuando encuentre un elemento lo trasponga con el anterior, excepto cuando lo encuentre en la primera posicin. E5.4. Insertar en lista ordenada. Comparar las dos funciones para insertar un nodo en una lista ordenada. pnodo inserteenorden (pnodo p, int k ) { pnodo p1, p2, p3; for( p2 = NULL, p1 = p; p1 != NULL && p1->clave < k; p2 = p1, p1 = p1->proximo ); if (p1 != NULL && p1->clave == k) return p; //no acepta claves repetidas p3= (pnodo) malloc (sizeof (nodo)) ; if(p3!=NULL) { p3->clave = k; if (p2 == NULL) { /* inserta al inicio */ p3->proximo = p1; return p3 ; } Profesor Leopoldo Silva Bijit 20-01-2010

36 p3->proximo = p2->proximo; p2->proximo = p3; } return p ; }

Estructuras de Datos y Algoritmos

pnodo inserteenordenHeader( pnodo p, int k ) { nodo header; pnodo p1,p2; header.proximo = p; for(p2 = &header; p != NULL && p->clave< k; p2 = p, p = p->proximo); if (p == NULL || p->clave !=k ){ p1 = (pnodo) malloc(sizeof(nodo)); if( p1!=NULL){ p1->clave = k; p1->proximo = p; p2->proximo = p1; } } return header.proximo ; } Notar que se trata el encabezado como una variable local.

Referencias.
En el apndice: Assemblers, Linkers, and the SPIM Simulator de James R. Larus, del libro de Patterson A. David y Hennessy L. John, Computer Organization and Design: The Hardware/software Interface, Morgan Kaufmann 2004, aparece una excelente descripcin del proceso de compilacin, de la creacin de archivos objetos, del proceso de ligado y carga de un programa.

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.

37

ndice general.
CAPTULO 5. ............................................................................................................................................ 1 CONJUNTOS DINMICOS. ................................................................................................................... 1 LISTAS, STACKS, COLAS. ..................................................................................................................... 1 5.1. NODOS. .............................................................................................................................................. 1 5.2. OPERACIONES. ................................................................................................................................... 1 5.2.1. Consultas:.................................................................................................................................. 1 5.2.2. Modificaciones. ......................................................................................................................... 1 5.3. LISTAS. .............................................................................................................................................. 2 5.3.1. Lista simplemente enlazada. ...................................................................................................... 2
5.3.1.1. Crea Nodo ........................................................................................................................................... 3 5.3.1.2. Operaciones de consultas en listas. ..................................................................................................... 4 a) Recorrer la lista. ...................................................................................................................................... 4 b) Buscar elemento. .................................................................................................................................... 5 c) Seleccionar un valor extremo. ................................................................................................................. 6 d) Buscar el ltimo nodo. ............................................................................................................................ 7 5.3.1.3. Operaciones de modificacin de listas. ............................................................................................... 7 a) Anlisis de insercin. .............................................................................................................................. 7 b) Anlisis de la operacin descarte. ........................................................................................................... 9 c) Anlisis adicionales en operacin Insertar despus............................................................................... 11

5.3.2. Listas doblemente enlazadas. .................................................................................................. 12 5.3.3. Lista circular. .......................................................................................................................... 14 5.3.4. Lista auto organizada. ............................................................................................................. 14 5.3.5. Lista ordenada. ........................................................................................................................ 15 5.3.6. Listas en base a cursores. ........................................................................................................ 15 5.4. EJEMPLOS DE OPERACIONES EN LISTAS SIN CENTINELA. .................................................................. 15 Ejemplo 5.1 Insercin de un nodo. .................................................................................................... 15
a) Insertar antes. ........................................................................................................................................ 15 b) Insertar despus. ........................................................................................................................................ 17 c) Insertar al final. .......................................................................................................................................... 17 d) Insertar al inicio y al final. ......................................................................................................................... 18 e) Procedimiento de insercin. ....................................................................................................................... 18 f) Error comn en pasos por referencia. ......................................................................................................... 19

Ejemplo 5.2. Descartar o Borrar nodo.............................................................................................. 20 5.5. STACK. PILA. ESTRUCTURA LIFO (LAST-IN, FIRST-OUT), ................................................................ 21 5.5.1. Definicin. ............................................................................................................................... 21 5.5.2. Diagrama de un stack. Variables. ........................................................................................... 21 5.5.3. Archivo de encabezado ( *.h). ................................................................................................. 22 5.5.4. Implementacin de operaciones. ............................................................................................. 23 Ejemplo 5.3. Uso de stack. Balance de parntesis. ........................................................................... 24
a) Especificacin del algoritmo: .................................................................................................................... 24 b) Descripcin inicial. .................................................................................................................................... 25

Ejemplo 5.4. Evaluacin de expresiones en notacin polaca inversa. .............................................. 25


a) Ejemplo de evaluacin. .............................................................................................................................. 25

Profesor Leopoldo Silva Bijit

20-01-2010

38

Estructuras de Datos y Algoritmos


b) Especificacin. ........................................................................................................................................... 26 c) Seudo cdigo.............................................................................................................................................. 26

Ejemplo 5.5. Conversin de notacin in situ a inversa. .....................................................................26


Seudo cdigo.................................................................................................................................................. 27

5.6. COLA. BUFFER CIRCULAR. ESTRUCTURA FIFO (FIRST-IN, FIRST-OUT). ............................................27 5.6.1. Definicin de estructura. ..........................................................................................................27 5.6.2. Buffer circular. .........................................................................................................................28 5.6.3. Cola vaca y llena. ...................................................................................................................28 5.6.4. Operaciones en colas. ..............................................................................................................29
Ejemplo 5.6. Diseo de buffer circular esttico de caracteres. ....................................................................... 30

PROBLEMAS RESUELTOS. ........................................................................................................................32 EJERCICIOS PROPUESTOS. ........................................................................................................................34 E5.1. Verificar que para la siguiente entrada: ..................................................................................34 E5.2. Se tienen los siguientes tipos de datos: .....................................................................................34 E5.3. Bsqueda autoorganizada en listas. .........................................................................................35 E5.4. Insertar en lista ordenada. .......................................................................................................35 REFERENCIAS. .........................................................................................................................................36 NDICE GENERAL. ....................................................................................................................................37 NDICE DE FIGURAS. ................................................................................................................................39

Profesor Leopoldo Silva Bijit

20-01-2010

Conjuntos dinmicos. Listas, stacks, colas.

39

ndice de figuras.
FIGURA 5.1. LISTA VACA Y CON TRES NODOS. ............................................................................................. 3 FIGURA 5.2. LISTA CON ENCABEZADO VACA Y CON TRES NODOS................................................................. 3 FIGURA 5.3. ESPACIO ANTES DE SALIR DE CREANODO. ................................................................................ 4 FIGURA 5.4. CREACIN DE LISTA VACA SIN CENTINELA............................................................................... 4 FIGURA 5.5. CREACIN DE LISTA VACA CON ENCABEZADO. ........................................................................ 4 FIGURA 5.6. VARIABLES EN LARGOLISTA. ................................................................................................... 5 FIGURA 5.7. INSERCIN EN LISTAS. PRIMER ENLACE. ................................................................................... 8 FIGURA 5.8. INSERCIN EN LISTAS. SEGUNDO ENLACE. ................................................................................ 8 FIGURA 5.9. INSERTAR ANTES. ...................................................................................................................... 9 FIGURA 5.10. FIJACIN DE T. ...................................................................................................................... 10 FIGURA 5.11. MANTENCIN DE LISTA LIGADA. ........................................................................................... 10 FIGURA 5.12. ESPACIO DESPUS DE LIBERAR EL NODO. .............................................................................. 11 FIGURA 5.13. LISTA DOBLEMENTE ENLAZADA. ........................................................................................... 13 FIGURA 5.14. INSERCIN DE NODO EN LISTA DOBLEMENTE ENLAZADA. ..................................................... 13 FIGURA 5.15. LISTA DOBLEMENTE ENLAZADA CIRCULAR CON CENTINELA. ................................................ 14 FIGURA 5.16. LISTA SIMPLEMENTE ENLAZADA CIRCULAR. ......................................................................... 14 FIGURA 5.17. NUEVO NODO QUE SER INSERTADO. .................................................................................... 15 FIGURA 5.18. ESCENARIOS PARA INSERCIN............................................................................................... 15 FIGURA 5.19. VARIABLES EN INSERTANODO. ............................................................................................. 16 FIGURA 5.20. INSERTA NODO CON VALOR 8 EN FIGURA 5.18. ..................................................................... 16 FIGURA 5.21. INSERCIN AL INICIO DE NODO CON VALOR 7 EN FIGURA 5.18. ............................................. 16 FIGURA 5.22. INSERCIN DEL NODO CON VALOR 4, DESPUS DEL NODO 2 EN FIGURA 5.18. ....................... 17 FIGURA 5.23. INSERCIONES AL INICIO Y AL FINAL. ...................................................................................... 18 FIGURA 5.23A. ESPACIO LUEGO DE INGRESAR A LA FUNCIN INSERTANODO_REF. ..................................... 19 FIGURA 5.24. TRES ESCENARIOS EN DESCARTE DE NODO. ........................................................................... 20 FIGURA 5.25. VARIABLES EN UN STACK...................................................................................................... 22 FIGURA 5.26. DIAGRAMA DE UNA COLA. .................................................................................................... 27 FIGURA 5.27. BUFFER CIRCULAR. ............................................................................................................... 28 FIGURA 5.28. COLA VACA Y CASI LLENA. .................................................................................................. 29 FIGURA 5.29. BUFFER DE CARACTERES....................................................................................................... 31 FIGURA P5.1. BUFFER DE CARACTERES. ..................................................................................................... 32 FIGURA P5.2. .............................................................................................................................................. 34 FIGURA E5.1. COLA. ................................................................................................................................... 35

Profesor Leopoldo Silva Bijit

20-01-2010

You might also like