Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II...

14
Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo V Listas Enlazadas 5.1. Introducción a las Estructuras de Datos Dinámica Hasta ahora, todos los tipos de datos que se han visto, ya sean simples o estructurados, tienen una propiedad común: son estáticos. Esto significa que las variables que se declaran en un programa de alguno de estos tipos mantendrán la misma estructura durante la ejecución del mismo. Son variables estáticas y se definen en tiempo de compilación. Ejemplo 1: Si se declara un Vector de 5 elementos de tipo int, éste podrá cambiar su contenido, pero no su estructura. Hay muchas situaciones en las que se desea cambiar el tamaño de las estructuras usadas. La técnica usada para manejar estas situaciones es la asignación dinámica de memoria. Con este tipo de asignación se tendrán variables dinámicas o referenciadas, que pueden crearse y destruirse en tiempo de ejecución. Ejemplo 2: Si se pide diseñar un programa para gestionar una agenda de teléfonos con los datos de personas (nombre, apellidos, dirección, cumpleaños, teléfono, email, etc...). ¿Qué estructura puede utilizarse para realizarla? Muere lentamente, quien abandona un proyecto antes de iniciarlo, no preguntando de un asunto que desconoce o no respondiendo cuando le indagan sobre algo que sabe. Evitemos la muerte en suaves cuotas, recordando siempre que estar vivo exige un esfuerzo mucho mayor que el simple hecho de respirar. Pablo Neruda

Transcript of Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II...

Page 1: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

Capitulo V Listas Enlazadas

5.1. Introducción a las Estructuras de Datos Dinámica

Hasta ahora, todos los tipos de datos que se han visto, ya sean simples o estructurados, tienen una propiedad común: son estáticos. Esto significa que las variables que se declaran en un programa de alguno de estos tipos mantendrán la misma estructura durante la ejecución del mismo. Son variables estáticas y se definen en tiempo de compilación.

Ejemplo 1:

Si se declara un Vector de 5 elementos de tipo int, éste podrá cambiar su contenido, pero no su estructura.

Hay muchas situaciones en las que se desea cambiar el tamaño de las estructuras usadas.

La técnica usada para manejar estas situaciones es la asignación dinámica de memoria. Con este tipo de asignación se tendrán variables dinámicas o referenciadas, que pueden crearse y destruirse en tiempo de ejecución.

Ejemplo 2:

Si se pide diseñar un programa para gestionar una agenda de teléfonos con los datos de personas (nombre, apellidos, dirección, cumpleaños, teléfono, email, etc...). ¿Qué estructura puede utilizarse para realizarla?

Muere lentamente, quien abandona un proyecto antes de iniciarlo, no preguntando de un asunto que desconoce

o no respondiendo cuando le indagan sobre algo que sabe.

Evitemos la muerte en suaves cuotas, recordando siempre que estar vivo exige un esfuerzo mucho mayor

que el simple hecho de respirar. Pablo Neruda

Page 2: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 66 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

Una respuesta inmediata pareciera ser la de usar un array (vector o matriz), de la siguiente forma:

Si se usa un arreglo de dimensión MAX, pueden surgir los siguientes problemas: ¿Qué pasa si se quieren insertar más personas de MAX en la agenda? Hay que recompilar el programa para ampliar MAX Si el número de personas de la agenda es mucho menor que MAX se está

desperdiciando memoria que podría ser utilizada por otros programas En muchos otros casos no es posible conocer anteladamente el numero de “elementos” que se van a usar. Ejemplo 3: Bases de datos para almacenar los datos de los estudiantes de la universidad Elementos de un dibujo de pantalla, cuyo tamaño depende del dibujo trazado

5.2. Mecanismos Para Enlazar Información

a. LISTAS o Listas simplemente enlazadas

Solo hay un enlace por nodo Solo se puede recorrer en una dirección

o Listas doblemente enlazadas o Lista circular simplemente enlazada o Lista circular doblemente enlazada

b. ÁRBOLES Y GRAFOS

#define MAX 50 Struct Persona { char nombre[10]; char apellidos[10]; char dirección[15]; char email[20] }; Persona per[MAX];

Page 3: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 67 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

5.3. El Tipo Puntero. Siempre que se habla de punteros hay que diferenciar claramente entre lo que es:

