Estructura de Datos: Lista. Facultad Ingeniería y Tecnología Informática Tecnicatura en Programación de Computadoras. Autora: Prof.

Documentos relacionados
APUNTADORES. Un apuntador es un objeto que apunta a otro objeto. Es decir, una variable cuyo valor es la dirección de memoria de otra variable.

Estructura de datos Colas

Este material es de uso exclusivo para estudio, los textos fueron tomados textualmente de varios libros por lo que está prohibida su impresión y

Elementos de un programa en C

Universidad Autónoma del Estado de México 2016, Año del 60 Aniversario de la Universidad Autónoma del Estado de México

Tema 13: Apuntadores en C

TEMA 8: Gestión dinámica de memoria

Capitulo V Listas Enlazadas

Clases e instancias. Algoritmos y Estructuras de Datos I. Clases e instancias. memoria dinámica.

Unidad Nº V Listas Enlazadas

PRÁCTICA No. 9 RECORRIDOS EN ÁRBOLES BINARIOS

Tema 5 Tabla de Símbolos

Listas. Programación en C LISTAS. Prof. Álvaro Sánchez Miralles. Programación en C. Listas. Objetivos

A l g o r i t m o y E s t r u c t u r a d e D a t o s Ing. en Sistemas de Información - 1º año -

Procesadores de lenguaje Tema 6 La tabla de símbolos

Clase adicional 9. Listas enlazadas. Temas. Listas enlazadas Árboles Problemas de la clase adicional Ejercicios de diseño

Tabla de Símbolos. Programación II Margarita Álvarez

Unidad I: Introducción a las estructuras de datos

Tabla de Símbolos. Programación II Margarita Álvarez

Estructura de Datos. Árboles Binarios de Búsqueda ABB. Primer Semestre, 2010

Apuntadores en C y C++

TEMA 2. EL LENGUAJE C. ELEMENTOS BÁSICOS

TADs en C. Matías Bordese Algoritmos y Estructuras de Datos II - Laboratorio 2013

Curso de Programación en C. Licenciatura, FCQeI. APUNTADORES.

Tema 6: Memoria dinámica

Estructura de Datos. TDA: Listas. Primer Semestre, Indice. Tipos de estructura de datos lineales

REPRESENTACIÓN DE DATOS

2.2 Nombres, Ligado y Ámbito

Instituto Politécnico Nacional

TIPO DE DATO ABSTRACTO (TDA)

1.1 Tipos de Datos Primitivos. 1.2 Tipos de datos estructurados. 1.3 Definición de estructura de datos

INTRODUCCIÓN. Estructura de Datos Tipos Abstractos de Datos (TAD S) Profs. Lorna Figueroa M. Mauricio Solar F. UTFSM 1 / 2008

Tema: Tipos Abstractos de Datos (TAD s) en C++.

Tema 2. Memoria Dinámica. 2.1 Datos estáticos y dinámicos

Capítulo. Listas, pilas y colas en C. Contenido. Introducción

TEMA 7: Ficheros. TEMA 7: Ficheros Concepto de fichero

Memoria Dinámica. Jornadas de Marzo 2010 Grupo de Usuarios de Linux Tania Pérez

Organización de Computadoras

Algoritmos y Programación I

Apuntadores (Punteros)

Árboles balanceados (AVL) Tablas de dispersión (Hash) Colas de prioridad (Heap)

UNIDAD 9. DATOS COMPLEJOS PILAS

Estructura de Datos. Unidad de Aprendizaje: Unidad de Competencia II: Estructuras de Datos Lineales. M. en C. Edith Cristina Herrera Luna

ESTRUCTURAS DINÁMICAS DE DATOS (LISTAS)

Estructuras de Datos Dinámicas Contenido del Tema

Lección 2 Introducción al lenguaje C

Unidad Didáctica 2. Elementos básicos del lenguaje Java Tipos, declaraciones, expresiones y asignaciones

Programación en Lenguaje C

En el siguiente ejemplo se declara un registro y luego variables de ese tipo de registro:

Programación Estructurada

TAD: Pila. TALLER: TAD Pila

TAD CONJUNTOS Y MULTICONJUNTOS

2^10 2^9 2^8 2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0 SUMA

Informática General 2016 Cátedra: Valeria Drelichman, Pedro Paleo, Leonardo Nadel, Norma Morales

ANEXO XVII DE LA RESOLUCION N

Laboratorio de Arquitectura de Redes. Punteros en lenguaje C

Examen Fundamentos de Programación 15 de enero de 2016 Curso 2015/16

Funciones Definición de función

Carlos Montenegro. Programación Orientada a Objetos Proyecto Curricular de Ingeniería de Sistemas

Programación 1. Diseño modular de nuevos tipos de datos en un programa. Diseño modular de un tipo de dato con estructura de conjunto

Programación estructurada (Introducción a lenguaje C)

Todo programa en 'C' consta de una o más funciones, una de las cuales se llama main.

Programación Básica Estructuras de Datos y Funciones en C

Al concluir está práctica el estudiante será capaz de elaborar programas utilizando apuntadores y arreglos.

Introducción rápida a la programación (estructurada ) con C++

UNIVERSIDAD DE LOS ANDES NUCLEO UNIVERSITARIO RAFAEL RANGEL (NURR) DEPARTAMENTO DE FISICA Y MATEMATICA AREA COMPUTACION TRUJILLO EDO.

El método main de la clase PruebaArbol, empieza creando una instancia de un objeto Árbol vacío y asigna su referencia a la variable árbol

Programación. Test Autoevaluación Tema 3

Programación Estructurada

Los elementos de una lista unidireccional o secuencia, llamados nodos, constan de dos partes:

Para la Implementación Dinámica de listas: Se usa la asignación dinámica de memoria mediante punteros o referencias.

Diagramas De Casos De Uso

FUNDAMENTOS DE INFORMÁTICA

DEFINICION. Ing. M.Sc. Fulbia Torres Asignatura: Estructuras de Datos Barquisimeto 2006

El programa que permite el manejo de la base de datos tiene la siguiente funcionalidad:

Árboles. Cursos Propedéuticos Dr. René Cumplido M. en C. Luis Rodríguez Flores

Unidad V Análisis Semántico. M.C. Juan Carlos Olivares Rojas

Tecnólogo Informático- Estructuras de Datos y Algoritmos- 2009

En este artículo vamos a conocer los tipos de datos que podemos manejar programando en C.

III. Generación de Código orientado a objetos

Estructuras de Datos Declaraciones Tipos de Datos

PRÁCTICA Nº 5: 2 sesiones (del 11 de Abril al 17 de Abril de 2003) Listas con punto de interés para la composición de melodías musicales

Laboratorio Informática

LECCION N 08 ARBOLES. Un árbol es un grafo A que tiene un único nodo llamado raíz que:

Estructuras de datos: Pilas, Colas, Listas

Desde los programas más simples escritos en un lenguaje de programación suelen realizar tres tareas en forma secuencial.

El TAD Grafo. El TAD Grafo

Lenguaje de programación C. Introducción

Programación Básica Apuntadores y Archivos en C

OBJETIVOS. Explicar el manejo de archivos de texto. Explicar el manejo de archivos binarios. Desarrollar programas que usen archivos.

Definición de Memoria

Variables. Una variable no es más que un nombre simbólico que identifica una dirección de memoria: vs.

Métodos que devuelven valor Dado el siguiente triángulo rectángulo:

ASIGNATURA: ESTRUCTURA DE DATOS II

SISTEMAS INFORMÁTICOS PROGRAMACION I - Contenidos Analíticos Ing. Alejandro Guzmán M. TEMA 2. Diseño de Algoritmos

Guía práctica de estudio 04. Almacenamiento en tiempo de ejecución

Lenguaje C Estructuras y Variables definidas por el Usuario.

Estructura de Datos: Archivos

TEMA 4. ESTRUCTURAS DE CONTROL

Administración de Archivos

Transcripción:

