Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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.
20-01-2010
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
lista
lista
1
nodo1
nodo2
nodo3
lista
c
c
nodo1
nodo2
nodo3
20-01-2010
20-01-2010
/*
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
p->proximo
3
numeroelementos
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.
20-01-2010
20-01-2010
20-01-2010
p->proximo
n
3
n->proximo
n->proximo
n
3
n->proximo
20-01-2010
3
temp
1
n
1
n->proximo
20-01-2010
10
t->proximo
t->proximo
20-01-2010
11
p->proximo
?
t
t->proximo
20-01-2010
12
20-01-2010
13
nx
pr
clave
20-01-2010
14
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
20-01-2010
15
dato
posicin
1
posicin
lista
20-01-2010
16
lista
nuevo
dato
posicin
nuevo
dato
//O(1)
20-01-2010
17
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;
}
posicin
lista
1
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.
20-01-2010
18
cabeza
1
cola
20-01-2010
19
lista1
p
t
5
20-01-2010
20
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
lista
p->proximo
lista
t
t
20-01-2010
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.
20-01-2010
22
stack
0
1
ltimo ocupado
2
3
NumeroDeElementos
4
5
MAXN-1
20-01-2010
23
/*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__ */
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.
20-01-2010
24
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
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 - *
20-01-2010
26
20-01-2010
27
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.
out
in
cabeza
cabeza
cola
Figura 5.26. Diagrama de una cola.
20-01-2010
cola
28
cabeza
N-1
0
1
2
3
4
5
cola
20-01-2010
29
cabeza
cola
N -1
N -1
0
1
1
2
cola
4
5
cabeza
20-01-2010
30
//buffer esttico
//administran el espacio
20-01-2010
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
cnt
rd
wr
20-01-2010
32
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.
20-01-2010
33
} 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);
}
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.
20-01-2010
34
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
35
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.
inicial
final
20-01-2010
36
}
return p ;
}
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.
20-01-2010
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
20-01-2010
38
20-01-2010
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
20-01-2010