Está en la página 1de 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

nodo2

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

lista

c
c

nodo1

nodo2

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

Estructuras de Datos y Algoritmos


pn
dato

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

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

Estructuras de Datos y Algoritmos

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;
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

Estructuras de Datos y Algoritmos


p->proximo

p->proximo

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->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->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

Estructuras de Datos y Algoritmos


p->proximo

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->proximo

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.

11
p->proximo

?
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.

13

typedef struct moldecelda


{
int clave;
struct moldecelda *nx; //next
struct modecelda *pr; // previo
} nodo, *pnodo;

nx

pr
clave

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

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.

15

5.3.5. Lista ordenada.


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

posicin

lista

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

dato

posicin
nuevo
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.

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.

Profesor Leopoldo Silva Bijit

20-01-2010

18

Estructuras de Datos y Algoritmos

d) Insertar al inicio y al final.


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

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.

19

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.

lista1
p

t
5

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

Estructuras de Datos y Algoritmos

Ejemplo 5.2. Descartar o Borrar nodo.


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

lista

p->proximo

lista

t
t

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

Base del stack

0
1

ltimo ocupado

2
3

NumeroDeElementos

4
5

Parte vaca del stack

MAXN-1

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.

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.

/*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.

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.

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

in
cabeza

cabeza

cola
Figura 5.26. Diagrama de una cola.

Profesor Leopoldo Silva Bijit

20-01-2010

cola

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.

29

cabeza
cola
N -1

N -1

0
1

1
2

cola

4
5

cabeza

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

Estructuras de Datos y Algoritmos

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); }

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;

Profesor Leopoldo Silva Bijit

//buffer esttico
//administran el espacio

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

cnt

rd

wr

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.

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.

Profesor Leopoldo Silva Bijit

20-01-2010

34

Estructuras de Datos y Algoritmos


Lista1

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.

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

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

Estructuras de Datos y Algoritmos


p3->proximo = p2->proximo;
p2->proximo = p3;

}
return p ;
}

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

También podría gustarte