Facultad Ingeniería y Tecnología Informática Tecnicatura en Programación de Computadoras Estructura de Datos: Lista Autora: Lunes a viernes de 9 a 21 h. Torre Universitaria, Zabala 1837, primer nivel inferior. C1426DQG - CABA Teléfono: 4788-5400, internos 5002 y 2122. Email: fasciculos@ub.edu.ar www.ub.edu.ar 004300

"La informática tiene que ver con los ordenadores lo mismo que la astronomía con los telescopios" Edsger W. Dijkstra Tipos Abstractos de Datos Lista dinámica Introducción Las estructuras de datos son necesarias para almacenar grandes cantidades de datos de una forma organizada para que el procesamiento y recuperación de la información sea lo más óptima posible. La estructura de datos básica por excelencia es la variable, pero la dificultad que encontramos es que almacena un dato por vez, si quisiéramos almacenar mucha más información simultáneamente, necesitaríamos definir en nuestro programa muchas variables, cada una con un nombre distinto, haciendo confusa sino imposible el procesamiento y recuperación posterior de la información. Los lenguajes de alto nivel poseen estructuras de datos "primitivas" como los arreglos para poder almacenar muchos datos simultáneamente. En un arreglo los datos se almacenan en secuencia en el mismo bloque de memoria y se distinguen por la posición física que ocupan, pero también, los lenguajes nos permiten "armar" estructuras de datos más complejas donde cada dato se almacena individualmente y se enlaza con el resto de la información mediante direcciones de memoria. Las estructuras de datos complejas, para que sean fáciles de manipular se deben de implementar formando lo que se denomina un Tipo Abstracto de Datos que encapsula la representación en memoria del dato y el conjunto de funciones para manipular la información de manera tal que con este conjunto de funciones se defina una biblioteca de operaciones que se pueden aplicar a los datos como si fueran funciones pertenecientes al lenguaje. Un Tipo Abstracto de Datos permite destacar la funcionalidad o características de la estructura de datos y ocultar al usuario la forma en la que se representa en memoria. Dentro de los tipos abstractos de datos encontramos la Lista. Vamos a comenzar a introducir el concepto de Lista como estructura de datos. Clasificación de las Estructuras de Datos Las estructuras de datos pueden ser de dos tipos según sea el tipo de almacenamiento que 1

se use: estáticas, cuyo ejemplo básico es el arreglo y dinámicas cuyo ejemplo clásico es la lista dinámica. En definitiva es esencial saber que tanto un arreglo como una lista organizan la información de forma secuencial, esto quiere decir que cada elemento tiene un antecesor y un predecesor a ecepción del primero que no tiene antecesor y el último que no tiene sucesor. Una lista almacenada en un arreglo estático tiene una desventaja muy importante que es que la cantidad de datos máxima que podrá contener debe quedar fijada antes de su uso. Cuando un programa usa arreglos debe estar sujeto a futuras modificaciones para mofidicar el tamaño de los arreglos adecuándolos al incremento o decremento del volumen de datos a procesar, pero también puede suceder que se reserve más memoria de la que se necesita y por lo tanto no se use todo el espacio reservado con lo cual estamos desperdiciando memoria que podría ser usada por otros procesos. Una estructura de datos almacenada en forma dinámica ajusta su tamaño a las necesidades del momento, crecerá o disminuirá su tamaño a medida que el programa se ejecute dependiendo del volumen de datos. Una estructura así se denomina Lista Dinámica. Una lista implementada en forma dinámica adapta su tamaño a la cantidad de datos a procesar haciendo que el programa no se deba modificar si cambia el volumen de datos. Esto se traduce en que cuando se ingresa un nuevo dato se solicita memoria, trámite que lleva a cabo el compilador del lenguaje en combinación con el sistema operativo. La solicitud de memoria se realiza en tiempo de ejecución por eso este tipo de implementación se denomina dinámica. Listas Una lista es una estructura secuencial <d 1, d 2, d 3,..., d n >, donde n indica la cantidad de elementos de la lista, si n = 0 se dice que la lista está vacía y se denota por <>. El primer elemento es d 1 y el último elemento es d n. Cada elemento de la lista tiene un elemento predecesor y un elemento sucesor con excepción del primero y último, d 1 no tiene elemento anterior a él y d n no tiene un elemento posterior a él. Dado d i con 1 < i < n, tiene como predecesor a d i-1 y como sucesor a d i+1. Las listas se pueden almacenar en memoria de diferentes formas, las dos formas básicas son la estática y la dinámica. Ya mencionamos la desventanja en tener un almacenamiento estático, por lo tanto, en este apunte vamos a tratar el almacenamiento dinámico. Vale la pena aclarar que en el comienzo de la computación, cuando los lenguajes de alto nivel eran muy pocos y se desconocía el almacenamiento dinámico, la única forma de representar una lista era a través de arreglos. Listas Dinámicas Las listas dinámicas se clasifican en listas simplemente encadenadas y listas doblemente encadenadas. En ambos casos el acceso a cada uno de los elementos es secuencial, esto 2