• Variable referencia o apuntadora (o simplemente puntero) • Variable referenciada o apuntada.

Una variable de tipo Puntero (variable referencia) contendrá una referencia, es decir la dirección en memoria de una variable de un tipo determinado (variable referenciada). Por tanto, siempre existirá asociado al tipo Puntero, otro tipo que es el de la variable referenciada. En C la definición será:

Tipo *TipoP tipo de la variable referenciada. Un puntero es una representación simbólica de una dirección de memoria, es decir, contiene la dirección de un objeto o variable. También se define como una variable que contiene una dirección de memoria. Un puntero apunta a una posición en memoria Operadores * y & & permite devolver la dirección de memoria donde se encuentra almacenada el valor de una variable & Contiene la dirección o posición de memoria en la cual se ha almacenado una variable. El operador & es unario, es decir, tiene un solo operando, y devuelve la dirección de memoria de dicho operando. Supongamos el siguiente ejemplo: Para int x = 4; se tiene * (operador de indirección) Representa el contenido de una dirección de memoria del mismo tipo que ha sido declarada.

Page 4: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 68 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

El operador * es unuario y toma a su operando como una dirección de memoria, entonces el operador accesa al contenido de esa dirección. Por ejemplo, suponga las variables declaradas anteriormente y la asignación, Declaración de puntero: Ejemplo 4:

• int *p; // declaración de puntero a una variable entera • flota *pf; // declaración de puntero a una variable real

Ejemplo 5: int *p; // Declaración puntero a una variable entera int y; // Declaración de variable de tipo entera p=&x; // a p se le asigna la dirección de almacenamiento de la variable y=*p; // se asigna a y el contenido donde apunta p Note que solamente un a puntero se le puede asignar una dirección de memoria. Gráficamente tenemos: Ejemplo 6:

En un momento dado, la variable p será un puntero, o lo que es lo mismo, contendrá la dirección en memoria de una variable de tipo entero, que a su vez, contendrá un entero.

Para acceder a la variable apuntada (referenciada) hay que hacerlo a través de la variable puntero, ya que aquella no tiene nombre (por esto, también se le denomina variable anónima).

TipoDato *NombrePuntero;

Page 5: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 69 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

NOTA Para intercambiar los valores de los parámetros actuales, las funciones deben recibir la dirección de memoria donde se encuentran las variables.

main() { TipoDato a,b; ...... ....... funcion(&a,&b); /*Traspaso de direcciones de las variables*/ ....... ....... } funcion( TipoDato *x, TipoDato *y) /*Los parámetros apuntan a las direcciones */ { ............. }

Ejemplo :

void intercambia(int *x, int *y) { int temp; temp=*x; *x=*y; *y=temp; }

EJERCICIOS: PROGRAMAS DE PUNTEROS

// Programa PUNTEROS. Uso de & y * #include <iostream.h> void main (void ) { int a; // a es un entero int *P; // P es un puntero a un entero a = 7; P = &a; // P asignado a la dirección de a cout << "La dirección de a es " << &a << endl

<< "\n El valor de P es " << P << endl << endl; cout << "El valor de a es " << a << endl << "El valor de *P es " << *P << endl << endl; cout << "Demostrando que * y & son complementos de " << "cada uno. " << endl << "&*P = " << &*P << endl << "*&P = " << *&P <<endl; return 0; }

Page 6: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 70 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

// Programa PUNTEROS. Uso de & y * #include <iostream.h> main ( ) { int a; // a es un entero int *aPtr; // aPtr es un apuntador a un entero a = 7; aPtr = &a; // aPtr asignado a la dirección de a cout << "La dirección de a es " << &a << endl

<< "El valor de aPtr es " << aPtr << endl << endl; cout << "El valor de a es " << a << endl << "El valor de *aPtr es " << *aPtr << endl << endl; cout << "Demostrando que * y & son complementos de " << "cada uno. " << endl << "&*aPtr = " << &*aPtr << endl << "*&aPtr = " << *&aPtr <<endl; return 0; }

Las listas enlazadas son estructura de datos naturales donde el número de elementos que la integran no es conocido, o en su mejor

caso, la cantidad de los mismos puede variar.

