Sie sind auf Seite 1von 13

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Listas enlazadas

La lista enlazada es una de las estructuras de datos ms verstiles, consiste en una secuencia de
elementos heterogneos llamados nodos, cada uno de ellos contiene arbitrariamente al menos un
campo dato, en el cual podemos almacenar algn tipo de informacin y una o dos referencias
llamadas enlaces, que almacenan la posicin al nodo siguiente o previo. De manera que una lista
enlazada es un tipo de dato con elementos (nodos) auto-referenciados debido a que cada nodo
contiene un puntero o enlace a otro dato del mismo tipo.

info sig

info sig

info sig

raz
null
nodo

nodo

nodo

La lista enlazada se accede a partir de un puntero externo llamado nodo raz que apunta al primer
nodo de la lista, cada campo del nodo esta compuesto (en su instancia ms simple) de un campo
informacin (info) y un campo siguiente (sig) que contiene la direccin del nodo consecutivo. El
campo siguiente del ltimo nodo de la lista contiene un valor especial llamado nulo (null) que
indica el fin de la lista.

4.1

Estructuras auto-referenciadas

Concepto de Nodo: es una estructura de datos abstracta, donde los nodos se caracterizan por ser
un tipo de dato auto-referenciado, debido a que uno de sus componentes (el campo siguiente) es
un puntero a una estructura del mismo tipo del nodo. De esta manera es posible realizar los
enlaces entre nodo y nodo. Adems de contener al menos un campo dato donde podemos
almacenar informacin, a continuacin vemos una definicin de Nodo en el lenguaje C++:
ANSI C++
struct Nodo {
int info;
Nodo * sig;
};

ANSI C++ (clase)


info
(int)

sig
(Nodo*)

class Nodo {
public:
//constructor
private:
int info;
Nodo * sig;
};

Apuntador a Nodo: el nodo raz y el campo siguiente de cada nodo sern de tipo apuntador a
nodo, debido a que son estructuras inherentemente dinmicas que almacenaran direcciones de
memoria de cada uno de los nodos que se vayan generando en tiempo de ejecucin. En C/C++ se
suelen emplear alias (typedef) para renombrar nuevos tipos estructurados como las autoreferenciadas ya que facilitan su uso, veremos ms adelante que en lenguajes que implementan
las orientacin a objetos de manera persistente no es necesario:

typedef nodo* pNodo;


De esta forma se dice que pNodo es un puntero a una estructura de tipo nodo, por lo que las
variables de este tipo nicamente sern capaces de almacenar direcciones de memoria.

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Apuntador Nulo: los punteros son asignados inicialmente a un valor vaci o nulo (NULL) que
representa sin direccin de memoria asignada, por lo general se dice que el ultimo nodo de la lista
no apunta a algn lugar en especial en la memoria, esta constante NULL suele ser empleada para
identificar el fin de la lista en la mayora de los casos.

pNodo raiz = NULL;


A continuacin se presenta un programa sencillo que crea una lista simple que contiene
nicamente un nodo. Observe que la instruccin new crea un bloque de memoria necesario para
contener una estructura de tipo Nodo.
#include <iostream>
using namespace std;
struct Nodo {
int info;
Nodo* sig;
};

info sig

typedef Nodo* pNodo;


int main ()
{
pNodo raiz = NULL, nuevo;
nuevo = new Nodo;
nuevo->info = 6;
nuevo->sig = NULL;
raiz = nuevo;
cout << raiz->info << endl;
cout << raiz->sig << endl;
delete raiz;
return 0;
}

raz

null

nuevo

Una lista con un solo elemento no resulta una estructura de datos interesante, adems de estar
muy acotada, por ello nos abocaremos a la construccin de estructuras de datos ms complejas,
elaboradas y funcionales. Antes que nada es necesario identificar primeramente las diversas
clases de listas enlazadas que existen; y analizaremos los diversos algoritmos existentes para
realizar operaciones que pueden realizarse con ellas, tales como la insercin de elementos, el
borrado de los mismos, la bsqueda de datos, entre otras operaciones indispensables para su
manipulacin apropiada.
Existen al menos tres clases de listas enlazadas:
o
o
o

