Listas Lineales

Transcripción

Listas Lineales
Listas Lineales
Estructura de Datos
3. LISTAS
3.1 Arreglos
3.1.1 Arreglos unidimensionales
Un arreglo se usa para agrupar, almacenar y organizar datos de un mismo tipo. En un
arreglo cada valor se almacena en una posición numerada específica dentro del arreglo. El
número correspondiente a cada posición se conoce como índice.
La estructura de un arreglo es:
Normalmente el primer objeto del arreglo tiene el índice 0, aunque esto varía de lenguaje en
lenguaje.
Declaración:
La declaración de un arreglo en pseudocódigo que se utilizará es:
tipo [ ] nombreArreglo
Un arreglo es un objeto por lo que para crear el espacio se utiliza el operador nuevo:
nombreArreglo Å nuevo tipo [max]
donde tipo puede ser un tipo de datos primitivo o un objeto, nombre es el nombre del arreglo
y max es el número de localidades del arreglo. Es una buena costumbre que max sea
declarado como una constante.
Pág. 1
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
También puede declararse de la siguiente forma:
tipo [] nombreArreglo Å nuevo tipo [max]
Operaciones:
• almacenamiento, una operación de almacenamiento inserta un valor en el arreglo en
una localidad específica.
nomArreglo [i] Å valor
• recuperación, una operación de recuperación regresa el valor almacenado en una
localidad específica del arreglo
valor Å nomArreglo[i]
donde i es la posición o índice del arreglo, que puede ser un entero o bien una expresión que
al ser evaluada regresa un entero
Por ejemplo:
const int MAX Å 10
int [] arregloEnteros Å nuevo int [MAX]
arregloEnteros [0] Å 1
arregloEnteros [1] Å 10
aÅ5
arregloEnteros [a-2] Å arreglosEnteros [0] + arregloEnteros [1]
Esquemáticamente arregloEnteros se vería como:
•
número de localidades, como dato miembro público y constante de un arreglo se
puede accesar el número de localidades:
int identificador Å nomArreglo.longitud
Ejemplos:
a) Arreglos que utilizan datos primitivos
1. Leer una lista de números, almacenarlos en un arreglo e imprimirlos en orden inverso
Pág. 2
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
clase NumerosReves
{
principal
comienza
const int MAX Å 10
//Número máximo de posiciones del arreglo
//creación del arreglo de reales
real [] numeros Å nuevo real [MAX]
//se piden al usuario los datos
escribe “El tamaño del arreglo es: “ + numeros.longitud
para indice Å 0 hasta numeros.longitud –1
comienza
escribe “Dame el número que se encuentra en ” + índice
lee numeros [indice]
termina
//se escribe al revés el arreglo
escribe “Los números al revés son: “
para indice Å numeros.longitud-1 hasta 0, -1
escribe numeros [indice]
termina
}
2. Considere los siguientes datos miembro y la función constructora por default:
clase EjemplosArreglo
{
const int MAXLOC Å 100
int [] info
EjemplosArreglo ()
comienza
info Å nuevo int [MAXLOC]
para i Å 0 hasta info.longitud-1
comienza
escribe “Dame el valor del arreglo en la posición “ + i
lee info [i]
termina
termina
}
A continuación se presentan algunas funciones miembro que podrían pertenecer a la clase
EjemplosArreglo
a) Calcular la suma de los elementos de un arreglo
int sumaElem ()
comienza
suma Å 0
para i Å 0 hasta info.longitud-1
suma Å suma + info[i]
regresa suma
termina
b) Calcular el promedio de los elementos de un arreglo
Pág. 3
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
real promedio ()
comienza
regresa (sumaElem() / info.longitud)
termina
c) Determinar el elemento más grande del arreglo
int maximo ()
comienza
max Å info [0]
//El mayor es el 1ro. hasta que se demuestre lo contrario
para i Å 1 hasta info.longitud-1
si max < info[i] entonces
max Å info [i]
regresa max
termina
d) Ordenar un arreglo por selección
El algoritmo de selección ordena un arreglo de valores poniendo un valor particular en su
posición final, i.e., el algoritmo selecciona el valor que debería ir en una posición y lo pone
ahí.
Considere un orden ascendente y el siguiente arreglo de números.
Se busca el elemento menor del arreglo y se pone en la 1ra. posición,
y entonces el arreglo queda como:
Note que ahora la celda amarilla contiene al elemento más pequeño y que está en la posición
que quedara cuando el arreglo esté completamente ordenado.
A continuación se busca el siguiente más pequeño que deberá de estar en la segunda
posición del arreglo:
Pág. 4
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
y se intercambia con el elemento que ocupa la 2da. posición del arreglo. Y posteriormente se
busca el siguiente más pequeño:
Ahora el 5 deberá de ocupar la tercera posición del arreglo. Y se buscará el elemento más
pequeño de los restantes:
Por último se coloca el 6 en su posición correspondiente, intercambiándolo con el 8. Y así el
arreglo queda ordenado:
Nótese que el último elemento quedará automáticamente ordenado.
A continuación se presenta el algoritmo:
void ordenSeleccion ()
comienza
int min, temp
para indice Å 0 hasta info.longitud –2
comienza
min Å indice
para busca Å indice+1 hasta info.longitud-1
si info[busca] < info [min] entonces
min Å busca
//se intercambian
temp Å info [min]
info [min] Å info [indice]
info [indice] Å temp
termina
termina
b) Arreglos que utilizan objetos
La creación de un arreglo y la creación de objetos son dos cosas separadas. El mecanismo
para trabajarlos es el mismo.
Considere la siguiente clase en Java para crear CD:
Pág. 5
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
/**
* Define un disco compacto
* */
class CD
{
// datos miembro
String titulo;
String autor;
int año;
double precio;
/**
* Constructor para objetos de la clase CD
*/
CD(String t, String au, int a, double p)
{
Titulo = t;
autor = au;
año = a;
precio = p;
}
/**
* Escribe los datos miembro
*/
void escribe ()
{
System.out.println ("Título: " + titulo);
System.out.println ("autor: " + autor);
System.out.println ("año: " + año + " $ " + precio);
System.out.println ("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}
}
Y la clase discoteca donde se utiliza un arreglo de CD
/**
* Maneja un arreglo de CD
*/
class discoteca
{
CD [] arregloCD;
int numCD;
final int MAXIMO = 100;
/**
* Constructora de la discoteca
*/
public discoteca()
{
numCD = 0;
arregloCD = new CD [MAXIMO];
}
/**
* Inserta un CD
Pág. 6
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
*/
void insertaCD (String tit, String au, int a, double costo)
{
// se crea un CD
if (numCD > MAXIMO)
System.out.println ("Ya no hay lugar");
else
{
CD nuevoCD = new CD (tit,au,a,costo);
arregloCD[numCD] = nuevoCD;
numCD++;
}
}
/**
* Escribe en pantalla todos los CD
*/
void escribe ()
{
for (int i = 0; i < numCD; i++)
arregloCD[i].escribe ();
}
/**
* Calcula el costo total de la inversión
*/
double costo ()
{
double c = 0;
for (int i = 0; i < numCD; i++)
c = c + arregloCD[i].precio;
return c;
}
public static void main (String [] a)
{
//Se crea una discoteca privada
discoteca privada = new discoteca ();
privada.insertaCD ( "Storm Front", "Billy Joel", 1960, 140.00);
privada.insertaCD ("Come On Over", "Shania Twain", 1990, 145.50);
privada.insertaCD ("Graceland", "Paul Simon", 1991, 123.60);
System.out.println ("\nDiscoteca privada");
privada.escribe ();
//Se crea una discoteca clasica
discoteca clasica = new discoteca ();
clasica.insertaCD ("Concierto No. 5 para piano", "Tchaikowsky", 1763, 57.90);
clasica.insertaCD ("Carmen", "Bizet", 1896, 67.25);
System.out.println ("\nDiscoteca clásica");
clasica.escribe ();
//Reporta inversiones
System.out.println ("El costo de lo invertido en la discoteca privada es $"+ privada.costo ());
System.out.println ("El costo de lo invertido en la discoteca clasica es $"+ clasica.costo ());
}
}
Pág. 7
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
La salida producida es:
Pág. 8
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
3.1.2 Arreglos de dos dimensiones
Los arreglos examinados hasta el momento han sido unidimensionales en el sentido de que
representan una lista simple de valores. Un arreglo bidimensional, como su nombre lo indica,
tiene valores en dos dimensiones, en algunas ocasiones se le llama también tabla y se tiene
acceso a sus elementos a través de la columna y el renglón del arreglo.
Para definir un arreglo en dos dimensiones es necesario poner el tipo de la información que
se desea almacenar (un tipo primitivo o un objeto) y especificar que se tendrán dos
dimensiones:
tipo [] [] nombre
Ejemplos:
a) Definir un arreglo bidimensional de enteros:
int [] [] matriz
b) Definir una tabla de booleanos:
bool [][] tablero
c) Definir un arreglo bidimensional de fichas
ficha [] [] juego
Como en los arreglos de una sola dimensión, el tamaño de las dimensiones se especifica
cuando el arreglo es creado y el tamaño de cada dimensión puede ser diferente.
Pág. 9
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Así, para el ejemplo anterior:
a) matriz Å nuevo int [5][5]
b) tablero Å nuevo bool [10][MAX], donde MAX almacena un valor entero
c) juego Å nuevo ficha [NUMCELDAS] [NUMCELDAS-2], donde NUMCELDAS almacena un
valor entero
Las operaciones que pueden realizarse con cada elemento de un arreglo son:
•
•
almacenamiento
recuperación
En cada operación es necesario especificar unívocamente el elemento por lo que es necesario
dar el índice para cada dimensión:
Por ejemplo:
Ejemplo
a)
b)
Almacenamiento
matriz [2][3] Å 3
tablero [3][1] Å verdadero
c)
juego [1][8] Å nuevo ficha ()
Recuperación
int x Å matriz [4][4]
si tablero [2][2] entonces
e1
ficha f Å juego [0][0]
Ejemplos:
1. Escribir una función estática que lea los elementos de una matriz de tamaño MAX.
estatica int [][] leeMatriz ()
comienza
matriz Å nuevo int [MAX][MAX]
para i Å 0 hasta MAX-1
para j Å0 hasta MAX-1
comienza
escribe “Dame el elemento “ + i + “,” + j
lee matriz[i][j]
termina
regresa matriz
termina
2. Suma de dos matrices
Las operaciones de suma y resta de matrices están definidas para dos matrices si
tienen el mismo número de:
a) renglones
b) columnas
Pág. 10
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
La suma de dos matrices es la matriz obtenida de sumar pares correspondientes de
elemento, de tal forma que si:
⎛a
⎜ 11
⎜a
A = ⎜ 21
⎜ ...
⎜a
⎝ n1
a
... a ⎞
12
1n ⎟
a
... a ⎟
22
2n ⎟
⎟
a
... a ⎟
n2
nn ⎠
y
b
... b ⎞
⎛b
21
1n ⎟
⎜ 11
b
... b ⎟
⎜b
22
2n ⎟
B = ⎜ 21
...
⎜
⎟
⎜b
b
... b ⎟
n2
nn ⎠
⎝ n1
entonces
a +b
... a + b ⎞
⎛a +b
11
12
12
1n
1n ⎟
⎜ 11
⎜ a 21 + b 21 a22 + b 22 ... a 2n + b 2n ⎟
A +B = ⎜
⎟
...
⎜
⎟
⎜a + b
a +b
... a + b ⎟
⎝ n1
n1
n2
n2
nn
nn ⎠
La diferencia A-B, es la matriz obtenida de restar los elementos de B de los elementos
correspondientes de A:
⎛ a11 − b11
⎜
⎜ a − b 21
A − B = ⎜ 21
...
⎜
⎜a − b
n1
⎝ n1
a12 − b12
a 22 − b 22
a n2 − b n2
... a1n − b1n ⎞
⎟
... a 2n − b 2n ⎟
⎟
⎟
... a nn − b nn ⎟⎠
Una función para sumar matrices podría ser:
estática int [] [] suma (int [][] A, int [][] B )
//Supone que A y B son del mismo tamaño
comienza
int [][] result Å nuevo int [A.longitud][A.longitud]
para i Å 0 hasta A.longitud-1
para j Å 0 hasta A.longitud-1
result [i][j] ÅA[i][j] + B[i][j]
regresa result
termina
Los algoritmos mostrados anteriormente pueden escribirse dentro de una clase en
Java, por ejemplo:
import Teclado;
/**
* Ejemplos de matrices
*/
class EjemploMatrices
{
//No existen datos miembro
/**
* Lee una matriz de tamaño MAX por MAX, donde MAX se pasa como parámetro
*/
static int [][] leeMatriz(int MAX)
Pág. 11
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
{
int [][] matriz = new int [MAX][MAX];
for (int i = 0; i < MAX; i++)
for (int j = 0; j < MAX; j++)
{
System.out.print ("matriz [" + i + "][" + j + "] = " );
matriz[i][j] = Teclado.readInt ();
}
return matriz;
}
/**
* Imprime los valores de una matriz
*/
static void escribe (int [][] matriz)
{
for (int i = 0; i < matriz.length; i++)
{
for (int j = 0; j < matriz.length; j++)
System.out.print (matriz [i][j] + " " );
System.out.println();
}
}
static int [][] suma (int [][] A, int [][] B )
{
int [][] result = new int [A.length][A.length];
for (int i = 0; i < A.length; i++)
for (int j = 0; j < A.length; j++)
result [i][j] = A[i][j] + B[i][j];
return result;
}
//
public static void main (String [] a)
{
EjemploMatrices ejem = new EjemploMatrices ();
//Se crea y se lee una matriz
int [][] A = leeMatriz (2);
//Se crea y se lee otra matriz
int [][] B = leeMatriz (2);
//Se suman A y B dejando el resultado en C
int [][] C = suma (A,B);
System.out.println ("Matriz A");
escribe (A);
System.out.println ("Matriz B");
escribe (B);
System.out.println ("La suma de A y B es");
escribe (C);
}
}
A continuación se muestra una ejecución:
Pág. 12
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Pág. 13
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Cuadrado Mágico
En la pintura Melancholia I del matemático y artista alemán Albrecht Dürer, aparece un
cuadrado mágico
Abajo aparece aislado el cuadrado mágico.
¿Qué propiedades debe tener un cuadrado para ser mágico?
Pág. 14
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
lsuma renglones
suma columnas
suma diagonal
suma antidiagonal
la suma debe ser (n3+n) / 2
clase CuadradoMágico
{
int [] elem
int n
//número de elementos
....
iint sumaDiagonal ()
comienza
int suma Å 0
para i Å1 hasta n
suma Å suma + elem [i][i]
regresa suma
termina
iint sumaAntiDiagonal ()
comienza
int suma Å 0
para i Å1 hasta n
suma Å suma + elem [ ][ ]
regresa suma
termina
...
}
Grado de similaridad:
Pág. 15
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
real gradoSimilaridad (bool [][] original)
comienza
int diferentes Å 0
para i Å0 hasta longitud ()-1
para j Å 0 hasta longitud () –1
si original [i][j] <> letra [i][j] entonces
diferentes ++
regresa (diferentes 100) /(n*n)
termina
Ejercicios:
1. Escribir una clase que lea una cadena (String) y construya un arreglo donde se
almacena la frecuencia de las letras de la cadena
2. Declare una clase que permita representar los siguientes datos de un automóvil:
dueño, marca, modelo y placas. Además, deberá tener 2 funciones constructoras
y una función para escribir los datos del automóvil.
3. Utilizando la clase anterior construya un arreglo de automóviles y escriba una
función que permita determinar cuál marca es la más popular
4. Escriba una función que multiplique dos matrices
5. Escriba una función que determine la suma de todos los elementos de una matriz
Ejercicio de programación (Programa)
Implemente en Java la clase EjemploVector vista en clase.
Implemente en Java una clase que permita determinar si un cuadrado es mágico
Pág. 16
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
3.2 Listas lineales
En la vida diaria frecuentemente se utilizan expresiones como 'lista de alumnos', 'lista de
equipos de football', 'lista de espera', etc., para enumerar elementos en un cierto orden. Así,
por ejemplo, se orden alumnos por su nombre o equipos de football por el número de
partidos que han ganado o personas que esperan recibir un servicio según el orden en que
se presentaron.
Cuando se trata de resolver un problema, por lo general resulta conveniente utilizar un
modelo matemático adecuado para su representación. Matemáticamente, una lista es una
secuencia que contiene cero o más elementos de un mismo tipo. De esta forma:
a1, a2, . . ., an
n >0
indica que una lista está formada por n elementos. Si n=0, se dice que la lista está vacía, es
decir, no contiene elementos. Si n>0, se dice que a1 la cabeza de la lista, es decir, el primer
elemento de la lista. Al último elemento de la lista, an, se le conoce como la cola de la lista.
Las listas poseen la propiedad de que sus elementos se encuentran linealmente ordenados
de acuerdo a su posición (ai está en la posición i de la lista), de esta forma, decimos que ai
precede a ai+1 para i = 1,2,...,n-1 y ai-1 está después de ai-1 para i = 1,2,..,n-1.
El tipo de datos abstracto (TDA) lista
Para formar el tipo de datos abstracto (TDA) lista, se debe definir su conjunto de
operaciones. En este caso como en algunos otros, el conjunto de operaciones
depende de la aplicación con la que se esté trabajando. A continuación se presenta
un conjunto de operaciones que pueden realizarse con el TDA lista.
Función
lista ()
bool vacía ()
void inserta (Objeto x)
Descripción
Crea una lista vacía.
Determina si existen o no elementos en la lista
Inserta en la lista el elemento x en la posición que
conserva el orden de la lista.
void borra (Objeto x)
Elimina el elemento x de la lista
int busca (Objeto x, bool Determina la posición donde se encuentra el elemento x
exito)
dentro de la lista, en caso de que exista; en caso de que
no exista, lo señalara e indica en qué posición debería
estar si existiese. La variable boolena éxito sirve para
determinar cuándo el elemento forma o no parte de la
lista.
int anterior (Nodo p)
Determina la posición que se encuentra antes de la
Pág. 17
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
posición p de la lista.
Determina la posición donde se encuentra el último
elemento de la lista.
int cola ()
La forma en la cual suele representarse el TDA lista en una computadora es por medio de
asignación estática de memoria (representación secuencial), o por medio de asignación
dinámica de memoria (representación ligada). Cada una de ellas tiene ventajas o desventajas
sobre la otra. A continuación se desarrollan para su estudio.
3.2.1 Representación secuencial
Para representar en memoria contigua una lista, se puede utilizar un arreglo, en el que cada
celda sea capaz de almacenar un elemento de la lista, y un índice que señale el número de
elementos almacenados.
La forma de representar una lista en representación secuencial es la siguiente:
clase Lista
{
Objeto info []
int último
Lista ()
bool vacía ()
void insertar (Objeto x)
void borrar (Objeto x)
bool buscar (Objeto x)
}
donde Objeto es el tipo de información con el cual trabajará la lista (entero, real, booleano,
cadena, registro, o bien alguna otra clase).
Suponga que se desea almacenar la lista de alumnos de Estructura de datos (llamada
ListaEst), con tres calificaciones de exámenes parciales y su promedio. Además, se desea
que la lista se encuentre en orden alfabético. En este caso, la clase alumno podría ser:
clase alumno
{
Cad Nombre
real E1, E2, E3
Cad prom
}
//Nombre del alumno
//Calificación de 3 exámenes
//Calificación numérica del promedio de exámenes
de esta forma, la lista se vería como:
Pág. 18
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
fig. 1
1.2.1 Operaciones sobre una lista en representación secuencial
Crear:
Para crear una lista, lo único que debe hacerse es inicializar el índice que señala el número
de registros almacenados. Así, la función constructora por defáult podría ser:
Lista ()
//crea una lista vacía
comienza
último Å 0
info Å nuevo Objeto [MAX]
termina
Vacía:
El procedimiento anterior sirve como guía para escribir la función que determina si una lista
se encuentra vacía:
bool vacía ()
//regresa verdadero si la lista se encuentra vacía, en otro caso regresa falso
comienza
vacía Å último = 0
termina
Insertar:
Para insertar un nuevo estudiante en nuestra lista, primero debe encontrarse la posición en
el arreglo donde colocar el objeto y mover hacia abajo un lugar a todos aquellos objetos que
se encuentren abajo de él para que la lista continúe siendo ordenada. Por ejemplo, si se
incorpora Guillermo en la lista de estudiantes de la fig. 1, ListEst sería:
Pág. 19
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
fig. 2
Note que el problema de mantener una lista ordenada, requiere algo más que un algoritmo
de ordenación.
De manera general, el algoritmo para insertar un elemento x en una lista es el siguiente:
void inserta (Objeto x)
//Inserta el elemento x en la lista de tal forma que el orden persista
comienza
//busca la posición donde debe ir el elemento
p Å busca (x,exito)
si exito entonces
//el elemento se encuentra
error (el elemento se encuentra)
otro
comienza
//el elemento debe insertarse
si ultimo >= Máximo entonces
error (No hay espacio)
otro
comienza
moverAbajo (L,p)
//recorre hacia abajo los elementos
info[p] Å x
//pone la información
termina
termina
termina
A continuación se desarrollan los métodos utilizados para insertar un elemento:
void moverAbajo (int p)
//recorre una posición hacia abajo a todos los elementos que se
//encuentran a partir de la posición p
comienza
último++
//se aumenta el número de elementos
aux Å último +1
Pág. 20
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
mientras aux > p
comienza
info[aux] Å info[aux-1]
//la información se recorre un lugar
//hacia abajo
aux-termina
termina
Buscar:
int busca (Objeto x; var exito:booleano)
//busca el elemento X en la lista y regresa la posición donde se encuentra
//en caso de que el elemento no forme parte de la lista, regresa la posición
//donde debería encontrarse, la búsqueda se realiza de arriba hacia abajo
//con ayuda de un centinela, colocándola una posición debajo de donde
//termina la lista
comienza
iÅ0
//i toma el valor del último índice de la lista
exito Å verdadero
info [MAXIMO] Å x
//variable centinela
mientras info[i] < x
i-si info[i] <> x or i = MAXIMO-1entonces
comienza
exito Å falso
i++
termina
busca Å i
termina
El procedimiento para insertar un nuevo elemento en una lista que se ha desarrollado,
consiste en agregar el elemento en la posición que conserva el orden alfabético o numérico
de sus elementos. Sin embargo, en algunas aplicaciones el orden que debe tomarse en
cuenta puede ser otro, como por ejemplo el orden de llegada.
Borrar:
Considere ahora cómo eliminar un elemento de una lista. Existen dos formas representativas
de hacerlo.
a) La primera consiste en buscar el elemento que se desea eliminar y dejar en blanco la
celda que lo contiene, o bien marcarla como borrada. Por ejemplo, si deseamos eliminar a
Luis, el arreglo de estudiantes mostrado en la fig. 1, quedaría como:
Pág. 21
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
fig. 3
Las tres principales desventajas que se presentan en este tipo de esquema son:
1. Puede ocurrir que la última posición del arreglo esté ocupada y se quiera insertar un
nuevo elemento que es mayor que el valor del elemento que se encuentra en la última celda
del arreglo. En este caso no se puede insertar este valor, aunque existan celdas vacías en el
arreglo. Esto puede solucionarse introduciendo un procedimiento de reorganización o
reacomodo que junte todas las casillas ocupadas, dejando al final aquellas que no contienen
información, cuando sea necesario. Lo cual es costoso.
2. El procedimiento para buscar un elemento deberá ver todas las casillas, aún cuando no
estén ocupadas.
3. El procedimiento para insertar un nuevo elemento deberá modificarse para que cuando la
posición deseada se encuentre vacía lo inserte en esa casilla y no recorra las posiciones hacia
abajo.
b) El segundo esquema que se presenta para borrar un elemento de una lista en
representación secuencial, consiste en buscar la posición donde se encuentra el elemento y
recorrer todos los elementos que están debajo de él una posición hacia arriba. Por ejemplo,
si se desea eliminar a Luis de la lista mostrada en la figura 1, se obtendría:
fig. 4
El algoritmo para eliminar un elemento de una lista dada, que utiliza el segundo esquema, se
Pág. 22
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
muestra a continuación:
void borra (tipoInfo x)
//elimina un elemento de una lista, si es que se encuentra
comienza
p Å busca (x,exito)
//se manda buscar el elemento
si ¬exito entonces
error (el elemento no se encuentra)
otro
comienza
MoverArriba (p)
//se recorren una posición hacia arriba
//todos los elementos a partir de p
último Å último-1
//disminuye el número de elementos
termina
termina
void MoverArriba (int p)
//recorre una posición hacia arriba los elementos que se encuentran a partir
//de la posición p de la lista
comienza
aux Å p
mientras aux < último
comienza
info[aux] Å info[aux+1]
aux ++
termina
termina
Nótese que no es necesario limpiar la última celda que estaba ocupada antes de eliminar un
elemento. Esto se debe a que nunca se tendrá acceso a esa celda de memoria mientras no
se inserte un nuevo elemento, momento en que esa localidad de memoria será reasignada
con un nuevo valor.
3.2.2 Representación ligada
Existen aplicaciones en las cual es el número de elementos cambia dinámicamente durante la
ejecución del algoritmo, (i.e. se realizan inserciones y/o eliminaciones). Es fácil ver que
cuando se inserta o elimina un elemento que se encuentra a mitad de la lista es necesario
mover la mitad de los elementos o peor aun cuando se inserta o elimina al principio de la
lista, donde se necesitan mover todos los elementos; esto implica mucho trabajo, es decir es
costoso.
Otro inconveniente que existe cuando se representa una lista en un arreglo, es que en
algunas ocasiones es muy difícil o imposible predecir el número de elementos que van a
existir. La solución a esto podría ser reservar un espacio de memoria mucho mayor que el
que se considera podría necesitarse. Esta deficiencia aunque es inherente a los arreglos, se
Pág. 23
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
ve reflejada en una ineficiencia en cuando al espacio de memoria requerido.
Como puede observarse las listas en representación secuencial tienen un alto costo tanto en
complejidad temporal como en complejidad espacial. Una solución a este problema es el uso
de la representación ligada o dinámica, donde cada elemento es representado como una
entidad por separado y todos los elementos se conectan a través de referencias.
Listas ligadas
El problema de mantener una lista ordenada en un arreglo, requiere de mover datos
cuando se realizan inserciones y borrados. Estos movimientos podrían eliminarse si
cada elemento indicara que elemento es el que se encuentra después de él. De esta
forma, no es necesario considerar la noción natural que tenemos, de que algo se
encuentra ordendo, si físicamente se encuentra ordenado. Conceptualmente
podemos dibujar una lista de la siguiente forma:
Una lista en representación ligada, es un conjunto de nodos, donde cada uno contiene al
menos dos datos miembros, el primer sirve para guardar la información que se desea y el
segundo sirve para saber quién es el elemento que se encuentra después de él. De esta
forma podemos considerar que cada elemento de la lista un objeto de la siguiente clase:
clase Nodo
{
//datos miembro
Objeto info
Nodo liga
//constructoras
Nodo ()
{
Nodo Å nil
}
Nodo (Objeto datos)
{
info Å datos
liga Å nil
}
Nodo (Objeto datos, Nodo l)
{
info Å datos
liga Å l
}
//Imprime la información del nodo
Pág. 24
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
}
void escribe ()
De tal forma que la clase lista quedaría definida como:
clase Lista
{
//dato miembro
Nodo cabeza
//Función constructora
Lista ()
//funciones miembro
bool vacía ()
bool busca (Objeto o, Nodo pos)
void borrar (Objeto o)
void inserta (Objeto o)
}
1.3.3 Operaciones sobre una lista en representación ligada
Al igual que en la representación secuencial de una lista, las operaciones que se desarrollan,
consideraran que los elementos de la lista se encuentran en orden ascendente.
Crear:
Cuando se desea crear una lista, esta debe de encontrarse vacía. Así:
Lista ()
//construye una lista vacía
comienza
this Å nil
termina
La forma gráfica en que representaremos que una lista está vacía es:
Vacía:
De forma semejante para determinar si una lista está o no vacía:
bool vacía ()
//regresa verdadero si la lista se encuentra vacía, en otro caso
//regresa falso
Pág. 25
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
comienza
vacía Å this = nil
termina
Buscar:
En muchas ocasiones es necesario saber si un elemento se encuentra y la posición que
ocupa en la lista, de esta forma, por medio de una referencia puede conocerse el elemento
que lo contiene; en caso de que el elemento que se busca no se encuentre, se regresa la
posición del elemento que debería ir después de él.
Aprovechando el hecho de que la lista se encuentra ordenada, el algoritmo puede escribirse
como:
Nodo busca(Objeto x, booleano exito)
//busca el objeto x en la lista; en caso de que exista regresa una referencia
//al elemento que lo contiene; en caso contrario, regresa una
//referencia al elemento que debería de ir después de él
comienza
aux Å cabeza
//aux sirve para moverse a través de la lista
//mientras no se termine la lista y la información del nodo sea
//menor que la buscada se avanza al siguiente nodo
mientras aux <> nil & aux.info < x
aux Å aux.liga
éxito Å aux <> nil & aux.info = x
busca Åaux
termina
Insertar:
Cuando se desea insertar un elemento en una lista, lo primero que debe hacer es establecer
si se permitirá o no elementos repetidos. Suponga que no se desea tener elementos
repetidos. Así, lo primero que debe hacerse es determinar si el elemento se encuentra o no.
En caso de que se encuentre, se invoca al procedimiento de error que tomará las acciones
pertinentes, dependiendo de la aplicación. Por otro lado si el elemento no forma parte de la
lista, debe ser incorporarlo a la lista.
Para estudiar cómo insertar un elemento en una lista, se dividirá en los siguientes casos.
a)
b)
c)
d)
La lista en la cual se desea insertar un elemento se encuentra vacía.
El elemento que se desea insertar, deberá ser la cabeza de la lista.
El elemento que se desea insertar, deberá ser la cola de la lista.
El elemento que se desea insertar, deberá ocupar una posición intermedia.
En cada caso, suponga que el elemento no se encuentra, y que esto fue determinado por el
procedimiento busca anterior, que fue invocado como:
Pág. 26
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
posterior Å busca (x,exito)
a) La lista en la cual se desea insertar un elemento se encuentra vacía.
En este caso se debe:
0. Determinar que la lista se encuentra vacía.
1. Crear un nodo cuya información sea la que deseamos insertar y cuya liga apunte a nil, ya
que no existen más elementos.
2. La lista ahora debe apuntar a este nuevo elemento.
Después de insertar el nodo, gráficamente la lista se vería como:
Las acciones enumeradas anteriormente, pueden escribirse en pseudocódigo como:
si vacía () entonces
comienza
Nuevo Å nuevo Nodo (x)
cabeza Å Nuevo
termina
b) El elemento que se desea insertar, deberá ser la cabeza de la lista.
En este caso se debe:
0. Determinar que debe ser la cabeza de la lista.
1. Crear un nodo cuya información sea la que deseamos insertar.
2. La liga del nuevo elemento, deberá apuntar a posterior (i.e., la cabeza actual de la lista.
3. La lista apunta ahora al nuevo elemento.
Considere estas acciones de manera gráfica:
Suponga que se desea insertar el elemento ´b´ en la siguiente lista; después de invocar a
busca, la lista se ve como:
Pág. 27
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Se crea un nodo cuya información sea ´b´:
La liga del nuevo elemento deberá referenciar a la cabeza de la lista:
Se cambia el valor de la cabeza para que apunte al nuevo primer elemento de la lista:
Una forma de escribir esto es pseudocógigo, puede ser la siguiente:
Para determinar que el elemento x debe insertarse como la cabeza de la lista L, puede
preguntarse si el elemento que es referenciado por posterior es el elemento al cual
referencia la lista, así:
si posterior = cabeza entonces
comienza
nuevoNodo Å nuevo Nodo (x)
nuevoNodo.liga Å cabeza
cabeza Å nuevoNodo
termina
c) El elemento que se desea insertar, deberá ser la cola de la lista.
En este caso se debe:
0. Determinar que debe ser la cola de la lista.
1. Crear un nodo cuya información sea la que deseamos insertar
2. La liga del nuevo elemento, deberá apuntar a nil, ya que después de él no hay más
elementos.
3. Determinar el elemento que se encuentra a la izquierda de posterior (nodoAnterior).
4. Cambiar la liga de nodoAnterior, para que ahora apunte al nodo que deseamos insertar.
Pág. 28
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Gráficamente:
Suponga que se desea insertar el elemento ´c´ en la siguiente lista; después de invocar a
busca, la lista se ve como:
Se crea un nodo cuya información es c y cuya liga apunta a nil:
Determinar quién es actualmente la cola de la lista:
por último:
En pseudocódigo:
En este caso después de invocar a busca, posterior tendrá el valor de nil.
Para encontrar quién es la cola de la lista se construye una función que regrese una
referencia al elemento que es la cola de la lista, note que la liga del elemento que es la cola
de la lista siempre apunta a nil:
Pág. 29
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Nodo cola ()
comienza
aux Å cabeza
mientras aux.liga <> nil
aux Å aux.liga
regresa aux
termina
// se recorre la lista
Así, el pseudocódigo para insertar un elemento x como la cola de la lista L:
si posterior = nil entonces
comienza
nuevoNodo Å nuevo Nodo (x)
anterior Å cola ()
anterior.liga Å nuevoNodo
termina
d) El elemento que se desea insertar, deberá ocupar una posición intermedia.
En este caso se debe:
1. Crear un nodo cuya información sea la que se desea insertar.
2. La liga del nuevo elemento, deberá apuntar al nodo posterior (encontrado por el
procedimiento busca).
3. Determinar el elemento que se encuentra a la izquierda de posterior (nodo Anterior).
4. Cambiar la liga del nodoAnterior para que ahora apunte al nodo que se desea insertar.
A continuación se presenta un ejemplo:
Suponga que se desea insertar un nodo cuya información sea "e" en la siguiente lista;
después de invocar a busca, la lista se ve como:
Se crea un nodo cuya información es e y cuya liga apunte a posterior:
Pág. 30
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Se determina quién es nodoAnterior:
Se modifica la liga de nodoAnterior para que apunte a Nuevo:
En pseudocódigo:
comienza
nuevoNodo Å nuevo Nodo (x)
nuevoNodo.liga Å posterior
nodoAnterior Å anterior (posterior)
nodoAnterior.liga Å nuevoNodo
termina
El algoritmo para encontrar el nodo anterior de una cierta posición y el algoritmo para
Pág. 31
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
encontrar la cola de la lista son muy parecidos y se dejan como ejercicio al lector.
Considere ahora un algoritmo que inserte un nodo cuya información es x, en una lista L, en
cualquiera de los casos anteriores. Nótese que:
i) En el caso que el elemento no se encuentre en la lista, siempre se crea un nodo con la
información que se desea incorporar.
ii) Las acciones que se realizan cuando la lista está vacía y cuando el nuevo elemento debe
insertarse como la cabeza de la lista son las mismas.
void inserta (Objeto x)
//Inserta el elemento x en la lista, si no existe en ella, la posición en la
//cual se inserta conserva el orden ascendente de la lista
comienza
posterior Å busca (x,exito)
si exito entonces
error (´El elemento ya se encuentra´)
otro
comienza
nuevoNodo Å nuevo Nodo (x, posterior)
//la lista está vacía o el elemento debe insertarse como la cabeza
si posterior = cabeza entonces
cabeza ÅnuevoNodo
otro
comienza
nodoAnterior Å anterior (posterior)
nodoAnterior.liga Å nuevoNodo
termina
termina
termina
Borrar:
La forma en la cual se elimina un elemento de una lista, es semejante a la inserción; después
de localizar al elemento, si es que este se encuentra pueden considerarse varios casos, en
donde el elemento que se desea eliminar dentro de la lista es:
a) el único
b) la cabeza
c) la cola
d) un nodo intermedio
De esta forma la primera parte del algoritmo sería:
nodo Å busca (x,exito)
si ¬exito entonces
error (el elemento no existe)
Pág. 32
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
otro
a or b or c or d
donde cada caso es mutuamente excluyente.
a) El elemento que se desea eliminar es el único de la lista.
En este caso la lista deberá quedar vacía:
si nodo=cabeza & nodo.liga= nil entonces
cabeza Å nodo.liga
b) El elemento que deseamos eliminar es la cabeza de la lista.
Aquí se debe indicar que la lista empezará en el elemento que se encuentra a la derecha del
elemento a eliminar.
otro
si nodo = cabeza entonces
comienza
cabeza Å nodo.liga
termina
c) El elemento que se desea eliminar es la cola de la lista.
En este caso se debe indicar que el elemento que se encuentra a la izquierda del elemento a
eliminar deberá ser la cola, esto es que su liga referencie a nil.
otro
si nodo.liga = nil entonces
comienza
nodoAnterior Å anterior (nodo)
nodoAnterior.liga Å nil
termina
// equivalente a nodo.liga
d) El elemento que se desea eliminar es un nodo intermedio.
Aquí el elemento que se encuentra a la izquierda del elemento que se desea eliminar deberá
referenciar al elemento que se encuentra a la derecha del que se desea elimina. Así:
otro
comienza
nodoAnterior Å anterior (nodo)
nodoAnterior .liga Å nodo.liga
termina
Ahora se escribirá un algoritmo para eliminar un elemento en una lista, que tome en cuenta
Pág. 33
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
los cuatro casos desarrollados anteriormente, para esto note que:
i) El caso a) y b) son el mismo
ii) El caso c) y d) son el mismo
Con las observaciones anteriores el algoritmo para eliminar un elemento x de una lista es el
siguiente:
void borra (Objeto x)
//elimina el elemento x de la lista , si se encuentra
comienza
nodo Å busca (x,exito)
si ¬exito entonces
error (El elemento no se encuentra)
otro
si nodo = cabeza entonces
cabeza Å nodo. liga
otro
comienza
nodoAnterior Å anterior (nodo)
nodoAnterior.liga Å nodo.liga
termina
termina
Las ventajas de la representación ligada sobre la representación secuencial son:
• aumentan y disminuyen a lo largo de su vida.
• mayor flexibilidad en las operaciones.
Las desventajas son:
• se requiere mayor espacio de memoria para guardar un elemento.
• si se quiere tener acceso al elemento 30, se necesita empezar en la cabeza de la lista
y recorrer 29 referencias, una a una; lo que con arreglos puede hacerse
directamente.
3.2.2 Ejemplos
Pág. 34
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Ejemplo 1: Número de elementos de una lista
Crear un algoritmo que cuente el número de elementos de una lista ligada.
Este problema puede resolverse recorriendo toda la lista, esto es pasando a través de cada
uno de sus elementos e irlos contando, para esto utilizaremos una variable auxiliar de tipo
Nodo (p) que ayude a recorrer la lista y una variable entera (numElem) para contabilizar el
número de elementos que existen en la lista.
int contar ()
comienza
p Å cabeza
numelem Å 0
mientras p <> nil
comienza
numelem++
p Å p.liga
termina
contar Å numelem
termina
//referencia al primer elemento de la lista
//se inicializa el número de elementos
// mientras no se termine la lista
//p avanza al siguiente elemento de la lista
Ejemplo 2: Dividir una lista en dos
Escribir un algoritmo que cree dos listas a partir de una dada, de tal forma que el primero, el
tercero, el quinto, etc., elementos pertenezcan a una lista; y el segundo, el cuarto, el sexto,
etc., pertenezcan a la otra lista.
Por ejemplo, suponga que se tiene la siguiente lista:
El algoritmo creara dos nuevas listas L1 y L2 como se muestra en la siguiente figura:
Pág. 35
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
El algoritmo para resolver este problema surge de manera natural, lo único que hay que
tener en cuenta es cuando la lista contiene un número impar de elementos, ya que en este
caso no agregaremos ningún elemento a la segunda lista.
void divide (Lista L1, Lista L2)
comienza
L1 Å nueva Lista ()
//se crea la lista vacía L1
L2 Å nueva Lista ()
//se crea la lista vacía L2
p1 ÅL1.cabeza
//p1 es la referencia a L1
p2 Å L2.cabeza
//p2 es la referencia a L2
p Å L.cabeza
//p es la referencia a L
mientras p <> nil
//mientras no se termine de recorrer la lista
comienza
//se copia el elemento referenciado por p en L1
nuevoNodo Å nuevo Nodo (p.info, nil)
si p1 = nil entonces
L1.cabeza Å nuevoNodo
otro
p1.liga Å nuevoNodo
p1 Å nuevoNodo
p Å p.liga
//si la lista contiene elementos por copiar se copia el referenciado
//por p en L2
si p <> nil entonces
comienza
nuevoNodo Å nuevo Nodo(p.info, nil)
si p2 = nil entonces
L2.cabeza Å nuevoNodo
otro
p2.liga Å nuevoNodo
p2 Å NuevoNodo
p Å p.liga
termina
termina
termina
Note que las partes donde se inserta el elemento a cada una de las listas es igual, excepto
que se agrega el elemento en distintas listas, esto sugiere la creación de un método, al cual
llamaremos agrega, de tal forma el procedimiento divide, se vería como:
void divide (Lista L1,Lista L2)
Pág. 36
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
comienza
L1 Å nueva Lista ()
//se crea la lista vacía L1
L2 Å nueva Lista ()
//se crea la lista vacía L2
p1 ÅL1.cabeza
//p1 es la referencia a L1
p2 Å L2.cabeza
//p2 es la referencia a L2
p Å L.cabeza
//p es la referencia a L
mientras p <> nil
//mientras no se termine de recorrer la lista
comienza
agrega (p,L1,p1)
si p <> nil entonces
agrega (p,L2,p2)
termina
termina
void agrega (Nodo p, Lista nuevaLista, Nodo colaNL)
//La información que contiene la referencia p es insertada al final de la
// lista nuevaLista
comienza
nuevoNodo Å nuevo Nodo(p.info, nil)
//se crea un nodo
si nuevaLista.vacía () entonces
nuevaLista.cabeza Å nuevoNodo
otro
//si la lista no es vacía se liga con Nuevo
colaNL.liga Å nuevoNodo
colaNL Å nuevoNodo
//el nuevo elemento insertado es ahora
//la cola de Nueva Lista
p Å p.liga
//p referencia al siguiente elemento
termina
Pág. 37
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
3.3 Listas doblemente ligadas
Cuando se trabaja con listas ligadas el acceso al nodo que se encuentra después de cualquier
otro resulta conveniente; pero no así, cuando se trata de accesar al nodo que se encuentra
antes de él. Considere los métodos posterior (Nodo p), que devuelve una referencia al nodo
que se encuentra después del nodo p, y anterior (Nodo p), que devuelve una referencia al
nodo que se encuentra antes del nodo p.
Para acceder a la posición que sigue a p en una lista, basta con acceder a su liga:
Nodo posterior (Nodo p)
comienza
si p = nil entonces
error (Lista vacía)
otro
posterior Å p.liga
termina
Cuando se desea acceder a la posición previa de p es necesario recorrer toda la lista, hasta
una posición antes de p, es decir hasta que la liga del elemento actual sea p:
Nodo anterior (Nodo p)
// Supone que el Nodo p existe dentro de la lista
comienza
aux Å cabeza
ant Å nil
mientras aux <> p
comienza
ant Å aux
aux Å aux.liga
termina
termina
Como puede observarse el trabajo realizado para obtener el nodo anterior es mucho mayor
que el trabajo para obtener el nodo posterior, esto se debe a que se tiene una referencia al
nodo posterior; siguiendo este mismo esquema podría tenerse otra referencia al nodo
anterior. De esta forma un nodo estaría compuesto por tres datos miembros:
• info, para almacenar un objeto
• ligader, referencia al nodo anterior
• ligaizq, referencia al nodo posterior
de forma gráfica:
Pág. 38
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
En una lista doblemente ligada cada nodo no sólo tiene una referencia al siguiente sino
también una referencia al nodo anterior. En forma esquemática una lista doblemente ligada
se vería como:
Como puede observarse, la ventaja de las listas doblemente ligadas es el acceso rápido y
directo tanto al elemento siguiente como al posterior de una posición determinada de la lista.
Por tanto, conviene utilizarlas cuando se necesita recorrer una lista en ambas direcciones.
Aunque las listas doblemente ligadas utilizan más espacio que una lista ligada, sus
operaciones pueden ejecutarse más eficientemente y esto compensa el espacio extra
requerido.
La clase nodo para una lista doblemente ligada es:
clase Nodo
{
Objeto info
Nodo ligader, ligaizq
Nodo (Objeto obj)
{
info Å obj
ligader Å ligaizq Å nil
}
Nodo (Objeto obj, Nodo izq, Nodo der)
{
info Å obj
ligaizq Å izq
ligader Å der
}
void escribe ()
//imprime la información del nodo
}
Operaciones sobre una lista doblemente ligada en
representación ligada
clase ListaDobleLigada
{
Nodo cabeza
ListaDobleLigada ()
Nodo busca (Objeto x, bool éxito)
void inserta (Objeto x)
Pág. 39
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
}
void borra (Objeto x)
Crear:
El procedimiento para crear una lista doblemente ligada es similar a la función constructora
de una lista ligada:
ListaDobleLigada ()
{
cabeza Å nil
}
Vacía:
De forma semejante el método para determinar si una lista doblemente ligada está o no
vacía es:
bool vacía ()
//regresa verdadero si la lista se encuentra vacía, en otro caso regresa falso
comienza
vacía Å cabeza = nil
termina
Buscar:
El algoritmo para buscar un elemento en una lista doblemente ligada es semejante al
algoritmo de búsqueda en una lista ligada, pero aprovechando las ventajas que se tienen,
cuando el elemento que se busca no se encuentra se regresará la posición del elemento que
debería ir antes de él. Por ejemplo, suponga la siguiente lista y que se desea buscar al
elemento 3, el cual no forma parte de la lista:
el método regresará una referencia al nodo cuya información es 2.
Para desarrollar este algoritmo considere que la lista doblemente ligada está ordenada en
forma creciente y que no existen elementos repetidos.
Nodo busca (Objeto x, var bool éxito)
//busca un elemento x en la lista; en caso de que exista, regresa una //referencia al
elemento que lo contiene; en caso contrario, regresa un
Pág. 40
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
//referencia al elemento que debería ir antes que él
comienza
si cabeza = nil entonces
// la lista es vacía
comienza
aux Å nil
éxito Å falso
termina
otro
comienza
aux Å cabeza
mientras aux.ligader <> nil & aux.info < x
aux Å aux.ligader
//se avanza un elemento
éxito Å aux.info = x
si aux.info > x entonces
aux Å aux.ligaizq
termina
regresa aux
termina
Insertar:
El algoritmo para insertar que será desarrollado no permitirá elementos repetidos y
conservará un orden lineal en sus elementos.
El algoritmo para insertar un nodo en una lista doblemente ligada será dividido en los
siguientes casos diferentes:
a)
b)
c)
d)
La lista en la cual se desea insertar el elemento se encuentra vacía
El elemento que se desea insertar deberá ser la cabeza de la lista
El elemento que se desea insertar deberá ser la cola de la lista
El elemento que se desea insertar deberá ocupar una posición intermedia
En cada caso, se supondrá que el elemento no se encuentra y que esto fue determinado por
el método busca, anteriormente desarrollado, que regresa la posición del nodo que debería ir
a la izquierda (llamado ant) del que se desea insertar, esto es:
ant Å busca (x, éxito)
si éxito entonces
error (el elemento se encuentra)
otro
a or b or c or d
donde a, b, c y d son mutuamente excluyentes.
a) La lista en la que se desea insertar se encuentra vacía
En este caso se debe:
Pág. 41
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
0. Determinar que la lista se encuentra vacía
1. Crear un nodo con la información que se desea insertar y cuyas ligas referencien a nil
2. Referenciar la cabeza a este nuevo nodo
Después de insertar el nodo, gráficamente la lista deberá ser:
Las acciones enumeradas anteriormente en forma de pseudocódigo son:
si vacía () entonces
comienza
nodoNuevo Å nuevo Nodo (x)
cabeza Å nodoNuevo
termina
b) El elemento que se desea insertar deberá ser la cabeza de la lista
En este caso se debe:
0. Determinar que debe ser la cabeza de la lista
1. Crear un nodo que contenga al objeto que se desea insertar, su liga izquierda deberá
referenciar a nil y su liga derecha a la cabeza actual de la lista
2. La liga izquierda de la cabeza deberá apuntar al nuevo nodo
3. La cabeza deberá referenciar al nuevo elemento
Suponga que se desea insertar el elemento ‘1’ en la siguiente lista: Las acciones anteriores
de manera gráfica son:
Se crea un nuevo nodo cuya información sea 1, cuya liga izquierda sea nil y cuya liga
derecha referencie a la cabeza de la lista:
Pág. 42
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Se cambia el valor de la liga izquierda de la cabeza para que referencie al nuevo elemento y
finalmente se indica que el nuevo elemento es ahora la cabeza de la lista, esto es:
De manera más formal estas acciones son:
otro
si ant = nil entonces
comienza
nodoNuevo = nuevo Nodo (x, nil, cabeza)
cabeza.ligaIzq Å nodoNuevo
cabeza Å nuevoNodo
termina
c) El elemento que se desea insertar deberá ser la cola de la lista
En este caso se debe:
0. Determinar que el nuevo elemento deberá ser la cola de la lista
1. Crear un nodo cuya información sea la que se desea insertar, su liga derecha deberá
referenciar a nil, ya que después de él no deben existir más elementos, su liga derecha
deberá apuntar al nodo anterior, que en este caso es la cola de la lista actual
2. Cambiar la liga derecha del nodo anterior para que ahora referencie al nodo que se desea
insertar
Gráficamente:
Suponga que se desea insertar un elemento cuya información sea 7 en la siguiente lista;
después de invocar a busca:
Pág. 43
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Se crea un nodo cuya información es 7 y cuya liga derecha referencie a nil.
La liga derecha del nodo anterior deberá referenciar al nuevo elemento, de lo cual se obtiene
la siguiente lista:
En pseudocódigo, las acciones para insertar un elemento como la cola de una lista son:
otro
si ant.ligaDer = nil entonces
comienza
nuevoNodo = nuevo Nodo (x, ant, nil)
ant.ligaDer ÅnuevoNodo
termina
d) El elemento que se desea insertar deberá ocupar una posición intermedia
Este caso es el más general de todos y se deberá:
0. Considerar que no fue ninguno de los casos anteriores
1. Crear un nodo cuya información contenga al objeto que se desea almacenar, su liga
izquierda deberá referenciar al nodo anterior y su liga derecha al elemento que se
encuentra a la derecha del anterior
2. La liga izquierda del nodo que se encuentra a la derecha del anterior deberá referenciar
al nuevo nodo
3. La liga derecha del nodo anterior deberá referenciar al nuevo elemento
A continuación se presenta un ejemplo. Suponga que se desea insertar un 4 en la siguiente
lista:
Pág. 44
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Primero se crea un nodo cuya información sea 4, con liga izquierda a anterior y liga derecha
a la referencia de la liga derecha del anterior:
Por último, la liga izquierda de la liga derecha de anterior deberá referenciar al nuevo nodo y
la liga derecha de anterior a nuevo.
En pseudocódigo:
otro
comienza
nuevoNodo Å nuevo Nodo (x, ant, ant.ligaDer)
anterior.ligaDer.ligaIzq Å nuevoNodo
anterior.ligaDer Å nuevoNodo
termina
Ahora se desarrolla un algoritmo para insertar un elemento en una lista doblemente ligada
de manera general, es decir, tomando en cuenta los cuatro casos desarrollados
anteriormente; considere que:
I.
II.
Pág. 45
siempre que el elemento no forma parte de la lista se crea un nodo cuya información
es la que se desea insertar y cuya liga izquierda referencia al nodo anterior,
determinado por la función busca
Los casos a y b son muy parecidos
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
III.
Los casos c y d son muy parecidos
void inserta (Objeto x)
comienza
ant Å busca (x, éxito)
si éxito entonces
error (el elemento ya existe)
otro
comienza
nuevoNodo Å nuevo Nodo (x, ant, cabeza)
si ant = nil entonces
comienza
si cabeza = nil entonces
cabeza.ligaizq Å nuevoNodo
cabeza Å nuevo Nodo ()
termina
otro
comienza
nuevoNodo.ligaDer Å ant.ligaDer
si ant.ligaDer <> nil entonces
ant.ligaDer.ligaIzq Å nuevoNodo
ant.ligaDer Å nuevoNodo
termina
termina
termina
Borrar:
El algoritmo debe considerar que cuando se desea eliminar un elemento de una lista
doblemente ligada este puede ser:
a)
b)
c)
d)
el único elemento
la cabeza de la lista
la cola de la lista
un nodo intermedio
Para cada uno de estos casos puede suponerse que el elemento forma parte de la lista; lo
cual fue determinado a través del método busca de la siguiente forma:
nodo Å busca (x, éxito)
si Žxito entonces
error (el elemento no se encuentra)
otro
a or b or c or d
A continuación se presenta cada caso:
Pág. 46
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
a) El elemento que se desea eliminar es el único de la lista
En este caso tanto la liga derecha como la izquierda del nodo referencian a nil y la lista
deberá quedar vacía. Así:
si nodo.ligaIzq = nil & nodo.ligaDer = nil entonces
cabeza Å nodo.ligaDer
//que es igual a nil
b) El elemento que se desea eliminar es la cabeza de la lista
Aquí la lista deberá iniciar en el elemento que se encuentra a la derecha del nodo:
otro
si nodo.ligaIzq = nil entonces
comienza
cabeza Å nodo.ligaDer
cabeza.ligaIzq Å nil
termina
c) El elemento que se desea eliminar es la cola de la lista
En este caso la liga derecha del elemento referenciado por la liga izquierda del elemento que
se desea eliminar deberá referenciar a nil, esto es:
otro si nodo.ligaDer = nil entonces
nodo.ligaIzq.ligaDer Å nil
d) El elemento que se desea eliminar es un nodo intermedio
En este caso, la liga derecha que se encuentra a la izquierda del nodo que se desea eliminar
deberá referenciar al elemento de la derecha del nodo que debe eliminarse y, de forma
semejante, la liga izquierda que se encuentra a la derecha del nodo que se desea eliminar
deberá referenciar al elemento de la izquierda del nodo en cuestión, es decir:
si nodo.ligaIzq = nil & nodo.ligaDer = nil entonces
cabeza Å nodo.ligaDer
//que es igual a nil
otro //B
si nodo.ligaIzq = nil entonces
comienza
cabeza Å nodo.ligaDer
cabeza.ligaIzq Å nil
termina
otro si nodo.ligaDer = nil entonces //C
nodo.ligaIzq.ligaDer Å nodo.ligaDer
otro //D
comienza
nodo.ligaIzq.ligaDer Å nodo.ligaDer
nodo.ligaDer.ligaIzq Å nodo.ligaIzq
Pág. 47
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
termina
El algoritmo general para eliminar un nodo en una lista doblemente ligada, resulta ser
elegantemente simple:
void borra (Objeto x)
comienza
nodo Å busca (x, éxito)
si Žxito entonces
error (el elemento no existe)
otro
comienza
si cabeza = nodo entonces
cabeza Å nodo.ligaDer
si nodo.ligaDer <> nil entonces
nodo.ligaDer.ligaIzq Å nodo.ligaIzq
si nodo.ligaIzq <> nil entonces
nodo.ligaIzq.ligaDer Å nodo.ligaDer
termina
termina
Ejemplos:
1. Encontrar el i-ésimo nodo
Encontrar el elemento que ocupa la posición i en una lista doblemente ligada:
Nodo encuentra (int i)
comienza
p Å cabeza
numElem Å 1
mientras numElem < i & p <> nil
comienza
p Å p.ligaIzq
numElem ++
termina
si numElem = i & i <> 0 entonces
encuentra Å p
otro
encuentra Å nil
termina
2. Copiar una lista doblemente ligada
Copiar la información almacenada en una lista doblemente ligada en otra lista doblemente
ligada.
Pág. 48
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
Lista clone ()
//crea una copia de la lista
comienza
copiaL Å nueva Lista ()
//se construye una nueva lista vacía
refL Å cabeza
//referencia a this
refCopiaL Å copiaL.cabeza
mientras refL <> nil
//mientras no se recorra toda la lista
comienza
nuevoNodo Å nuevo Nodo (refL.info, refCopiaL, nil)
si refCopiaL = nil entonces
copiaL.cabeza Å nuevoNodo
nuevoNodo.ligaIzq Å refCopiaL
refCopiaL Å nuevoNodo
refL Å refL.ligaDer
termina
termina
3. Multiplicación de polinomios
Recuerde con un ejemplo cómo se realiza la multiplicación de polinomios
6x6
7
18x8
18x8
6x6
6x
7
+ 6x
6
+ 6x
+ 24x5
+ 24x5
+ 8x4
+ 12x4
+ 20x4
+ 8x3
X
+ 8x3
+ 4x3
+ 12x4
+ 18x4
+ 4x2
3x2
+ 4x2
+ 2x2
+ 3x2
+ 9x2
+ 2x
+x
+ 2x
+x
+1
+1
+1
+ 3x
+1
Como puede observarse, para realizar la multiplicación de dos polinomios, se toma el primer
término de uno de los polinomios y se multiplica por todos los términos del otro polinomio,
después se toma el segundo término del primer polinomio y se multiplica nuevamente por
todos los términos del segundo polinomio y así sucesivamente hasta que se llega al último
término del primer polinomio, el cual nuevamente se multiplica por cada uno de los términos
del segundo polinimio. Del ejemplo puede verse que se obtiene un nuevo polinomio cada vez
que se multiplica un nuevo término.
Un polinomio puede representarse a través de una lista.
Ejercicios
Escribir un algoritmo para:
1.
2.
3.
4.
Intercalar dos listas doblemente ligadas ordenadas
Ordenar una lista ligada desordenada en forma creciente
Ordenar una lista doblemente ligada desordenada en forma creciente
Copiar una lista doblemente ligada
Pág. 49
ISC Gregorio García Estrada
Listas Lineales
Estructura de Datos
5. Dividir una lista doblemente ligada en dos
6. Restar dos polinomios almacenados en listas ligadas
7. Intercambiar los elementos de las posiciones p y siguiente de p en una lista:
a) ligada
b) doblemente ligada
8. Suprimir todas las apariciones del elemento x en una lista:
a) ligada
b) doblemente ligada
9. Escribir una lista ligada en orden ascendente
10. Escribir una lista ligada en orden descendente
11. Borrar el mayor valor de una lista desordenada de números enteros
12. Determinar si dos listas son iguales
13. Intercalar dos listas ordenadas dejando el resultado en una tercera lista
14. Intercalar dos listas ordenadas dejando el resultado en una de ellas
15. De al menos dos aplicaciones en las que es conveniente usar listas ligadas
16. De al menos dos aplicaciones en las que es conveniente usar listas doblemente ligadas
Pág. 50
ISC Gregorio García Estrada

Documentos relacionados