UN EJEMPLO DEL USO DE PUNTEROS: LAS LISTAS ENLAZADAS Estructuras de datos estáticas y dinámicas En capítulos anteriores se han estudiado estructuras de datos estáticas (conjuntos, arrays, registros...), cuya característica esencial es que su almacenamiento en memoria (número de elementos) se determina a priori en el momento de escribir el programa y se fija durante la compilación. Durante la ejecución del programa sus valores se pueden modificar, pero no se puede cambiar el número de elementos de la estructura, que es fijo. Para paliar este inconveniente, se utilizan las estructuras dinámicas de datos, que son aquellas en las que podemos aumentar o reducir su número de elementos (nodos) de acuerdo con las necesidades del programa. Las estructuras dinámicas de datos se dividen en dos grandes grupos: lineales (listas, pilas, colas...) y no lineales (árboles, grafos...). Definición de lista enlazada Son estructuras dinámicas lineales de elementos llamados nodos, que se encuentran enlazados o relacionados cada uno con el siguiente mediante un enlace o puntero. Cada nodo debe tener dos campos: un campo (info) que contiene el valor de ese elemento y un campo (enlace o puntero) que indica la posición del siguiente elemento. Existe una marca para fin de lista, que es la constante NIL, representada por una barra inclinada en el último nodo. Declaración de un nodo Seguidamente indicamos cómo se declara un puntero para referenciar un nodo correspondiente a una estructura dinámica de datos: Type Tptro = ^Tnodo; Tnodo = Record Info: TipoDato; (* cualquier tipo de datos definido por el usuario *) Enlace: Tptro End; Observe que la definición de Tptro precede a la definición del registro tipo Tnodo. Esta es una de las pocas situaciones en las que está permitido utilizar un identificador (Tnodo) antes de ser definido. Para las operaciones que a continuación se describen (inicialización, insertar al principio, insertar al final, recorrido y eliminación de un nodo de una lista), se declara una lista de la siguiente forma: Type Tinfo: string[9]; Tlista = ^Tnodo; Tnodo = Record info: Tinfo; enlace: Tlista End; Var L: Tlista; Inicialización de una lista enlazada Construye una lista vacía, es decir, que no contiene elementos: Procedure Inicializar(var L: Tlista); Begin L:=nil (* fija el puntero del primer nodo a nil *) End; Insertar al principio de la lista Crea un nodo y lo pone en el primer lugar de la lista: Procedure InsertarAntes (var L: Tlista; x: Tinfo); Var Aux: Tlista; Begin New(Aux); Aux^.info:=x; (*almacena la información en el nuevo nodo*) Aux^.enlace:= L; (*1:insertar el nodo al principio de la lista*) L:=aux; (*2:la cabecera de la lista apunta al nuevo nodo*) End; Insertar al final de la lista Crea un nodo y lo pone en el último lugar de la lista: Procedure InsertarDespués(var L: Tlista, x: Tinfo); Var Anterior, Actual, Aux: Tlista; Begin New(Aux); Aux^.info:=x; (*almacena la información en el nuevo nodo*) Aux^.enlace:=nil; (*el nuevo nodo apunta a NIL*); Anterior:= nil; Actual:=L; (* Buscar el último nodo *) While Actual <> nil do Begin Anterior:=Actual; Actual:=Actual^.enlace; End; (* Si la lista está vacía, entonces el primer puntero apunta al nuevo nodo *) If Anterior = nil then L:=aux Else Anterior^.enlace:=Aux; End; Recorrido de una lista La operación de imprimir los datos de una lista se conoce como recorrido. Se debe comenzar por la cabecera de la lista y seguir con todos los punteros de la lista, imprimiendo todos los campos info (datos): Procedure ImprimirLista(L: Tlista), Begin While L<>nil do Begin Writeln(L^.info); (* imprime el valor del nodo *) L:=L^.enlace End End; Eliminar un nodo Borrar o eliminar el nodo al que se está apuntando, exige conectar el nodo precedente al nodo después del que se desea borrar: Procedure Borrar(var L: Tlista, N: Tlista); (* N es el nodo a borrar *) var q1: Tlista (* Puntero que se mueve por la lista *) begin if N=L then (* Si es el primer elemento *) L:=L^.enlace (* Eliminar N^ *) else begin (* sino, encontrar elemento antes de N^*) q1:= L; while q1^.enlace<>N do q1:=q1^.enlace; q1^.enlace:=N^.enlace (* 1:eliminar N^ *) end; dispose(N); (* 2: liberar memoria *) end;