1 estructuras de datos elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas),...

27
1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González ELO320: Estructura de Datos y Algoritmos 1 Sem. 2004

Upload: trini-guitierrez

Post on 23-Jan-2016

223 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

1

Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz)

Agustín J. GonzálezELO320: Estructura de Datos y Algoritmos

1 Sem. 2004

Page 2: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

2

Introducción

Los conjuntos son tan fundamentales en los computación como lo son en matemáticas.

En nuestro campo, los conjuntos cambian (crecen, se reducen, etc.). A éstos se les llama conjuntos dinámicos.

Dependiendo de las operaciones que el algoritmo requiere sobre el conjunto, éstos son conocidos con diferentes nombres.

Por ejemplo, el conjunto que permite insertar, eliminar, y consultar por pertenencia, es llamado diccionario.

Page 3: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

3

Operaciones sobre conjuntos dinámicos: Search(S,k) retorna

Insert(S,x) Modifica S incorporando el elemento apuntado o denotado por x.

Delete(S,x) Modifica S removiendo el elemento apuntado o denotado por x.

Minimum(S) Retorna el elemento de S con la menor clave. Maximum(S) Retorna el elemento de S con la mayor clave. Successor(S,x) Retorna el elemento de S cuya clave es la menor no

inferior a la clave de x. Si x es el elemento máximo, retorna NIL. La clave es un atributo ordenable de los elementos.

Predecessor(S,x) Retorna el elemento de S cuya clave es la mayor no superior a la clave de x. Si x es el elemento menor, retorna NIL.

k clavecon elementoshay no Si nil

k es clavesu que talSen elementoun a puntero

Introducción (cont)

Page 4: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

4

Stacks y Queues como conjuntos dinámicos

Los stacks y las queues son conjuntos dinámicos en los que el elemento removido por Delete está predefinido.

En stacks sólo se puede remover el elemento más recientemente insertado. Implementa así una política last-in, first-out o LIFO.

Las queues sólo pueden remover el elemento más antiguo del conjunto. Implementa una política first-in, first-out o FIFO.

Page 5: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

5

Stacks

La operación Insert es llamada aquí PUSH. La operación Delete es llamada POP. Si se hace un POP de un stack vacío, decimos que

hay un underflow, lo cual es un error de programa. Si la implementación del stack posee un límite para

el número de elementos y éste se excede, decimos que hay un overflow. También es un error.

Se incorpora la función TOP que retorna el valor más reciente sin modificar el stack.

Ejemplos de uso: Cuando hacemos undo en editores. Cuando hacemos back en un navegador.

Page 6: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

6

Implementación de stack con arreglo #include <assert.h> /* para eliminar validaciones, agregar #define NDEBUG*/ Const int MAX_ELEMENTS = 100; typedef struct stack {

int top;int element [MAX_ELEMENTS];

} STACK; void MakeNull(STACK *S) {

S->top=-1;}

int Stack_Empty(STACK * S) {return (S->top == -1);

} void Push( STACK *S, int x) {

assert (S->top < MAX_ELEMENTS);(*S).top++; /* los paréntesis son requeridos por precedencia de operandos */(*S).element[(*S).top]=x; /* pudo ser S->element [S->top]=x; */

/* o ambas en (*S).element[++(*S).top]=x; */}

int Pop (STACK *S) {assert((*S).top > -1); /* stack no vacío */return((*S).element[(*S).top--]);

} int Top(STACK *S) {

assert(S->top > -1);return (S->element[S->top]);

}

Page 7: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

7

Queues

La operación Insert es llamada Enqueue. La operación Delete es llamada Dequeue. Cada queue tiene una head (cabeza) y un tail

(cola). También se pueden producir las condiciones

de overflow y underflow cuando la implementación tiene capacidad limitada.

Se incorpora la función Head que retorna el valor más antiguo de la cola.

Page 8: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

8

Implementación de queue con arreglo circular

Const int MAX_ELEMENTS = 100; typedef struct stack {

int head; /* apunta al elemento más antiguo de la queue, el “primero” */

int tail; /* apunta al elemento más recientemente ingresado a la cola, el de atrás */

int count; /* cantidad de elemento en la cola. Permite distinguir cola vacía de llena */

int element [MAX_ELEMENTS];} QUEUE;