Listas simples enlazadas


Listas simples circulares
Listas dobles

4.1.1 Listas simples enlazadas


Las listas permiten operaciones como la insercin y remocin de nodos en cualquier punto de la
misma en un tiempo constante de ejecucin, es importante recordar que la listas son elementos
inherentemente dinmicos y tiene que ser manipulados en tiempo de ejecucin.
Las listas simples ligadas son el tipo de lista ms sencillo de implementar y quizs uno de los ms
tiles al momento de elaborar alguna herramienta de programacin, debido a que su esquema
puede ser fcilmente adaptado para generar pilas y colas dinmicas. Las listas simples son
colecciones de datos enlazados por un campo de auto-referencia (siguiente), donde el ltimo
elemento apunta a una direccin (null) que representa el fin de la coleccin como se mostr en el
dibujo del inicio de la seccin.

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Las operaciones ms comunes en una lista enlazada son: insertar, eliminar y recorrer, pero como
veremos dichas operaciones suelen diversificarse generando versiones ms elaboradas de las
mismas para cada tipo de lista, a continuacin se mostraran los algoritmos correspondientes a
algunas de esas variantes para una lista simple enlazada.
Como podr darse cuenta algunos de estos algoritmos pueden ser tiles al momento de disear
pilas y colas dinmicas, o algn otro tipo de programa que implemente dichos mecanismos.
Algoritmo para insertar un nodo al inicio de la lista: este algoritmo inserta elementos justo
despus del nodo raz, por lo que cada nodo insertado justo despus de raz ser considerado
como el primer nodo. Como podemos intuir siempre el primer nodo que se inserte apuntara al valor
de raz, es decir null, por lo que estar confinado a permanecer al final de la lista.
raiz = null
repetir {
crea( nuevo )
nuevo->info = valor
(a)
nuevo->sig = raiz
(b)
raiz = nuevo
(c)
} hasta que no se inserten mas nodos
El algoritmo crea un nuevo nodo y posteriormente se almacena el valor correspondiente dentro de
la parte informacin del nuevo nodo (a). Despus ser necesario insertar el nuevo nodo en la lista,
para ello se le asigna la direccin contenida en raz (primer nodo) al nuevo nodo en su parte
siguiente (b). Finalmente ser necesario como ltimo paso asignar la direccin del nuevo nodo al
nodo raz de manera que raz se preserva como el puntero de inicio y el nuevo nodo para ser el
primero en la lista (c). El proceso concluye con una lista enlazada (d). Podemos observar que 2 fue
el primero en ser insertado y despus 8, 5 y finalmente 6.

nuevo

6
raz

null

null

null

(a)

nuevo

6
raz

5
(b)

nuevo
6
raz

5
(c)

raz

null

(d)

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Algoritmo para eliminar al inicio de la lista: este algoritmo se encarga de eliminar justo el primer
nodo al que apunta la raz por lo que el puntero raz tendr que ser reasignado al nodo primero en
su parte siguiente, de manera que el nodo raz apuntara al siguiente elemento en la lista. En caso
de eliminar el ltimo nodo de la lista, raz apuntara a null.
si ( raz != null ) {
tempo = raiz
raiz = raiz->sig
elimina( tempo )
}

(a)
(b)
(c)
(d)

La siguiente secuencia de figuras muestran los pasos realizados a lo largo del proceso de
eliminacin del primer nodo de la lista. Lo primero es verificar si existen nodos en la lista (a), una
vez verificado esto se asigna la direccin del primer nodo a un nodo temporal (tempo), de esta
manera obtenemos la direccin del nodo que mas tarde eliminaremos. Despus raz es asignado al
siguiente nodo de la lista (c) y finalmente se elimina el nodo temporal (d).

raz

null

(a)

tempo
6
raz

null

null

null

(b)

tempo

6
raz

5
(c)

tempo

6
raz

5
(d)

