3. listas · un arreglo es un objeto por lo que para crear el espacio se ... de almacenamiento...

50
Listas Lineales Estructura de Datos Pág. 1 ISC Gregorio García Estrada 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.

Upload: dangtuong

Post on 19-Sep-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

Listas Lineales Estructura de Datos

Pág. 1 ISC Gregorio García Estrada

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.

Listas Lineales Estructura de Datos

Pág. 2 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 3 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 4 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 5 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 6 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 7 ISC Gregorio García Estrada

*/ 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 ()); } }

Listas Lineales Estructura de Datos

Pág. 8 ISC Gregorio García Estrada

La salida producida es:

Listas Lineales Estructura de Datos

Pág. 9 ISC Gregorio García Estrada

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.

Listas Lineales Estructura de Datos

Pág. 10 ISC Gregorio García Estrada

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 Almacenamiento Recuperación a) matriz [2][3] 3 int x matriz [4][4] b) tablero [3][1] verdadero si tablero [2][2] entonces

e1 c) juego [1][8] nuevo ficha () 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

Listas Lineales Estructura de Datos

Pág. 11 ISC Gregorio García Estrada

La suma de dos matrices es la matriz obtenida de sumar pares correspondientes de elemento, de tal forma que si:

⎟⎟⎟⎟⎟

⎜⎜⎜⎜⎜

=

⎟⎟⎟⎟⎟

⎜⎜⎜⎜⎜

=

nnb...n2bn1b...

2nb...22b21b1nb...21b11b

B y

nna...n2an1a...

2na...22a21a1na...12a11a

A

entonces

⎟⎟⎟⎟⎟

⎜⎜⎜⎜⎜

+++

+++

+++

=+

nnbnna...n2bn2an1bn1a...

2nb2na...22b22a21b21a1nb1na...12b12a11b11a

BA

La diferencia A-B, es la matriz obtenida de restar los elementos de B de los elementos correspondientes de A:

⎟⎟⎟⎟⎟

⎜⎜⎜⎜⎜

−−−

−−−−−−

=−

nnnnn2n2n1n1

2n2n22222121

1n1n12121111

ba...baba...

ba...bababa...baba

BA

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)

Listas Lineales Estructura de Datos

Pág. 12 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 13 ISC Gregorio García Estrada

Listas Lineales Estructura de Datos

Pág. 14 ISC Gregorio García Estrada

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?

Listas Lineales Estructura de Datos

Pág. 15 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 16 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 17 ISC Gregorio García Estrada

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 Descripciónlista () Crea una lista vacía.bool vacía () Determina si existen o no elementos en la lista void inserta (Objeto x) 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 exito)

Determina la posición donde se encuentra el elemento x 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

Listas Lineales Estructura de Datos

Pág. 18 ISC Gregorio García Estrada

posición p de la lista. int cola () Determina la posición donde se encuentra el último

elemento de la lista. 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 //Nombre del alumno real E1, E2, E3 //Calificación de 3 exámenes Cad prom //Calificación numérica del promedio de exámenes } de esta forma, la lista se vería como:

Listas Lineales Estructura de Datos

Pág. 19 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 20 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 21 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 22 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 23 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 24 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 25 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 26 ISC Gregorio García Estrada

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) La lista en la cual se desea insertar un elemento se encuentra vacía. b) El elemento que se desea insertar, deberá ser la cabeza de la lista. c) El elemento que se desea insertar, deberá ser la cola de la lista. d) 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:

Listas Lineales Estructura de Datos

Pág. 27 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 28 ISC Gregorio García Estrada

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.

Listas Lineales Estructura de Datos

Pág. 29 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 30 ISC Gregorio García Estrada

Nodo cola () comienza aux cabeza mientras aux.liga <> nil aux aux.liga // se recorre la lista regresa aux termina 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:

Listas Lineales Estructura de Datos

Pág. 31 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 32 ISC Gregorio García Estrada

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)

Listas Lineales Estructura de Datos

Pág. 33 ISC Gregorio García Estrada

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 // equivalente a nodo.liga termina 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

Listas Lineales Estructura de Datos

Pág. 34 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 35 ISC Gregorio García Estrada

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 //referencia al primer elemento de la lista numelem 0 //se inicializa el número de elementos mientras p <> nil // mientras no se termine la lista comienza numelem++ p p.liga //p avanza al siguiente elemento de la lista termina contar numelem termina 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:

Listas Lineales Estructura de Datos

Pág. 36 ISC Gregorio García Estrada

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)

Listas Lineales Estructura de Datos

Pág. 37 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 38 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 39 ISC Gregorio García Estrada

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)

Listas Lineales Estructura de Datos

Pág. 40 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 41 ISC Gregorio García Estrada

//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) La lista en la cual se desea insertar el elemento se encuentra vacía b) El elemento que se desea insertar deberá ser la cabeza de la lista c) El elemento que se desea insertar deberá ser la cola de la lista d) 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:

Listas Lineales Estructura de Datos

Pág. 42 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 43 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 44 ISC Gregorio García Estrada

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:

Listas Lineales Estructura de Datos

Pág. 45 ISC Gregorio García Estrada

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

II. Los casos a y b son muy parecidos

Listas Lineales Estructura de Datos

Pág. 46 ISC Gregorio García Estrada

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) el único elemento b) la cabeza de la lista c) la cola de la lista d) 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:

Listas Lineales Estructura de Datos

Pág. 47 ISC Gregorio García Estrada

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

Listas Lineales Estructura de Datos

Pág. 48 ISC Gregorio García Estrada

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.

Listas Lineales Estructura de Datos

Pág. 49 ISC Gregorio García Estrada

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 + 8x3 + 4x2 + 2x + 1 X 3x2 + x + 1 6x6 + 8x3 + 4x2 + 2x + 1 6x7 + 8x4 + 4x3 + 2x2 + x

18x8 + 24x5 + 12x4 + 12x4 + 3x2 18x8 + 6x7 + 6x6 + 24x5 + 20x4 + 18x4 + 9x2 + 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. Intercalar dos listas doblemente ligadas ordenadas 2. Ordenar una lista ligada desordenada en forma creciente 3. Ordenar una lista doblemente ligada desordenada en forma creciente 4. Copiar una lista doblemente ligada

Listas Lineales Estructura de Datos

Pág. 50 ISC Gregorio García Estrada

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