significa que para acceder a un elemento determinado, para llegar a él hay que acceder a todos los elementos anteriores a él, es decir que si queremos acceder a di debemos pasar por d 1, d 2, d 3,..., d i-1. Una lista simplemente encadenada, enlaza cada uno de los elementos usando un puntero mientras que una lista doblemente encadenada usa dos punteros, ambos casos se pueden simular usando arreglos pero en este apunte vamos a usar punteros. Lista Simplemente Encadenada Una lista está compuesta de elementos enlazados entre sí a través de direcciones de memoria que se denomina nodo. Cada nodo de una lista consta de dos partes: un dato, di, y un puntero al elemento siguiente, pi, dando por resultado el par [di,pi], con 1 <= i <= n. El último nodo tiene la característica que pi representa una dirección nula pues no hay un elemento siguiente. La figura siguiente representa una lista simplemente encadenada: Un ejemplo de lista se muestra en la siguiente figura: 3

En nuestra notación, la lista de la figura anterior es: <[2,2184],[4,3225],[6,5571],[7,NULL]> donde, el primer elemento está almacenado en la dirección 2003 y estamos suponiendo que el par [dato, puntero] ocupa 2 bytes. En la figura anterior se puede apreciar que los enlaces son hacia adelante, hacia el último elemento, este tipo de encadenamiento no permite retroceder. TAD: Lista Una lista como tipo abstracto de dato es una caja negra. Esto significa que tanto la forma en la cual representemos los datos en memoria y las funciones que se definan sobre esa representación quedan encapsuladas formando una unidad. Esta forma de trabajar con las estructuras de datos permite escribir programas más óptimos permitiendo que el mantenimiento del mismo sea mucho más fácil. Cuando debamos modificar la representación de los datos para actualizarla a las necesidades del momento sabremos en qué unidad está y lo mismo sucede con el conjunto de funciones definidas. Las funciones hacen las operaciones definidas sobre la lista. Las operaciones que se pueden realizar son: insertar un elemento nuevo modificar un elemento dado eliminar un elemento dado buscar un elemento imprimir los elementos de la lista esvacia (<>) Existe otro conjunto de operaciones básicas sobre un elemento de la lista: obtener el dato obtener el puntero al siguiente asignar un dato asignar un puntero Para completar la abstracción hacen falta dos datos más que son la dirección del primer elemento de la lista y el elemento que está siendo procesado en un determinado momento al que se denomina elemento actual. Todas las operaciones salvo algunas puntuales se hacen a través de el elemento actual, entonces podemos tener que insertar o eliminar el elemento anterior al actual o el elemento posterior al actual, obtener el dato del elemento actual, modificar el valor del elemento actual. 4