Algoritmo para recorrer una lista: este algoritmo nos permite recorrer todos los elementos de la
lista a partir del inicio, para ello asignamos la direccin del nodo de inicio (raz) a un nodo temporal,
mientras la lista no este vaca vamos mostrando la informacin del nodo, ser necesario asignar la
direccin del siguiente nodo a temporal, de manera que el recorrido se vaya realizando.
tempo = raiz
mientras ( tempo != null ) {
imprimir( tempo->info )
tempo = nodo->sig
}

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Con los algoritmos ya vistos es posible construir un programa que realice las operaciones bsicas
de una pila dinmica: insertar, eliminar y mostrar. En este esquema consideraremos al nodo raz
como el tope de la pila dinmica.

tope

null

A continuacin se muestra un programa en C++ que pueda realizar las operaciones antes
mencionadas para un esquema de pila dinmica, tome en cuenta que el programa bsico puede
adaptarse a las necesidades del programador variando el mecanismo de las funciones.
#include <iostream> /* Implementa una lista como una pila dinamica */
using namespace std;
struct Nodo {
int info;
Nodo *sig;
};
typedef Nodo* pNodo;
/* funciones con listas: */
void insertar( pNodo *, int ); // inserta al frente
pNodo eliminar( pNodo * ); // elimina al frente
void mostrar( pNodo ); // muestra la lista como pila
bool esVacia( pNodo ); // pregunta si esta vacia
void vaciarLista( pNodo * ); // elimina la lista completa
int main() {
pNodo tope = NULL;
int op, valor;
do {
pNodo t = NULL;
cout << "Pila dinamica\n1) insertar\n2) eliminar\n3) mostrar\n4) salir\n";
cin >> op;
switch (op) {
case 1:
cout << "insertar ? ";
cin >> valor; // &tope manda la direccion de la variable tope
insertar(&tope, valor);
break;
case 2:
t = eliminar(&tope);
cout << "se elimino " << t->info << endl;
delete t; // libera nodo
break;
case 3:
cout << "tope -> ";
mostrar(tope);
break;
}
} while (op != 4);
vaciarLista(&tope);
return 0;
}
// recibe la direccion de un puntero de alli pNodo * en lista
void insertar(pNodo *lista, int val) { // inserta al frente
pNodo nuevo; /* crear un nuevo nodo */
nuevo = new Nodo;
nuevo->info = val;
nuevo->sig = NULL;
nuevo->sig = *lista; /* agregamos el nuevo nodo al inicio de la lista */
*lista = nuevo; /* ahora, el comienzo de lista es nuevo nodo */
}

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

pNodo eliminar(pNodo *lista) { // elimina al frente de la lista


pNodo tempo = NULL;
if (*lista != NULL){ // lista apunta al inicio de la lista
tempo = *lista;
*lista = tempo->sig; // lista apunta al siguiente nodo
}
return tempo;
}
void mostrar(pNodo lista) {
pNodo tempo = lista;
while( tempo ) { // mientras no sea null
cout << tempo->info;
tempo = tempo->sig;
}
cout << endl;
}
bool esVacia(pNodo lista) {
return (lista == NULL); // lista igual a nulo
}
void vaciarLista(pNodo *lista) {
pNodo tempo;
while( !esVacia(*lista) ) { // mientras no se llegue a nulo
tempo = *lista; // tempo apunta al inicio de la lista
*lista = tempo->sig; // lista apunta al siguiente nodo
delete tempo; // libera tempo
}
}

Los algoritmos de insercin y borrado desde inicio, as como el de recorrido (mostrar) estn
implementados en el programa antes visto, adicionalmente se agrego la funcin esVaco como una
manera de hacer ms evidente la construccin de operaciones bsicas con estructuras de datos.
Por ltimo es necesario crear una funcin que nos permita eliminar todos los nodos creados en
tiempo de ejecucin (vaciarLista) de manera de que la memoria est disponible y lista para una
futura ejecucin de programas.
Recordando el funcionamiento de una cola, el primer elemento en entrar es el primer elemento en
salir (FIFO), para el caso de una cola dinamica se podran necesitar dos elementos auxiliares: una
referencia al inicio de la cola y una referencia al ltimo elemento de la cola. Los siguientes dos
algoritmos pueden ser utilizados para implementar una cola dinmica.
Algoritmo para insertar un nodo al final de la lista: en este algoritmo se insertara un nuevo nodo
al final de la lista. Para ello ser necesario conservar dos punteros uno al inicio y otro al ltimo de
la lista, de manera que no sea necesario recorrer toda la lista cada vez que se requiera insertar un
elemento. El ultimo puntero nos indica la posicin en la que se insertara el nuevo nodo, y una vez
hecho esto la direccin al ltimo nodo deber ser actualizada.
inicio = ultimo = null
repetir {
crea( nuevo )
nuevo->info = valor
nuevo->sig = null
si ( ultimo == null )
inicio = nuevo
sino
ultimo->sig = nuevo
ultimo = nuevo
} hasta que no se inserten mas nodos

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

