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



Documentos relacionados
En cualquier caso, tampoco es demasiado importante el significado de la "B", si es que lo tiene, lo interesante realmente es el algoritmo.

Tema: Arreglos de Objetos en C++.

Tema: Sobrecarga de Operadores.

Estructuras Dinámicas

TEMA 5. CONTROL DE FLUJO DEL PROGRAMA. Sentencia Instrucción Expresión Operadores + Operandos Sintaxis: Sentencia ;

Arreglos. // Incluir E/S y Librerías Standard #include <stdlib.h> #include <stdio.h>

Introducción al tipo de dato ARRAY

Contenidos. Gestión dinámica de memoria. Gestión dinámica de memoria. Introducción. 1. Introducción 2. El operador NEW 3. El operador DELETE

Introducción a la programación orientada a objetos

INTRODUCCIÓN AL TIPO COMPUESTO CADENA CONTENIDOS

Ejercicio 1 (2 puntos. Tiempo: 25 minutos)

Tema 4: Estructuras de Control Estructura y Contenidos

Capítulo 6. Introducción a la POO

Objetivos de la práctica: - Practicar uso de ficheros: abrir, cerrar y tratamiento de información contenida en el fichero.

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

ISTP CIDET COMPUTACION E INFORMATICA ARREGLOS EN JAVA

Organización de Computadoras

Modulo 1 El lenguaje Java

Informática FACULTAD DE FÍSICAS

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

Estructuras de repetición

Curso 0 de Informática

CASO PRÁCTICO DISTRIBUCIÓN DE COSTES

Un puntero no es más que una variable estática cuyo contenido es una dirección de memoria.

Práctica 2: Simón dice

TIPOS DE DATOS DEFINIDOS POR EL PROGRAMADOR: Estructuras CONTENIDOS

OPERADORES LÓGICOS Y DE COMPARACIÓN EN PHP. PRIORIDADES. EJEMPLOS. EJERCICIOS RESUELTOS. (CU00818B)

1. Ejemplo de clase : La clase Cuenta 2. Uso de la clase Cuenta. 3. Métodos y objetos receptores de mensajes (Importante)

Se guardan en archivos con extencion c y los cabezales con extension h

Contenidos. Funciones (suplemento) Funciones. Justificación del uso de Funciones

Clases y Objetos. Informática II Ingeniería Electrónica

A25. Informática aplicada a la gestión Curso 2005/2006 Excel Tema 7. Funciones avanzadas de Excel II

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

Esta extensión está obsoleta a partir de PHP 5.5.0, y será eliminada en el futuro

Examen de Fundamentos de sistemas distribuidos

Capítulo 4 Procesos con estructuras de repetición

Punteros. Definición Un puntero es un dato que contiene una dirección de memoria.

Figura 1 Abrir nueva hoja de cálculo

MANUAL DE USUARIO DE LA HERAMIENTA CONFIGURACION DE PRESUPUESTOS PARA DISTRIBUIDORES

TEMA 3: EL LENGUAJE C: PRESENTACIÓN

MANUAL DEL PROGRAMA DE ASESORAMIENTO (Asesores) Navegador y limpiar caché/cookies...2 Acceso al programa de Asesoramiento... 7

Escuela Politécnica Superior de Ingeniería Departamento de Ingeniería Informática

LEER Y ESCRIBIR ARCHIVOS O FICHEROS EN C. FOPEN, FCLOSE, MODOS DE ACCESO READ, WRITE Y APPEND (CU00536F)

Vectores. 27/05/05 Programación Digital I 1

Módulo II - PowerPoint

República Bolivariana de Venezuela Aldea Universitaria Liceo Fray Pedro de Agreda. Lenguaje C++ Contadores y Acumuladores

Memoria compartida y semáforos r/w. La página del manual que podría servir para describir estas funciones es la siguiente:

Instructivo de Microsoft Excel 2003

INSTITUTO TECNOLOGICO de la laguna Programación Orientada a Objetos en C++

Introduccion al Lenguaje C. Omar Andrés Zapata Mesa Grupo de Fenomenología de Interacciones Fundamentales, (Gfif) Universidad de Antioquia

LABORATORIO Nº 2 GUÍA PARA REALIZAR FORMULAS EN EXCEL

Programación 1. Tema II. Diseño de programas elementales. Lección 7. Diseño modular y descendente de programas