Implementación en Lenguaje C Para llevar la abstracción a la implementación en lenguaje C debemos hacer uso del tipo struct. Usaremos el struct para encapsular los datos del nodo y de la lista. Vamos a definir el nodo sólo con dos variables miembro, el dato y el puntero al nodo siguiente, mientras que la lista estará definida por la dirección del primer nodo, que es la única dirección realmente necesaria para acceder a todos los elmentos de la lista y el elemento actual, pero podemos agregar más información útil como por ejemplo la longitud de la lista, mantener este dato hace que sea más fácil determinar si la lista está vacía, longitud igual a 0, y además nos evita contar los elementos de la lista cada vez que es necesario conocer su longitud. Para llevar a cabo la implementación vamos a hacer uso de lo que se denomina Nodo Cabecera, este nodo no llevará información útil en sí mismo sino que se lo utilizará de comodín para que ciertas operaciones sean más fáciles de implementar. Por ejemplo para poder insertar un elemento nuevo en una lista se deben tener en cuenta las siguientes posibilidades: insertar en una lista vacía insertar delante del primer elemento insertar entre dos elementos cualesquiera de la lista insertar después del último Al tener una lista con un elemento cabecera nunca va a estar vacía físicamente, siempre habrá un elemento que es el nodo cabecera por lo tanto nunca tendremos que insertar en una lista vacía. Otro elemento que vamos a incorporar es el Nodo Centinela para facilitar la operación de eliminación cuando debemos eliminar el nodo último. Todas las operaciones se realizarán sobre el elemento actual salvo algunas excepciones. Vamos a tener en cuenta que los nodos centinela y cabecera permiten considerar que la lista no está vacía desde el punto de vista físico, pero la lista está vacía desde el punto de vista lógico porque aún no contiene información, la información por la cual estamos construyendo la lista. TDA Nodo #ifndef _nodo #define _nodo typedef struct nodo #endif tdato dato; struct nodo * prox; Nodo; 5

TDA Lista #ifndef ListaSimple #define ListaSimple #include <stdio.h> #include <stdlib.h> #include "nodo.h" typedef Nodo * Actual; typedef struct Nodo * com; Nodo * actual; int longitud; Lista; void crearlista (Lista * l) Nodo * p = (Nodo *) malloc (sizeof(nodo)); Nodo * q = (Nodo *) malloc (sizeof(nodo)); p->prox = q; q->prox = NULL; l->longitud = 0; l->com = p; l->actual = p; //cambiar el contenido del actual void setdato (Lista * l, tdato t) l->actual->dato = t; //obtener el dato del actual tdato veractual (Lista *l) return l->actual->dato; //verificar el estado actual de la lista int eslistavacia (Lista *l) return l->longitud == 0; //nodo cabecera //nodo centinela //el nodo cabecera apunta al nodo centinela cuando la //lista está vacía //el primer nodo es el nodo cabecera //el elemento actual es el nodo cabecera cuando la // lista está vacía //verificar si el elemento actual es el primero de la lista lógica int esprimero (Lista * l) return l->actual == l->com->prox; //asigna al elemento actual la dirección del primer elemento lógico void primero (Lista * l) if (!esprimero(l)) l->actual = l->com->prox; //verifica que el elemento actual apunte al nodo centinela int esultimo (Lista * l) return l->actual->prox == NULL; 6

//avanza al siguiente elemento de la lista a partir del elemento actual void siguiente (Lista * l) if (!esultimo(l)) l->actual = l->actual->prox; //recorre la lista mostrando la información almacenada en cada nodo void mostrarlista (Lista * l) primero(l); while(!esultimo(l)) mostrardato(veractual(l)); siguiente(l); printf("\n"); //inserta el dato d después del elemento actual void insertardespues (Lista * l, tdato d) Nodo * p = (Nodo *) malloc (sizeof(nodo)); //mostrardato corresponde a la //abstracción de la //información almacenada en el nodo //pide memoria para el nuevo // dato p->dato = d; //actualiza los punteros para que el nodo p quede enlazado entre el actual y // el próximo p->prox = l->actual->prox; l->actual->prox = p; l->actual = p; (l->longitud)++; //incrementa en 1 el tamaño de la lista //inserta el nuevo dato d antes del elemento actual void insertarantes (Lista *l, tdato d) Nodo * p ; if (l->longitud > 0) p = (Nodo *) malloc (sizeof(nodo)); p->prox = l->actual->prox; l->actual->prox = p; p->dato = l->actual->dato; l->actual->dato = d; else insertardespues(l, d); //si la lista está vacía inserta después del //nodo cabecera (l->longitud)++; //elimina el nodo actual void eliminaractual (Lista *l) Nodo * p = l->actual->prox; (l->longitud)--; //actualiza la longitud de la lista decrementando en 1 // el campo longitud l->actual->dato = l->actual->prox->dato; l->actual->prox = l->actual->prox->prox; free(p); 7