ultimo
inicio

ultimo

null
6

nuevo

null

Ahora, ya con el algoritmo de insercin al final de la lista estamos en posibilidades de implementar


un esquema de cola dinmica.

ultimo
inicio

null

En esencia casi se conservan todas las operaciones del programa anterior. La funcin de insercin
se modifica, pues ahora ser necesario manipular dos punteros, uno al inicio y otro para el ltimo
nodo de la cola dinmica (atrs), de manera que ltimo siempre conserve la referencia al final de la
cola.
Algoritmo para eliminar un nodo al frente de la lista con inicio y ltimo: Es importante poner
especial atencin en la funcin de eliminar, pues cuando ya no se cuenten con ms elementos en
la cola, ser necesario que tanto inicio como ltimo apunten a null. De manera que la cola este lista
y en posibilidades de comenzar nuevamente su funcin.
tempo = null
si ( inicio != null ) {
tempo = inicio
inicio = tempo->sig
si ( inicio == null ) ultimo = null
}
regresa tempo
A continuacin se muestra un programa en C++ que pueda realizar las operaciones antes
mencionadas para un esquema de cola dinmica, tome en cuenta que el programa bsico puede
adaptarse a las necesidades del programador variando el mecanismo de las funciones.
/* implementacion de una lista como cola dinmica para este caso se
recomienda el empleo de un puntero adicional al nodo raz que haga la
labor de la parte trasera de la cola */
#include <iostream>
using namespace std;
// estructura nodo
struct Nodo {
int info;
Nodo *sig;
};
// definicion de tipos
typedef Nodo* pNodo;
/* declaracion de Funciones con listas: */
void insertarAtras( pNodo &, pNodo &, int );
pNodo extraer( pNodo &, pNodo & );
void mostrar( pNodo );
int esVacia( pNodo );
void vaciarLista( pNodo &, pNodo & );

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

int main() {
pNodo frente = NULL, atras = NULL, t = NULL;
int op, valor;
do {
cout << " 1 insertar\n 2 extraer\n 3 mostrar\n 4 Salir\n";
cin >> op;
switch (op) {
case 1:
cout << "valor ";
cin >> valor;
insertarAtras(frente, atras, valor);
break;
case 2:
t = extraer(frente, atras);
if (t != NULL)
cout << " se extrajo " << t->info << endl;
else
cout << "cola vacia !!!\n";
delete t;
break;
case 3:
cout << "frente -> ";
mostrar(frente);
break;
}
} while (op != 4);
vaciarLista(frente, atras);
return 0;
}
void insertarAtras(pNodo &frente, pNodo &atras, int val) { // insertar un elemento al final
pNodo nuevo;
nuevo = new Nodo;
nuevo->info = val;
nuevo->sig = NULL;
if ( atras == NULL )
frente = nuevo;
else
atras->sig = nuevo;
atras = nuevo;
}
pNodo extraer(pNodo &frente, pNodo &atras) { // extraer un elemento al frente de la cola
pNodo tempo = NULL;
if (frente != NULL) {
tempo = frente; // nodo apunta al inicio de la cola
frente = tempo->sig; // lista apunta al siguiente nodo
if ( frente == NULL ) atras = NULL; // no hay mas elementos en la cola
}
return tempo;
}
void mostrar(pNodo lista) {
for(pNodo nodo = lista; !esVacia(nodo); nodo = nodo->sig )
cout << nodo->info << " -> ";
cout << "null\n";
}
int esVacia(pNodo lista) { return (lista == NULL); } // lista igual a nulo
void vaciarLista(pNodo &frente, pNodo &atras) {
pNodo tempo;
while( !esVacia(frente) ) { // mientras no se llege a nulo
tempo = frente; // tempo apunta al inicio de la lista
frente = tempo->sig; // lista apunta al siguiente nodo
if ( frente == NULL ) atras = NULL;
delete tempo; // libera tempo
}
}

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Se deja como caso de estudio el generar una lista ordenada de elementos en la cual se persigue
que conforme se ingresan los datos en la lista, los valores sern insertados segn su valor dentro
de la lista.
4.1.2