// Programa Nº2 PUNTEROS.suma de dos números A y B y resultado en C #include <iostream.h> #include<conio.h> void main (void) { int A, B, C; // A, B y C son enteros int *P1, *P2; // P1 y P2 son punteros a enteros A = 4; B = 6; P1 = &A; // a P1 se le asigna la dirección de A. P2 = &B; // a P2 se le asigna la dirección de B. C = (*P1) + (*P2); Cout<<”el resultado de la suma de “<< *P1 <<”+”<<*P2<<” = “<<C; getch(); }

Page 7: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 71 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

Info enlace

Características:

Las listas son una secuencia ordenada de elementos llamados nodo

Toda lista tiene una cabecera y una cola. Todos los nodos de la lista son del mismo tipo. Los nodo pueden ser agregados o suprimidos en cualquier momento en la lista

• Una lista enlazada es una estructura de datos en la que los objetos están ubicados

linealmente. • En lugar de índices de arreglo aquí se emplean punteros para agrupar linealmente

los elementos. • La lista enlazada permite implementar todas las operaciones de un conjunto

dinámico. • Si el predecesor de un elemento es NULL, se trata de la cabeza de la lista. • Si el sucesor de un elemento es NULL, se trata de la cola de la lista. • Cuando la cabeza es NULL, la lista está vacía o no existe la lista

Gráficamente una lista simplemente enlazada se representa de la siguiente forma:

NOTA Las listas almacenadas en arreglos están sujetos al tamaño del arreglo para su crecimiento. Si estos están vacíos se esta ocupando memoria sin uso. Además las listas reflejan el orden lógico y el orden físico de los elementos en el arreglo.

Toda lista cuenta con un encabezado y nodos.

Nodo Cabeza de la Lista

Ultimo elemento de la Lista

P

23 12 4

Dirección de memoria

Page 8: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 72 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

Una lista enlazada es una secuencia ordenada de elementos llamados nodos

5.4. Tipo De Dato Abstracto Nodo

La implementación de un nodo podría ser de la siguiente forma:

5.5. Punteros La construcción y manipulación de una lista enlazada requiere el acceso a los nodos de la lista a través de uno o más punteros a nodos. Normalmente, un programa incluye un puntero al primer nodo (cabeza) y un puntero al último nodo (cola). De cualquier forma el último elemento de la lista contiene un valor de 0, esto es, un puntero nulo (NULL) que señala el final de la lista.

5.5.1. Operador “->”

El nodo puede contener objetos de cualquier tipo.

struct Nodo { int info; nodo *sig; }; typedef Nodo *LISTA;

Todo nodo contará con un apuntador hacia el

siguiente nodo. Valor que almacenará

el nodo.

Un NODO es una estructura compuesta básicamente de dos partes: • Un campo de información, en cual se almacenan datos o

estructuras • Un campo de dirección, en el cual se almacena la dirección

del nodo siguiente.

Page 9: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 73 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

Si p es un puntero a una estructura y m es un elemento de esa estructura entonces p->m accede al miembro m de la estructura puntada por p

5.6. Operaciones Basicas Sobre Listas Enlazadas

Existen cinco operaciones básicas que se pueden hacer sobre listas enlazadas:

• Creación: de una lista vacía • Vacía: Retorna verdadero si la lista L está vacía y falso en caso contrario. • Inserción: Agregar un elemento al final de la lista. • Acceso: Examinar el primer elemento de la lista. • Eliminar: Se puede quitar el primer elemento de la lista, o remover todos los

elementos de la misma. • Buscar: ver si la lista contiene un elemento determinado. • Anula: Permite convertir la lista en una lista vacía • Imprime Lista: Imprime los elementos de la lista L

Creación de una lista vacía A continuación implementamos el procedimiento para crear una lista vacía.

Inserción

a) Inserción al Final de la lista

En el siguiente segmento de código se ejemplifica solamente el caso en el que el elemento se adiciona al final de la lista tal y como si se tratase de una estructura Pila o Cola.

void CREAR_LISTA (LISTA *L) { *L = NULL; cout<<”\n lista creada…”; }

Page 10: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 74 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

void Inserta_Final (LISTA *p, int elem) {LISTA Q, T; T = new Nodo; T -> Info = elem; T -> sig = NULL; if(*p != NULL) { Q = *p; while ( Q-> sig != NULL) Q = Q -> sig; Q -> sig = T; } else { *p = T; } }