void MakeNull(QUEUE *Q) {Q->head=0; Q->tail=MAX_ELEMENTS-1;Q->count=0;

} int Queue_Empty(QUEUE * Q) {

return (Q->count == 0);}

Page 9: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

9

Const int MAX_ELEMENTS = 100; typedef struct stack {

int head; /* apunta al elemento más antiguo de la queue, el “primero” */ int tail; /* apunta al elemento más recientemente ingresado a la cola, el

de atrás */int count; /* cantidad de elemento en la cola. Permite distinguir cola vacía

de llena */int element [MAX_ELEMENTS];

} QUEUE; ...... void Enqueue( QUEUE *Q, int x) {

assert (Q->count++ < MAX_ELEMENTS);Q->tail= ++Q->tail%MAX_ELEMENTS;Q->element[Q->tail] = x;

} int Dequeue (QUEUE *Q) {

int aux=Q->head;assert(Q->count-- > 0); /* Queue no vacío */Q->head= ++Q->head % MAX_ELEMENTS;return((*Q).element[aux]);

} int Head(QUEUE *Q) {

assert(Q->count >0);return (Q->element[Q->head]);

}

Ejercicio:Otra forma de distinguir

entre cola llena o vacía es dejar libre una casilla al final de la cola. Implemente esta estrategia (elimina la variable count.)

Implementación de queue con arreglo circular (cont)

Page 10: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

10

Listas Enlazadas

Una lista enlazada es una estructura de datos en la que los objetos están ubicados linealmente.

En lugar de índices de arreglo aquí se emplean punteros para agrupar linealmente los elementos.

La lista enlazada permite implementar todas las operaciones de un conjunto dinámico.

En una lista doblemente enlazada cada elemento contiene dos punteros (next, prev). Next apunta al elemento sucesor y prev apunta la predecesor.

Page 11: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

11

Listas Enlazadas (cont.)

Si el predecesor de un elemento es nil, se trata de la cabeza de la lista.

Si el sucesor de un elemento es nil, se trata de la cola de la lista.

Cuando la cabeza es nil, la lista está vacía. Una lista puede ser simplemente enlazada. En

este caso se suprime el campo prev. Si la lista está ordenada, el orden lineal de la

lista corresponde al orden lineal de las claves. En una lista circular, el prev de la cabeza

apunta a la cola y el next de la cola apunta a la cabeza.

Page 12: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

12

Vistazo a punteros en C

Implementación de Listas doblemente Enlazadas (Sin Centinela)

typedef struct nodo_lista {struct nodo_lista * prev;struct nodo_lista * next;int elemento;

} NodoLista;typedef NodoLista * P_NodoLista;

P_NodoLista List_Search(P_NodoLista L, int k) {P_NodoLista x = L;while ( x != NULL) {

if ( x->elemento == k) return x;

x = x->next;}

return (NULL);}

Page 13: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

13

Obs: el detalle de los argumentos formales son parte del protocolo para usar la estructura de datos.Lo mostrado aquí no es la única opción válida. Los argumentos para List_Insert pudieron ser, por ejemplo,List_Insert(P_NodoLista *posicion, int elemento)¿Cuál es el tiempo de ejecución de List_Search?¿Cuál es el tiempo de ejecución de List_Insert?

Implementación de Listas doblemente Enlazadas (Sin centinela) (cont) typedef struct nodo_lista {

struct nodo_lista * prev;struct nodo_lista * next;int elemento;

} NodoLista;typedef NodoLista * P_NodoLista;

/* Inserción sólo al comienzo de la lista */ void List_Insert (P_NodoLista *pL, P_NodoLista x) {

x->next = *pL;if (*pL != NULL) /* No es una lista vacía*/

(*pL)->prev = x;*pL = x;x->prev = NULL;

} Si deseamos insertar elementos en cualquier posición, podemos hacerlo más

fácilmente usando una lista doblemente enlazada con centinela.

Page 14: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

14

1.- ¿Cuál es el tiempo de ejecución de List_Delete?2.- Ejercicios: Implementar las operaciones antes descritas para una lista simplemente enlazada.3.- Otra opción para implementar listas doblemente enlazadas es hacer uso de un “Centinela”. En este caso la lista es circular y se inicia con un elemento auxiliar o mudo, apuntando a sí mismo. Así el código no requiere tratar en forma especial las condiciones de borde (eliminación del último elemento y eliminación del primero). Hacer código como ejercicio.

