Professional Documents
Culture Documents
DEPARTAMENTO DE INFORMTICA
Listas
N 1
NOTA:
OBSERVACIONES:
Lista
Una lista enlazada es una de las estructuras de datos fundamentales, y puede ser usada para implementar otras estructuras de datos. Consiste en una secuencia de nodos, en los que se guardan campos de datos arbitrarios y una o dos referencias, enlaces o punteros al nodo anterior o posterior. El principal beneficio de las listas enlazadas respecto a los vectores convencionales es que el orden de los elementos enlazados puede ser diferente al orden de almacenamiento en la memoria o el disco, permitiendo que el orden de recorrido de la lista sea diferente al de almacenamiento. Una lista enlazada es un tipo de dato autorreferenciado porque contienen un puntero o enlace a otro dato del mismo tipo. Las listas enlazadas permiten inserciones y eliminacin de nodos en cualquier punto de la lista en tiempo constante (suponiendo que dicho punto est previamente identificado o localizado), pero no permiten un acceso aleatorio. Existen diferentes tipos de listas enlazadas: listas enlazadas simples, listas doblemente enlazadas, listas enlazadas circulares y listas enlazadas doblemente circulares. Las listas enlazadas pueden ser implementadas en muchos lenguajes. Lenguajes tales como Lisp y Scheme tiene estructuras de datos ya construidas, junto con operaciones para acceder a las listas enlazadas. Lenguajes imperativos u orientados a objetos tales como C o C++ y Java, respectivamente, disponen de referencias para crear listas enlazadas.
Nodos centinelas
A veces las listas enlazadas tienen un nodo centinela (tambin llamado falso nodo o nodo ficticio) al principio o al final de la lista, el cual no es usado para guardar datos. Su propsito es simplificar o agilizar algunas operaciones, asegurando que cualquier nodo tiene otro anterior o posterior, y que toda la lista (incluso alguna que no contenga datos) siempre tenga un primer y ltimo nodo.
Ventajas
Como muchas opciones en programacin y desarrollo, no existe un nico mtodo correcto para resolver un problema. Una estructura de lista enlazada puede trabajar bien en un caso pero causar problemas en otros. He aqu una lista con algunas de las ventajas ms comunes que implican las estructuras de tipo lista. En general, teniendo una coleccin dinmica donde los elementos estn siendo aadidos y eliminados frecuentemente e importa la localizacin de los nuevos elementos introducidos se incrementa el beneficio de las listas enlazadas.
Lenguajes soportados
Muchos lenguajes de programacin tales como Lisp y Scheme tienen listas enlazadas simples ya construidas. En muchos lenguajes de programacin, estas listas estn construidas por nodos, cada uno llamado cons o celda cons. Las celdas cons tienen dos campos: el car, una referencia del dato al nodo, y el cdr, una referencia al siguiente nodo. Aunque las celdas cons pueden ser usadas para construir otras estructuras de datos, este es su principal objetivo. En lenguajes que soportan tipos abstractos de datos o plantillas, las listas enlazadas ADTs o plantillas estn disponibles para construir listas enlazadas. En otros lenguajes, las listas enlazadas son tpicamente construidas usando referencias junto con el tipo de dato record. En la seccin de implementaciones hay un ejemplo completo en C y en Maude
En general, si una serie de estructuras de datos necesita ser incluida en mltiples listas enlazadas, el almacenamiento externo es el mejor enfoque. Si una serie de estructuras de datos necesitan ser incluidas en una sola lista enlazada, entonces el almacenamiento interno es ligeramente mejor, a no ser que un paquete genrico de listas genricas que use almacenamiento externo est disponible. Asimismo, si diferentes series de datos que pueden ser almacenados en la misma estructura de datos son incluidos en una lista enlazada simple, entonces el almacenamiento interno puede ser mejor.
record Node { data // El dato almacenado en el nodo next // Una referencia al nodo siguiente, nulo para el ltimo nodo prev // Una referencia al nodo anterior, nulo para el primer nodo } record List {
// apunta al primer nodo de la lista; nulo para la // apunta al ltimo nodo de la lista; nulo para la
node := list.firstNode while node null <do something with node.data> node := node.next
Hacia Atrs
node := list.lastNode while node null <do something with node.data> node := node.prev
function insertAfter(List list, Node node, Node newNode) newNode.prev := node newNode.next := node.next if node.next = null node.next := newNode list.lastNode := newNode else node.next.prev := newNode node.next := newNode function insertBefore(List list, Node node, Node newNode) newNode.prev := node.prev newNode.next := node if node.prev is null
Tambin necesitamos una funcin para insertar un nodo al comienzo de una lista posiblemente vaca.
function insertBeginning(List list, Node newNode) if list.firstNode = null list.firstNode := newNode list.lastNode := newNode newNode.prev := null newNode.next := null else insertBefore (list, list.firstNode, newNode)
function insertEnd(List list, Node newNode) if list.lastNode = null insertBeginning (list, newNode) else insertAfter (list, list.lastNode, newNode)
Borrar un nodo es fcil, solo requiere usar con cuidado firstNode y lastNode.
function remove(List list, Node node) if node.prev = null list.firstNode := node.next else node.prev.next := node.next if node.next = null list.lastNode := node.prev else node.next.prev := node.prev destroy node Listas enlazadas doblemente circulares
Asumiendo que someNodo es algn nodo en una lista no vaca, esta lista presenta el comienzo de una lista con someNode. Hacia Delante
node := someNode do do something with node.value node := node.next while node != someNode
Hacia Atrs
node := someNode do do something with node.value node := node.prev while node := someNode
Esta funcin inserta un nodo en una lista enlazada doblemente circular despus de un elemento dado:
function insertAfter(Node node, Node newNode) newNode.next := node.next newNode.prev := node node.next.prev := newNode node.next := newNode
Para hacer "insertBefore", podemos simplificar "insertAfter (node.prev, newNode)". Insertar un elemento en una lista que puede estar vaca requiere una funcin especial.
function insertEnd(List list, Node node) if list.lastNode = null node.prev := node node.next := node else insertAfter (list.lastNode, node) list.lastNode := node
function remove(List list, Node node) if node.next = node list.lastNode := null else node.next.prev := node.prev node.prev.next := node.next if node = list.lastNode list.lastNode := node.prev; destroy node
Como una lista doblemente enlazada, "removeAfter" y "removeBefore" puede ser implementada con "remove (list, node.prev)" y "remove (list, node.next)".
record Entry { integer next; // ndice de la nueva entrada en el vector integer prev; // entrada previa string name; real balance; }
Entry Records[1000];
/* algunos compiladores no requieren un casting del valor del retorno para malloc */ node *n = (node *)malloc(sizeof(node)); if (n == NULL) return NULL; n->next = *p; *p = n; n->data = i; return n; } void list_remove(node **p) { /* borrar cabeza*/ if (*p != NULL) { node *n = *p; *p = (*p)->next; free(n); } } node **list_search(node **n, int i) { while (*n != NULL) { if ((*n)->data == i) { return n; } n = &(*n)->next; } return NULL; } void list_print(node *n) { if (n == NULL) { printf("lista esta vaca\n"); } while (n != NULL) { printf("print %p %p %d\n", n, n->next, n->data); n = n->next; } } int main(void) { node *n = NULL; list_add(&n, 0); /* lista: 0 */ list_add(&n, 1); /* lista: 1 0 */ list_add(&n, 2); /* lista: 2 1 0 */
list_add(&n, 3); /* lista: 3 2 1 0 */ list_add(&n, 4); /* lista: 4 3 2 1 0 */ list_print(n); list_remove(&n); list_remove(&n->next); (primera) */ list_remove(&n->next); list_remove(&n); list_print(n); return 0; } /* eliminar segundo nodo del final(0)*/ /* eliminar ultimo nodo (3) */ /* borrar primero(4) */ /* borrar nuevo segundo (2) */
record member { // miembro de una familia member next string firstName integer age } record family { // // la propia familia family next string lastName string address member members // de la lista de miembros de la familia }
Para mostrar una lista completa de familias y sus miembros usando almacenamiento interno podramos escribir algo como esto:
aFamily := Families // comienzo de la lista de familias while aFamily null { // bucle a travs de la lista de familias print information about family aMember := aFamily.members // coger cabeza de esta lista de miembros de esta familia while aMember null { //bucle para recorrer la lista de miembros
record node { // estructura genrica de enlace node next pointer data // puntero genrico del dato al nodo } record member { // estructura de una familia string firstName integer age } record family { // estructura de una familia string lastName string address node members // cabeza de la lista de miembros de esta familia }
Para mostrar una lista completa de familias y sus miembros usando almacenamiento externo, podramos escribir:
famNode := Families // comienzo de la cabeza de una lista de familias while famNode null { // bucle de lista de familias aFamily = (family) famNode.data // extraer familia del nodo print information about family memNode := aFamily.members // coger lista de miembros de familia while memNode null { bucle de lista de miembros aMember := (member) memNode.data // extraer miembro del nodo print information about member memNode := memNode.next } famNode := famNode.next }