ALGORITMOS Y ESTRUCTURA DE DATOS Punteros Estructuras Enlazadas Pilas Punteros y direcciones de memoria Un puntero es un tipo de dato, como todo tipo de dato se define por dos conjuntos: Conjunto de valores o Direcciones de memoria Conjunto de operaciones o Asignación Estática Mediante el operador de dirección int *p = & a; Con el valor Nulo int *p = NULL Dinámica: para crear o liberar instancias Crear instancia int *p = new int(); Liberar instancia delete p; int a = 25; int* p1 = &a; int* p2 = NULL; float* q1 = NULL; Asignaciones *p1 = 30; // *p2 = 10; // p2 = p1; // q1 = p1 ; // diferentes modifica el valor de a ERROR no se instancio a p2 ps apunta a la misma dirección que p1 ERROR los punteros son con tipo y por definición apuntan a tipos C/C++ y los operadores de dirección e indireccion C provee los operadores & que es un operador de dirección: antepuesto a un identificador indica la dirección de memoria del mismo. * operador de induración o desreferenciacion: antepuesto a un puntero indica el contenido de la dirección de memoria a la que apunta el puntero. -> operador de acceso a un miembro de una struct apuntada por un puntero equivale a (*struct).miembro. 2 Struct TR { int a; int b; }; int x = 25; int* p = &x; TR r; TR* pr Asignación dinámica new crea una instancia del puntero, toma como valor la dirección de memoria creada, se dice que apunta a esa dirección de memoria. delete libera la instancia creada por new. int* p = new int(); Crea una instancia de p, a la dirección creada se accede con el operador de indireccion, *p y es un dato de tipo entero TR* pro = new TR(); Crea una instancia de TR, a la dirección creada se accede con el operador de indireccion, *pro y es un dato de tipo struct TR. (*pro).a es el entero correspondiente al campo a de la struct TR apuntada por el puntero pro delete p; delete pro; eliminan las instancias creadas Estructuras secuenciales Vs. Estructuras enlazadas Estructuras secuenciales: El primer elemento lógico coincide con el primer elemento físico El siguiente elemento lógico coincide con el siguiente elemento físico Estructuras enlazadas: Se debe conocer la posición del primer elemento lógico ya que no necesariamente coincide con el primer elemento físico Cada posición tiene una struct (nodo) con la información y la referencia al siguiente elemento lógico que no necesariamente es el siguiente fisico A B 3 B D -1 C A 0 D C 1 Tipos de estructuras Lineales o Pilas o Colas o Listas (SE, Circ, DE, CircDE Arboles Grafos 3 Las estructuras enlazadas pueden implementarse con vectores, en este caso no se resolvería la restricción de tamaño fijo de esta estructura de dato. El vector requiere el conocimiento a priori del tamaño físico máximo. Para resolver esta debilidad se pueden implementar con punteros de podo de generar los nodos que se requieran en tiempo de ejecución, en este caso se tendrán exactamente la cantidad de posiciones que cada aplicación requiera. Esta ventaja es a a un costo: Como el siguiente lógico puede estar en una posición de memoria no contigua, el nodo debe tener una referencia precisa al siguiente elemento por lo que requiere un puntero que referencie al siguiente elemento lógica. Esta estructura que tiene un puntero a una estructura del mismo tipo de la cual parte se la denomina estructura autoreferenciada Nodo struct Nodo { int info; Nodo* sgte; }; Estructuras enlazadas implementadas con Punteros Para trabajar con estas estructuras es necesario: 1. Declarar la estructura del nodo 2. Definir una variable de control de la estructura de tipo puntero 3. Definir una variable para las distintas instancias de tipo puntero 4. De finir una variable para contener la información del tipo de la información 5. Crear la estructura hacer apuntar a NULL a la variable de control de la estructura 6. Cargar la estructura a. Tomar la información b. Pedir memoria c. Guardar la información d. Actualizar los punteros Pilas Una pila es una estructura en la que los elementos se insertan delante del primero, se apilan; y cuando se saca, se elimina el que está en el tope, son estructuras LIFO: Last In First Out. Para la carga se pueden diferenciar dos situaciones, cuanto la pila esta vaci, es decir el puntero de contol (pila) tiene el valor NULL o cuando la pila tiene datos Acciones Pedir memoria Guardar la información Enlazar el nodo Actualizar la pila Con pila Vacía Nodo* Aux = new Nodo(); Aux->info = valor; Aux->sgte = NULL; Pila = Aux; Función: push void push(Nodo*& p, int v){ Nodo* Aux = new Nodo(); Aux->info = valor; Aux->sgte = p; p = Aux; return; } Con pila con datos Nodo* Aux = new Nodo(); Aux->info = valor; Aux->sgte = Pila; Pila = Aux 4 Con plantillas template <typename T> struct Nodo { T info; Nodo<T>* sgte; }; template <typename T> void push(Nodo<T>* &p, T v){ Nodo<T>* Aux = new Nodo<T>(); Aux->info = valor; Aux->sgte = p; p = Aux; return; } Función: pop int pop(Nodo* &p){ int x; Nodo* Aux = p; x = Aux->info; Aux = Aux->sgte; delete p; return x; } Con plantillas template <typename T> pop(Nodo<T>* &p){ T x; Nodo<T>* Aux = p; x = Aux->info; Aux = Aux->sgte; delete p; return x; } Ejemplo de uso int main(){ Nodo<int>* p = NULL; push<int>(p,1); push<int>(p,2); push<int>(p,3); while( p!=NULL ){ cout << pop<int>(p) << endl; } return 0; }