Implementación de Listas doblemente Enlazadas sin Centinela (Continuación) typedef struct nodo_lista {

struct nodo_lista * prev;struct nodo_lista * next;int elemento;

} NodoLista;typedef NodoLista * P_NodoLista; /* repetidos aquí por conveniencia para explicar la función de abajo */

void List_Delete(P_NodoLista * pL, P_NodoLista x) {/* esta función asume que x pertenece a la lista. */

if (x->prev != NULL ) /* No se trata del primer elemento */x->prev -> next = x->next;

else*pL = (*pL)->next; /* elimina el primer elemento */

if ( x->next != NULL)x->next->prev = x->prev;

}

Page 15: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

15

head

Mudo

“Centinela”

Implementación de Listas doblemente Enlazadas Con Centinela

Void List_Delete’( P_NodoLista x) {x->prev->next = x->next;x->next->prev = x->prev;

} P_NodoLista List_Search’(P_NodoLista head, int k) {

P_NodoLista x = head->next;while (x != head && x->elemento != k)

x = x->next;return x==head?NULL:x;

} Void List_Insert’ (P_NodoLista pn, P_NodoLista x) {

/* inserta delante de nodo apuntado por pn*/x->next = pn->next;pn->next->prev = x;pn->next = x;x->prev = pn;

}

Page 16: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

16

Ejercicio 1 Dada una lista circular L doblemente enlazada. Desarrolle una función que

dado dos punteros x, y a nodos de la lista L, permita intercambiarlos. typedef struct _nodo { struct _nodo * next, * prev; Elemento elemento; } NODO;Solución:void swap (NODO *x; NODO*y) {

NODO * tmp;tmp =x->prev;x->prev = y->prev;y->prev = tmp;tmp = x->next;x->next=y->next;y->next = tmp;

}

Page 17: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

17

Pero ¿qué tal si uno de los nodos es el primero o último?void swap_nodos( NODO * * pl, NODO * x, NODO * y) {

NODO * aux = x;

if (x==y) return;

if (*pl==x) *pl = y;

if (*pl==y) *pl = x;

if (x->next !=y && x->prev != y) {

x->prev->next = y;

x->next->prev=y;

y->prev->next = x;

y->next->prev=x;

swap (x,y);

} else if (x->next == y && x->prev == y)

return;

else if (x->next == y) {

y->next->prev = x;

x->prev->next = y;

swap(x,y);

}else {

x->next ->prev =y;

y->prev->next =x;

}

}

Page 18: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

18

Ejercicio 2: Codifique en C las operaciones Enqueue y

Dequeue de una cola (queue) implementada con la estructura de datos lista doblemente enlazada con centinela. Considere la siguiente estructura para cada nodo:

typedef struct nodo_lista {struct nodo_lista * prev;struct nodo_lista * next;int elemento;

} NODO_LISTA;

Page 19: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

19

Ejercicio 2: Solución: Enqueue

void Enqueue(NODO_LISTA * head, NODO_LISTA * x)

{

/* head apunta al centinela y x apunta al nodo que se desea incorporar en la cola*/

x->next = head->next;

head->next->prev = x;

x->prev = head;

head->next=x;

}

Page 20: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

20

Ejercicio 2: Solución :Dequeue

NODO_LISTA * Dequeue(NODO_LISTA * head)

{

/* head apunta al centinela, Dequeue retorna el más antiguo de la cola*/

NODE_LISTA * x; /* nodo a eliminar y retornar */

x = head->prev;

head->prev = x->prev;

x->prev->next=head;

return x;

}

¿Y qué tal si la cola estaba vacía?

Page 21: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

21

Árboles con Raíz

La idea de usar estructuras de datos enlazados se puede usar también para representar árboles.

Como en las listas enlazadas, supondremos que cada nodo del árbol contiene un campo clave, los restantes campos contienen punteros a otros nodos del árbol.

Árboles Binarios En este caso usamos los campos p, left, y right para almacenar punteros al

padre, hijo izquierdo, e hijo derecho de cada nodo del árbol binario. Si x.p == NULL, entonces x es la raíz. Posible estructura a usar:

struct arbol_tag {struct arbol_tag *p; /* Corrección aportada por Enrique Pastene 2004

*/ struct arbol_tag *left;

struct arbol_tab *right;};

Para representar árboles con mayor número de hijos se puede agregar nuevos punteros hacia hijo1, hijo2, .., hijon.

