Estructuras de Datos y de la Información I
Problemas
Introducción
1. (E) La asignatura FEJ I tiene 4 temas en su programa, y en su examen se preguntan 2.
Un estudiante la aprueba si responde a un tema. ¿Cuántos temas hay que estudiar para
para tener una probabilidad del 50% de aprobar?
2. (E) En el curso 2003–2004 se cambian las normas de evaluación de FEJ I y aunque en su
examen se preguntan 2 de los 4 temas, una de las preguntas es de aprobado obligatorio
para aprobar. ¿Cuántos temas hay que saberse en 2003–2004 para tener una probabilidad
del 50% de aprobar?
3. (P) Un problema de Algebra: Otto y Fritz son dos estudiantes de Ingenierı́a Informática
en la UAM. Otto, que atiende con regularidad a las clases de Algebra, propone a Fritz el
siguiente truquete aritmético:
• piensa un número entre 6 y 15;
• multiplı́calo por 9 y réstale 6;
• mientras el número resultante tenga dos cifras, súmalas;
• suma 1 al número resultante;
• piensa ahora un paı́s europeo cuyo nombre empiece por la letra del abecedario cuyo
orden corresponde al del número obtenido;
• piensa en un animal cuyo nombre empiece por la letra siguiente;
• dime el color de dicho animal.
Fritz efectúa diligentemente estas operaciones, respondiendo finalmente que gris, a lo que
Otto contesta que el paı́s es Dinamarca y el animal el elefante. Fritz queda entonces maravillado por que precisamente habı́a pensado en ese paı́s y ese animal. ¿Cómo consiguió
Otto leer el pensamiento de Fritz?
4. (P) Otro problema de Algebra: Tras lo anterior Otto intenta explicar el truco a Fritz, pero
éste sale de estampida a hacer un programa con memoria dinámica mediante TurboH
Intercalada y que se linke al diccionario de su procesador de textos WordGuay para
encontrar el paı́s y el animal. A los pocos dı́as Fritz, que no atiende con regularidad a
las clases de Algebra, busca desesperado a Otto y le dice que pese a haber compilado
el programa con siete modos de memoria distintos y debuggeado hasta el agotamiento,
la respuesta ya no es Dinamarca y elefante. Otto pide a Fritz el pseudocódigo de su
programa y éste le muestra lo siguiente:
• piensa un número entre 6 y 15;
• réstale 6 y multiplı́calo por 9;
1
• mientras el número resultante tenga dos cifras, súmalas;
• suma 1 al número resultante;
• piensa ahora un paı́s europeo cuyo nombre empiece por la letra del abecedario cuyo
orden corresponde al del número obtenido;
• piensa en un animal cuyo nombre empiece por la letra siguiente;
• dime el color de dicho animal (Fritz indica que su respuesta es también gris)
Tras un momento, Otto comenta que el programa dará como paı́s probablemente Alemania
y ballena como animal. Otra vez Fritz queda maravillado porque ésa es la respuesta de
su programa. ¿Cómo ha conseguido Otto predecirla de nuevo?
5. (P) Un problema de Ingenierı́a Informática: Los dos problemas anteriores tienen moraleja.
¿Cuál es?
C básico
6. (E) Reescribir la siguiente función para que haga lo mismo de manera más transparente:
void HaceAlgo(int *uno, int *otro)
{
*uno = *otro - *uno;
*otro = *otro - *uno;
*uno = *otro + *uno;
}
7. (E) Hacer lo mismo con:
void Pregunta(int *a17, int *algo)
{
int uno, otro, otromas;
uno=otro;otromas=*a17;
otro=*algo;uno=otromas;*a17=otro;
unomas=otro;
*algo=uno;uno=otro;otro=*algo;
}
8. (E) Escribir una función C que calcule la media, varianza y la desviación tı́pica de una
sucesión de números.
9. (P) Si y es una aproximación a la raı́z cúbica de x, z definida como
z=
2y + x/y 2
3
lo es aun mejor. Usar este hecho para escribir una función que calcule por sucesivas
iteraciones la raiz cúbica de un número, y un programa que la use. Prestar atención al
diseño de la función y a su validación.
10. (E) Diseñar pruebas funcionales para comprobar lo siguiente
2
• una función que devuelve el máximo de sus tres parámetros, siendo estos floats;
• una función que calcule la raiz cuadrada de un número x iterando el hecho de que
si y es una aproximación a dicha raiz
Ã
x
1
z=
y+
2
y
!
lo es aún mejor;
• una función que devuelve el máximo común divisor de sus dos parámetros, siendo
estos ints (construir esta función sobre el algoritmo de Euclides).
11. (E) Diseñar y programar una función que calcule las raı́ces (posiblemente complejas) de
la ecuación de segundo grado ax2 + bx + c.
12. (Q) En C el operador sizeof(type) da el tamaño en bytes de los datos de un cierto
tipo type. Hallar el tamaño de los tipos predefinidos en C, incluyendo su variantes.
Hacer lo mismo con los correspondientes punteros y con tablas de datos de dichos tipos
relacionándolas con sus dimensiones.
13. (Q) ¿Qué valores puede devolver la siguiente llamada a fscanf
fscanf(p_file,"%d %s %d", &int1, string, &int2); ?
Dar ejemplos de situaciones en que dichos retornos se produzcan.
14. (Q) Supongamos que en un programa C una tabla se declara como int ii[20]; y
se inicializa mediante la sentencia ii[i]=i;. ¿Cuál es el resultado de las siguientes
expresiones?
(int) *((float *) (ii+2) + 2);
65 + (char) * (int *) ((char *) (ii + 1) +16);
15. (Q) ¿Cuáles son los valores en tu compilador C de las constantes EOF y NULL?
16. (E) Sobre el siguiente código para el cálculo del mcd de dos enteros
#include<stdio.h>
main()
{
int A,B,R,T;
printf("\nA, B? ");
scanf("%d %d", &A, &B);
if (A<B) {
T=A;
A=B;
B=T;
3
}
R=A%B;
while(R>0) {
A=B;
B=R;
R=A%B;
}
printf("\nmcd = %d", B);
}
y sucesivamente:
(a) eliminar una variable;
(b) eliminar la sentencia if;
(c) reducir al máximo el bloque while;
¿Cómo habrı́a que modificarlo para hacerlo a prueba de crashes?
17. (Q) A la hora de comprobar la validez de un programa hay que utilizar para las pruebas
datos normales, datos extremos (esto es, datos aceptables, pero que aparecen en raras
ocasiones) y datos ilegales (esto es, datos que el programa no puede aceptar).
Dar ejemplos de estos datos para el programa anterior, comprobar su efecto, y reescribirlo
para que pueda recibir y procesar adecuadamente cualquier entrada.
18. (Q) En el programa anterior, reescribir la entrada y salida de datos para que funcione
mediante ficheros.
19. (Q) En el programa anterior, reescribir el bloque while como un for.
20. (P) En C los ı́ndices de una tabla de dimensión N empiezan desde 0 y acaban en N-1.
¿Cómo se podrı́a hacer que empezaran en 1 y acabaran en N? (Sugerencia: punteros).
21. (E) Escribir una función que reciba dos vectores float y devuelva su producto escalar,
calculado mediante una suma en ı́ndices desde 1 a la dimensión de los vectores.
22. (P) Construir una función C que intercambie el contenido de dos tablas float intercambiando punteros a las mismas.
23. (Q) Definir un tipo mes mediante una constante enumerada que contenga los nombres
de los meses del año. Construir una función mes siguiente que reciba un dato mes y
devuelva el mes siguiente o un código de error.
24. (P) Construir una función C que intercambie el contenido de dos matrices (esto es, dos
tablas bidimensionales) float intercambiando punteros a las mismas.
25. (P) Supongamos que un número real se representa en C como
4
struct real {
int izqda, unsigned int drcha;
};
donde izqda representa las cifras a la izquierda del punto decimal y drcha las cifras
decimales. Si izqda es negativo, se supone que el número es negativo.
•
•
•
•
Definir un tipo de dato a partir de struct real.
Escribir una función que transforma floats en reals.
Escribir una función que transforma reals en floats.
Escribir funciones suma, resta y prod que reciban dos datos real y devuelvan un
dato real con su suma, resta y producto.
26. (Q) Un programa C necesita unas funciones para manejar números complejos y otras
funciones de propósito general. A su vez las funciones complejas necesitan llamar en
ocasiones a las funciones de propósito general. Por razones de reusabilidad, interesa
repartir el programa en tres ficheros: uno para main y otras funciones espcı́ficas del
programa, otro para las funciones complejas y otro para las de propósito general. ¿Cuáles
deben ser los inicios de los tres ficheros?
27. (E) Escribir una función C de nombre Aviso que reciba una cadena de caracteres y
la imprima en el dispositivo stderr (pregunta: ¿qué es stderr?; solución: Kernighan–
Ritchie).
28. (E) Escribir una función C de nombre Error que reciba una cadena de caracteres, la
imprima en el dispositivo stderr y fuerce una salida del programa.
Tipos Abstractos de Datos
29. (E) Diseñar y programar funciones C que sobre el tipo abstracto fcomplex realicen el
producto y división de números complejos, la inversión de un complejo y el cálculo del
módulo de un complejo.
30. (E+) Las primitivas de un TAD ent para datos que sean números enteros tienen las
especificaciones siguientes:
ent Suma(ent A, ent B);
ent Neg(ent A);
donde la primera devuelve la suma de dos enteros y la segunda el negativo.
Especificar en detalle y dar el psc de las siguientes rutinas derivadas a partir de las dos
anteriores:
ent Resta(ent A, ent B): devuelve la resta de dos enteros;
ent Prod(ent A, ent B): devuelve el producto de dos enteros;
ent Cociente(ent A, ent B): devuelve el cociente de dos enteros;
ent Resto(ent A, ent B): devuelve el resto de dividir dos enteros;
ent Pot(ent A, ent B): devuelve el valor de AB ;
ent Log(ent A, ent B): devuelve el valor de la parte entera de logA B.
5
31. (E+) Las primitivas de un TAD CC para cadenas de caracteres tienen las especificaciones
siguientes:
bool CCVacia(CC A): devuelve V o F según A esté o no vacı́a;
status CCIns(car c, pos i, CC A): intenta insertar el carácter c en la posición i de
la cadena A, y devuelve Ok o Error;
status CCExt(car c, pos i, CC A): intenta extraer en el carácter c el situado en la
posición i de la cadena A, y devuelve Ok o Error;
status CCIni(CC A): intenta crear una cadena vacı́a A y devuelve Ok o Error.
Especificar en detalle y dar el psc de las siguientes rutinas derivadas a partir de las
anteriores:
status CCIdent(car c, pos i, CC A): intenta situar en el carácter c el situado en la
posición i de la cadena A sin modificar ésta, y devuelve Ok o Error;
ent CCLong(CC A): devuelve la longitud de la cadena A;
status CCConcat(CC A, CC B): intenta concatenar la cadena B tras la cadena A, y
devuelve Ok o Error;
pos CCLocaliza(car c, CC A): devuelve la posición del carácter c en la cadena o un
código de error adecuado.
32. (E+) Para el TAD CC se decide usar como EdD una tabla de caracteres de longitud fija
LONG MAX CC, donde se indica con el sı́mbolo especial @ el final de la cadena.
Dar el psc de las primitivas del TAD CC indicadas en el problema anterior.
33. (Q) Si se utiliza la EdD anterior, ¿convendrı́a, por razones de eficacia u otras, incorporar
algunas de las rutinas derivadas del TAD CC al grupo inicial de primitivas? Dar el psc
de las nuevas primitivas.
34. (E+) Para el TAD de datos enteros se va a usar el dato int de C como estructura de
datos y el operador binario de suma y el unario de negativo para las primitivas de suma
y negativo de la interfaz del TAD.
Usando únicamente los elementos anteriores y sin efectuar control de errores, dar el código
C de las siguientes funciones
int Resta(int A, int B): devuelve la resta de dos enteros;
int Prod(int A, int B): devuelve el producto de dos enteros;
int Cociente(int A, int B): devuelve el cociente de dos enteros;
int Resto(int A, int B): devuelve el resto de dividir dos enteros;
int Pot(int A, int B): devuelve el valor de AB ;
int Log(int A, int B): devuelve el valor de la parte entera de logA B.
35. (P) Si en el problema anterior se desea controlar posibles overflows, ¿cómo habrı́a que
modificar los prototipos de las funciones? Implementar las correspondientes versiones C
de las rutinas en cuestión.
6
36. (E+) Para el TAD CC se propone usar como estructura de datos en C una tabla de
datos char de longitud fija LONGMAX. Definir el correspondiente tipo de dato CC y dar
el código C de las primitivas bool CCVacia(CC A), status CCIns(char c, int i, CC
A), status CCExt(char c, int i, CC A), status CCIni(CC A) y bool CCLlena(CC
A).
37. (E+) Sobre las primitivas del problema anterior, dar el código C del resto de las funciones
consideradas anteriormente sobre el TAD CC.
Pilas
38. (P) Escribir el psc de una función de prototipo status CheckParChap(cadena str)
que compruebe si los paréntesis de la cadena de caracteres str están bien equilibrados
utilizando para ello un contador de paréntesis.
39. (P) Modificar el psc la función anterior construyendo otra de nombre CheckTodoChap
que mediante contadores compruebe el buen equilibrado de paréntesis, corchetes y llaves.
40. (E) Sobre las sentencias siguientes, determinar la corrección del equilibrado de sı́mbolos
{, (, [ mediante la evolución de una pila:
1: ({[a+((b+c)-(d*e)+{h-g}]+{[b-a]*(f-i)}});
2: (a+[b*{c-{d+e/(f)}-g}]+(v-{w*(x+a)}));
41. (P) Escribir el psc de una función que reciba una cadena y la imprima mediante una pila
en forma invertida usando para ello las primitivas de este TAD.
42. (P) Escribir el psc de una función (o un conjunto de funciones) que proporcionen la
representación binaria de un int mediante el algoritmo de división repetida por 2 y
acumulación de restos (Pista: introducir los restos en una pila e imprimir su contenido
hasta vaciarla).
Hacer lo mismo para las representaciones octales y hexadecimales.
43. (E+) Dar versiones más compactas del código de las funciones primitivas de pilas discutidas en clase.
44. (E) Evaluar mediante pilas las siguientes expresiones sufijo:
5 4 3 2 1 * + 1 2 3 * / 2 * 1 / 2 + - + *;
1 2 * 4 2 / 3 4 + 5 * - +;
2 1 / 4 2 * + 6 5 - 8 2 / + +;
45. (P) Modificar el psc de la función de evaluación mediante pilas de expresiones sufijo
descrita en clase incluyendo cuidadosamente posibles condiciones de error.
Hacer también lo mismo con la función de conversión de expresiones infijo en sufijo.
46. (E) Convertir en sufijo las siguientes expresiones infijo según las reglas de precedencia y
asociatividad de operadores discutidas en clase:
7
A + B * C / D - F + G - H * I * J;
A * B * C / D / E - F * G + H - I;
47. (E) Convertir en sufijo las expresiones anteriores utilizando una pila (señalar adecuadamente la evolución de la misma).
48. (E) Escribir una función C de nombre Top que reciba un puntero a pila y devuelva el
dato en su top sin efectuar un pop del mismo.
49. (E) El operador de exponenciación suele denotarse como ^ (esto es, A ^ B = AB ) y su
precedencia es superior a la de * y /. Convertir a sufijo las siguientes expresiones de
acuerdo con las reglas habituales de precedencia y asociatividad:
A + B ^ C / D ^ F + G ^ H * I * J;
A * B * C ^ D ^ E - F * G + H ^ I;
50. (P) Ampliar el algoritmo de evaluación de expresiones postfijo con respecto al operador
de exponenciación ^ , que se considera de prioridad superior a * o /.
51. (P) ¿Cómo habrı́a que modificar el algoritmo de conversión infijo–sufijo para que tenga
en cuenta el uso de paréntesis en la expresión infijo?
52. (E) Convertir a prefijo las siguientes expresiones infijo:
(a) A + B - C;
(b) (A + B) * (C - D);
(c) A ^ B * C - D + E / F / (G + H);
(d) ((A + B) * C - (D - E)) ^ (F + G);
(e) A - B / (C * D ^ E).
53. (E) A la vista del problema anterior, desarrollar un algoritmo de evalución de expresiones
prefijo, y evaluar las expresiones
(a) + - 1 2 3;
(b) + + 4 - * ^ 1 2 1 / + 5 6 * 7 8 9;
(c) + - ^ 2 2 2 * 3 * * 4 5 4.
54. (P) Desarrollar sobre ejemplos un algoritmo de conversión de expresiones postfijo a infijo
y comprobarlo adecuadamente.
55. (P) Desarrollar sobre ejemplos un algoritmo de conversión de expresiones infijo a prefijo
y comprobarlo adecuadamente.
56. (P) El siguiente pseudocódigo corresponde a una primera versión de una rutina de evaluación de expresiones sufijo:
8
status EvalSufijo(CC S, valor V)
PilaIni(P) ;
mientras (op = LeerCC(S)) != EOS :
si EsOperando (op) == T :
Push (op, P) ;
else si EsOperador (op) == T :
Pop (op2, P) ;
Pop (op1, P) ;
res = Evaluar(op1, op, op2) ;
Push (res, P) ;
else
devolver Error ;
Pop (V, P) ;
si PilaVacia (P) == T :
devolver Ok;
else :
devolver Error ;
En el mismo,
1. la función elemento LeerCC(CC S) lee un elemento de una cadena S;
2. la función bool EsOperando(elemento op) devuelve T o F según el elemento op sea
o no un operando;
3. la función bool EsOperador(elemento op) devuelve T o F según el elemento op sea
o no un operador;
4. la función valor Evaluar (elemento op1, elemento op2, elemento op3) evalúa
el resultado de la operación de acuerdo al operador y operandos indicados.
Modificar dicha rutina de manera adecuada para que procese correctamente posibles situaciones de error.
57. (P+) Una máquina tiene un único registro y las siguientes cuatro instrucciones:
• LD A: coloca el contenido de A en el registro;
• ST A: coloca el contenido del registro en A;
• AD A: suma al valor en el registro el valor de A;
• SB A: resta al valor en el registro el valor de A;
Escribir un programa que acepte una expresión postfijo con los operadores +, - e imprima
una sucesión de instrucciones máquina que dejen en el registro el valor de la expresión.
Por ejemplo, al recibir
A B C - +
el programa escribirá
LD B
SB C
AD A
9
58. (E) Se quiere traducir a sufijo la expresión A + B * C ^ E - F / G; mediante una pila y
según el algoritmo visto en clase. Leyendo esta expresión de izquierda a derecha, indicar
razonadamente y para cada operando u operador leı́dos la evolución de dicha pila, ası́
como la traducción parcial efectuada hasta el momento de procesar cada operando u
operador.
59. (P) Una implementación de pilas tiene como único interfaz las funciones status PilaIni(pila
S), que inicializa una pila, boolean PilaVacia(pila S), que comprueba si una pila está
vacı́a, status Push(elemento E, pila S), que coloca el elemento E en la pila, y status
Pop(elemento E, pila S), que saca el elemento que ocupa el top (o la cima) de la pila
y lo sitúa en el elemento pasado como parámetro.
Dar razonadamente el pseudocódigo de una función de prototipo int PilaCuenta(pila
S) que reciba como parámetro un pila como las descritas arriba, y devuelva su número de
elementos. Por supuesto, los datos de la pila pasada como parámetro no deben alterarse,
y la función debiera hacer el control de errores pertinente.
60. (P) Una implementación de pilas tiene como único interfaz las funciones
status PilaIni(pila S), que inicializa una pila,
boolean PilaVacia(pila S), que comprueba si una pila está vacı́a,
status Push(elemento E, pila S), que coloca el elemento E en la pila, y
status Pop(elemento E, pila S), que saca el elemento que ocupa el top (o la cima)
de la pila y lo sitúa en el elemento pasado como parámetro.
Dar razonadamente el pseudocódigo de una función de prototipo status PilaPrimero(elemento
E, pila S) que sitúe en el dato E el primer elemento introducido en dicha pila.
Escribir primero una versión simple de dicha función, que no efectúe control de errores,
para luego ampliarla con dicho control. Suponer para ello que un Push precedido de un
Pop sobre la misma pila no causa error.
Colas
61. (E) Se implementa una cola circular mediante un tabla de dimensión 5 y se inicializa
dando a front y rear el valor 0. Además, si se intenta efectuar en la cola una operación
no factible, el programa de implementación lo indica, pero permite continuar el trabajo
con ella. Indicar la ubicación de front y rear tras las siguientes operaciones, considerando
si pueden llevarse a cabo:
• dos Adds; dos Removes; tres Adds; un Remove; tres Adds; un Remove.
• dos Adds; un Remove; tres Adds; cuatro Removes.
62. (P) Una implementación de colas tiene como único interfaz las funciones
status ColaIni(cola Q),
boolean ColaVacia(cola Q),
status ColaIns(dato D, cola Q), que inserta el dato D en la cola, devolviendo OK o
ERR según proceda, y
status ColaExt(dato D, cola Q), que extrae el elemento inicial de la cola y sitúa su
dato en D, devolviendo OK o ERR según proceda.
Además se dispone de un elemento especial FLAG, generalmente distinto de los almacenados en una cola, y que puede insertarse y extraerse de las mismas.
10
Suponiendo que una inserción precedida de una extracción sobre la misma cola no causa
error, dar razonadamente el pseudocódigo de una función int ColaCuenta(cola Q) que
reciba una cola sin ningún elemento FLAG y devuelva el número de datos en la misma.
Para ello
• dar una primera versión sin control de errores;
• identificar posibles situaciones de error;
• dar una segunda versión que detecte dichos errores y efectúe la gestión y recuperación
adecuadas.
63. (P) De una implementación del TAD Cola se dispone solamente de las primitivas status
ColaIni(cola Q),
bool ColaVacia(cola Q), status ColaInsertar(dato D, cola Q),
status ColaExtraer(dato D, cola Q).
Definiendo el tipo dato como una estructura con dos campos de tipo int llamados
Prioridad y Valor inserción, dar el pseudocódigo de una función int InsertaConPrioridad(dato
D, cola Q) que actualize Q poniendo D detras de todos los datos con mayor o igual prioridad y delante de todos los datos con menor prioridad. Los elementos ya en la cola
debe mantenerse tras la inserción en el mismo orden en el que se encontraban.
64. (P) Si se desea definir un TAD Cola de Prioridad, ¿cuáles serı́an las correspondientes
primitivas?
65. (P) Una implementación de colas tiene como único interfaz las funciones
status ColaIni(cola Q),
boolean ColaVacia(cola Q),
status ColaIns(dato D, cola Q), que inserta el dato D en la cola, y
status ColaExt(dato D, cola Q), que extrae el elemento inicial de la cola y sitúa su
dato en D,
int ColaCuenta(cola Q), que devuelve el número de elementos en la cola.
Dar razonadamente el pseudocódigo de una función de prototipo status JuntaColas(cola
A, cola B) que reciba dos tales colas A, B y modifique la primera situando a continuación de su último elemento los de la cola B (que debe de quedar vacı́a si la unión de
colas se efectúa con éxito) en su orden propio. La función sólo debe utilizar las primitivas
anteriores y hacer el control y recuperación de errores pertinente. Para ello
• dar una primera versión sin control de errores;
• identificar posibles situaciones de error;
• dar una segunda versión que detecte dichos errores y efectúe la gestión y recuperación
adecuadas. Suponer para ello que una inserción precedida de una extracción sobre
la misma cola no causa error.
C avanzado
66. (Q) En C una tabla bidimensional se declara en la forma tipo ntb[dim1][dim2], donde
tipo denota un tipo de dato C, ntb es el nombre de la tabla y dim1, dim2 son números
enteros con las dimensiones de la tabla. Declarar
11
(a) una tabla bidimensional de datos float y dimensiones DIM1 y DIM2;
(b) una tabla bidimensional de punteros a datos char y dimensiones DIM1 y DIM2.
67. (Q) Una tabla bidimensional se almacena en memoria de tal manera que varı́e más rápido
el ı́ndice más a la derecha. ¿Cuál serı́a el orden de almacenamiento de los elementos de
la tabla int tbi[2][3]? ¿Cuántos bytes requiere el almacenamiento de dicha tabla?
68. (Q) Supongamos declarada la tabla int tbi[3][2]; C la maneja como una “tabla de
tablas” en el sentido siguiente: tbi se interpreta como una tabla con 3 elementos, cada
uno de los cuales es una tabla con 2 datos int. Por ejemplo, en la declaración anterior
tbi[2] es una tabla de 2 datos int.
Sobre el almacenamiento en memoria de la tabla int tbi[3][2], ¿a donde apuntan
tbi[0], tbi[1] y tbi[2]?
Expresar el contenido de tbi[j][k] en función de tbi[j] y de k.
69. (Q) Sobre la tabla anterior, expresar el valor designado como tbi[j] en función de tbi
y de j. ¿Cómo se expresarı́a entonces tbi[j][k] en función de tbi, j y k?
70. (Q) Sobre la tabla anterior, dar expresiones equivalentes a &tbi[j], &tbi[j][k], tbi[0][0]
y &tbi[0][0].
71. (P) ¿Cómo se podrı́a hacer que empezaran en 1 los ı́ndices de una tabla bidimensional
de dimensiones M y N?
72. (E) Escribir una función C que reciba como argumento un puntero a un vector de datos
genéricos y libere la memoria que éste ocupe.
73. (P) Escribir una función C que reciba como argumento un entero n, reserve memoria
para un vector de floats de ese tamaño y devuelva un puntero (esto es, el puntero es el
retorno de la función) que permita trabajar con dicho vector en un rango de ı́ndices entre
1 y n.
74. (P) Escribir un programa C que lea un entero n, reserve mediante una función memoria
para dos vectores de dimensión n, los lea, llame a una función que calcule su producto
escalar, lo imprima y libere la memoria ocupada por ambos vectores. El programa debe
considerar posibles condiciones de error.
75. (P) Escribir una función C que reciba como argumentos dos enteros m, n, y reserve
memoria para una matriz de floats de esas dimensiones de acuerdo al siguiente esquema:
i. se reserva memoria para un vector de m punteros a float, uno para cada una de las
filas de la matriz;
ii. se reserva memoria para n vectores float guardándose los correspondientes punteros
en el vector anteriormente definido;
la función debe devolver un puntero (identificar cuál) que permita trabajar con dicha
matriz según la notación habitual a[i][j].
76. (P) Escribir una función C que reciba como argumento un puntero a una matriz como la
del problema anterior y libere la memoria que ocupe.
77. (Q) Se define el siguiente macro del preprocesador:
12
#define max (A, B) ((A) > (B) ? (A) : (B))
¿Qué problemas puede plantear esta definición? (obsérvese que supone una doble evaluación de las expresiones para A y B).
78. (E) La función calloc de la librerı́a stdlib.h realiza las mismas funciones que malloc
pero inicializa a 0 los bytes de memoria reservados. Escribir una versión de nombre
micalloc que con el mismo prototipo que calloc efectúe también sus mismas tareas.
¿Cómo se efectuarı́a en las mismas el control de errores?
79. (P) Modificar la rutina push de la implementación de pilas estáticas discutida en clase
para que en caso de que la pila esté llena, recoloque sus datos en una pila de tamaño
incrementado por una cantidad fija PILAINCR.
80. (P) En el modelo de pila del problema anterior, modificar la función pop para que ajuste
el tamaño de la pila al número de datos existentes de manera que no estén en uso a lo
sumo el doble de PILAINCR posiciones.
Listas
81. (E) Escribir variantes de la función GetNodoLE que
1. o bien no reciban ningún argumento;
2. o bien no devuelvan dato alguno.
82. (P) Definir macros en C para el acceso a los campos info y next de los nodos de una
lista enlazada.
83. (E+) Implementar una función C que reciba un puntero a una lista y proporcione el
número de nodos de una lista enlazada. Escribir primero su pseudocódigo y considerar
posibles situaciones de error.
84. (E+) Implementar una función C que reciba dos punteros a listas enlazadas y devuelva un
puntero a una lista que concatene ambas. Escribir primero su pseudocódigo y considerar
posibles situaciones de error.
85. (E+) Reescribir las funciones básicas sobre listas para su funcionamiento en listas circulares enlazadas.
86. (E+) Implementar una función que reciba un puntero a un nodo de una lista enlazada
e intercambie dicho puntero con el que le sigue. Escribir primero su pseudocódigo y
considerar posibles situaciones de error.
87. (E+) Implementar una función que reciba un puntero a un nodo de una lista doblemente
enlazada e intercambie dicho puntero con su anterior. Escribir primero su pseudocódigo
y considerar posibles situaciones de error.
88. (E) Implementar las primitivas del TAD cola usando como edd una lista enlazada circular.
13
89. (P) Flavio Josefo fué un famoso historiador del primer siglo. Aparentemente, su talento
matemático le permitió vivir lo suficiente para alcanzar la fama. En efecto, durante la
guerra judeo–romana, Josefo formó parte de un grupo de 41 rebeldes judı́os que fueron
cercados por los romanos. Prefiriendo morir a rendirse, decidieron formar un cı́rculo y
procediendo en el sentido de las agujas de un reloj, matar a uno de cada tres rebeldes
hasta que sólo quedaran dos que, en teorı́a, se suicidarı́an entonces. Sin embargo, a
Josefo y a un amigo suyo esta idea no les parecı́a demasiado buena y decidieron situarse
adecuadamente para ser los dos últimos supervivientes. ¿Cómo podrı́an Josefo y su amigo
haber decidido las posiciones adecuadas de haber dispuesto de un ordenador (portátil) y
de una implementación en C de listas circulares enlazadas?
90. (P) Construir una función C que usando como edd una lista enlazada circular, reciba
dos números N y M representando respectivamente un número de rebeldes cercados,
supuestamente numerados de 1 a N, y de cuanto en cuanto estos se rebeldes se matan
(empezando por la primera posición y en el sentido de las agujas del reloj); en el caso
del problema clásico de Josefo la función recibirı́a 41 y 3. La función debe imprimir
los números iniciales de los sucesivos muertos, ası́ como las posiciones originales de los
rebeldes que no mueren (al menos de momento).
91. (E) En una lista circular enlazada inicialmente vacı́a y apuntada por un puntero p se
añaden sucesivamente nodos en su final. El campo info del nodo añadido en i–ésimo
lugar contiene el valor i, donde i toma los valores 1, 2, 3, etc.
Dar dos diagramas que recojan el estado del puntero p y de los nodos con sus campos
info y next tras la inserción del primer nodo y del cuarto nodo.
92. (E) En una lista circular doblemente enlazada inicialmente vacı́a y apuntada por un
puntero p se añaden sucesivamente nodos en su final. El campo info del nodo añadido
en i–ésimo lugar contiene el valor i, donde i toma los valores 1, 2, 3, etc.
Dar dos diagramas que recojan el estado del puntero p y de los nodos con sus campos
info, prev y next tras la inserción del primer nodo y del cuarto nodo.
93. (E+) Escribir funciones C de nombres LeInsHead, LeInsTail, LeDelHead y LeDelTail
que implementen las correspondientes primitivas para listas enlazadas.
94. (E+) Escribir funciones C de nombres LdeInsHead, LdeInsTail, LdeDelHead y LdeDelTail
que implementen las correspondientes primitivas para listas doblemente enlazadas.
95. (P) Escribir funciones C que inserten un dato en el lugar i–ésimo de una lista enlazada,
y también que lo extraigan del mismo.
96. (P) Escribir una función C que intercambie el nodo i–ésimo con el que le sigue.
97. (P) Dar versiones de las funciones anteriores para listas doblemente enlazadas.
98. (E+) Escribir una función C que reciba un puntero a una lista enlazada e intercambie las
posiciones de sus nodos primero y último. Escribir primero su pseudocódigo y considerar
posibles situaciones de error.
99. (P) Escribir una función C que reciba un puntero a una lista doblemente enlazada e
intercambie las posiciones de sus nodos primero y último.
Escribir primero su pseudocódigo y considerar posibles situaciones de error.
14
100. (P) Escribir una función C de nombre LeReset que reinicialice una lista enlazada.
101. (P) Escribir una función C de nombre LdeProcInv que recorra una lista circular doblemente enlazada en sentido inverso y sobre cada nodo haga actuar una cierta función, que
se pasará a LdeProcInv mediante un puntero a función.
102. (E+) Escribir razonadamente una función C de prototipo int ListaCuenta(listaC
*lc) que reciba un puntero lc a una lista enlazada circular y devuelva su número de
nodos.
Utilizar la siguiente implementación de nodos y listas enlazadas, suponiendo que un puntero del tipo listaC apunta al último nodo de una tal lista.
typedef struct NODO {
generico info;
struct NODO *next;
} nodo;
typedef nodo *listaC;
Árboles
103. (Q) Escribir funciones C que implementen la inserción de un dato en un árbol binario de
búsqueda, la creación de un árbol binario de búsqueda y la búsqueda en un tal árbol de
un dato con una clave dada de acuerdo a la implementación de árboles binarios dada en
clase y a los correspondientes pseudocódigos.
104. (E) Dada la lista de enteros
34 43 12 58 17 92 21 67 56 72 80,
dibujar su correspondiente árbol binario de búsqueda. ¿Qué posiciones ocuparı́an los
enteros
41 77 30
tras su inserción?
105. (E) Escribir funciones C que implementen el recorrido de un árbol binario en órdenes
posterior y simétrico.
106. (Q) Escribir una función C de prototipo ab CombinaAB(generic *pg, ab t1, ab t2)
que reciba dos árboles binarios t1, t2 y devuelva un árbol con t1 como nodo izquierdo,
t2 como nodo derecho y *pg en su campo info.
107. (E) Definir macros en C para el acceso a los campos info y next de los nodos de una
lista enlazada.
15
108. (E) Dadas las expresiones
(A + B) * (C - D)
A ^ B * C - D + E / F / (G + H)
((A + B) * C - (D - E)) ^ (F + G)
A - B / (C * D ^ E),
construir sus respectivos árboles de expresión y recorrerlos en órdenes simétrico, previo y
posterior.
109. (E)
A B
A B
A B
Construir el árbol de
C * D - E F / G /
+ C D - / E F * G
C D - - E F G + +
expresión de las siguientes expresiones sufijo:
- *
- * *
110. (P) Sobre los ejemplos anteriores diseñar un algoritmo de construcción de árboles asociados a expresiones sufijo.
111. (P) Un árbol binario se recorre en nivel si sucesivamente se recorren de izquierda a
derecha los nodos a profundidades 0, 1, 2, etc. Dar el pseudocódigo de una función
Nivel(arbol T) que implemente esta forma de recorrido (sugerencia: utilizar una cola a
la que se añadan los hijos de un nodo dado tras visitarlo).
112. (P) Un árbol binario se recorre en preorden y proporciona la lista
G B Q A C K F P D E R H;
dibujar una posible estructura de nodos. ¿Hay más?
113. (E+) Un árbol binario se recorre en simorden y proporciona la lista
Q B K C F A G P E D H R
dibujar posibles estructuras de nodos.
114. (E) Construir el árbol binario de búsqueda a partir de las listas siguientes
G B Q A C K F P D E R H,
15 22 12 35 31 13 10 27 6 9 25.
115. (E) En un árbol binario de búsqueda se define el predecesor de un dato D como el mayor
dato D’ del árbol tal que D’ < D. Sobre los árboles del problema anterior, encontrar los
predecesores de cada uno de sus elementos.
116. (E) En un árbol binario de búsqueda se define el sucesor de un dato D como el menor
dato D’ del árbol tal que D’ > D. Sobre los árboles del problema anterior, encontrar los
sucesores de cada uno de sus elementos.
117. (P) Dar el pseudocódigo de funciones que reciban un dato de un árbol binario y encuentren su predecesor y su sucesor.
118. (Q) Dar el código de una función C para buscar un dato en un árbol binario de búsqueda.
16
119. (E) Un árbol binario puede usar como edd una tabla con 1 como primer ı́ndice según el
siguiente esquema:
en él la raı́z se ubica en la posición 1, sus hijos en 2 y 3, los hijos respectivos en 4, 5 y 6,
7, etc. Comprobar que, en general, los hijos del nodo de ı́ndice k se hallan en los ı́ndices
2k y 2k + 1.
120. (P) Suponer que los elementos de la tabla del problema anterior son structs, uno de
cuyos campos indica si el nodo está o no vacı́o, y que no hay nodos más allá de la dimensión
TREESIZE de la tabla, que supondremos de la forma 2K − 1. ¿Cómo se implementarı́an
con esta edd las operaciones primitivas del tad árbol binario?
121. (P) Con la implementación del tad árbol binario anterior, escribir funciones C que devuelvan el número de nodos, la profundidad del árbol binario y el número de hojas. Dar
también una función C que reciba un ı́ndice de un nodo de tal tabla y devuelva el ı́ndice
del padre de dicho nodo.
Recursión
122. (E+) Identificar en qué rutinas de recorrido de árboles hay recursiones de cola, eliminar
las que haya y simplificar al máximo el código resultante.
123. (Q) Dar una definición recursiva de árbol binario de búsqueda.
124. (P) Escribir una función C que calcule la profundidad de un árbol binario.
125. (E+) Sobre el pseudocódigo de la función InsABdB, eliminar posibles recursiones de cola
y simplificar al máximo el pseudocódigo resultante.
126. (Q) Escribir una función recursiva en C que reciba un dato generico y un árbol binario
de búsqueda y devuelva un puntero al subárbol en cuya raı́z está dicho dato o señale
adecuadamente que no hay tal subárbol.
127. (P) Eliminar posibles recursiones de cola en la función anterior y simplificar todo lo
posible el código resultante.
128. (P) Los números de Fibonacci se definen mediante la fórmula recurrente
F0 = 0; F1 = 1; Fn = Fn−1 + Fn−2 , n > 1.
Escribir una función recursiva y otra iterativa en C que reciban n y devuelvan Fn .
17
129. (P) Supongamos que los conejitos se hacen adultos en un mes y que una pareja de conejos
adultos, sea cual sea su sexo, producen una pareja de conejitos al mes. Si dos conejos
adultos náufragos llegan a una isla desierta, ¿cuántas parejas de conejos habrá en la isla
tras N meses? (por ejemplo, tras dos meses habrá dos parejas, una de conejitos y otra de
conejos adultos).
130. (P) Simplificar todo lo que sea posible la versión no recursiva de la función factorial dada
en clase (eliminar gotos, simplificar la pila, etc).
131. (P) Dar una versión no recursiva de las torres de Hanoi mediante una pila de manera
análoga a la versión no recursiva de la función factorial dada en clase.
132. (P) A partir de la función recurrente de impresión de los movimientos de las torres de
Hanoi, estimar el número de movimientos necesarios para mover N discos.
133. (Q) Suponiendo que, en efecto, Brahma entregó a los monjes de Hanoi 64 discos al
comienzo de los tiempos, que el mundo acabará cuando todos los discos lleguen al poste
de destino y que los monjes hacen un movimiento correcto por segundo, ¿cuál será aproximadamente la duración del mundo?
134. (Q) Modificar las funciones básicas sobre listas utilizando la implementación del dato
lista obtenido de la definición recursiva de la misma.
135. (Q) Dar una definición recursiva de expresión prefijo.
136. P+ Escribir el pseudocódigo de una función recursiva que reciba una expresión prefijo y
la convierta en sufijo.
137. (E+) Escribir una función C que implemente una versión recursiva del algoritmo de
Euclides de determinación del máximo común divisor de dos enteros positivos.
138. (E+) Escribir funciones recursivas en C que calculen el producto y el cociente de dos
enteros positivos.
139. (E+) Escribir una función recursiva en C que reciba dos enteros positivos X, N y calcule
el valor de XN .
140. (E+) En las funciones de los problemas anteriores identificar posibles recursiones de
cola, eliminarlas y depurar lo más posible el código resultante (en particular, eliminar
sentencias goto).
141. (E+) Identificar y eliminar posibles recursiones de cola en el algoritmo recursivo de
búsqueda binaria explicado en clase.
142. (P) Eliminar mediante una pila la recursión de las funciones anteriores de cálculo de
productos y cocientes, y depurar al máximo el código resultante.
143. (P) El siguiente pseudocódigo corresponde a una función recursiva de multiplicación de
dos enteros positivos:
18
int MultRec(int A, int B)
si B == 0 :
devolver 0 ;
else :
devolver A + MultRec(A, B-1) ;
Suponiendo que sus argumentos son siempre mayores o iguales que 0, eliminar la recursión
de la misma mediante los tres pasos siguientes:
a. Efectuar una eliminación mecánica mediante sentencias goto
b. Eliminar las sentencias goto del código del apartado anterior.
c. Modificar el código del apartado b hasta llegar a una función iterativa con la misma
funcionalidad
144. (P) Sobre la implementación habitual de árboles binarios, el siguiente pseudocódigo sirve
para intercambiar los hijos izquierdo y derecho de todos los subárboles de un AB T:
CambIzqDer(AB T)
si ABVacio(T) == F :
abTemp = izq(T) ; //abTemp: variable temporal
izq(T) = der(T) ;
der(T) = abTemp ;
CambIzqDer(izq(T)) ;
CambIzqDer(der(T)) ;
Eliminar mediante pilas la recursión de la misma de acuerdo a los pasos siguientes:
a. Definir el elemento de pila y efectuar una eliminación mecánica mediante sentencias
goto
b. Eliminar las sentencias goto del código del apartado anterior.
19
Problemas de repaso
145. Al igual que lo explicado para tablas bidimensionales, una tabla multidimensional en C se
contempla como una tabla de tablas de tablas, etc. Por ejemplo, en char tmdc[2][3][2],
tmdc es una tabla con 2 elementos, que a su vez son tablas con 3 elementos, que a su vez
son tablas de 2 datos char. ¿Cómo se almacena en memoria dicha tabla?
146. Dar en función de tmdc, i, j y k expresiones equivalentes de tmdc[i], tmdc[i][j] y
tmdc[i][j][k]. ¿Qué ocurre cuando i=j=k=0?
147. En C una tabla de punteros se declara como tipo *tp[dim]. Un programa concreto
contiene las declaraciones
int tbdi[5][10];
int *tpi[5];
¿Cómo se podrı́a utilizar tpi para manejar datos en tbdi[j][k]?
148. Un cierto compilador C proporciona a char e int los tamaños 1 y 2, respectivamente.
¿Qué produce el siguiente código C?
int i,j;
int tbi[10][10];
int *tpi[10];
int **ppi;
ppi=tpi;
for (i=0; i<10; i++)
{
tpi[i]=tbi[i];
for(j=0; j<10; j++)
tbi[i][j]=i+j;
}
printf("%d %d %d \n", **(ppi+1), *(*(ppi+2)+3),
*((char *)(*(ppi+3)) + 8));
149. Un programa de gráficos maneja puntos del plano como pares de floats, y también
circunferencias, que caracteriza mediante un punto, su centro, y un float, su radio.
(a) Definir razonadamente un tipo de dato C circunf para las circunferencias.
(b) Escribir una función C que modifique un dato circunf multiplicando su radio por
un factor de dilatación contenido en una variable dilat.
150. Escribir una función C de nombre esint que reciba una cadena de caracteres y una
dirección de variable int, compruebe si dicha cadena corresponde efectivamente a un
dato int, sitúe su valor en la dirección recibida y devuelva un código status (Sugerencia:
usar la función sscanf).
Escribir funciones análogas esfloat y esdouble para estos tipos de datos.
20
151. ¿Qué hace este programa? ¿Para qué sirve? Incluso después de conocer la especificación
correspondiente, es decir, el enunciado del problema que resuelve, imagı́nate el trabajo
que serı́a si te dijeran que hay un error y que tu tienes que arreglarlo, o que hay que
modificarlo para que ahora haga más cosas.
#include <stdio.h>
main () {int v1,res; int li,il,ncrt;
li=0; il=300; ncrt=20;
v1=li; while (v1<=il){res=5*(v1/9-3.5555);
printf("%d\t%d\n",v1,res); v1=v1+ncrt;}}
152. Lo siguiente es otra versión del mismo programa. Mucho mejor, pero todavı́a incumple
algunas guı́as para la buena programación. ¿Cuáles?
#include <stdio.h>
main ()
{int fahr, celsius;
int lower, upper, step;
lower = 0; upper = 300; step = 20; fahr = lower;
while (fahr <= upper) {
celsius = 5 * (fahr - 32) / 9;
printf("%d\t%d\n", fahr, celsius);
fahr = fahr + step;}
}
153. La siguiente versión está muchı́simo mejor ¿verdad? Pero todavı́a es mejorable. ¿Cómo?
#include <stdio.h>
/* Imprime una tabla de conversion de temperaturas
entre escalas Fahrenheit y Celsius, para valores
Farenheit = 0, 20, 40, ... 300 */
main ()
{
int fahrenheit, celsius;
int lower, upper, step;
lower = 0;
/* limite inferior de temperaturas fahrenheit en la tabla */
upper = 300; /* limite superior idem. */
step = 20;
/* incremento de valores fahrenheit en tabla */
fahrenheit = lower;
while (fahrenheit <= upper) {
/* conversion de fahrenheit a celsius */
celsius = 5 * (fahrenheit - 32) / 9;
/* imprimir una fila de la tabla */
printf("%d\t%d\n", fahrenheit, celsius);
21
/* avanzar a la siguiente temperatura fahrenheit */
fahrenheit = fahrenheit + step;
}
}
154. El primer programa hace algunas (no todas) las conversiones incorrectamente, sacando
un grado Celsius de menos. ¿Por qué? Modifica uno de esos programas para que:
• los lı́mites y el incremento de la tabla se lean de la entrada estándar
• los lı́mites y el incremento de la tabla se tomen como argumentos del programa
principal
• se utilice un bucle for en vez de un while
• la conversión de cada temperatura se haga llamando a una función
• se utilicen el menor número posible de variables
• ...
155. Las matrices en C pueden inicializarse sin más que añadir a su declaración una relación de
valores iniciales dados en el orden habitual de almacenamiento de datos en tablas. Decir
cuál es la salida del siguiente programa C:
void main()
{
int B[5][3]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int *pi[5];
int *pp;
int x;
pp = B[0];
x = (int) *( (int *) pp+2 ) + 2;
x = (int) *( (char *) pp + 6 ) + 3;
printf("4.1 %d\n", x);
printf("4.2 %d\n", x);
pi[1]= B[1];
x = (int) *pi[1];
x = *(pp + 9);
x = *(B[4]+ 1);
printf("4.3 %d\n", x);
printf("4.4 %d\n", x);
printf("4.5 %d\n", x);
}
156. a. Indicar razonadamente qué devuelve el operador sizeof al aplicarse al tipo de datos
tipoRaro definido como sigue
typedef struct TIPORARO {
char miembro1;
int miembro2;
float miembro3;
} tipoRaro;
b. Indicar razonadamente si el siguiente código C es correcto o incorrecto:
22
float tf1[10], tf2[20];
tf1 = tf2;
c. Se ha definido un tipo de dato circunf como sigue
typedef struct CIRCUNF {
struct PUNTO {
float X, Y ;
} centro ;
float radio ;
} circunf ;
Escribir una función C que reciba un dato circunf y un dato float factor y que devuelva
el área de una circunferencia obtenida dilatando el radio de la recibida según el valor de
factor.
157. Un programa C contiene las lı́neas
int i, j;
int ti[20][20], *tpi[20], **ppi1, **ppi2;
for (i=0; i<20; i++) {
for (j=0; j<20; j++)
ti[i][j] = 100*i+j;
tpi[i] = ti[i];
}
ppi1 = tpi;
ppi2 = tpi+1;
a. Sabiendo que un dato char ocupa 1 byte y que uno int 2, dar razonadamente los
valores de las dos expresiones
*((int *) ((char *) *(ti + 3) + 16)), y
*(*(ppi2 + 3) + 4).
(Si se desea, utilizar las expresiones equivalentes *((int *) (((char *) (*(ti + 3)))
+ 16)), y *((*(ppi2 + 3)) + 4).)
b. Escribir razonadamente el prototipo y el cuerpo de una función C de nombre ppiSwap
que intecambie el contenido de las variables ppi1, ppi2. ¿Cómo habrı́a que llamar a
dicha función para intercambiar ppi1, ppi2?
158. Una implementación de pilas tiene como único interfaz las funciones
boolean PilaVacia(pila S),
status Push(elemento E, pila S), que coloca el elemento E en la pila, y
status Pop(elemento E, pila S), que sitúa el elemento en el top de la pila en el elemento E.
Se supone que esta implementación garantiza que tras una serie de Pops de una pila dada,
siempre se puede efectuar el mismo número de Push sobre la misma.
23
Dar razonadamente el pseudocódigo de una función de prototipo status JuntaPilas(pila
A, pila B) que reciba dos tales pilas A, B y modifique la primera apilando encima suyo
y en el orden de salida de pilas los elementos de la segunda (que naturalmente ha de
quedar vacı́a). Seguir estos pasos:
1. dar una primera versión que no efectúe control de errores;
2. dar una segunda versión que efectúe el control de errores necesario.
Identificar sobre la primera versión posibles fuentes de error y justificar la corrección que
se les dé.
159. a. Indicar mediante una pila y sobre cada uno de los caracteres leı́dos la evolución del
algoritmo de conversión de expresiones infijo a sufijo sobre la expresión
(A + B) ^ C - (D - E) * (F + G).
b. Construir razonadamente el árbol de expresión de la siguiente expresión sufijo
A B + C * E ^ F G / utilizando para ello una pila e indicando su estado en los correspondientes pasos.
c. Construir razonadamente el árbol binario de búsqueda asociado a la lista 24 33 2 47
7 13 41 51, y dar el resultado de su recorrido en orden previo y posterior.
160. 1. Convertir a sufijo mediante el algoritmo explicado en clase la expresión (A + B) * (C
- D ^ E) indicando en cada paso el estado de la pila a utilizar.
2. Construir el árbol de expresión asociado a la siguiente expresión sufijo A B + C D E ^
F + - * utilizando, de acuerdo con el algoritmo explicado en clase, una pila de punteros
a árboles de expresión e indicando en cada paso el estado de la misma.
161. Se tiene una implementación de listas circulares enlazadas donde sus nodos tienen campos
info y next, y donde un dato L de tipo lista apunta al último nodo de la misma.
Dar el pesudocódigo de una función status ListaExtFin(dato D, lista L) que sitúe
en el dato D el contenido del campo info del último nodo y elimine el mismo. Dar un
esquema con los pasos efectuados por la función en el orden adecuado.
162. 1. Dada la lista [10, 6, 15, 3, 8, 12, 19], construir su árbol binario de búsqueda
asociado, indicando su evolución mediante el estado del árbol tras cada inserción.
2. Dar razonadamente el recorrido de dicho árbol en orden previo.
163. Dar el prototipo y el código de una función C de nombre IntMatAlloc que reciba un
par de ints M, N y reserve memoria para una matriz M×N de ints según el siguiente
esquema:
1. se reserve memoria para una tabla de M punteros a int;
2. en cada uno de estos punteros se sitúe la dirección de una tabla de N ints, cuya
memoria deberá ser también reservada.
La función debe devolver la dirección del primer elemento de la tabla del apartado 1, y
debe efectuar el control de errores pertinente.
164. El logaritmo binario lg satisface lg(N ) = 1 + lg(N/2). Utilizar este hecho para dar el
pseudocódigo de una función recursiva int LogBinEnt(int N) que devuelva la parte entera del logaritmo binario de un entero N, esto es LogBinEnt(N ) = blg(N )c (por ejemplo,
LogBinEnt(3) = 1).
24
165. Se quiere eliminar usando una pila la recursión en la siguiente función para el cálculo del
número de nodos de un árbol binario:
int NumNodos(AB T)
si ABVacio(T) == V:
devolver 0;
else:
I = NumNodos(izq(T)); D = NumNodos(der(T));
devolver I+D+1;
Decidir razonadamente qué datos guardar en la pila (suponer un mecanismo tipo struct
para su almacenamiento) y eliminar sus llamadas recursivas dando en primer lugar una
versión que use goto y eliminando luego tales sentencias.
166. Se dispone de una función C de prototipo int **IntMatAlloc(int M, int N que reserva
memoria para una matriz M×N de ints según el siguiente esquema:
1. se reserva memoria para una tabla de M punteros a int;
2. en cada uno de estos punteros se sitúa la dirección de una tabla de N ints, cuya
memoria también se reserva.
Los posibles errores en dicha función se señalan devolviendo NULL.
Dar el código de una función C de prototipo int **IntMatSuma(int **A, int **B,
int M, int N) con A, B matrices de enteros definidas según el esquema anterior, y siendo
sus dimensiones ints M, N y devuelva una nueva matriz con la suma de las recibidas. La
función deberá hacer el control de errores pertinente.
167. Dada la expresión sufijo A B C + D ^ E + F * + G * H ^,
a. dar la expresión prefijo equivalente;
b. construir su árbol de expresión.
En cada caso, se seguirán los algoritmos descritos en clase, y se indicará en cada paso el
estado de la pila a utilizar, ası́ como los criterios empleados en cada paso para almacenar
y extraer datos de la pila.
168. Se tiene una implementación de listas circulares doblemente enlazadas cuyos nodos tienen
campos info, prev y next, y donde un dato de tipo lista es exactamente un puntero al
primer nodo de la lista.
Dar el pseudocódigo de una función status InsertarFinal(dato D, lista L) que
añada un nodo al final de la lista, almacenando D en el campo info de dicho elemento.
Dar un esquema con los pasos efectuados por la función en el orden adecuado.
169. Dar el pseudocódigo de una función recursiva de prototipo int Eval(AB T) que toma
como parámetro la representación en forma de árbol binario de una expresión aritmética
cuyos nodos contienen valores enteros u operadores, y devuelve el valor entero resultante
de evaluar dicha expresión.
Suponer la existencia de una una función dato Info(AB T) que devuelve el dato almacenado en la raı́z de un árbol binario T y de otra función booleana EsOperador(dato D)
que devuelve V si el dato D es un operador y F si es un operando. Para simplificar el
código, no efectuar control de errores.
25
170. Mediante el uso de una pila, se quiere eliminar eliminar la recursión en la siguiente función
para el recorrido en orden medio de un árbol binario:
void SimOrden(AB T)
si T == NULL :
volver ;
else :
SimOrden(izq(T)) ;
imprimir info(T) ;
SimOrden(der(T)) ;
Decidir razonadamente qué estructura de datos guardar en la pila, y eliminar sus llamadas
dando en primer lugar una versión que use goto y eliminando luego tales sentencias.
171. De una implementación del TAD Cola se disponen las primitivas status ColaIni(cola
Q),
bool ColaVacia(cola Q), status ColaIns(dato D, cola Q), status ColaRem(dato
D, cola Q).
Dar el pseudocódigo de una función int ColaNumElem(cola Q) que devuelva el número
de elementos en la cola Q sin alterar, por supuesto, el estado de la misma (10 puntos).
172. De una implementación del TAD Cola se dispone solamente de las primitivas status
ColaIni(cola Q),
bool ColaVacia(cola Q), status ColaInsertar(dato D, cola Q),
status ColaExtraer(dato D, cola Q).
Definiendo el tipo dato como una estructura con dos campos de tipo int llamados
Prioridad y Valor inserción, dar el pseudocódigo de una función int InsertaConPrioridad(dato
D, cola Q) que actualize Q poniendo D detras de todos los datos con mayor o igual prioridad y delante de todos los datos con menor prioridad. Los elementos ya en la cola
debe mantenerse tras la inserción en el mismo orden en el que se encontraban.
173. Un programa C contiene las lı́neas
int i, j;
int t[3][2], *tp[3], **pp;
for (i=0; i<3; i++) {
for (j=0; j<2; j++)
t[i][j] = 10*i+j;
tp[i] = t[i];
}
pp = tp-1;
a. Sabiendo que un dato char ocupa 1 byte y que uno int ocupa 2 consecutivos, dar
razonadamente los valores de las dos expresiones
*((int *) ((char *) *(tp + 1) + 2)), y
*(*(pp + 1) + 3).
b. Escribir razonadamente el prototipo y el cuerpo de una función C de nombre ppa0 que
incremente en 1 el valor almacenado en la variable pp.
26
174. Escribir razonadamente una función C de prototipo status ListaDelIni(lista *pl,
int *pd) que elimine el primer nodo de una lista enlazada circular apuntada por pl,
almacenando el dato en dicho nodo en la variable apuntada por pd.
Dar para ello un esquema que recoja la situación inicial de la lista, su situación final, e
indique los pasos realizados.
Utilizar la siguiente implementación de nodos y listas:
typedef struct NODO {
int info;
struct NODO *next;
} nodo;
typedef nodo *lista;
175. Se quiere traducir a sufijo mediante una pila la expresión (A + B) * C ^ (E - F/G),
donde se supone que el operador de exponenciación ^ tiene prioridad mayor que los demás
operadores . Leyendo esta expresión de izquierda a derecha, indicar razonadamente y para
cada operando u operador leı́dos la evolución de dicha pila, ası́ como la traducción parcial
efectuada para cada elemento leı́do.
176. La función de Ackermann se define como
A(0, n) = n+1; A(m, 0) = A(m−1, 1), m > 0; A(m, n) = A(m−1, A(m, n−1)), m > 0, n > 0.
Escribir una función C con un procedimiento iterativo para el cálculo de esta función y
otra con un procedimiento recursivo.
177. a. Dar sobre una pila la evolución de la traducción a sufijo de la expresión
(A * B ^ C) / (D - E) ;
indicando para cada elemento leı́do la traducción parcial y el estado de la pila.
b. Dar la evolución de la construcción del árbol de expresión asociado a la expresión sufijo
A B C + - E F * G / ^
indicando para cada elemento leı́do la traducción parcial y el estado de la pila de punteros.
c. Sobre el árbol obtenido en el apartado (b), dar la expresión prefijo equivalente a la
expresión sufijo de dicho apartado.
178. Dar razonadamente el código de una función C void ListaLiberar(lista *pl) que
reciba una lista enlazada circular y libere toda la memoria ocupada por la misma.
Indicar en un diagrama los pasos a efectuar y utilizar la siguiente implementación de
nodos y listas:
typedef struct NODO {
int info ;
struct NODO *next ;
} nodo;
typedef nodo *lista ;
27
179. Una función C tiene el siguiente prototipo
int *algo(int *pa, int **ppb).
¿Cómo habrı́a que llamarla para pasarle unas variables declaradas como int **ppa,
*pb; como argumentos primero y segundo respectivamente?
¿Y cómo se harı́a dicha llamada para que el orden de los argumentos anteriores fuera al
revés?
180. Dar el código de una función C void CambiaIzqDer (ab t) que reciba un dato árbol
binario e intercambie los hijos izquierdo y derecho de cada uno de sus nodos.
Utilizar la siguiente implementación de nodos y árboles:
typedef struct NODOAB {
int info ;
struct NODOAB *izq ;
struct NODOAB *der ;
} nodoAB ;
typedef nodoAB *ab ;
181. i. Se quiere utilizar la búsqueda binaria para encontrar la clave 4 en la tabla 1 2 3 5 6
7 8. Indicar paso a paso contra qué elementos de la tabla se comparará el valor 4 y sobre
qué subtablas se hacen las sucesivas búsquedas.
ii. sobre la cola circular Q del esquema inferior se efectúan las siguientes operaciones: a.
ColaIns(3, Q); b. ColaRem(A, Q); c. ColaIns(4, Q); d. ColaIns(5, Q).
R
2
F
1
Indicar para cada operación la posición de front y rear y el estado de la cola. ¿Cuál es el
valor final de la variable A?
iii. Construir el árbol binario de búsqueda asociado a la lista 6 16 3 4 9 31 1 8 2 7
indicando el estado del mismo tras cada inserción.
182. Para una implementación de pilas dinámicas se utiliza el siguiente tipo C:
typedef struct PILA {
int top ;
// primera posicion ocupada
int numElem ;
// num. elementos en un momento dado
generic *pg ;
} pila;
a. Utilizando la función malloc, dar el código C de una función status PilaIni(pila
*ps, int numDatos) que inicialize una tal pila con espacio suficiente para almacenar
numDatos elementos.
b. Se quiere escribir una función de nombre PilaReset que libere la memoria ocupada
por una pila del tipo anterior. ¿Qué datos podrı́a recibir y devolver dicha función? Dar
su prototipo y escribir su código C.
28
183. En una implementación de árboles binarios, se utilizan info(T) para obtener el valor en
su campo info, y izq(T), der(T) para acceder a sus subárboles izquierdo y derecho. Se
dispone también de una función int NumElem(T) que devuelve el número de nodos de un
árbol T y de otra bool ABVacio(T) que devuelve V o F según T esté o no vacio.
Dar razonadamente el pseudocódigo de una función int NumElemMen(T, K) que devuelve
el número de nodos de un árbol binario de búsqueda T cuyos campos info son menores
o iguales que la clave K.
184. a. Traducir a sufijo la expresión inferior indicando para cada uno de sus elementos la
traducción parcial de la expresión y el estado de la pila en ese momento.
(A * B) ^ (C + D) * E ^ F
b. Construir el árbol de expresión de la expresión sufijo inferior, indicando para cada uno
de sus elementos el estado de la pila usada para gestionar dicha traducción.
A B - C / D ^ E F * +
c. Suponiendo que el 1 es el primer elemento y 3 el último, indicar la evolución de la cola
circular Q cuyo estado se refleja en el diagrama inferior tras las siguientes operaciones:
i) ColaIns(4, Q);
iv) ColaIns(5, Q);
ii) ColaExt(D, Q);
v) ColaIns(6, Q);
1
2
iii) ColaExt(D, Q);
vi) ColaIns(7, Q).
3
185. Dar el código de una función C que reciba una tabla de N doubles en memoria dinámica,
duplique su memoria, copie por duplicado en la nueva tabla cada elemento de la tabla
original, libere la memoria de la primera tabla y devuelva un puntero a la nueva tabla.
Por ejemplo, una tabla [a b c] ha de transformarse en [a a b b c c].
186. Escribir razonadamente una función C de prototipo int InsPenultimo(listaC lc) que
reciba una lista enlazada circular lc e inserte un nuevo nodo antes del situado inicialmente
en último lugar o indique que dicha inserción no ha sido posible.
Dar un diagrama indicativo de los datos implicados y utilizar una función
nodo *getNodo()
de obtención de un nuevo nodo y la siguiente implementación de nodos y listas enlazadas,
suponiendo que un puntero del tipo listaC apunta al último nodo de una tal lista.
typedef struct NODO {
generico info;
struct NODO *next;
} nodo;
typedef nodo *listaC;
187. Una implementación de pilas tiene como ÚNICO interfaz las funciones
status PilaIni(pila S), que inicializa una pila,
boolean PilaVacia(pila S), que comprueba si una pila está vacı́a,
status Push(elemento E, pila S), que coloca una copia del elemento E en la pila, y
29
status Pop(elemento E, pila S), que saca el elemento que ocupa el top (o la cima)
de la pila y lo sitúa en el elemento E pasado como parámetro.
Dar el pseudocódigo de una funcion status swapPU(pila P) que reciba una pila P y la
transforme situando en su top el elemento que entró en primer lugar y en su fondo el
elemento que ocupaba el top al llamar a la función. Todos los demás elementos, si los
hay, deberı́an quedar en el mismo orden en que estaban en la pila original.
Efectuar para ello los siguientes pasos: dar una primera versión sin control de errores,
indicar en qué puntos de la misma puede haberlos y modificar finalmente la versión inicial
con el control y corrección de errores pertinente. Para simplificar el problema:
1. Suponer que un Push precedido de un Pop no causa errores.
2. Suponer que la pila tiene 2 o más elementos.
188. a. Traducir a sufijo la expresión (A ^ B * C) ^ (D - E) ; indicando para cada uno de
sus elementos la traducción parcial de la expresión y el estado de la pila en ese momento.
b. Construir el árbol de expresión asociado a la expresión sufijo A B ^ C + D * E F ^
*, indicando en cada paso el estado de los elementos implicados.
c. Construir el ABdB asociado a la lista 16 23 12 37 32 14 11 28 indicando en cada
paso el estado parcial del mismo. Una vez construido, recorrerlo en orden previo y posterior.
189. Escribir el pseudocódigo (y NO el código C) de una función recursiva limpia(lista le,
dato D) que reciba una lista enlazada le y la modifique para que solamente contenga
nodos cuyos valores info sean menores que el argumento D. Suponer para ello que se
identifica una lista con un puntero a su primer nodo, que NULL indica una lista vacia, y
la disponibilidad de las siguientes instrucciones:
info(le) devuelve el campo info del nodo apuntado por le;
next(le) devuelve un puntero del nodo siguiente a le;
del(le) elimina el nodo apuntado por le.
190. El pseudocódigo inferior corresponde al recorrido en orden Posterior de un árbol binario.
Eliminar las llamada recursivas del mismo efectuando los siguientes pasos:
a. Indicar la estructura del elemento de pila a utilizar.
b. Efectuar una primera eliminación mecánica utilizando si es preciso sentencias goto.
c. Dar finalmente un código sin sentencias goto.
postOrden(AB T)
si ABVacio(T) == F :
preOrden(izq(T)) ;
preOrden(der(T)) ;
visitar(T) ;
191. Una implementación de colas tiene como único interfaz las funciones status ColaIni(cola
Q), boolean ColaVacia(cola Q), status ColaIns(dato D, cola Q), que inserta D en
la cola, y status ColaExt(dato D, cola Q), que extrae a D el elemento inicial de la
cola. Además se dispone de un elemento especial FL que puede insertarse y extraerse de
la cola, pero que NO corresponde a nigún dato tı́picamente almacenado en la misma.
30
Dar razonadamente el pseudocódigo de una función de prototipo status extrUltimo(dato
D, cola Q) que reciba una cola sin ningún elemento FL y sitúe en D el elemento insertado en dicha cola en último lugar. Suponer para ello que una inserción precedida de una
extracción sobre la misma cola no causa error.
192. a. Comprobar mediante una pila la corrección de paréntesis, corchetes y demás de la
siguiente expresión:
(a*[b+{c-d/(f-g)}]+(v-w)*x)
b. sobre la cola circular Q del esquema inferior se efectúan las siguientes operaciones:
1. ColaIns(2, Q); 2. ColaIns(3, Q); 3. ColaRem(A, Q); 4. ColaIns(4, Q); 5.
ColaIns(5, Q); 6. ColaRem(A, Q). Indicar para el estado inicial y para cada operación
las posiciones de front y rear y el estado de la cola. ¿Cuál es el valor final de la variable
A?
1
c. Construir el árbol binario de búsqueda asociado a la lista 4 15 3 5 9 19 1 7 2 6
indicando el estado del mismo tras cada inserción.
d. Indicar mediante una pila la evolución de la evaluación de la siguiente expresión sufijo:
2 1 * 4 2 / + 6 5 + 8 2 / - + ;
193. Una implementación de árboles binarios (AB) usa las expresiones info(T), izq(T),
der(T) para acceder a los correspondientes elementos de un AB. Además, NULL identifica
a un AB vacı́o.
Dar el pseudocódigo de una funcion void acotarAB(dato D, AB T) que sitúe el valor D
en los campos info de todos los subárboles T’ de T para los que info(T’) > D.
194. El pseudocódigo inferior corresponde al recorrido en orden Medio de un árbol binario.
Eliminar las llamadas recursivas del mismo efectuando los siguientes pasos:
(a) Eliminar PRIMERO las recursiones de cola que haya.
(b) Indicar la estructura del elemento de pila a utilizar para eliminar las recursiones que
puedan quedar tras el paso anterior.
(c) Para estas, efectuar una eliminación mecánica utilizando si es preciso sentencias
goto.
(d) Dar finalmente un código sin sentencias goto.
simOrden(AB T)
si T != NULL :
simOrden(izq(T)) ;
visitar(T) ;
simOrden(der(T)) ;
195. a. Se ha definido un tipo de dato fcompl cuyos elementos se almacenan mediante una
estructura de la forma
struct { float Re, Im; }
31
Dar el prototipo y el código de una función de nombre swapReIm que reciba de manera
adecuada un dato fcompl z e intercambie sus partes real e imaginaria.
b. Dar la evolución sobre una pila de un algoritmo de comprobación de la corrección de
paréntesis, corchetes y llaves en la expresión:
{[A*(B-C)]/([D-E]*F)-G}*H;
196. Una implementación de pilas tiene como único interfaz las funciones
status PIni(pila P),
boolean PVacia(pila P),
status Push(dato D, pila P), que inserta el dato D en la pila, y
status Pop(dato D, pila P), que extrae el elemento D desde la pila.
Dar razonadamente el pseudocódigo de una función de prototipo status InvertirPila(pila
A) que reciba una tal pila A y resitúe en ella sus elementos en orden inverso al inicial.
Para ello, indicar brevemente el método a seguir y
a. dar primero una versión sin control de excepciones;
b. identificar sobre dicha versión la ubicación de las posibles excepciones, y
c. dar finalmente un pseudocódigo incorporando la detección y recuperación de excepciones.
197. Escribir razonadamente una función C de prototipo void resetLCE(lec *pl) que libere
la memoria de una lista enlazada circular. Usar para ello la siguiente implementación C
de listas enlazadas circulares:
typedef struct NODO {
gen info ;
struct NODO *next ;
} nodo ;
typedef nodo *lec ;
32