Escolar Documentos
Profissional Documentos
Cultura Documentos
3 LISTAS ENLAZADAS
En Ciencias de la Computación, una lista enlazada es una de las estructuras de datos fundamentales, y
puede ser usada para implementar otras estructuras de datos. Consiste en una secuencia de nodos, en los
que se guardan campos de datos arbitrarios y una o dos referencias (punteros) al nodo anterior o
posterior. El principal beneficio de las listas enlazadas respecto a los array convencionales es que el
orden de los elementos enlazados puede ser diferente al orden de almacenamiento en la memoria o el
disco, permitiendo que el orden de recorrido de la lista sea diferente al de almacenamiento.
Una lista enlazada es un tipo de dato auto-referenciado porque contienen un puntero o link a otro dato
del mismo tipo. Las listas enlazadas permiten inserciones y eliminación de nodos en cualquier punto de
la lista en tiempo constante (suponiendo que dicho punto está previamente identificado o localizado),
pero no permiten un acceso aleatorio. Existen diferentes tipos de listas enlazadas: Lista Enlazadas
Simples, Listas Doblemente Enlazadas, Listas Enlazadas Circulares y Listas Enlazadas Doblemente
Circulares.
Las listas enlazadas pueden ser implementadas en muchos lenguajes. Lenguajes tales como Lisp y
Scheme tiene estructuras de datos ya construidas, junto con operaciones para acceder a las listas
enlazadas. Lenguajes imperativos u orientados a objetos tales como C o C++ y Java, respectivamente,
disponen de referencias para crear listas enlazadas.
La lista enlazada básica es la lista enlazada simple la cual tiene un enlace por nodo. Este enlace apunta
al siguiente nodo en la lista, o al valor NULL o a la lista vacía, si es el último nodo.
Una lista enlazada simple contiene dos valores: el valor actual del nodo y un enlace al siguiente nodo
El campo de datos de un nodo puede ser otra lista enlazada. Mediante este mecanismo, podemos
construir muchas estructuras de datos enlazadas con listas; esta practica tiene su origen en el lenguaje de
programación Lisp, donde las listas enlazadas son una estructura de datos primaria (aunque no la única),
y ahora es una característica común en el estilo de programación funcional.
A veces, las listas enlazadas son usadas para implementar arrays asociativos, y estas en el contexto de
las llamadas listas asociativas. Hay pocas ventajas en este uso de las listas enlazadas; hay mejores
formas de implementar éstas estructuras, por ejemplo con árboles binarios de búsqueda equilibrados. Sin
Ventajas
Como muchas opciones en programación y desarrollo, no existe un único método correcto para resolver
un problema. Una estructura de lista enlazada puede trabajar bien en un caso pero causar problemas en
otros. He aquí una lista con un algunas de las ventajas más comunes que implican las estructuras de tipo
lista. En general, teniendo una colección dinámica donde los elementos están siendo añadidos y
eliminados frecuentemente e importa la localización de los nuevos elementos introducidos se incrementa
el beneficio de las listas enlazadas.
Lenguajes soportados
Muchos lenguajes de programación tales como Lisp y Scheme tienen listas enlazadas simples ya
construidas. En muchos lenguajes de programación, estas listas están construidas por nodos, cada uno
llamado cons o celda cons. Las celdas cons tienen dos campos: el car, una referencia del dato al nodo, y
el cdr, una referencia al siguiente nodo. Aunque las celdas cons pueden ser usadas para construir otras
estructuras de datos, este es su principal objetivo.
En lenguajes que soportan tipos abstractos de datos o plantillas, las listas enlazadas ADTs o plantillas
están disponibles para construir listas enlazadas. En otros lenguajes, las listas enlazadas son típicamente
construidas usando referencias junto con el tipo de dato record.
Cuandos se manipulan listas enlazadas, hay que tener cuidado con no usar valores que hayamos
invalidado en asignaciones anteriores. Esto hace que los algoritmos de insertar y borrar nodos en las
listas sean algo especiales. A continuación se expone el pseudocódigo para añadir y borrar nodos en
listas enlazadas simples, dobles y circulares.
Nuestra estructura de datos tendrá dos campos. Vamos a mantener la variables PrimerNodos que
siempre apunta al primer nodo de tal lista, ó nulo para la lista vacía.
record Node {
data // El dato almacenado en el nodo
next // Una referencia al nodo siguiente, nulo para el último nodo
}
record List {
Node PrimerNodo // Apunta al primer nodo de la lista; nulo para la lista
vacía
}
node := list.PrimerNodo
while node not null {
node := node.next
}
El siguiente código inserta un elemento a continuación de otro en una lista simple. El diagrama muestra
como funciona.
Insertar al principio de una lista requiere una función por separado. Se necesita actualizar PrimerNodo.
De forma similar, también tenemos funciones para borrar un nodo dado ó para borrar un nodo del
principio de la lista. Ver diagrama.
EJEMPLO
Recorrido
Definición:
Recorrido simplemente despliega los datos almacenados en el arreglo Info, con ayuda de un segundo
arreglo llamado Indice el cual guarda el orden en el que encuentran enlazados cada uno de los datos.
Explicación:
Apuntador toma el valor de Inicio, después ve si la condición cumple para efectuar un Ciclo mientras
Apuntador sea diferente de 0, si cumple lo que hace es que despliega la Info[Apuntador], después
Apuntador toma el valor de Indice[Apuntador] (El cual nos indica el siguiente nodo que sigue en la lista)
y hace esto hasta que Apuntador sea igual a 0 (Cuando llega a este punto a llegado al fin de la Lista
Enlazada).
Algoritmo:
Apuntador ←- Inicio
Imprimir Info[Apuntador]
Apuntador ←- Indice[Apuntador]
Salir
Diagrama:
#include <conio.h>
#include <iostream.h>
void Recorrido(char Info[8][2],int Indice[8],int Inicio,int Disp);
void main()
{
char Info[8][2]={{"G"},{"I"},{" "},{"T"},{"O"},{"A"},
{" "},{"T"}};
int Indice[8]={5,7,6,1,-999,3,-999,4};
int Inicio=0,Disp=2;
cout<<"El Recorrido es:\n";
Recorrido(Info,Indice,Inicio,Disp);
getch();
}
Corrida:
Definición:
Explicación:
Apuntador toma el valor de Inicio, después ve si la condición cumple para efectuar un Ciclo mientras
Apuntador sea diferente de 0, si cumple lo que hace a continuación es la comparación de Elemento (El
dato que vamos a buscar) con Info[Apuntador], cuando lo encuentre lo despliega y sale del método. Si
no, regresa el valor de Apuntador para así saber que no se encontró el dato.
Algoritmo:
Apuntador ←- Inicio
Imprimir Info[Apuntador]
Regresa Apuntador
Apuntador ←- Indice[Apuntador]
Regresar Apuntador
Programa:
#include <conio.h>
#include <iostream.h>
int Busqueda(int Info[8],int Indice[8],int Inicio,int Disp,int Elemento);
void main()
{
int Info[8]={12,10,0,9,5,3,0,20};
int Indice[8]={5,7,6,1,-999,3,-999,4};
int Inicio=0,Disp=2,Elemento,Res;
cout<<"Que Numero deseas buscar?";
cin>>Elemento;
Res=Busqueda(Info,Indice,Inicio,Disp,Elemento);
if(Res==-999)
cout<<"Dato No Encontrado...";
getch();
}
CORRIDA:
Inserción al Principio
Definición:
La Inserción al Principio básicamente busca si existe algún lugar disponible en el arreglo Info y lo
agrega como primer Nodo si es que es posible.
Explicación:
Hace una comparación para ver si es posible insertar otro Elemento al arreglo Info, para esto checa si
Disp es Diferente de Nulo. Si no cumple con la condición se desplegar “Sobre Carga” ya que no se
puede insertar un Nuevo Elemento. Si es cierto Apuntador toma el valor de Inicio, Disp cambia a
Indice[Disp] ya que el primer Disp tomara el valor del Nuevo Elemento, después de esto solo copia la
información de Elemento al arreglo Info en la posición que guarda Apuntador, Indice[Apuntador] toma
el valor de Inicio y finalmente Inicio toma el valor de Apuntador.
Algoritmo:
Apuntador ←- Disp
Disp ←- Indice[Disp]
Info[Apuntador] ←- Elemento
Indice[Apuntador] ←- Inicio
Inicio ←- Apuntador
Si no:
Salir
Diagrama:
Programa:
void main()
{
int Info[8]={12,10,0,9,5,3,0,20};
int Indice[8]={5,7,6,1,-999,3,-999,4};
int Inicio=0,Disp=2,Elemento,Res;
cout<<"Lista Original\n";
Recorrido(Info,Indice,Inicio,Disp);
cout<<"Que Numero deseas Insertar?";
cin>>Elemento;
InsPr(Info,Indice,Inicio,Disp,Elemento);
getch();
}
CORRIDA:
Definición:
La Inserción después de un Nodo Determinado básicamente hace lo mismo que la inserción al principio,
la única diferencia es que este recibe la posición del nodo en la que será Insertada. Este Algoritmo se usa
para Inserción Ordenada que mas adelante explicaremos.
Explicación:
Primero confirma que sea posible insertar el Dato, si no es posible solo desplegara “Sobre Carga”. Si es
posible insertar un dato nuevo lo posiciona en la primer posición Disponible en el arreglo Info, después
compara la Nueva Posición (Npos) que le mandamos con Nill si cumple la condición el dato es insertado
en la primer posición, de otra forma se posicionara en la posición que guarde Npos.
Algoritmo:
Apuntador ←- Disp
Disp ←- Indice[Disp]
Indice[Apuntador] ←- Inicio
Inicio ←- Apuntador
Si no:
Indice[Apuntador] ←- Indice[Npos]
Indice[Npos] ←- Apuntador
Si no:
Salir
Inserción Ordenada
Definición:
La Inserción Ordenada busca la posición en donde será Insertado el Elemento y la posición anterior
donde será Insertado, después de encontrar la posición en la que será Insertado el Elemento nos regresa
ese valor y lo mandamos al método de la Inserción después de un Nodo.
Explicación:
En esta ocasión usaremos dos variables para determinar la posición deseada, comparamos si Inicio es
igual a Nill ó si Elemento es menor al dato que se encuentra en Info[Inicio], si alguna de las dos cumple
regresamos Nill, de esta manera Indicamos que el Elemento será el primero de todo el Arreglo Info, si
no es así Temp tomara el valor de Inicio y Temp2 de la posición que le sigue a Inicio. Hace un ciclo
hasta encontrar la posición en donde se insertara el Nuevo Elemento y va moviéndose de posición con
las variables Temp y Temp2 para así determinar que posición debe de regresar.
Regresar Nill
Temp ←- Inicio
Temp2 ←- Indice[Inicio]
Regresar Temp
Temp ←- Temp2
Temp2 ←- Indice[Temp2]
Regresar Temp
Diagrama:
#include <conio.h>
#include <iostream.h>
int InsOrd(int Info[8],int Indice[8],int Inicio,int Elemento);
void Recorrido(int Info[8],int Indice[8],int Inicio,int Disp);
void InsNd(int Info[8],int Indice[8],int Inicio,int Disp, int Elemento, int Npos);
void main()
{
int Info[8]={12,10,0,9,5,3,0,20};
int Indice[8]={5,7,6,1,-999,3,-999,4};
int Inicio=0,Disp=2,Elemento,Res;
cout<<"Lista Original\n";
Recorrido(Info,Indice,Inicio,Disp);
cout<<"Que Numero deseas Insertar?";
cin>>Elemento;
Res=InsOrd(Info,Indice,Inicio,Elemento);
InsNd(Info,Indice,Inicio,Disp,Elemento,Res);
getch();
}
Recorrido(Info,Indice,Inicio,Disp);
}
else
cout<<"Overflow...";
}
CORRIDA:
La Eliminación simplemente cambia los nodos para que el dato que se desea eliminar sea el primer
disponible, de esta forma ya no estará en el Arreglo de Info.
Explicación:
Lo primero que hace es ver si existe algún dato en la lista para eliminar, si Inicio es igual a Nill entonces
solo desplegara “Imposible Eliminar”. De otra formas cambiar de Posición en Posición hasta encontrar
el Elemento que sea desea Eliminar con ayudar de dos variables que guardan la Posición actual y la
anterior en donde se encuentre el dato. Ya que lo encuentra cambia ese dato como la primera posición
Disponible y lo apunta al siguiente nodo disponible. Si no encuentra el dato simplemente desplegara
“Dato no encontrado”
Algoritmo:
Temp ←- Inicio
Si Temp = Nill
Inicio ←- Indice[Inicio]
Si no:
Indice[Temp2] ←- Indice[Temp]
Indice[Temp] ß Disp
Disp ←- Temp
Si no:
Temp2 ←- Temp
Temp ←- Indice[Temp]
Diagrama:
#include <conio.h>
#include <iostream.h>
void Recorrido(int Info[8],int Indice[8],int Inicio,int Disp);
void EliBusq(int Info[8],int Indice[8],int Inicio,int Disp,int Elemento);
void main()
{
int Info[8]={12,10,0,9,5,3,0,20};
int Indice[8]={5,7,6,1,-999,3,-999,4};
int Inicio=0,Disp=2,Elemento,Res;
cout<<"Lista Original\n";
Recorrido(Info,Indice,Inicio,Disp);
cout<<"Que Numero deseas Eliminar?";
cin>>Elemento;
EliBusq(Info,Indice,Inicio,Disp,Elemento);
getch();
}
CORRIDA:
#include <stdio.h>
#include <conio.h>
#include <iomanip.h>
#include <iostream.h>
class Alumno
{
private:
char Nombre[10][30];
int N_control[10],Edad[10],Indice1[10],Indice2[10],Inicio,Fin,Disp;
public:
//Constructor
Alumno()
{
int i,j;
Inicio=0;
Fin=0;
Disp=1;
Indice1[Inicio]=-999;
Indice2[Fin]=-999;
for(i=1,j=2;i<9;i++,j++)
Indice1[i]=j;
Indice1[9]=-999;
}
//Funcion de Recorrido
void Recorrido(int op)
{
int i=0,Temp;
if(op==1)
{
Temp=Indice1[Inicio];
if(Temp!=-999)
{
cout<<"Numero de Control"<<setw(19)<<"Nombre del Alumno"<<setw(5)<<"Edad"<<endl;
while(Temp!=-999)
{
if(i==(int(Edad[Inicio]/2)))
{
N_control[Inicio]=N_control[i];
strcpy(Nombre[Inicio],Nombre[i]);
}
cout<<setw(9)<<N_control[Temp]<<setw(22)<<Nombre[Temp]<<setw(9)<<Edad[Temp]<<endl;
Temp=Indice1[Temp];
i++;
}
}
main()
{
int op=0,res;
char inom[30];
int in_c,iedad;
while(op!=6)
{
clrscr();
cout<<"\n1) Recorrido por Inicio\n2) Recorrido por Final\n3) Busqueda\n";
cout<<"4) Insercion\n5) Eliminar un Dato\n6) Salir"<<endl;
gotoxy(1,1);
cout<<"Que deseas hacer: ";
cin>>op;