Unidad III. Estructuras de Datos. Objetivos Adquirir los conceptos básicos referente a estructura de datos. Conocer las principales estructuras de datos, desde un punto de vista abstracto, y sus formas de representación, así como las operaciones que se puedan realizar sobre ellas. Aplicar todos los conceptos adquiridos mediante resolución de supuestos prácticos siendo capaces de elegir siempre la representación más eficiente. Aplicar y adaptar las estructuras de datos a nuevos requisitos de operación y representación de información. Tema I. Definición, características y ventajas Definición de Estructura de Datos Una estructura o registro es un conjunto de variables que pueden ser agrupadas y tratadas como si fuesen una sola. Este conjunto puede estar compuesto de variables de cualquier tipo, inclusive de otras estructuras. Una estructura, generalmente, define un nuevo tipo de dato. Semánticamente, una estructura agrupa datos que tienen alguna relación según un cierto criterio. Imagine que se desea almacenar los datos de un estudiante y sus notas. Todo esto puede ser agrupado en una estructura que podríamos llamar: DatosEstudiante , que de forma práctica define un nuevo tipo de dato. Ejemplo struct DatosEstudiante { char cedula[12]; char nombre[32]; char telefono[10]; int notaPrimerParcial; int notaSegundoParcial; }; Tema II. Forma de una Estructura de Datos Tiene la forma de todos sus componentes pero serializados. La serialización consiste en la juntura de todos los contenidos de las variables de un registro, uno detrás del otro. Hay un caso especial que distinguir: cuando hay punteros en la estructura. Ve por qué? Piense en el contenido de una variable puntero. Ventajas • • • Mejor organización del código, más expresividad y en consecuencia un mejor código. Acorta el tamaño del código para hacer asignaciones de estructuras completas. Ej: estudiante1 = estudiante2. Donde ambas variables son del tipo RegistroEstudiante. Pueden ser pasadas por parámetro y ocurren copias, inclusive de arreglos, al menos con el compilador de GNU. Tema III. Sintaxis Sintaxis Algoritmica La notación algorítmica de una estructura es: Estructura { tipo1 dato1; tipo2 dato2; … tipon daton; } Una propiedad interesante del tipo de dato estructura es que pueden anidarse otras “Estructuras” indefinidamente. Sintaxis en C struct punto { int x; int y; }; // observe el punto y coma al final. El nombre de la estructura es “punto” y tiene como miembros un par de variables enteras: “x” e “y”. Podemos declarar una o mas variables del tipo estructura de la siguiente manera: struct { int var1; float var2; } i, j, k; Notese que i, j y k son declaradas análogamente como si lo hiciéramos con cualquiera de los tipos conocidos. Una vez definida la estructura, pueden hacerse sucesivas declaraciones: struct point w; Y también puede utilizarse la inicialización con llaves: struct point h = {10, 20}; // coordenadas (10,20). Tema IV. Operadores sobre Estructuras (Anidamiento, Arreglos, Punteros, ...) Anidamiento de Estructuras Las estructuras pueden estar anidadas unas dentro de otras: struct point { int x; int y; }; struct triangle { struct point p1, p2, p3; } t; int main() { // En algún lugar dentro del programa t.p1 = {4, 6}; t.p2.x = 20; t.p2.y = 32; t.p3.x = t.p2.x – 10; t.p3.y = t.p3.y + t.p3.x; return 0; } Anidamiento de Estructuras Las estructuras pueden estar anidadas unas dentro de otras: struct point { int x; int y; }; struct triangle { struct point p1, p2, p3; } t; int main() { // En algún lugar dentro del programa t.p1 = {4, 6}; t.p2.x = 20; t.p2.y = 32; t.p3.x = t.p2.x – 10; t.p3.y = t.p3.y + t.p3.x; return 0; } Pase de Estructuras como Parámetros Las estructuras al igual que las variables simples, pueden ser pasadas por parámetro a cualquier función. struct recta { float m; //pendiente float b; // termino independiente }; float area_triagulo (struct triangle ta) { float base = longitud(ta.p1, ta.p2); struct recta r1 = ec_recta_perpendicular(ta.p1, ta.p2, ta.p3); struct recta r2 = ec_recta(ta.p1, ta.p2); struct point p_i = interseccion(r1, r2); float altura = longitud(p_i, p3); return (base * altura) / 2; } Arreglos de Estructuras Una manera de resolver los arreglos de estructuras es a través de arreglos paralelos. cadena nombres[n]; cadena apellidos[n]; … entero primerParcial[n]; Punteros a Estructuras Las estructuras también pueden ser trabajadas a través de su dirección de memoria. struct RegistroEstudiante * pre; struct RegistroEstudiante reg1; reg1.nombre = “Fernando”; reg1.apellido = “Corbato”; pre = &reg1; pre->notaPrimerParcial = 20; // el operado de indirección -> sirve para acceder // los miembros de una estructura a través de un // puntero. Ejemplo 1 Suponga el siguiente caso de una estructura que tiene un apuntador a una estructura de su mismo tipo. struct bloque { int a; float b; struct bloque * s }; Observe que s es un apuntador a estructura del tipo struct bloque. Ejemplo 2 Como se obtendrian un par de estructuras enlazadas? struct bloque bl1; struct bloque bl2; bl2.s = &bl1; Luego, que puedo hacer con bl1 desde bl2? bl2.s->a = 145; // a pertenece a bl1 cout << bl1.a; // imprime 145... Estructuras y Funciones struct trickyStruct { int longArray[10000]; }; void f1(trickyStruct ts); void f2(trickyStruct *ts); void f3() { struct trickyStruct p; // cualquier operacion para llenar p f1(p); // copia todo f2(&p); // solamente pasa la direccion, nos ahorramos 9999 copias... } Tema V. Tipos Abstractos de Datos Definición de Abstracción de Datos. La abstracción de datos se refiere la extracción de las supracaracterísticas de un objeto en particular. La abstracción consiste en enfocar los aspectos esenciales inherentes a una entidad e ignorar las propiedades accidentales. Un tipo de dato abstracto, no posee instancia. Como diría Platón: es la idea del objeto. Ejemplo Para comprender mejor un tipo de dato abstracto, podriamos citar la idea (o abstracción) de poligono. Un polígono es una figura geométrica plana, compuesta por lineas rectas que se tocan en puntos extremos. Con un polígono podemos hacer las operaciones: rotar, area, perímetro. Abstracción como proceso Consiste en separar las propiedades esenciales de un objeto, sistema, fenómeno o problema y omitir las propiedades no esenciales. Abstracción como producto Es una descripción o especificación de un sistema en el que se enfatizan algunos detalles o propiedades y se suprimen otros. Una abstracción como producto nos dice que un objeto puede poseer partes que pueden ser refinadas a posteriori. Es importante destacar que las propiedades esenciales de un sistemas son tratadas como un todo. Ejemplo Suponga que se tiene la clase persona: class persona { protected: char nombre[20]; char apellido[20]; char edad; public: virtual void pago_pasaje() { ... } }; class estudiante : public persona { public: void pago_pasaje() { // otro procedimiento } }; Tipo de Dato Abstracto Un TDA es un tipo de dato definido por el programador que se puede manipular de un modo similar a los tipos de datos definidos por el sistema. Está formado por un conjunto válido de elementos y un número de operaciones primitivas que se pueden realizar sobre ellos. Tipos básicos de operaciones en un TDA Constructores: Crean una nueva instancia del tipo. Transformación: Cambian el valor de uno o más elementos de una instancia del tipo. Observación: Nos permiten observar el valor de uno o varios elementos de una instancia sin modificarlos. Iteradores: Nos permiten procesar todos los componentes en un TDA de forma secuencial. Implementacion Cuando ya se tiene bien diseñado un Tipo Abstracto, el siguiente paso es decidir una implementación para el mismo. Esto implica escoger unas estructuras de datos para representar cada uno de los objetos abstractos y escribir una rutina(Procedimiento o función) en un lenguaje de programación, que simule el funcionamiento de cada una de las operaciones de acuerdo con su especificación. La selección de las estructuras de datos determina, en muchos casos, la complegidad del algoritmo que implementa una operación y es, por esta razón, de gran importancia su escogencia. Existen estructuras de datos muy dependientes de un lenguaje de programación y debido a esto deben trata de evitarse cuando el TAD se quiere hacer portable. Propiedades de los TAD Encapsulación: Consiste en poder esconder datos y operaciones poco relevantes para la interfaz del objeto. Se imagina que para poder ver tuvieramos conciencia de todo el proceso de la visión? Generalización: Como se dijo antes, un TAD trata de representar las características escenciales de un objeto. Polimorfismo: Es una propiedad que permite tener distintas conductas para una misma función. Ejemplo class Estudiante { public: float calculo_matricula() }; class Estud_Pregrado : public Estudiante { public: float calculo_matricula() }; Aquí no hay duda, la función calculo_matricula será invocada dependiendo del tipo del objeto. Existe un reemplazo en tiempo de compilaci'on de una función por la otra en la clase derivada. Tema VI. TDA Pilas Definición Es una colección lineal, dinámica y homogénea, en la que los elementos de insertan y se extraen por el mismo extremo. También conocida como estructura LIFO (Last In, First Out). Operaciones CrearPila Meter Sacar DestruirPila EstaVacia Representación Utilizaremos un array para representar la pila. Definiremos un tamaño máximo de array (MaxElemPila). Llevaremos una variable: cima que indicará cual es el último elemento ocupado en el array. Ejemplo #define MaxElemPila struct _TPilaEnteros { int elementos[MaxElemPila]; int cima; }; typedef struct _TPilaEnteros TPilaEnteros; void CreaPila (TPilaEnteros *p) { p->cima = 0; }; int InsertaPila (int nelem, TpilaEnteros *p) { if (p->cima==MaxElemPila) { return 0; /*No se ha podido insertar*/ } else { p->cima++; p->elementos[p->cima]=nelem; return 1; }; }; Tema VII. TDA Colas Definición Las inserciones se hacen por un extremo y los borrados se hacen por el otro extremo. Se conocen como estructuras FIFO (First In First Out). Operaciones Crear_cola (C: cola, ok:lógico) Borrar_cola (C: cola, ok:lógico) Vacía? (C: cola, resp:lógico) Llena? (C: cola, resp:lógico) Queue (C: cola, E: Elto, resp: lógico) Dequeue (C: cola, E: Elto, resp: lógico) Tamaño (C: cola, n: numérico) Representación Primero: indica el índice de la posición del primer elemento de la cola, es decir, la posición el elemento a retornar cuando se invoque sacar. Último: indica el índice de la posición de último elemento de la cola. Si se invoca encolar, el elemento debe ser insertado en el casillero siguiente al que indica la variable. numElem: indica cuántos elementos posee la cola. Definiendo MAX_ELEM como el tamaño máximo del arreglo, y por lo tanto de la cola, entonces la cola esta vacía si numElem==0 y está llena si numElem==MAX_ELEM. Ejemplo class ColaArreglo { private Object[] arreglo; private int primero, ultimo, numElem; private int MAX_ELEM=100; // maximo numero de elementos en la cola public ColaArreglo() { arreglo=new Object[MAX_ELEM]; primero=0; ultimo=MAX_ELEM-1; numElem=0; } public void encolar(Object x) { if (numElem<MAX_ELEM) // si esta llena se produce OVERFLOW { ultimo=(ultimo+1)%MAX_ELEM; arreglo[ultimo]=x; numElem++; } } public Object sacar() { if (!estaVacia()) // si esta vacia se produce UNDERFLOW { Object x=arreglo[primero]; primero=(primero+1)%MAX_ELEM; numElem--; return x; } } public boolean estaVacia() { return numElem==0; } } Tema VIII. TDA Listas Definición Una lista es una colección homogénea de elementos con una relación lineal entre ellos. Es decir, cada elemento de la lista (excepto el primero) tiene un único elemento predecesor y cada elemento (excepto el último) tienen un elemento sucesor Operaciones Creación: CreaLista Transformacion: VaciarLista, InsertarElementoLista, BorrarElementoLista, ModificarElementoLista Observación: LongitudLista, RecuperarElementoLista Iteradores: PrimeroLista, SiguienteLista, AnteriorLista, FinalLista Representación La representación escogida dependerá de la utilización que se le vaya a dar al tipo. Lo normal es implementarlas como listas de elementos enlazados mediante punteros. Los elementos serán estructuras con un uno o varios campos de datos y un campo de tipo puntero que hará referencia al siguiente elemento. Ejemplo /*lista de enteros*/ struct Tnodo { int dato; struct Tnodo *siguiente; }; typedef struct Tnodo *TLista; /*Permitira iterar sobre la lista*/ typedef struct Tnodo *TPosicion;