Qué son los monomios?

Un kilobyte (KB) son 1024 bytes, un Megabyte (MB) son 1024 KB, un Gigabyte son 1024 Mb

EDWIN KÄMMERER ORCASITA INGENIERO ELECTRÓNICO

EL MODELO DE ESTRATIFICACIÓN POR CAPAS DE TCP/IP DE INTERNET

Tema 2: La clase string

Centro de Capacitación en Informática

Capítulo 6. Asociaciones y Declaraciones.

Para crear formularios se utiliza la barra de herramientas Formulario, que se activa a través del comando Ver barra de herramientas.

INSTITUTO POLITECNICO NACIONAL. ESCUELA SUPEIRIOR DE INGENIERIA MECANICA Y ELECTRICA. UNIDAD CULHUACAN. INTEGRANTES: FLORES ACOLTZI ONESIMO

SOLUCION EXAMEN junio 2006

Programa Presupuestos de Sevillana de Informática.

Índice Introducción Números Polinomios Funciones y su Representación. Curso 0: Matemáticas y sus Aplicaciones Tema 1. Números, Polinomios y Funciones

SESION El comando Integrate 2. Aproximación de integrales definidas 3. Integración de funciones racionales

11. Algunas clases estándar de Java (II)

3.2 Operaciones aritmético-lógicas en Pascal

Teclado sobre una PDA para Personas con Parálisis Cerebral

Estructuras de Datos Dinámicas. Diseñar y programar en lenguaje C soluciones utilizando estructuras de datos dinámicas

LENGUAJE. Tema 1 - Introducción

Microsoft Office XP Excel XP (I)

INVENTARIO INTRODUCCIÓN RESUMEN DE PASOS

CÓMO CREAR NUESTRO CATÁLOGO

Elementos de Microsoft Word

LABORATORIO 1 OPERACIONES DE ENTRADA Y SALIDA

8.1 Un primer bucle FOR Varias formas de utilizar el bucle FOR Calcular el factorial de un número mediante un bucle FOR...

Programación: QBASIC

Manual Time One Software control de horarios

Prof. Dr. Paul Bustamante

Microsoft Word Los formatos son las características que le asignamos a cualquier carácter, a un conjunto de caracteres o a otros elementos.

Sistemas Operativos Práctica 4

INSTITUTO POLITÉCNICO NACIONAL ESCUELA SUPERIOR DE INGENIERÍA MECÁNICA Y ELÉCTRICA UNIDAD CULHUACÁN INTEGRANTES

Tema 6. Reutilización de código. Programación Programación - Tema 6: Reutilización de código

Tema : ELECTRÓNICA DIGITAL

HERRAMIENTAS DE ACCESS ACCESS Manual de Referencia para usuarios. Salomón Ccance CCANCE WEBSITE

FOCO GESTIÓN DE GRUPOS

Funciones en Excel (II)

TEMA 8: ESTRUCTURAS DE DATOS COMPLEJAS 1 VECTORES ESTÁTICOS 2 2 CADENAS O VECTORES DE CARACTERES 6 3 PASO DE CADENAS Y ARRAYS A FUNCIONES 8

Sistemas de numeración y aritmética binaria

Operación de Microsoft Excel. Guía del Usuario Página 79. Centro de Capacitación en Informática

Vamos a ver los principales conceptos básicos que debemos

MATERIAL 2 EXCEL 2007

Objetivos. El alumno conocerá los elementos indispensables de un ambiente de programación y será capaz de realizar programas básicos en lenguaje C.

Prof. Dr. Paul Bustamante

Tema: FUNCIONES, PROCEDIMIENTOS Y RECURSIVIDAD.

Manual de usuario para Android de la aplicación PORTAFIRMAS MÓVIL

Herencia. 3.- Herencia. Declaración de una clase derivada en Delphi. Jerarquía de clases

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

Lección 1-Introducción a los Polinomios y Suma y Resta de Polinomios. Dra. Noemí L. Ruiz Limardo 2009

Estimado usuario. Tabla de Contenidos

Transcripción:

Tema 2 Memoria Dinámica 2.1 Datos estáticos y dinámicos Datos estáticos: su tamaño y forma es constante durante la ejecución de un programa y por tanto se determinan en tiempo de compilación. El ejemplo típico son los arrays. Tienen el problema de que hay que dimensionar la estructura de antemano, lo que puede conllevar desperdicio o falta de memoria. Datos dinámicos: su tamaño y forma es variable (o puede serlo) a lo largo de un programa, por lo que se crean y destruyen en tiempo de ejecución. Esto permite dimensionar la estructura de datos de una forma precisa: se va asignando memoria en tiempo de ejecución según se va necesitando. Cuando el sistema operativo carga un programa para ejecutarlo y lo convierte en proceso, le asigna cuatro partes lógicas en memoria principal: texto, datos (estáticos), pila y una zona libre. Esta zona libre (o heap) es la que va a contener los datos dinámicos, la cual, a su vez, en cada instante de la ejecución tendrá partes asignadas a los mismos y partes libres que fragmentarán esta zona, siendo posible que se agote si no se liberan las partes utilizadas ya inservibles. (La pila también varía su tamaño dinámicamente, pero la gestiona el sistema operativo, no el programador): Para trabajar con datos dinámicos necesitamos dos cosas: 1. Subprogramas predefinidos en el lenguaje que nos permitan gestionar la memoria de forma dinámica (asignación y liberación). 2. Algún tipo de dato con el que podamos acceder a esos datos dinámicos (ya que con los tipos vistos hasta ahora sólo podemos acceder a datos con un tamaño y forma ya determinados). 1