//recorre la lista buscando el dato d retornando verdadero o falso int buscardato (Lista *l, tdato d) tdato e; int encontrado = 0; if (!eslistavacia(l)) primero (l); while (!esultimo(l)) e = veractual(l); if (compara(e, d) == 0) encontrado = 1; break; siguiente (l); return encontrado; //obtiene la dirección del elemento actual Actual getactual(lista * l) return l->actual; //la función compara es necesaria //dependiendo de la complejidad del // dato almacenado en la lista //obtiene la dirección del siguiente dato al actual en la lista Actual getsiguiente (Lista * l) return l->actual->prox; //obtiene la longitud de la lista int getlongitud (Lista *l) return l->longitud; //el elemento actual se ubica en el k-ésimo elemento de la lista void posicionar (Lista *l, int k) int i=1; if (l->longitud > = k) primero (l); while (i<k) siguiente (l); i++; //sitúa el elemento actual en el anterior a él void anterior (Lista *l) Nodo * p = l->actual; primero(l); while (getsiguiente(l)!= p) siguiente(l); 8

//convierte la lista en un archivo almacenando los datos en un archivo void listaaarchivo (Lista *l, char *arch) tdato e; FILE *fp; if((fp=fopen(arch,"wb"))==null) printf("\n\n Error, no se puede abrir el archivo: %s\n",arch); exit(0); primero(l); while(!esultimo(l)) e=veractual(l); fwrite(&e,sizeof(tdato),1,fp); siguiente(l); fclose(fp); //vacía la lista, eliminando cada nodo de ella sin eliminar los nodos cabecera y centinela void vaciarlista(lista *l) int i; primero(l); while (!esultimo(l)) eliminaractual(l); l->actual = l->com; //Crea una lista a partir de los datos almacenados en un archivo void archivoalista(lista *l, char *arch) tdato e; FILE *fp; vaciarlista(l); if((fp=fopen(arch,"rb"))==null) printf("\n\n Error, no existe el archivo: %s\n",arch); exit(0); while(fread(&e,sizeof(tdato),1,fp)==1) insertardespues(l,e); fclose(fp); //destruye la lista vaciándola y destruyendo el nodo cabecera y el nodo centinela void destruir(lista *l) vaciarlista(l); free(l->com->prox); free(l->com); 9

//crea una lista cuyos datos están ordenados de menor a mayor void insertarenorden(lista *l, tdato d) if(eslistavacia(l)) insertardespues(l,d); else primero(l); while(!esultimo(l)) if(compara(d,veractual(l))>0) siguiente(l); else break; insertarantes(l,d); //elimina el elemento de la posición k void eliminarpos (Lista *l, int k) posicionar(l, k); eliminaractual(l); //inserta un nuevo dato en la posición k void insertaren (Lista *l, tdato d, int k) posicionar(l, k-1); insertardespues(l, d); #endif El hombre tiene mil planes para sí mismo. El azar, sólo uno para cada uno. Mencio o Meng Zi, filósofo chino confucciano 10

Facultad Ingeniería y Tecnología Informática Tecnicatura en Programación de Computadoras Estructura de Datos: Lista Autora: Lunes a viernes de 9 a 21 h. Torre Universitaria, Zabala 1837, primer nivel inferior. C1426DQG - CABA Teléfono: 4788-5400, internos 5002 y 2122. Email: fasciculos@ub.edu.ar www.ub.edu.ar 004300