Listas simples circulares

Una lista enlazada circular posee una caracterstica adicional y es que su estructura parecera que
no tiene fin ya que el primero y el ltimo nodo estn unidos bajo una misma referencia. Para que la
lista sea sin fin, el puntero siguiente del ltimo elemento apuntar hacia el 1er elemento de la lista
en lugar de apuntar al valor NULL.
Para recorrer una lista enlazada circular podemos empezar por cualquier nodo y seguir la lista en
cualquier direccin hasta que se regrese hasta el nodo original. Desde otro punto de vista, las listas
enlazadas circulares pueden ser vistas como listas sin comienzo ni fin. Este tipo de listas es el ms
usado para dirigir buffers para ingerir datos, y para visitar todos los nodos de una lista a partir de
un nodo dado.

raz
info sig

info sig

info sig
ltimo

primero
nodo

nodo

nodo

Debido a que las listas circulares no cuentan de manera natural con un nodo primero y ltimo,
debe de establecerse una convencin para el manejo del nodo primero y ltimo. De manera que el
nodo raz apunta siempre al ltimo nodo de la lista lo que le permite acceder de manera natural al
ltimo nodo y la referencia raz->sig le permite acceder al primer nodo de la lista como se muestra
en la figura anterior. Esta caracterstica de las listas circulares facilita la insercin de nodos al inicio
o al final, as es posible generar pilas y colas dinmicas ms fcilmente.
Algoritmo para insertar un nodo al final de la lista circular: este algoritmo inserta elementos
justo despus del nodo raz, de manera que en cada insercin raz apuntara al ltimo elemento de
la lista, mientras que raz->sig apunta al primer nodo de la lista.
raiz = null
repetir {
crea( nuevo )
nuevo->info = valor
si raiz == null entonces
raz = nuevo
sino
nuevo->sig = raz->sig
fin si
raz->sig = nuevo
raz = nuevo
} hasta que no se inserten mas nodos

UAA Sistemas Electrnicos

nuevo

Estructura de Datos

raz

Eduardo Serna-Prez

raiz

nuevo

raz

Algoritmo para eliminar un nodo al inicio de la lista circular: ahora eliminaremos el nodo que
se encuentra justo al inicio de la lista, este nodo puede ser alcanzado a partir del nodo raz en su
parte siguiente raz->sig. De manera que el nodo del frente se supone ser atendido y la referencia
deber de ir ahora al siguiente nodo en la cola.
tempo = null
si ( raz != null ) {
tempo = raz->sig
raz->sig = tempo->sig
elimina( tempo )
}
Algoritmo para recorrer una lista circular: el algoritmo permite recorrer todos los elementos de la
lista a partir del nodo raz, para ello asignamos la direccin del nodo de inicio (raz) a un nodo
temporal, mientras la lista no este vaca vamos mostrando la informacin del nodo, ser necesario
asignar la direccin del siguiente nodo a temporal, de manera que el recorrido se vaya realizando.
tempo = raz->sig
si (raiz == null) return
mientras ( tempo != raiz ) {
imprimir( tempo->info )
tempo = tempo->sig
}

tempo

tempo

raz