b) Inserción al Final de la lista

En el siguiente segmento de código se implementa el caso en el que el nuevo nodo se inserta en la cabeza de la lista:

Para el siguiente ejemplo se ilustra el proceso de adición de un nuevo nodo p = Inserta_Inicio(&p,10);

Recorrido hasta el final de la lista

Se realiza el enlace al final de la lista

Se crea un nuevo nodo asociado al puntero T

void Inserta_Inicio(LISTA *p, int elem) { LISTA Q; Q = new Nodo; Q - > Info = elem; Q - > sig = *p; *p = Q; }

Page 11: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 75 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

Recorrido de una Lista: Para mostrar los elementos de la lista se realiza el recorrido de la misma, a partir del nodo cabeza de la lista hasta encontrar el último nodo. Recordemos que el último nodo tiene la propiedad que su miembro SIG es igual a NULL.

Eliminar el nodo del inicio de la Lista La siguiente función recibe una lista y devuelve esa misma lista, sin el nodo que ocupaba inicialmente la posición de la cabeza. La función será la siguiente:

void Recorrido (LISTA p) { LISTA Q; Q = p; while ( Q != NULL) { cout<<” \n “<< Q - > Info; Q= Q-> sig; } }

Page 12: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 76 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

Eliminar el nodo del Final de la Lista

A continuación realizamos la implementación del programa completo de listas simples:

void Elimina_Inicio( LISTA *P) { LISTA Q; Q = *P; if ( Q->sig !=NULL) *P = Q -> sig; delete Q; *P = NULL; }

void Elimina_Final( LISTA *P) { LISTA Q, T ; Q = *P; if ( Q->sig !=NULL) { while (Q->sig != NULL) { T = Q; Q = Q-> sig;

} T->sig = NULL;

} else *p=NULL; delete Q; }

Page 13: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 77 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

#include<iostream.h> #include<conio.h> struct Nodo { int info; nodo *sig; }; typedef Nodo *LISTA; void CREAR_LISTA (LISTA *L) { *L = NULL; cout<<”\n lista creada…”; } void Inserta_Inicio(LISTA *p, int elem) { LISTA Q; Q = new Nodo; Q - > Info = elem; Q - > sig = *p; *p = Q; } void Recorrido (LISTA p) { LISTA Q; Q = p; while ( Q != NULL) { cout<<” \n “<< Q - > Info; Q= Q-> sig; } } void Elimina_Inicio( LISTA *P) { LISTA Q; Q = *P; if ( Q->sig !=NULL) *P = Q -> sig; delete Q; *P = NULL; }

void Elimina_Final( LISTA *P) { LISTA Q, T ; Q = *P; if ( Q->sig !=NULL) { while (Q->sig != NULL) { T = Q; Q = Q-> sig;

} T->sig = NULL;

} else *p=NULL; delete Q; } //PROGRAMA PRINCIPAL void main (void) { LISTA A ; int n , e; CREAR_LISTA(&A) ; clrscr() ;

cout<< « cuantos elementos desea ingresar a la lista « ;

for (int i = 1; i<=n; i++) { cout<<”ingrese elemento”; cin>>e; Inserta_Inicio(&A, e); } Recorrido (A); getch(); }

Page 14: Capitulo V Listas Enlazadasvirtual.usalesiana.edu.bo/web/conte/archivos/284.pdf · Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capitulo

Programación II 78 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia

EJERCICIO Escribir un procedimiento para concatenar dos listas L1 y L2. L2 debe estar concatenado al final de L1.

EJERCICIOS

1. Escriba un programa que permita adicionar elementos no repetidos a una lista. Es decir antes de insertar un determinado elemento se debe verificar antes que éste no se encuentre en la lista.

2. Escriba las subrutinas que permitan adicionar elementos a una lista de forma

ordenada 3. Escriba un programa que permita eliminar el elemento de la cola de una lista

4. Escriba un programa que permita contar los elementos de una lista

void CONCATENA (LISTA L1, LISTA L2) { LISTA Q; Q = L1; while ( Q - >sig != NULL) Q = Q->sig; Q->sig = L2; Recorrido (L1) ; }