UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO320 Estructuras de Datos y Algoritmos Solución Certamen 01 #include <stdlib.h> #include <stdio.h> #define MAX 5 typedef struct nn { int i1; int i2; struct nn * next; } nodo, * pnodo; pnodo lista1=NULL; pnodo lista2=NULL; pnodo getnodo(void) { pnodo p; if( (p=(pnodo)malloc(sizeof(nodo)))==NULL) return(NULL); else return(p); } void Push(pnodo *ref, int dato1, int dato2) { pnodo newnodo; if ( (newnodo=getnodo( )) == NULL) exit(1); newnodo->i1 = dato1; newnodo->i2 = dato2; newnodo->next = *ref; *ref = newnodo; } void crealista(void) { int i; for(i=0; i<MAX; i++) { Push(&lista1, i, i+1); } Push(&lista2, MAX-i, i-1); } pnodo busca(pnodo pp) { pnodo qq=NULL; while (pp !=NULL) {qq = pp; pp = pp->next;} return (qq); } pnodo busca2(pnodo p, int j, pnodo q) { if(p !=NULL) { while( p->i1 != j ) p = p->next; if (p != NULL) while (q !=NULL) { if(p->i2 == q->i1) return(q); return (NULL); } return(NULL); } Prof. Leopoldo Silva Bijit. 20-08-2004 q = q->next; } 1 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO320 Estructuras de Datos y Algoritmos Solución Certamen 01 int main(void) { pnodo tt; crealista(); printf(" %d \n", ( *(busca(lista1)) ).i1); printf(" %d \n", busca(lista2)->i2); if( (tt=busca2(lista1, 3, lista2)) !=NULL) printf(" %d \n", tt->i2); return(0); } a) Explicar, empleando un diagrama, el paso por referencia de Push. 10 puntos. b) Diagrama de la estructura después de ejecutar crealista(); 20 puntos. c) Explicar acción realizada por busca. 20 puntos. d) Explicar acción realizada por busca2. 30 puntos. e) Determinar qué imprime el programa. 20 puntos. Solución. a) En la función crealista, se tiene un ejemplo de uso de Push. Consideremos el llamado: Push(&lista1, 0, 1). En la definición de Push, el primer argumento es un puntero a puntero a nodo. Dentro de la función, la ocurrencia de *ref, denota a la variable, cuya dirección es pasada en la invocación a la función, en el caso del ejemplo referencia a variable global lista1. Si *ref, aparece a la derecha, formando parte de una expresión, su valor es el almacenado en lista1; si aparece a la izquierda, se escribe en la variable lista. Luego de la invocación a la función, se tiene el siguiente esquema para las variables. Datos Stack Lista1 newnodo ? ref dato1 = 0 dato2 = 1 Nótese que los argumentos son variables almacenadas en el stack, iniciadas con los valores de los parámetros de la invocación. La variable local newnodo, al no estar inicializada en su definición apunta a cualquier lado. Razón por la cual, conviene definirlas e inicializarlas simultáneamente. Después de un llamado exitoso a getnodo(); es decir, malloc asignó una estructura en el heap, un diagrama de la situación es el siguiente: Prof. Leopoldo Silva Bijit. 20-08-2004 2 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO320 Estructuras de Datos y Algoritmos Solución Certamen 01 Datos Stack heap Lista1 newnodo i1 = ? ref i2 = ? dato1 = 0 next = ? dato2 = 1 El resto de la función, escribe en los campos de la estructura creada en el heap. Y además, en último término, sobreescribe en la variable global lista1(en el ejemplo que se analiza). Datos Stack heap Lista1 newnodo i1 = 0 ref i2 = 1 dato1 = 0 next dato2 = 1 Al salir de Push, desaparecen las variables automáticas, ubicadas en el stack, y la situación queda: Datos Stack heap Lista1 i1 = 0 i2 = 1 next Si se hubiera pasado solamente un puntero a nodo como referencia, se tendría el diseño: Prof. Leopoldo Silva Bijit. 20-08-2004 3 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO320 Estructuras de Datos y Algoritmos Solución Certamen 01 void Push2(pnodo ref, int dato1, int dato2) { pnodo newnodo; if ( (newnodo=getnodo()) == NULL) exit(1); newnodo->i1 = dato1; newnodo->i2 = dato2; newnodo->next = ref; ref = newnodo; } Y un ejemplo de uso, sería: Push2(lista1, 0, 1); Lo cual no es equivalente al diseño anterior. Puede comprobarse efectuando un diagrama. Las listas con cabecera ( header ) permiten diseños alternativos. b) Después de crealista, el espacio queda: Datos Lista1 Lista2 heap i1=4 i1 = 3 i1 = 2 i1 = 1 i1 = 0 i2 = 5 i2 = 4 i2 = 3 i2 = 2 i2 = 1 next next next next next i1 =1 i1 = 2 i1 = 3 i1 = 4 i1 = 5 i2 = 3 i2 = 2 i2 = 1 i2 = 0 next next next next i2 = -1 next c) A la función busca, se le pasa la dirección del primer nodo de la lista. Si la lista es vacía, retorna un puntero nulo. Si no es vacía, con pp recorre la lista, dejando qq apuntado al nodo corriente y con pp al próximo. Cuando se llega con pp, al final de la lista, qq apunta al último nodo de la lista. d) La función busca2 intenta encontrar el valor j en el campo i1 del nodo apuntado por el argumento p. Si la lista es vacía retorna un puntero nulo; en caso de encontrar el valor j, busca a partir del nodo apuntado por q, el valor del campo i1 que sea igual al valor del campo i2 del nodo donde quedó apuntando p. Si lo encuentra, retorna un puntero al nodo que cumple la condición anterior; en caso contrario, retorna un puntero nulo. La invocación: busca2(lista1, 3, lista2), después del primer while, deja p apuntando al segundo nodo de la lista1, el segundo while deja q apuntando al cuarto nodo de la lista2; ya que busca en ésta el valor 4 en el campo i1. Prof. Leopoldo Silva Bijit. 20-08-2004 4 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO320 Estructuras de Datos y Algoritmos Solución Certamen 01 El diseño de la función incurre en un error frecuente, en este tipo de problemas. ¿Qué ocurre si el valor j no se encuentra en ningún campo i1 de la lista apuntada por p?. Cuando p tome valor nulo, intentará leer p->i1 en las primeras direcciones de memoria, las cuales suelen pertenecer a un segmento del sistema operativo. Lo más seguro que la ejecución del proceso aborte debido a un error de segmentación. Se podría corregir, cambiando: while( p->i1 != j ) p = p->next; por: while ((p != NULL)&&( p->i1 != j )) p = p->next; Lo cual funciona debido a que el and opera con cortocircuito. e) En primer término imprime el campo i1 del último nodo de la lista1. Luego el campo i2 del último nodo de la lista2. Finalmente el campo i2 de la lista 2, cuyo campo i1 es 4; ya que en la lista1, el campo i2 es 4, cuando el campo i1 es 3. Imprimiría: 0 -1 0 Prof. Leopoldo Silva Bijit. 20-08-2004 5