E.T.S.I. Telecomunicación Laboratorio de Programación 2 Texto del programa Datos estáticos zona dinámica fragmentada límite de la zona estática puntero de pila Pila límite de la pila 2.2 Tipo puntero Las variables de tipo puntero son las que nos permiten referenciar datos dinámicos. Tenemos que diferenciar claramente entre: 1. la variable referencia o apuntadora, de tipo puntero; 2. la variable anónima referenciada o apuntada, de cualquier tipo, tipo que estará asociado siempre al puntero. Físicamente, un puntero no es más que una dirección de memoria. En el siguiente ejemplo se muestra el contenido de la memoria con un puntero que apunta a la dirección 78AC (16, la cual contiene 6677 (16 : puntero contenido 7881 (16 78AA (16 78AB (16 78AC (16 78AD (16 78AE (16 78AC (16 AACC (16 6743 (16 6677 (16 AACC (16 6743 (16 Antes de definir los punteros, vamos a recordar cómo puede darse nombre a tipos de datos propios utilizando la palabra reservada typedef. El uso de typedef (definición de tipo) en C++ permite definir un nombre par un tipo de datos en C++. La declaración de typedef es similar a la declaración de una variable. La forma es: typedef tipo nuevo-tipo;. Ejemplos: 2

Memoria Dinámica typedef char LETRA; LETRA caracter; typedef enum luces_semaforo {Rojo, Amarillo, Verde estado_luces; estado_luces semaforo; typedef int vector_de_20[20]; vector_de_20 mivector; Introduciremos las declaraciones de tipo antes de las declaraciones de variables en el código. Definiremos un tipo puntero con el carácter asterisco (*) y especificando siempre el tipo de la variable referenciada. Ejemplo: typedef int *PtrInt; PtrInt p; // puntero a enteros // puntero a enteros O bien directamente: int *p; // puntero a enteros Cuando p esté apuntando a un entero de valor -13, gráficamente lo representaremos así: p -13 Para acceder a la variable apuntada hay que hacerlo a través de la variable puntero, ya que aquélla no tiene nombre (por eso es anónima). La forma de denotarla es *p. En el ejemplo *p = -13 (y p = dirección de memoria de la celda con el valor -13, dirección que no necesitamos tratar directamente). El tipo registro o estructura en C++: struct. Una declaración de estructura define una variable estructura e indica una secuencia de nombres de variables -denominadas miembros de la estructura o campos del registro- que pueden tener tipos diferentes. La forma básica es: struct identificador_del_registro { tipo identificador_1; tipo identificador_2;... 3

E.T.S.I. Telecomunicación Laboratorio de Programación 2 tipo identificador_3; Ejemplo: struct resistencias { char fabricante[20]; int cantidad; float precio_unitario; // Fabricante de la resistencia // Numero de resistencias // Precio de cada resistencia El acceso a los miembros de la estructura se realiza mediante el operador punto (.). Ejemplo: resistencias.cantidad=20; gets(resistencias.fabricante); if (resistencias.precio_unitario==50.0) Podemos crear una plantilla de estructura para más tarde declarar variables de ese tipo. Ejemplo: struct registro_resistencias { char fabricante[20]; // Fabricante de la resistencia int cantidad; // Numero de resistencias float precio_unitario; // Precio de cada resistencia registro_resistencias resistencias; Por último, podemos usar typedef: typedef struct unaresistencia { char fabricante[20]; // Fabricante de la resistencia int cantidad; // Numero de resistencias float precio_unitario; // Precio de cada resistencia registro_resistencias; registro_resistencias resistencias; Ejemplo de punteros a estructuras: struct TipoRegistro { int num; 4

Memoria Dinámica char car; typedef TipoRegistro *TipoPuntero; TipoPuntero p; Así: p es la dirección de un registro con dos campos (tipo puntero) *p es un registro con dos campos (tipo registro) (*p).num es una variable simple (tipo entero) p->num es una variable simple (tipo entero) (*p).car es una variable simple (tipo carácter) p->car es una variable simple (tipo carácter) & x es la dirección de una variable x, siendo x, por ejemplo int x;. Gráficamente: p -13 A Si deseamos que una variable de tipo puntero en un momento determinado de la ejecución del programa no apunte a nada, le asignaremos la macro NULL (p = NULL) y gráficamente lo representaremos: p 2.3 Gestión de la memoria dinámica Cuando declaramos una variable de tipo puntero, por ejemplo int *p; estamos creando la variable p, y se le reservará memoria -estática- en tiempo de compilación; pero la variable referenciada o anónima no se crea. En este momento tenemos: 5

E.T.S.I. Telecomunicación Laboratorio de Programación 2 p (memoria estática) basura posición de memoria indefinida basura La variable anónima debemos crearla después mediante una llamada a un procedimiento de asignación de memoria -dinámica- predefinido. El operador new asigna un bloque de memoria que es el tamaño del tipo del dato apuntado por el puntero. El dato u objeto dato puede ser un int, un float, una estructura, un array o, en general, cualquier otro tipo de dato. El operador new devuelve un puntero, que es la dirección del bloque asignado de memoria. El formato del operador new es: puntero = new nombretipo (inicializado opcional); Así: p = new int; donde p es una variable de tipo puntero a entero. En tiempo de ejecución, después de la llamada a este operador, tendremos ya la memoria (dinámica) reservada pero sin inicializar: p memoria dinámica basura tamaño Para saber el tamaño necesario en bytes que ocupa una variable de un determinado tipo, dispondremos también de una función predefinida: sizeof(tipo) que nos devuelve el número de bytes que necesita una variable del tipo de datos Tipo. Podemos usar este operador con datos más complejos: struct TipoRegistro { int num; char car; typedef TipoRegistro *TipoPuntero; TipoPuntero p = new TipoPuntero; Tras usar la memoria asignada a un puntero, hay que liberarla para poder utilizarla con otros fines y no dejar datos inservibles en el mapa de la memoria dinámica. Se libera la memoria asignada a un puntero p, cuyo tipo ocupa t bytes, también dinámicamente, mediante el operador delete: delete p;. 6

Memoria Dinámica 2.4 Operaciones con punteros Al definir una variable de tipo puntero, implícitamente podemos realizar las siguientes operaciones con ella: 1. Acceso a la variable anónima 2. Asignación 3. Comparación 4. Paso como parámetros Utilizaremos el siguiente ejemplo para ver estas operaciones: struct PartesComplejas { float ParteReal; float ParteCompleja; typedef PartesComplejas *PunteroAComplejo; int main() { PunteroAComplejo punt1, punt2; punt1 = new PartesComplejas; punt2 = new PartesComplejas;... El acceso a la variable anónima se consigue de la forma ya vista con el operador *, y la asignación se realiza de la misma forma que en los tipos simples. En el ejemplo, suponiendo que punt1 ya tenga memoria reservada, podemos asignar valores a los campos de la variable anónima de la siguiente forma: punt1->partreal = 5.0; punt1->partimag = 1.2; Tras las asignaciones tendremos (suponiendo que un real ocupara 4 bytes): A una variable puntero también se le puede asignar la macro NULL (sea cual sea el tipo de la variable anónima): punt = NULL;, tras lo cual tendremos: 7

E.T.S.I. Telecomunicación Laboratorio de Programación 2 punt1 memoria dinámica 5.0 1.2 2 x 4 bytes p Se puede asignar el valor de una variable puntero a otra siempre que las variables a las que apuntan sean del mismo tipo: punt2 = punt1; La comparación de dos punteros, mediante los operadores!= y ==, permite determinar si apuntan a la misma variable referenciada: if (punt1 == punt2) // expresion logica o si no apuntan a nada: if (punt1!= NULL) // expresion logica El paso de punteros como parámetros permite el paso de variables por referencia a funciones. En la función llamame() del siguiente ejemplo el parámetro p es un puntero a un número entero; en la función main() del ejemplo se declara un entero x y se pasa su dirección de memoria a la función llamame() usando, para ello, el operador &. El funcionamiento de la llamada sería equivalente al uso de un parámetro por referencia en la función llamame() mediante el operador & y la llamada desde la función main() del modo llamame(x). Lo que ocurre, realmente, es un paso por dirección en la llamada a la función llamame(). Aunque puede usarse en C++ el paso de parámetros por dirección (con parámetros formales que son punteros) no resulta nada recomendable, aconsejándose el uso del paso de parámetros por refencia (mediante el operador & en los parámetros formales) como se vio en los temas anteriores. Ejemplo: #include <iostream> using namespace std; void llamame(int *p); 8

Memoria Dinámica punt1 memoria dinámica 5.0 1.2 punt2 2 x 4 bytes int main() { int x = 0; cout << "El valor de x es " << x << endl; llamame(&x); cout << "El nuevo valor de x es " << x << endl; return 0; void llamame(int *p) { *p=5; 2.5 Listas enlazadas Lo que realmente hace de los punteros una herramienta potente es la circunstancia de que pueden apuntar a variables que a su vez contienen punteros. Los punteros así creados sí son variables dinámicas, a diferencia de los declarados explícitamente, los cuales son estáticos porque pueden crearse en tiempo de compilación. Cuando en cada variable anónima o nodo tenemos un solo puntero que apunta al siguiente nodo tenemos una lista enlazada. Ejemplo: cadenas de caracteres de longitud variable. struct TpNodo{ char car; TpNodo *sig; // Caracter // Al siguiente nodo 9

E.T.S.I. Telecomunicación Laboratorio de Programación 2 TpNodo *primero; Con estos tipos de datos podemos crear estructuras que no malgastan la memoria, y que sí malgastaría un array de longitud fija: primero car sig car sig car sig car sig H O L A Un algoritmo que crea una lista enlazada similar sería: #include <iostream> #include <cstdlib> using namespace std; struct TpNodo { int dato; TpNodo *sig; typedef TpNodo *LISTA; void mostrar_lista(const LISTA ptr); void insertar(lista &ptr, const int elemento); int main() { LISTA n1 = NULL; int elemento; do { cout << endl << "Introduzca elemento: "; cin >> elemento; if(elemento!= 0) insertar(n1, elemento); while(elemento!= 0); 10

Memoria Dinámica cout << endl << "La nueva lista enlazada es: "; mostrar_lista(n1); return 0; void mostrar_lista(const LISTA ptr) { while(ptr!= NULL) { cout << ptr->dato << " "; ptr = ptr->sig; cout << endl; void insertar(lista &ptr, const int elemento) // Al final de la lista { LISTA p1, p2; p1 = ptr; if (p1 == NULL) // Lista vacia { p1 = new TipoNodo; p1->dato = elemento; p1->sig = NULL; ptr = p1; else { while(p1->sig!= NULL) p1 = p1->sig; p2 = new TipoNodo; p2->dato = elemento; p2->sig = NULL; p1->sig = p2; 11

E.T.S.I. Telecomunicación Laboratorio de Programación 2 2.5.1 Operaciones básicas sobre listas enlazadas 1. Inserción de un nodo al principio 2. Insertar un nodo en una lista enlazada ordenada 3. Eliminar el primer nodo 4. Eliminar un nodo determinado Vamos a basarnos en las declaraciones de una lista enlazada de enteros: struct TipoNodo{ int dato; TipoNodo *sig; // Elemento entero // Al siguiente nodo typedef TipoNodo *TipoLista; TipoLista lista; // Cabeza de la lista TipoLista nodo; // Nuevo nodo a insertar TipoLista ptr; // Puntero auxiliar Y partiremos del estado inicial de la lista: lista 3 4 9 Inserción de un nodo al principio Los pasos a dar son los siguientes: 1. Reservar memoria para el nuevo nodo: nodo = new TipoNodo; 2. Asignar valor nuevo al nuevo nodo: nodo->dato = 5; // por ejemplo 12

Memoria Dinámica 3. Enlazar la lista al siguiente del nuevo nodo: nodo->sig = lista; 4. Actualizar la cabeza de la lista: lista = nodo; Gráficamente (la línea discontinua refleja el enlace anterior a la operación, y también se especifica el orden de asignación de valores a los punteros): p 3 4 5 nodo 3º 1º 5 2º Inserción de un nodo en una lista ordenada (ascendentemente) Los pasos a seguir son los siguientes: 1. Reservar memoria para el nuevo nodo: nodo = new TipoNodo; 2. Asignar valor nuevo al nuevo nodo: nodo->dato = 5; // por ejemplo 3. Si la lista está vacía o el dato nuevo es menor que el de la cabeza, el nodo se inserta al principio y se actualiza la cabeza lista para que apunte al nuevo nodo. 4. Si la lista no está vacía y el dato nuevo es mayor que el que hay en la cabeza, buscamos la posición de inserción usando un bucle del tipo: while ((ptr->sig!= NULL) && (nodo->dato > (ptr->sig)->dato)) ptr = ptr->sig; /* ptr queda apuntando al predecesor */ y enlazamos el nodo: 13

E.T.S.I. Telecomunicación Laboratorio de Programación 2 nodo->sig = ptr->sig; ptr->sig = nodo; // con el sucesor // con el predecesor Gráficamente (observar el orden de asignación de punteros -el segundo paso son las sucesivas asignaciones al puntero ptr): ptr lista 2º 3 4 9 nodo 4º 1º 5 3º Eliminar el primer nodo Los pasos son (suponemos lista no vacía): 1. Guardar el puntero a la cabeza actual: ptr = lista; 2. Avanzar una posición la cabeza actual: lista = lista->sig; 3. Liberar la memoria del primer nodo: delete ptr; Gráficamente: lista 2º 3 4 9 ptr 1º 14

Memoria Dinámica Eliminar un nodo determinado Vamos a eliminar el nodo que contenga un valor determinado. Los pasos son los siguientes (suponemos lista no vacía): Caso a) El dato coincide con el de la cabeza: Se elimina como en la operación anterior. Caso b) El dato no coincide con el de la cabeza: 1. Se busca el predecesor y se almacena en una variable de tipo puntero, por ejemplo, en ant. En otra variable, ptr, se almacena el nodo actual: ant = lista; ptr = lista->sig; while ((ptr!= NULL) && (ptr->dato!= valor)){ ant = ptr; ptr = ptr->sig; 2. Se comprueba si se ha encontrado, en cuyo caso se enlaza el predecesor con el sucesor del actual: if (ptr!= NULL){ // encontrado ant->sig = ptr->sig; delete ptr; // ptr = NULL; //Opcional. No seria necesario Gráficamente quedaría (eliminando el nodo de valor 4): ptr lista 3 4 9 ant 15

E.T.S.I. Telecomunicación Laboratorio de Programación 2 2.6 Listas doblemente enlazadas Hasta ahora, el recorrido de la lista se realizaba en sentido directo (hacia delante). En algunos casos puede realizarse en sentido inverso (hacia atrás). Sin embargo, existen numerosas aplicaciones en las que es conveniente poder acceder a los elementos o nodos de una lista en cualquier orden. En este caso se recomienda el uso de una lista doblemente enlazada. En tal lista, cada elemento contiene dos punteros, aparte del valor almacenado en el elemento. Un puntero apunta al siguiente elemento de la lista y el otro puntero apunta al elemento anterior. Ejemplo gráfico (lista doblemente enlazada circular): 3 8 12 12 cabeza Existe una operación de insertar y eliminar (borrar) en cada dirección. En caso de eliminar un nodo de una lista doblemente enlazada es preciso cambiar dos punteros. Por ejemplo, para insertar un nodo a la derecha del nodo actual tenemos que asignar cuatro nuevos punteros, como puede comprobarse gráficamente: 3 8 12 12 cabeza 10 Gráficamente, borrar significaría: 3 8 12 12 cabeza Una lista doblemente enlazada con valores de tipo int necesita dos punteros y el valor del campo datos: struct TipoNodo{ int dato; TipoNodo *siguiente; 16

Memoria Dinámica TipoNodo *anterior; Ejercicios 1. Implementa las siguientes operaciones adicionales con listas enlazadas: 1.1 void imprimir elementos(const TipoLista lista); /* Imprime por pantalla todos los elementos de una lista */ 1.2 void copiar lista(const TipoLista listaorg, TipoLista &listades); /* Copia la lista listaorg en la lista listades */ 1.3 int longitud lista(const TipoLista lista); /* Devuelve la longitud de la lista */ 1.4 void eliminar ultimo(tipolista &lista); /* Elimina el último elemento de la lista */ 1.5 bool en lista(const TipoLista lista, const int dato); /* Devuelve true si el dato está en la lista */ 1.6 void insertar tras(tipolista &lista, const int dato, const int dato ant); /* Inserta "dato"tras "dato ant */ 1.7 void purgar dup(tipolista &lista); /* Elimina elementos duplicados en una lista */ 1.8 void ordenar lista(tipolista &lista); /* Ordena la lista de menor a mayor */ Suponer la siguiente declaración de tipos: struct TipoNodo{ int dato; TipoNodo *sig; typedef TipoNodo *TipoLista; 2. Realizar todas las operaciones del ejercicio 1 con listas doblemente enlazadas. Supondremos la siguiente declaración de tipos: 17

E.T.S.I. Telecomunicación Laboratorio de Programación 2 struct TipoNodo{ int dato; TipoNodo *sig; TipoNodo *ant; typedef TipoNodo *TipoListaDob; 3. Hay muchas aplicaciones en las que se debe almacenar en la memoria un vector de grandes dimensiones. Si la mayoría de los elementos del vector son ceros, éste puede representarse más eficientemente utilizando una lista enlazada con punteros, en la que cada nodo es un registro con tres campos: el dato en esa posición, si es distinta de cero, el índice de esa posición y un puntero al siguiente nodo. Por ejemplo: vector 25 1-14 5 Esto indica que hay sólo dos elementos distintos de cero en el vector, es decir, éste es: (25, 0, 0, 0, -14, 0, 0) si consideramos los vectores con siete posiciones. Escribe un programa que lea dos vectores por teclado, los introduzca en listas enlazadas y calcule su producto escalar. Diseña también las funciones para insertar y consultar el valor en una determinada posición del vector, teniendo en cuenta que si introducimos un elemento en una posición previamente existente se debe eliminar el valor anterior y sustituirlo por el nuevo. 4. Una forma de almacenar un número natural de valor mayor que el permitido en una computadora es introducir cada dígito en un nodo de una lista enlazada. Por ejemplo, la siguiente lista representa al número 92578: num 9 2 5 7 8 Escribe una función que tome como parámetro un puntero a una lista enlazada y devuelva el número correspondiente en una variable de tipo unsigned int. Diseña también una función que lea por teclado una sucesión de dígitos (caracteres) y los introduzca como dígitos (naturales) en una lista enlazada, y otras dos funciones 18

Memoria Dinámica que realicen, respectivamente, la suma y el producto de números representados de esta forma. 5. Un polinomio en x de un grado arbitrario se puede representar mediante una lista enlazada con punteros, donde cada nodo contiene el coeficiente y el exponente de un término del polinomio, más un puntero al siguiente nodo. Por ejemplo el polinomio: 25x 14x 5 se puede representar como: polinomio 25 1-14 5 5.1 Escribe una función que evalúe un polinomio P en un x = valor. int evaluar (const TipoPolinomio p, const int valor); 5.2 Escribe una función que devuelva el coeficiente del término de grado i de un polinomio P. int obtener (const TipoPolinomio p, const int i); 5.3 Escribe una función que sume 2 polinomios P1 y P2: TipoPolinomio sumar(const TipoPolinomio p1, const TipoPolinomio p2); 5.4 Escribe una función que realice la derivada de un polinomio P: TipoPolinomio derivada (const TipoPolinomio p); 6. Supongamos el tipo Conjunto definido mediante una lista enlazada según la siguiente cabecera: struct TipoNodo{ int dato; TipoNodo *sig; typedef TipoNodo *Conjunto; Diseñar las siguientes operaciones para la intersección y unión de conjuntos: Conjunto interseccion (const Conjunto c1, const Conjunto c2); Conjunto union(const Conjunto c1, const Conjunto c2); 19