Cuando el número máximo de hijos no está determinado, se puede usar una estructura similar a la de árboles binarios.

Page 22: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

22

NULL

NULLNULL

NULLNULL

NULL

NULL

T

Árboles con Raíz (cont) En este caso una posible estructura es:

struct arbol_tag {struct arbol_tag p; /* apunta hacia el padre */struct arbol_tag left; /* hijo de más a la izquierda */struct arbol_tab right_sibling; /* hacia el hermano derecho */

}; Si p==NULL => se trata de la raíz Si left==NULL => se trata de una hoja. Si right_sibling ==NULL => se trata de el hijo de más a la derecha.

Page 23: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

23

Vistazo a Punteros en C En C se emplea un * para declarar un puntero. Si p apunta a un

carácter, se declara: char *p;Una forma de ver esto es usando la interpretación del operador * para desreferenciar. * permite acceder el contenido del valor de la celda apuntada por la variable que acompaña. Ésta debería ser un puntero para que tenga sentido semántico. Es así como la declaración de varias variables de tipo puntero se puede hacer como: char *p, *q, *s; Esto se puede interpretar como *p es un char, *q es otro char, etc; lo cual indica que p, q, y s son punteros a char.

Las estructura en C se pueden definir como sigue:struct node_tag {

.....};La parte de node_tag es opcional, pero normalmente es necesaria para referirse nuevamente a la misma estructura cuando definimos o declaramos variables, como en:struct node_tag miNodo; /* miNodo es la variable de tipo struct node_tag */

Page 24: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

24

Vistazo a Punteros en C (cont)

Para dar claridad a las estructuras de un programa es común que se defina un nuevo tipo y así se le pueda asignar un nombre que represente de mejor forma el tipo de dato. Por ejemplo:typedef struct node_tag Node_Type;Se crea así un nuevo tipo que si requiere se puede usar como:Node_Type miNodo;

Los campos de una estructura se pueden acceder con el operador . /* un punto*/Por ejemplo: miNodo.next, cuando next es un campo de la estrucutra Node_Type.

Cuando se tiene un puntero a una estructura, hay dos formas de acceder a los miembros de ésta.Vía operador ->, como en:

Sea Node_Type *p, miNode;......... /* otras instrucciones, que asignan valor a p */miNode.next = p-> next;

Vía operador *, como enmiNode.next = (*p).next; /* paréntesis son requeridos por

precedencia de . sobre * */

Page 25: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

25

Vistazo a Punteros en C (cont)

Cuando un puntero no tiene definido su valor, sus campos no deben ser accedidos para no incurrir en errores de acceso fuera del espacio de memoria del usuario (segmentation fault).

Un puntero p=NULL; queda mejor definido para explícitamente indicar que su valor no ha sido definido.NULL está definido en <stdio.h>

Para solicitar memoria de la zona de memoria dinámica se emplea el siguiente llamado al sistema:Sea Node_Type *p;

p = (Node_Type *) malloc (sizeof(Node_Type));malloc asigna un espacio de memoria de tamaño en bytes dado por el argumento y retorna un puntero a esa zona de memoria.

Page 26: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

26Volver

Vistazo a Punteros en C (cont)

sizeof(Node_Type) es un operador interpretado por el compilador y retorna el tamaño en byte requerido por cada variable del tipo indicado.(Node_Type *) es necesario para forzar el tipo del valor retornado, de otra manera el compilar no permitiría el acceso a los campos. El compilador diferencia punteros que apuntan a distintos tipos de datos.El contenido de la memoria asignada está inicialmente indefinido.

Para retornar espacio de memoria dinámica no usado por el programa se usa la función free, como en:

free(p);Luego de este llamado, el puntero p queda apuntando a un valor indefinido.

Page 27: 1 Estructuras de Datos Elementales:stacks (pilas), queues (colas), linked lists (listas enlazadas), y rooted trees (árboles con raíz) Agustín J. González

27

Divertimento

Una fábrica de aspirinas tiene 7 depósitos con aspirinas de 100 mg correspondientes a la producción de cada día de una semana.

El generente de producción fue notificado que durante un día de la semana las aspirinas resultaron de 110 mg.

Usando una pesa sólo una vez, ¿Cómo puede determinar el depósito con las aspirinas defectuosas?

Lu Ma Mi Ju Vi Sa Do