10

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Apndice A
Manejo de argumentos en la funcin main
Todos los programas que se construyen en el lenguaje ANSI C/C++ tienen la capacidad de recibir
argumentos de entrada, esto se debe principalmente a la capacidad que tiene la funcin main de
recoger o recibir dichos argumentos de entrada y depositarlos en variables pertinentes para su uso.
La funcin main recibe un par de argumentos:
int main ( int argc, char *argv[] )
El primer argumento, denominado argc (abreviatura en ingles para argument count) es una
variable entera que determina la cantidad de elementos de entrada separados por un espacio en
blanco (token), mientras que el segundo argumento, denominado como argv (abreviatura en ingles
para argument vector) es un arreglo de punteros tipo char que almacena cada una de las
constantes literales (tokens) introducidos en la lnea de comandos.
Ambos argumentos en la funcin main, argc y argv son muy tiles cuando pretendemos que
nuestro programa capture valores de entrada desde la lnea de comandos. Todos los programas
de computadora requieren al menos un cierto nmero de argumentos de entrada, ya que estos dan
versatilidad y opciones de uso a un cierto programa.
Por ejemplo el mismo compilador de ANSI C/C++ requiere argumentos de entrada para poder
trabajar, quizs esto ya no sea tan perceptible al programador, pues los ambientes de desarrollo
han ido cubriendo esta necesidad de manejo de argumentos, un ambiente de desarrollo en
realidad manda llamar a otro programa denominado compilador que tomar la entrada de
argumentos y realizara un proceso de compilado. En los ambientes tipo UNIX, existen
compiladores de C llamado gcc, y otro de C++ llamado g++, stos requiere algunos argumentos
de entrada para poder hacer su labor de compilacin:
g++ o miprograma miprograma.cpp
Entonces g++ es el compilador (programa) y los argumentos de entrada son las literales o,
miprograma y miprograma.cpp, estas literales sern usadas por el compilador para generar
el programa de manera que se solicito.
Es importante recordad que la ejecucin de programas desde la lnea de comando ha sido
gradualmente sustituida por elementos de programacin ms poderosos, pero sin embargo la
ejecucin desde lnea de comando es una de las formas ms rpidas de ejecucin.
De esta forma los programas que construyamos en C/C++ tendrn esa misma caracterstica para
el manejo de argumentos.
Veamos un ejemplo muy simple que muestra el uso de argumentos de entrada, en este caso
emplearemos argc para determinar la cantidad de argumentos de entrada y argv para mostrar el
contenido del arreglo de caracteres que contiene a cada argumento que ha ingresado.
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
cout << "numero de argumentos " << argc << endl;
for(int i = 0; i < argc; i++)
cout << argv[i] << endl;

11

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

return 0;
}
El programa ser salvado con el nombre tempo.cpp, el compilador entonces generara un
programa llamado tempo.exe, que es precisamente el programa que se ejecutara desde la lnea
de comandos de la siguiente manera:
C:\Mi Directorio\tempo.exe Hola mundo
Las literales Hola y mundo sern recibidos por argv y contados por argc, de manera que producir
la siguiente salida:
numero de argumentos 3
C:\Mi Directorio\tempo.exe
Hola
mundo
Presione una tecla para continuar . . .
Tome en cuenta que el nombre de nuestro programa as como la ruta en que lo ejecutemos ser el
primer argumento (argv[0]).
Recuerde que los argumentos de entrada tambin pueden ser ingresados desde el ambiente de
desarrollo, por ejemplo en Dev-C++ se encuentran en men Excecute Parameters

12

UAA Sistemas Electrnicos

Estructura de Datos

Eduardo Serna-Prez

Bibliografa
Garcia de Jalon, Javier et al. Aprenda lenguaje ANSI C: Como si estuviera en primero,
Universidad de Navarra.
Harvey Deitel & Paul Deitel, Cmo programar en C, Prentice Hall, segunda edicin, 1999, ISBN
970-17-0254-9.
Naba Barkakati, Turbo C/C++ Bible, Sams first edition, 1992, ISBN 0-672-22742-8.
Y. Langsam, M. J. Augenstein, A. Tenenbaum. Data Structures using C and C++. Prentice Hall,
Second edition. ISBN 0-13-036997-7.

13

Das könnte Ihnen auch gefallen