Вы находитесь на странице: 1из 239

Programación II

Relación de Ejercicios
y Soluciones
UNIVERSIDAD DE MÁLAGA
Dpto. Lenguajes y CC. Computación
E.T.S.I. Telecomunicación
Sonido e Imagen

Índice
Tema 1: Almacenamiento en Memoria Secundaria. Ficheros 2

Tema 2: Tipos Abstractos de Datos 15


Tema 2.1: Programacion Modular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Tema 2.2: Tipos Abstractos de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Tema 2.3: Tipos Abstractos de Datos Genéricos . . . . . . . . . . . . . . . . . . . . . . . . 49

Tema 3: Gestión de Memoria Dinámica 74


Tema 3.1: Listas Enlazadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Tema 3.2: Abstracción en la Gestión de Memoria Dinámica . . . . . . . . . . . . . . . . . 93
Tema 3.3: Genericidad en la Gestión de Memoria Dinámica . . . . . . . . . . . . . . . . . 136

Tema 4: Colecciones 147

Prácticas de Laboratorio 162


Práctica 1: Almacenamiento Persistente de Datos . . . . . . . . . . . . . . . . . . . . . . . 162
Práctica 2: Tipos Abstractos de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
Práctica 3: Tipos Abstractos de Datos Genéricos . . . . . . . . . . . . . . . . . . . . . . . 181
Práctica 4: Gestión de Memoria Dinámica . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Práctica 5: Abstracción en la Gestión de Memoria Dinámica . . . . . . . . . . . . . . . . . 200
Práctica 6: Biblioteca Estándar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208

Problemas de Examen 214


Examen 1: Gestión de Agencia de Viajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Examen 2: Juego de Ajedrez . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Examen 3: Gestión de Hotel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

Este obra está bajo una licencia Reconocimiento-NoComercial-CompartirIgual 3.0 Unported de


Creative Commons: No se permite un uso comercial de la obra original ni de las posibles obras derivadas,
la distribución de las cuales se debe hacer con una licencia igual a la que regula la obra original. Para
ver una copia de esta licencia, visite http://creativecommons.org/licenses/by-nc-sa/3.0/deed.es ES o
envie una carta a Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.

1
Nota: en la solución a los ejercicios, se ha utilizado el tipo array de TR1, que ha sido incorporado a la biblio-
teca de C++ en el estándar de 2011. Si su biblioteca estándar no contiene la definición del tipo array, puede
descargarla desde la siguiente dirección: http://www.lcc.uma.es/%7Evicente/docencia/cpplibs/array_tr1.zip

Tema 1: Almacenamiento en Memoria Secundaria. Ficheros


1. Escribir un programa que cuente el número de letras minúsculas, de letras mayúsculas y de dı́gitos de un
fichero.
Solución
#include <iostream>
#include <fstream>
using namespace std;
struct Estad {
unsigned may;
unsigned min;
unsigned dig;
};

// inicializa cuenta de estadisticas


void inic(Estad& sts)
{
sts.may = 0;
sts.min = 0;
sts.dig = 0;
}
// muestra las estadisticas
void escribir(const Estad& sts)
{
cout << "Mayusculas: " << sts.may << endl;
cout << "Minusculas: " << sts.min << endl;
cout << "Digitos: " << sts.dig << endl;
}
inline bool es_mayuscula(char c)
{
return (c >= ’A’ && c <= ’Z’);
}
inline bool es_minuscula(char c)
{
return (c >= ’a’ && c <= ’z’);
}
inline bool es_digito(char c)
{
return (c >= ’0’ && c <= ’9’);
}
void procesar(char c, Estad& sts)
{
if (es_mayuscula(c)) {
++sts.may;
} else if (es_minuscula(c)) {
++sts.min;
} else if (es_digito(c)) {
++sts.dig;
}
}

void estadisticas(const string& nombre, bool& ok, Estad& sts)


{
ifstream fich;
fich.open(nombre.c_str());
if (fich.fail()) {
ok = false;
} else {
inic(sts);
char c;
fich.get(c);
while (! fich.fail()) {
procesar(c, sts);
fich.get(c);
}
ok = fich.eof();
fich.close();
}

2
}

int main()
{
bool ok;
Estad sts;
cout << "Introduzca el nombre del fichero: ";
string nombre;
cin >> nombre;
estadisticas(nombre, ok, sts);
if (ok) {
escribir(sts);
} else {
cout << "Error al procesar el fichero" << endl;
}
}

2. Escribir un programa con la opción de encriptar y de desencriptar un fichero de texto, dependiendo de la


extensión del fichero de entrada. La encriptación (codificación) consiste en que dado un fichero de texto
de entrada (extensión txt) genere otro fichero de salida encriptado (extensión cod). Esta codificación
consiste reemplazar cada letra por la tercera siguiente de forma circular (ej. a→d, b→e, · · ·, w→z, x→a,
y→b, z→c). La opción de desencriptado consiste en leer un fichero codificado (extensión cod) y recuperar
la información original en un fichero de texto (extensión txt).
Solución
#include <iostream>
#include <fstream>
using namespace std;

const unsigned DESPL = 3;

enum Operacion {
CIFRAR, DESCIFRAR, ERROR
};

// cifrado rotacional (DESPL) de letra


char cifrar(char c)
{
if (c >= ’a’ && c <= ’z’) {
c = char(c + DESPL);
if (c > ’z’) {
c = char(’a’ + c - ’z’ - 1);
}
} else if (c >= ’A’ && c <= ’Z’) {
c = char(c + DESPL);
if (c > ’Z’) {
c = char(’A’ + c - ’Z’ - 1);
}
}
return c;
}
// descifrado rotacional (DESPL) de letra
char descifrar(char c)
{
if (c >= ’a’ && c <= ’z’) {
c = char(c - DESPL);
if (c < ’a’) {
c = char(’z’ + c - ’a’ + 1);
}
} else if (c >= ’A’ && c <= ’Z’) {
c = char(c - DESPL);
if (c < ’A’) {
c = char(’Z’ + c - ’A’ + 1);
}
}
return c;
}

// transforma una letra segun el codigo de operacion


char transformar(char c, Operacion op)
{
char res;
if (op == CIFRAR) {

3
res = cifrar(c);
} else {
res = descifrar(c);
}
return res;
}

// trasnforma el fichero de entrada y lo escribe en el fichero de salida


void transformar_fichero(const string& entrada, const string& salida,
Operacion op, bool& ok)
{
ifstream f_ent;
f_ent.open(entrada.c_str());
if (f_ent.fail()) {
ok = false;
} else {
ofstream f_sal;
f_sal.open(salida.c_str());
if (f_sal.fail()) {
ok = false;
} else {
char ch;
f_ent.get(ch);
while (! f_ent.fail() && ! f_sal.fail()) {
f_sal.put(transformar(ch, op));
f_ent.get(ch);
}
ok = (f_ent.eof() && ! f_sal.fail());
f_sal.close();
}
f_ent.close();
}
}

// busca un caracter e una cadena


unsigned buscar(const string& nombre, char c)
{
unsigned i = 0;
while ((i < nombre.size())&&(c != nombre[i])) {
++i;
}
return i;
}

// devuelve la extension del nombre de fichero


string extension(const string& nombre)
{
string res = "";
unsigned i = buscar(nombre, ’.’);
if (i < nombre.size()) {
res = nombre.substr(i+1, nombre.size()-(i+1));
}
return res;
}

// calcula la operacion a realizar a partir de las extensiones de los


// ficheros de entrada y salida
Operacion codigo_op(const string& entrada, const string& salida)
{
Operacion res;
if ((extension(entrada)=="txt")&&(extension(salida)=="cod")) {
res = CIFRAR;
} else if ((extension(entrada)=="cod")&&(extension(salida)=="txt")) {
res = DESCIFRAR;
} else {
res = ERROR;
}
return res;
}

int main()
{
bool ok;
cout << "Introduzca el nombre del fichero de entrada: ";

4
string nombre_ent;
cin >> nombre_ent;
cout << "Introduzca el nombre del fichero de salida: ";
string nombre_sal;
cin >> nombre_sal;
Operacion op = codigo_op(nombre_ent, nombre_sal);
if (op != ERROR) {
transformar_fichero(nombre_ent, nombre_sal, op, ok);
if (ok) {
cout << "Procesamiento correcto" << endl;
} else {
cout << "Error al procesar el fichero" << endl;
}
} else {
cout << "Error: extensiones erroneas" << endl;
}
}

3. Escribir un programa para procesar información sobre los clientes de una agencia matrimonial. El programa
debe crear un fichero de texto (cuyo nombre se leerá por teclado) en el que se guarde la información de
un número indeterminado de personas. La información que se guardará por cada persona será:
Nombre: Cadena de caracteres.
Edad int.
Género char (M/F).
Arte char (S/N).
Deporte char (S/N).
Libros char (S/N).
Música char (S/N).
La información correspondiente a cada persona se leerá del teclado. El proceso finalizará cuando se teclee
un campo Nombre que esté vacı́o.
Solución
#include <iostream>
#include <fstream>
#include <string>
#include <tr1/array>
using namespace std;
using namespace std::tr1;

enum Genero {
FEMENINO, MASCULINO
};

const unsigned NAFICCIONES = 4;


typedef array<string, NAFICCIONES> StrAficciones;
const StrAficciones AFICCIONES = {{
"Arte", "Deporte", "Libros", "Musica"
}};
typedef array<bool, NAFICCIONES> Aficciones;

struct Persona {
string nombre;
unsigned edad;
Genero gen;
Aficciones af;
};

//---------------------------------
// convierte una letra a mayusculas
inline char mayuscula(char c)
{
if (c >= ’a’ && c <= ’z’) {
c = char(c - ’a’ + ’A’);
}
return c;
}

//------------------------------------------------------------------
//-- Leer de Teclado -----------------------------------------------
//------------------------------------------------------------------

5
// leer una opcion (S/N) a bool
inline void leer(bool& a)
{
char c;
cin >> c;
a = (mayuscula(c) == ’S’);
}

// leer el genero (M/F) a tipo enumerado


inline void leer(Genero& g)
{
char c;
cin >> c;
if (mayuscula(c) == ’M’) {
g = MASCULINO;
} else {
g = FEMENINO;
}
}

// leer aficciones
void leer(Aficciones& af)
{
for (unsigned i = 0; i < af.size(); ++i) {
cout << AFICCIONES[i] << " (S/N): ";
leer(af[i]);
}
}

// leer datos de persona


void leer(Persona& p)
{
cout << "Nombre: ";
getline(cin, p.nombre);
if (p.nombre.size() > 0) {
cout << "Edad: ";
cin >> p.edad;
cout << "Genero (M/F): ";
leer(p.gen);
leer(p.af);
cin.ignore(1000, ’\n’);
}
}

//------------------------------------------------------------------
//-- Escribir a Fichero --------------------------------------------
//------------------------------------------------------------------
// escribir una opcion (S/N) bool a fichero
inline void escribir(ofstream& fich, bool a)
{
if (a) {
fich << ’S’;
} else {
fich << ’N’;
}
}
// escribir el genero (M/F) tipo enumerado a fichero
inline void escribir(ofstream& fich, Genero g)
{
if (g == MASCULINO) {
fich << ’M’;
} else {
fich << ’F’;
}
}
// escribir una persona a fichero
void escribir(ofstream& fich, const Persona& p)
{
fich << p.nombre << endl;
fich << p.edad << " " ;
escribir(fich, p.gen);
for (unsigned i = 0; i < p.af.size(); ++i) {
escribir(fich, p.af[i]);
}

6
fich << endl;
}

void crear_fichero(const string& nombre, bool& ok)


{
ofstream fich;
fich.open(nombre.c_str());
if (fich.fail()) {
ok = false;
} else {
Persona p;
leer(p);
while ( !cin.fail() && (p.nombre.size() > 0)&& ! fich.fail()) {
escribir(fich, p);
leer(p);
}
ok = !cin.fail() && ! fich.fail();
fich.close();
}
}

int main()
{
bool ok;
cout << "Introduzca el nombre del fichero: ";
string nombre;
cin >> nombre;
cin.ignore(1000, ’\n’);
crear_fichero(nombre, ok);
if (ok) {
cout << "Procesamiento correcto" << endl;
} else {
cout << "Error al procesar el fichero" << endl;
}
}

4. Ampliar el programa que procesa clientes de una agencia matrimonial para que tome los datos de todos los
candidatos a estudiar del fichero del ejercicio anterior, lea el cliente del teclado y finalmente genere como
resultado un fichero (cuyo nombre será leı́do desde teclado) con toda la información correspondiente a los
candidatos aceptados. Una persona del fichero de entrada se considerará aceptable como candidato si tiene
diferente género y por lo menos tres aficiones comunes con respecto al aspirante introducido por pantalla.
(El programa debe ser capaz de trabajar con cualquier número de personas en el fichero de entrada).
Solución
#include <iostream>
#include <fstream>
#include <string>
#include <tr1/array>
using namespace std;
using namespace std::tr1;

enum Genero {
FEMENINO, MASCULINO
};

const unsigned NAFICCIONES = 4;


typedef array<string, NAFICCIONES> StrAficciones;
const StrAficciones AFICCIONES = {{
"Arte", "Deporte", "Libros", "Musica"
}};
typedef array<bool, NAFICCIONES> Aficciones;

const unsigned NAFICCOMP = 3;

struct Persona {
string nombre;
unsigned edad;
Genero gen;
Aficciones af;
};

//---------------------------------
// convierte una letra a mayusculas

7
inline char mayuscula(char c)
{
if (c >= ’a’ && c <= ’z’) {
c = char(c - ’a’ + ’A’);
}
return c;
}

//------------------------------------------------------------------
//-- Leer de Teclado -----------------------------------------------
//------------------------------------------------------------------
// leer una opcion (S/N) a bool
inline void leer(bool& a)
{
char c;
cin >> c;
a = (mayuscula(c) == ’S’);
}

// leer el genero (M/F) a tipo enumerado


inline void leer(Genero& g)
{
char c;
cin >> c;
if (mayuscula(c) == ’M’) {
g = MASCULINO;
} else {
g = FEMENINO;
}
}

// leer aficciones
void leer(Aficciones& af)
{
for (unsigned i = 0; i < af.size(); ++i) {
cout << AFICCIONES[i] << " (S/N): ";
leer(af[i]);
}
}

// leer datos de persona


void leer(Persona& p)
{
cout << "Nombre: ";
getline(cin, p.nombre);
if (p.nombre.size() > 0) {
cout << "Edad: ";
cin >> p.edad;
cout << "Genero: ";
leer(p.gen);
leer(p.af);
cin.ignore(1000, ’\n’);
}
}
//------------------------------------------------------------------
//-- Leer de Fichero -----------------------------------------------
//------------------------------------------------------------------
// leer una opcion (S/N) a bool
inline void leer(ifstream& in, bool& a)
{
char c;
in >> c;
a = (mayuscula(c) == ’S’);
}

// leer el genero (M/F) a tipo enumerado


inline void leer(ifstream& in, Genero& g)
{
char c;
in >> c;
if (mayuscula(c) == ’M’) {
g = MASCULINO;
} else {
g = FEMENINO;

8
}
}

// leer datos de persona


void leer(ifstream& in, Persona& p)
{
getline(in, p.nombre);
in >> p.edad;
leer(in, p.gen);
for (unsigned i = 0; i < p.af.size(); ++i) {
leer(in, p.af[i]);
}
in.ignore(1000, ’\n’);
}
//------------------------------------------------------------------
//-- Escribir a Fichero --------------------------------------------
//------------------------------------------------------------------
// escribir una opcion (S/N) bool a fichero
inline void escribir(ofstream& fich, bool a)
{
if (a) {
fich << ’S’;
} else {
fich << ’N’;
}
}
// escribir el genero (M/F) tipo enumerado a fichero
inline void escribir(ofstream& fich, Genero g)
{
if (g == MASCULINO) {
fich << ’M’;
} else {
fich << ’F’;
}
}
// escribit una persona a fichero
void escribir(ofstream& fich, const Persona& p)
{
fich << p.nombre << endl;
fich << p.edad << " " ;
escribir(fich, p.gen);
for (unsigned i = 0; i < p.af.size(); ++i) {
escribir(fich, p.af[i]);
}
fich << endl;
}
//------------------------------------------------------------------
// dos personas son compatibles si sus aficciones comunes superan un
// determinado umbral
bool es_compatible(const Persona& p1, const Persona& p2)
{
unsigned cnt = 0;
for (unsigned i = 0; i < p1.af.size(); ++i) {
if (p1.af[i] == p2.af[i]) {
++cnt;
}
}
return (p1.gen != p2.gen) && (cnt >= NAFICCOMP);
}
//------------------------------------------------------------------
void procesar_fichero(const string& nombre_ent, const string& nombre_sal,
const Persona& pers, bool& ok)
{
ifstream fich_ent;
fich_ent.open(nombre_ent.c_str());
if (fich_ent.fail()) {
ok = false;
} else {
ofstream fich_sal;
fich_sal.open(nombre_sal.c_str());
if (fich_sal.fail()) {
ok = false;
} else {
Persona p;

9
leer(fich_ent, p);
while ( ! fich_ent.fail() && ! fich_sal.fail()) {
if (es_compatible(pers, p)) {
escribir(fich_sal, p);
}
leer(fich_ent, p);
}
ok = fich_ent.eof() && ! fich_sal.fail();
fich_sal.close();
}
fich_ent.close();
}
}
//------------------------------------------------------------------
int main()
{
bool ok;
cout << "Introduzca el nombre del fichero de entrada: ";
string nombre_ent;
cin >> nombre_ent;
cout << "Introduzca el nombre del fichero de salida: ";
string nombre_sal;
cin >> nombre_sal;
cin.ignore(1000, ’\n’);
cout << "Introduzca datos de la persona: ";
Persona pers;
leer(pers);
if (pers.nombre.size() > 0) {
procesar_fichero(nombre_ent, nombre_sal, pers, ok);
if (ok) {
cout << "Procesamiento correcto" << endl;
} else {
cout << "Error al procesar el fichero" << endl;
}
}
}

5. Codifique un programa que cree un fichero para contener los datos relativos a los artı́culos de un almacén.
Para cada artı́culo habrá de guardar la siguiente información:
Código del artı́culo Numérico
Nombre del artı́culo Cadena de caracteres
Existencias Numérico
Precio Numérico
Se deberá pedir datos de cada artı́culo por teclado hasta que se teclee 0 (cero) como código de artı́culo.
Solución
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

struct Articulo {
unsigned codigo;
string nombre;
unsigned existencias;
unsigned precio;
};

// leer articulo desde el teclado


void leer(Articulo& a)
{
cout << "Codigo de articulo: ";
cin >> a.codigo;
if (a.codigo != 0) {
cout << "Nombre de articulo: ";
cin >> ws; // salta separadores del codigo de articulo
getline(cin, a.nombre);
cout << "Existencias: ";
cin >> a.existencias;
cout << "Precio: ";
cin >> a.precio;
}

10
}

// escribe articulo a fichero


void escribir(ofstream& fich, const Articulo& a)
{
fich << a.codigo << " " << a.nombre << endl;
fich << a.existencias << " " << a.precio << endl;
}

// lee articulos y los escribe en el fichero


void crear_fichero(const string& nombre, bool& ok)
{
ofstream fich;
fich.open(nombre.c_str());
if (fich.fail()) {
ok = false;
} else {
Articulo a;
leer(a);
while ( !cin.fail() && (a.codigo != 0)&& ! fich.fail()) {
escribir(fich, a);
leer(a);
}
ok = !cin.fail() && ! fich.fail();
fich.close();
}
}

int main()
{
bool ok;
cout << "Introduzca el nombre del fichero: ";
string nombre;
cin >> nombre;
cin.ignore(1000, ’\n’);
crear_fichero(nombre, ok);
if (ok) {
cout << "Procesamiento correcto" << endl;
} else {
cout << "Error al procesar el fichero" << endl;
}
}

6. Escriba un programa que tome como entrada el fichero del ejercicio anterior y una condición sobre los
campos existencias o precio. La condición podrá ser: <campo> [<,<=,>,>=, ==, ! =] <número>
Este programa debe generar como salida un fichero que contenga todos aquellos artı́culos para los que se
cumple la condición de entrada.
Solución
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

enum Campo {
EXISTENCIAS, PRECIO
};
enum Cmp {
MENOR, MENIG, MAYOR, MAYIG, IGUAL, DISTINTO
};

struct Condicion {
Campo campo;
Cmp cmp;
unsigned num;
};

struct Articulo {
unsigned codigo;
string nombre;
unsigned existencias;
unsigned precio;
};

11
// convierte a mayusculas
inline char mayuscula(char c)
{
if (c >= ’a’ && c <= ’z’) {
c = char(c - ’a’ + ’A’);
}
return c;
}

//------------------------------------------------------------------
//-- Leer de Teclado -----------------------------------------------
//------------------------------------------------------------------
// lee el campo por el que realizar la comparacion
inline void leer(Campo& c, bool& ok)
{
string s;
cin >> s;
ok = true;
if (s == "existencia") {
c = EXISTENCIAS;
} else if (s == "precio") {
c = PRECIO;
} else {
ok = false;
}
}
// lee el codigo de comparacion
inline void leer(Cmp& c, bool& ok)
{
string s;
cin >> s;
ok = true;
if (s == "<") {
c = MENOR;
} else if (s == "<=") {
c = MENIG;
} else if (s == ">") {
c = MAYOR;
} else if (s == ">=") {
c = MAYIG;
} else if (s == "==") {
c = IGUAL;
} else if (s == "!=") {
c = DISTINTO;
} else {
ok = false;
}
}

// lee la condicion de procesamiento (campo y comparacion)


inline void leer(Condicion& c, bool& ok)
{
bool ok1, ok2;
cout << "Introduzca campo: (existencia, precio) ";
leer(c.campo, ok1);
cout << "Introduzca comparacion: (< <= > >= == !=) ";
leer(c.cmp, ok2);
cout << "Introduzca numero: ";
cin >> c.num;
ok = ok1 && ok2;
}

//------------------------------------------------------------------
//-- Leer de Fichero -----------------------------------------------
//------------------------------------------------------------------
// lee un articulo de fichero
void leer(ifstream& in, Articulo& a)
{
in >> a.codigo;
in >> ws;
getline(in, a.nombre);
in >> a.existencias;
in >> a.precio;

12
}
//------------------------------------------------------------------
//-- Escribir a Fichero --------------------------------------------
//------------------------------------------------------------------
// escribe articulo a fichero
void escribir(ofstream& fich, const Articulo& a)
{
fich << a.codigo << " " << a.nombre << endl;
fich << a.existencias << " " << a.precio << endl;
}
//------------------------------------------------------------------
// comprueba si un articulo es compatible con la condicion de comparacion
bool es_compatible(const Articulo& a, const Condicion& c)
{
unsigned valor = 0;
switch (c.campo) {
case EXISTENCIAS: valor = a.existencias; break;
case PRECIO: valor = a.precio; break;
}
bool res = false;
switch (c.cmp) {
case MENOR: res = valor < c.num; break;
case MENIG: res = valor <= c.num; break;
case MAYOR: res = valor > c.num; break;
case MAYIG: res = valor >= c.num; break;
case IGUAL: res = valor == c.num; break;
case DISTINTO: res = valor != c.num; break;
}
return res;
}
//------------------------------------------------------------------
// lee articulos de fichero y escribe en otro fichero aquellos que son
// compatibles con un codigo de comparacion
void procesar_fichero(const string& nombre_ent, const string& nombre_sal,
const Condicion& c, bool& ok)
{
ifstream fich_ent;
fich_ent.open(nombre_ent.c_str());
if (fich_ent.fail()) {
ok = false;
} else {
ofstream fich_sal;
fich_sal.open(nombre_sal.c_str());
if (fich_sal.fail()) {
ok = false;
} else {
Articulo a;
leer(fich_ent, a);
while ( ! fich_ent.fail() && ! fich_sal.fail()) {
if (es_compatible(a, c)) {
escribir(fich_sal, a);
}
leer(fich_ent, a);
}
ok = fich_ent.eof() && ! fich_sal.fail();
fich_sal.close();
}
fich_ent.close();
}
}
//------------------------------------------------------------------
int main()
{
bool ok;
cout << "Introduzca el nombre del fichero de entrada: ";
string nombre_ent;
cin >> nombre_ent;
cout << "Introduzca el nombre del fichero de salida: ";
string nombre_sal;
cin >> nombre_sal;
cin.ignore(1000, ’\n’);
cout << "Introduzca la condicion de procesamiento: " << endl;
Condicion c;
leer(c, ok);

13
if (ok) {
procesar_fichero(nombre_ent, nombre_sal, c, ok);
if (ok) {
cout << "Procesamiento correcto" << endl;
} else {
cout << "Error al procesar el fichero" << endl;
}
}
}

14
Tema 2: Tipos Abstractos de Datos
Tema 2.1: Programacion Modular
Diseñe e implemente los siguientes Módulos, dentro del espacio de nombres umalcc, ası́ como programas de
utilización que permitan comprobar su funcionamiento.

1. Diseñe un módulo que proporcione soporte adecuado a la arı́metica con números complejos. El módulo
deberá definir el tipo Complejo, ası́ como los siguientes subprogramas:

Solución: complejos.hpp
#ifndef _complejos_hpp_
#define _complejos_hpp_
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6 ;
//----------------------------------
struct Complejo {
double real ; // parte real del numero complejo
double imag ; // parte imaginaria del numero complejo
} ;
//----------------------------------
void sumar(Complejo& r, const Complejo& a, const Complejo& b) ;
// Devuelve un numero complejo (r) que contiene el resultado de
// sumar los numeros complejos (a) y (b).
//----------------------------------
void restar(Complejo& r, const Complejo& a, const Complejo& b) ;
// Devuelve un numero complejo (r) que contiene el resultado de
// restar los numeros complejos (a) y (b).
//----------------------------------
void multiplicar(Complejo& r, const Complejo& a, const Complejo& b) ;
// Devuelve un numero complejo (r) que contiene el resultado de
// multiplicar los numeros complejos (a) y (b).
//----------------------------------
void dividir(Complejo& r, const Complejo& a, const Complejo& b) ;
// Devuelve un numero complejo (r) que contiene el resultado de
// dividir los numeros complejos (a) y (b).
//----------------------------------
bool iguales(const Complejo& a, const Complejo& b) ;
// Devuelve true si los numeros complejos (a) y (b) son iguales.
//----------------------------------
void escribir(const Complejo& a) ;
// muestra en pantalla el numero complejo (a)
//----------------------------------
void leer(Complejo& a) ;
// lee de teclado el valor del numero complejo (a).
// lee la parte real y la parte imaginaria del numero
//----------------------------------
}
#endif

Solución: complejos.cpp
#include "complejos.hpp"
#include <iostream>
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//----------------------------------
//-- Subprogramas Auxiliares -------
//----------------------------------
// cuadrado de un numero (a^2)
inline double sq(double a)
{
return a*a ;
}
//----------------------------------
// Valor absoluto de un numero
inline double abs(double a)

15
{
if (a < 0) {
a = -a ;
}
return a ;
}
//----------------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b)
{
return abs(a-b) <= ERROR_PRECISION ;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//----------------------------------
//-- Implementación ----------------
//----------------------------------
// Devuelve un numero complejo (r) que contiene el resultado de
// sumar los numeros complejos (a) y (b).
void sumar(Complejo& r, const Complejo& a, const Complejo& b)
{
r.real = a.real + b.real ;
r.imag = a.imag + b.imag ;
}
//----------------------------------
// Devuelve un numero complejo (r) que contiene el resultado de
// restar los numeros complejos (a) y (b).
void restar(Complejo& r, const Complejo& a, const Complejo& b)
{
r.real = a.real - b.real ;
r.imag = a.imag - b.imag ;
}
//----------------------------------
// Devuelve un numero complejo (r) que contiene el resultado de
// multiplicar los numeros complejos (a) y (b).
void multiplicar(Complejo& r, const Complejo& a, const Complejo& b)
{
double p_real = (a.real * b.real) - (a.imag * b.imag) ;
double p_imag = (a.real * b.imag) + (a.imag * b.real) ;
r.real = p_real;
r.imag = p_imag;
}
//----------------------------------
// Devuelve un numero complejo (r) que contiene el resultado de
// dividir los numeros complejos (a) y (b).
void dividir(Complejo& r, const Complejo& a, const Complejo& b)
{
double divisor = sq(b.real) + sq(b.imag) ;
if (::iguales(0.0, divisor)) {
r.real = 0 ;
r.imag = 0 ;
} else {
double p_real = ((a.real * b.real) + (a.imag * b.imag)) / divisor ;
double p_imag = ((a.imag * b.real) - (a.real * b.imag)) / divisor ;
r.real = p_real;
r.imag = p_imag;
}
}
//----------------------------------
// Devuelve true si los numeros complejos (a) y (b) son iguales.
bool iguales(const Complejo& a, const Complejo& b)
{
return ::iguales(a.real, b.real) && ::iguales(a.imag, b.imag) ; }
//----------------------------------
// muestra en pantalla el numero complejo (a)
void escribir(const Complejo& a)
{
cout << "{ " << a.real << ", " << a.imag << " }" ;
}

16
//----------------------------------
// lee de teclado el valor del numero complejo (a).
// lee la parte real y la parte imaginaria del numero
void leer(Complejo& a)
{
cin >> a.real >> a.imag ;
}
//----------------------------------
}

Solución: main.cpp
#include <iostream>
#include "complejos.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------
void leer(Complejo& c)
{
cout << "Introduzca un numero complejo { real img }: " ;
// cualificacion explı́cita del espacio de nombres uamalcc
// para evitar colisión en invocación a subprograma
// leer(Complejo& c) del espacio de nombres umalcc
umalcc::leer(c) ;
}
//------------------------------------
void prueba_suma(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
sumar(c0, c1, c2) ;
escribir(c1) ;
cout <<" + " ;
escribir(c2) ;
cout <<" = " ;
escribir(c0) ;
cout << endl ;
Complejo aux ;
restar(aux, c0, c2) ;
if (! iguales(c1, aux)) {
cout << "Error en operaciones de suma/resta"<< endl ;
}
}
//------------------------------------
void prueba_resta(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
restar(c0, c1, c2) ;
escribir(c1) ;
cout <<" - " ;
escribir(c2) ;
cout <<" = " ;
escribir(c0) ;
cout << endl ;
Complejo aux ;
sumar(aux, c0, c2) ;
if (! iguales(c1, aux)) {
cout << "Error en operaciones de suma/resta"<< endl ;
}
}
//------------------------------------
void prueba_mult(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
multiplicar(c0, c1, c2) ;
escribir(c1) ;
cout <<" * " ;
escribir(c2) ;
cout <<" = " ;
escribir(c0) ;
cout << endl ;
Complejo aux ;
dividir(aux, c0, c2) ;
if (! iguales(c1, aux)) {
cout << "Error en operaciones de mult/div"<< endl ;
}

17
}
//------------------------------------
void prueba_div(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
dividir(c0, c1, c2) ;
escribir(c1) ;
cout <<" / " ;
escribir(c2) ;
cout <<" = " ;
escribir(c0) ;
cout << endl ;
Complejo aux ;
multiplicar(aux, c0, c2) ;
if (! iguales(c1, aux)) {
cout << "Error en operaciones de mult/div"<< endl ;
}
}
//------------------------------------
int main()
{
Complejo c1, c2 ;
// cualificación explı́cita del espacio de nombres global
// para evitar colisión en invocación al subprograma
// leer(Complejo& c) del espacio de nombres global
::leer(c1) ;
::leer(c2) ;
//--------------------------------
prueba_suma(c1, c2) ;
prueba_resta(c1, c2) ;
prueba_mult(c1, c2) ;
prueba_div(c1, c2) ;
//--------------------------------
}

2. Diseñe un módulo que proporcione soporte adecuado a la arı́metica con números racionales. El módulo
deberá definir el tipo Racional, ası́ como los subprogramas adecuados teniendo en cuenta las siguientes
consideraciones:
Los números racionales se representan mediante fracciones, donde tanto el numerador como denom-
inador son de tipo int).
Las fracciones se representarán en todo momento normalizadas (simplificadas):
• Es recomendable implementar un subprograma privado de simplificación de la fracción mediante
el cálculo del máximo común divisor (m.c.d.) del numerador y el denominador y su posterior
división por dicho m.c.d.
• Para simplificar la determinación del signo de la fracción puede asumirse que el denominador de
la fracción es siempre positivo (por lo que una fracción de signo negativo tendrá un numerador
de signo negativo y un denominador de signo positivo).
El número cero se guardará siempre en su forma canónica 0/1.
Debe evitarse en todo momento la asignación de cero a un denominador.
Debe proporcionar subprogramas para leer, escribir, sumar, restar, multiplicar y dividir fracciones.
Pueden también suministrarse métodos para comparación de números racionales (iguales, menor,
etc).
Solución: racionales.hpp
#ifndef _racionales_hpp_
#define _racionales_hpp_
namespace umalcc {
//----------------------------------
struct Racional {
int num; // numerador
int denom; // denominador
};
//------------------------------
void sumar(Racional& r, const Racional& r1, const Racional& r2);
// Asigna al número racional (r) el resultado de
// sumar los números racionales (r1) y (r2)

18
//------------------------------
void restar(Racional& r, const Racional& r1, const Racional& r2);
// Asigna al número racional (r) el resultado de
// restar los números racionales (r1) y (r2)
//------------------------------
void multiplicar(Racional& r, const Racional& r1, const Racional& r2);
// Asigna al número racional (r) el resultado de
// multiplicar los números racionales (r1) y (r2)
//------------------------------
void dividir(Racional& r, const Racional& r1, const Racional& r2);
// Asigna al número racional (r) el resultado de
// dividir los números racionales (r1) y (r2)
//------------------------------
void escribir(const Racional& r);
// Muestra en pantalla el valor del número racional (r)
//------------------------------
void leer(Racional& r);
// Lee de teclado el valor del número racional (r)
//------------------------------
bool igual(const Racional& r1, const Racional& r2);
// Devuelve true si el número racional (r1) es igual a (r2)
//------------------------------
bool menor(const Racional& r1, const Racional& r2);
// Devuelve true si el número racional (r1) es menor que (r2)
//----------------------------------------------------------
}
#endif

Solución: racionales.cpp
#include "racionales.hpp"
#include <iostream>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//----------------------------------
//-- Subprogramas Auxiliares -------
//----------------------------------
// valor absoluto de un numero entero
inline int abs(int x)
{
if (x < 0) {
x = -x;
}
return x;
}
//---------------------------------
// maximo comun divisor. algoritmo de euclides
int mcd(int a, int b)
{
a = abs(a);
b = abs(b);
int res;
if (a == 0) {
res = b;
} else {
while (b != 0) {
if (a > b) {
a = a - b;
} else {
b = b - a;
}
}
res = a;
}
return res;
}
//---------------------------------
// minimo comun multiplo
// inline int mcm(int a, int b)
// {

19
// a = abs(a);
// b = abs(b);
// return (a / mcd(a, b)) * b;
// }
//----------------------------------
// normalizar un numero racional
void normalizar(Racional& r)
{
if (r.denom == 0) {
// Error denominador cero
r.num = 0;
r.denom = 0;
} else if (r.num == 0) {
// Forma canonica
r.num = 0;
r.denom = 1;
} else {
// dividir numerador y denominador por MCD
int max_com_div = mcd(r.num, r.denom);
r.num /= max_com_div;
r.denom /= max_com_div;
// si denominador negativo, entonces cambiar signo
if (r.denom < 0) {
r.num = -r.num;
r.denom = -r.denom;
}
}
}
//--------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//-----------------------------
void sumar(Racional& r, const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*d2 n2*d1 (n1*d2)+(n2*d1)
// ---- + ---- == ------- + ------- == -----------------
// d1 d2 d1*d2 d2*d1 d1*d2
//-------------------------
r.num = ((r1.num * r2.denom) + (r2.num * r1.denom));
r.denom = r1.denom * r2.denom;
normalizar(r);
}
//-----------------------------
void restar(Racional& r, const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*d2 n2*d1 (n1*d2)-(n2*d1)
// ---- - ---- == ------- - ------- == -----------------
// d1 d2 d1*d2 d2*d1 d1*d2
//-------------------------
r.num = ((r1.num * r2.denom) - (r2.num * r1.denom));
r.denom = r1.denom * r2.denom;
normalizar(r);
}
//-----------------------------
void multiplicar(Racional& r, const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*n2
// ---- * ---- == -------
// d1 d2 d1*d2
//-------------------------
r.num = r1.num * r2.num;
r.denom = r1.denom * r2.denom;
normalizar(r);
}
//-----------------------------
void dividir(Racional& r, const Racional& r1, const Racional& r2)
{

20
//-------------------------
// n1 n2 n1*d2
// ---- / ---- == -------
// d1 d2 d1*n2
//-------------------------
int p_num = r1.num * r2.denom;
int p_denom = r1.denom * r2.num;
r.num = p_num;
r.denom = p_denom;
normalizar(r);
}
//-----------------------------
void escribir(const Racional& r)
{
if (r.denom == 0) {
cout << "Error";
} else {
cout << r.num << " / " << r.denom;
}
}
//-----------------------------
void leer(Racional& r)
{
cin >> r.num >> r.denom;
normalizar(r);
}
//-----------------------------
bool igual(const Racional& r1, const Racional& r2)
{
// ambos numeros estan normalizados
return (r1.num == r2.num && r1.denom == r2.denom);
}
//-----------------------------
bool menor(const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*d2 n2*d1 (n1*d2) < (n2*d1)
// ---- < ---- ==> ------- < ------- ==> -----------------
// d1 d2 d1*d2 d2*d1 d1*d2
//-------------------------
return (r1.num * r2.denom) < (r2.num * r1.denom);
}
//-----------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include <cassert>
#include "racionales.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Racional& r)


{
cout << msj;
escribir(r);
cout << endl;
}
int main()
{
//-------------------------
cout << "Introduzca numerador y denominador: ";
Racional r1;
leer(r1);
escribir("R1: ", r1);
//-------------------------
cout << "Introduzca numerador y denominador: ";
Racional r2;
leer(r2);
escribir("R2: ", r2);
//-------------------------
Racional r3;
sumar(r3, r1, r2);

21
escribir("R3 (r1 + r2): ", r3);
//-------------------------
Racional r4;
restar(r4, r3, r2);
escribir("R4 (r3 - r2): ", r4);
if ( ! igual(r1, r4)) {
cout << "Error en suma/resta"<<endl;
}
//-------------------------
Racional r5;
multiplicar(r5, r1, r2);
escribir("R5 (r1 * r2): ", r5);
//-------------------------
Racional r6;
dividir(r6, r5, r2);
escribir("R6 (r5 / r2): ", r6);
if ( ! igual(r1, r6)) {
cout << "Error en multiplicacion/division"<<endl;
}
//-------------------------
}

3. Diseñe un módulo que proporcione soporte adecuado a la manipulación de polinomios de grados positivos
menores que 100. El módulo deberá definir el tipo Polinomio, ası́ como las siguientes operaciones:

Solución: polinomios.hpp
#ifndef _polinomio_hpp_
#define _polinomio_hpp_
#include <iostream>
#include <tr1/array>
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6;
const unsigned MAX = 100;
typedef std::tr1::array<double, MAX> Datos;
struct Polinomio {
unsigned mg; // maximo grado del polinomio
Datos coef; // coeficientes del polinomio (indice es el exp. de la X)
};
//----------------------------------
double evaluar(const Polinomio& a, double x);
// Devuelve el resultado de evaluar el polinomio (a)
// para un valor de (x) especificado como parametro
void derivar(Polinomio& r, const Polinomio& a);
// Devuelve un polinomio (r) que contiene el resultado de
// calcular la derivada del polinomio (a)
void sumar(Polinomio& r, const Polinomio& a, const Polinomio& b);
// Devuelve un polinomio (r) que contiene el resultado de
// sumar los polinomios (a) y (b)
void escribir(const Polinomio& a) ;
// Muestra en pantalla el contenido del polinomio (a)
void leer(Polinomio& a) ;
// Lee de teclado el valor del polinomio (a)
// Lee pares de coeficiente y grado hasta que el coeficiente sea cero
}
#endif

Solución: polinomios.cpp
#include "polinomios.hpp"
#include <iostream>
#include <cassert>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//-------------------------
// Subprogramas Auxiliares
//-------------------------
// Valor absoluto de un numero

22
inline double abs(double a) {
return (a >= 0) ? a : -a;
}
//-------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b) {
return abs(a-b) <= ERROR_PRECISION;
}
//-------------------------
double potencia(double base, unsigned exp)
{
double res = 1;
for (unsigned i = 0; i < exp; ++i) {
res *= base;
}
return res;
}
//----------------------------------
void inicializar(Polinomio& a)
{
a.mg = 0;
for (unsigned i = 0; i < a.coef.size(); ++i) {
a.coef[i] = 0.0;
}
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
double evaluar(const Polinomio& a, double x)
{
// Devuelve el resultado de evaluar el polinomio (a)
// para un valor de (x) especificado como parametro
double res = 0;
for (unsigned i = 0; i <= a.mg; ++i) {
res += a.coef[i] * potencia(x, i);
}
return res;
}
void derivar(Polinomio& r, const Polinomio& a)
{
assert(&r != &a);
// Devuelve un polinomio (r) que contiene el resultado de
// calcular la derivada del polinomio (a)
inicializar(r);
for (unsigned i = 1; i <= a.mg; ++i) {
r.coef[i-1] = a.coef[i]*i;
}
r.mg = a.mg-1;
}
void sumar(Polinomio& r, const Polinomio& a, const Polinomio& b)
{
assert((&r != &a)&&(&r != &b));
// Devuelve un polinomio (r) que contiene el resultado de
// sumar los polinomios (a) y (b)
inicializar(r);
if (a.mg >= b.mg) {
r.mg = a.mg;
} else {
r.mg = b.mg;
}
for (unsigned i = 0; i <= r.mg; ++i) {
r.coef[i] = a.coef[i] + b.coef[i];
}
}
void escribir(const Polinomio& a)
{
// Muestra en pantalla el contenido del polinomio (a)
for (unsigned i = 0; i <= a.mg; ++i) {
if (! iguales(a.coef[i], 0.0)) {

23
cout << (a.coef[i] > 0.0 ? " +" : " ")
<< a.coef[i] << " X^" << i ;
}
}
}
void leer(Polinomio& a)
{
// Lee de teclado el valor del polinomio (a)
// Lee pares de coeficiente y grado hasta que el coeficiente sea cero
inicializar(a);
double c;
cout << "Introduzca coeficiente [0 -> fin]: ";
cin >> c;
while ( !cin.fail() && ! iguales(c, 0.0) ) {
unsigned e;
cout << "Introduzca exponente: ";
cin >> e;
if ( ! cin.fail() && (e < a.coef.size())) {
a.coef[e] = c;
if (e > a.mg) {
a.mg = e;
}
}
cout << "Introduzca coeficiente [0 -> fin]: ";
cin >> c;
}
}
//--------------------------------
}

Solución: main.cpp
#include <iostream>
#include "polinomios.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Polinomio& p)


{
cout << msj;
escribir(p);
cout << endl;
}

int main()
{
Polinomio p1, p2, p3;
leer(p1);
escribir("P1: ", p1);
cout << "P1(2) = " << evaluar(p1, 2.0) << endl;
derivar(p2, p1);
escribir("P2: ", p2);
sumar(p3, p1, p2);
escribir("P3: ", p3);
}

Tema 2.2: Tipos Abstractos de Datos


Diseñe e implemente los siguientes TADs, dentro del espacio de nombres umalcc, ası́ como programas de
utilización que permitan comprobar su funcionamiento.

1. TAD número complejo que defina los siguientes métodos públicos:

Solución: complejo.hpp
#ifndef _complejos_hpp_
#define _complejos_hpp_
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6 ;
//----------------------------------
class Complejo {
public:

24
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
Complejo() ; // Constructor por Defecto
//----------------------------
double parte_real() const ;
// devuelve la parte real del numero complejo
//----------------------------
double parte_imag() const ;
// devuelve la parte imaginaria del numero complejo
//----------------------------
void sumar(const Complejo& a, const Complejo& b) ;
// asigna al numero complejo (actual) el resultado de
// sumar los numeros complejos (a) y (b).
//----------------------------
void restar(const Complejo& a, const Complejo& b) ;
// asigna al numero complejo (actual) el resultado de
// restar los numeros complejos (a) y (b).
//----------------------------
void multiplicar(const Complejo& a, const Complejo& b) ;
// asigna al numero complejo (actual) el resultado de
// multiplicar los numeros complejos (a) y (b).
//----------------------------
void dividir(const Complejo& a, const Complejo& b) ;
// asigna al numero complejo (actual) el resultado de
// dividir los numeros complejos (a) y (b).
//----------------------------
bool igual(const Complejo& b) const ;
// Devuelve true si el numero complejo (actual) es
// igual al numero complejo (b)
//----------------------------
void escribir() const ;
// muestra en pantalla el numero complejo (actual)
//----------------------------
void leer() ;
// lee de teclado el valor del numero complejo (actual).
// lee la parte real y la parte imaginaria del numero
//------------------------------
private:
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
double real ; // parte real del numero complejo
double imag ; // parte imaginaria del numero complejo
//----------------------------------------------------------
} ;
}
#endif

Solución: complejo.cpp
#include "complejos.hpp"
#include <iostream>
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//----------------------------------------------------------
//-- Subprogramas Auxiliares -------------------------------
//----------------------------------------------------------
// cuadrado de un numero (a^2)
inline double sq(double a)
{
return a*a ;
}
//-------------------------
// Valor absoluto de un numero
inline double abs(double a)
{
return (a >= 0) ? a : -a ;
}
//-------------------------

25
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b)
{
return abs(a-b) <= ERROR_PRECISION ;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
Complejo::Complejo() // Constructor por Defecto
: real(0.0), imag(0.0) {}
//----------------------------
// devuelve la parte real del numero complejo
double Complejo::parte_real() const
{
return real ;
}
//----------------------------
// devuelve la parte imaginaria del numero complejo
double Complejo::parte_imag() const
{
return imag ;
}
//----------------------------
// asigna al numero complejo (actual) el resultado de
// sumar los numeros complejos (a) y (b).
void Complejo::sumar(const Complejo& a, const Complejo& b)
{
real = a.real + b.real ;
imag = a.imag + b.imag ;
}
//----------------------------
// asigna al numero complejo (actual) el resultado de
// restar los numeros complejos (a) y (b).
void Complejo::restar(const Complejo& a, const Complejo& b)
{
real = a.real - b.real ;
imag = a.imag - b.imag ;
}
//----------------------------
// asigna al numero complejo (actual) el resultado de
// multiplicar los numeros complejos (a) y (b).
void Complejo::multiplicar(const Complejo& a, const Complejo& b)
{
double p_real = (a.real * b.real) - (a.imag * b.imag) ;
double p_imag = (a.real * b.imag) + (a.imag * b.real) ;
real = p_real;
imag = p_imag;
}
//----------------------------
// asigna al numero complejo (actual) el resultado de
// dividir los numeros complejos (a) y (b).
void Complejo::dividir(const Complejo& a, const Complejo& b)
{
double divisor = sq(b.real) + sq(b.imag) ;
if (iguales(0.0, divisor)) {
real = 0.0 ;
imag = 0.0 ;
} else {
double p_real = ((a.real * b.real) + (a.imag * b.imag)) / divisor ;
double p_imag = ((a.imag * b.real) - (a.real * b.imag)) / divisor ;
real = p_real;
imag = p_imag;
}
}
//----------------------------
// Devuelve true si el numero complejo (actual) es
// igual al numero complejo (b)

26
bool Complejo::igual(const Complejo& b) const
{
return iguales(real, b.real) && iguales(imag, b.imag) ;
}
//----------------------------
// muestra en pantalla el numero complejo (actual)
void Complejo::escribir() const
{
cout << "{ " << real << ", " << imag << " }" ;
}
//----------------------------
// lee de teclado el valor del numero complejo (actual).
// lee la parte real y la parte imaginaria del numero
void Complejo::leer()
{
cin >> real >> imag ;
}
//----------------------------
}

Solución: main.cpp
#include <iostream>
#include "complejos.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------
void leer(Complejo& c)
{
cout << "Introduzca un numero complejo { real img }: " ;
c.leer() ;
}
//------------------------------------
void prueba_suma(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
c0.sumar(c1, c2) ;
c1.escribir() ;
cout <<" + " ;
c2.escribir() ;
cout <<" = " ;
c0.escribir() ;
cout << endl ;
Complejo aux ;
aux.restar(c0, c2) ;
if ( ! c1.igual(aux)) {
cout << "Error en operaciones de suma/resta"<< endl ;
}
}
//------------------------------------
void prueba_resta(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
c0.restar(c1, c2) ;
c1.escribir() ;
cout <<" - " ;
c2.escribir() ;
cout <<" = " ;
c0.escribir() ;
cout << endl ;
Complejo aux ;
aux.sumar(c0, c2) ;
if ( ! c1.igual(aux)) {
cout << "Error en operaciones de suma/resta"<< endl ;
}
}
//------------------------------------
void prueba_mult(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
c0.multiplicar(c1, c2) ;
c1.escribir() ;
cout <<" * " ;
c2.escribir() ;
cout <<" = " ;

27
c0.escribir() ;
cout << endl ;
Complejo aux ;
aux.dividir(c0, c2) ;
if ( ! c1.igual(aux)) {
cout << "Error en operaciones de mult/div"<< endl ;
}
}
//------------------------------------
void prueba_div(const Complejo& c1, const Complejo& c2)
{
Complejo c0 ;
c0.dividir(c1, c2) ;
c1.escribir() ;
cout <<" / " ;
c2.escribir() ;
cout <<" = " ;
c0.escribir() ;
cout << endl ;
Complejo aux ;
aux.multiplicar(c0, c2) ;
if ( ! c1.igual(aux)) {
cout << "Error en operaciones de mult/div"<< endl ;
}
}
//------------------------------------
int main()
{
Complejo c1, c2 ;
leer(c1) ;
leer(c2) ;
//--------------------------------
prueba_suma(c1, c2) ;
prueba_resta(c1, c2) ;
prueba_mult(c1, c2) ;
prueba_div(c1, c2) ;
//--------------------------------
}

2. Diseñe e implemente un TAD para números racionales, ası́ como un programa para poder probar su
funcionamiento. Se deberán seguir las siguientes consideraciones:
Los números racionales se representan mediante fracciones, donde tanto el numerador como denom-
inador son de tipo int).
Las fracciones se representarán en todo momento normalizadas (simplificadas):
• Es recomendable implementar un método privado de simplificación de la fracción mediante el
cálculo del máximo común divisor (m.c.d.) del numerador y el denominador y su posterior división
por dicho m.c.d.
• Para simplificar la determinación del signo de la fracción puede asumirse que el denominador de
la fracción es siempre positivo (por lo que una fracción de signo negativo tendrá un numerador
de signo negativo y un denominador de signo positivo).
Por defecto, un número racional debe asignarse a cero cuando se crea.
El cero se guardará siempre en su forma canónica 0/1.
Debe evitarse en todo momento la asignación de cero a un denominador.
Un determinado método permitirá comprobar si un determinado número racional se encuentra en
estado de error. Un denominador igual a cero (0) se considera un estado de error.
Debe proporcionar operaciones para crear, leer, asignar valores, escribir, sumar, restar, multiplicar y
dividir fracciones.
Debe poder ser posible crear números racionales especificando su numerador y denominador, como
sólamente especificando su numerador (en este último caso, se considerará un denominador igual a
1).
Pueden también suministrarse métodos para comparación de números racionales (iguales, menor,
etc).

28
Solución: racionales.hpp
#ifndef _racionales_hpp_
#define _racionales_hpp_
namespace umalcc {
class Racional {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
//~Racional() {}
//Racional(const Racional& o);
//Racional& operator=(const Racional& o);
//-------------------------
Racional() ;
// Construye el número racional 0/1
//------------------------------
Racional(int n) ;
// Construye el número racional n/1
//------------------------------
Racional(int n, int d) ;
// Construye el número racional n/d
//------------------------------
bool fail() const;
// Devuelve true si el número racional actual esta en estado erroneo
//------------------------------
void sumar(const Racional& r1, const Racional& r2);
// Asigna al número racional actual el resultado de
// sumar los números racionales (r1) y (r2)
//------------------------------
void restar(const Racional& r1, const Racional& r2);
// Asigna al número racional actual el resultado de
// restar los números racionales (r1) y (r2)
//------------------------------
void multiplicar(const Racional& r1, const Racional& r2);
// Asigna al número racional actual el resultado de
// multiplicar los números racionales (r1) y (r2)
//------------------------------
void dividir(const Racional& r1, const Racional& r2);
// Asigna al número racional actual el resultado de
// dividir los números racionales (r1) y (r2)
//------------------------------
void escribir() const;
// Muestra en pantalla el valor del número racional actual
//------------------------------
void leer();
// Lee de teclado el valor del número racional actual
//------------------------------
bool igual(const Racional& r1) const;
// Devuelve true si el número racional actual es igual a (r1)
//------------------------------
bool menor(const Racional& r1) const;
// Devuelve true si el número racional actual es menor que (r1)
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void normalizar();
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int num; // numerador
int denom; // denominador
//----------------------------------------------------------
};
}
#endif

Solución: racionales.cpp
#include "racionales.hpp"
#include <iostream>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------

29
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//----------------------------------
//-- Subprogramas Auxiliares -------
//----------------------------------
// valor absoluto de un numero entero
inline int abs(int x)
{
if (x < 0) {
x = -x;
}
return x;
}
//---------------------------------
// maximo comun divisor. algoritmo de euclides
int mcd(int a, int b)
{
a = abs(a);
b = abs(b);
int res;
if (a == 0) {
res = b;
} else {
while (b != 0) {
if (a > b) {
a = a - b;
} else {
b = b - a;
}
}
res = a;
}
return res;
}
//---------------------------------
// minimo comun multiplo
// inline int mcm(int a, int b)
// {
// a = abs(a);
// b = abs(b);
// return (a / mcd(a, b)) * b;
// }
//--------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
Racional::Racional() : num(0), denom(1) {}
//----------------------------------
Racional::Racional(int n) : num(n), denom(1) {}
//----------------------------------
Racional::Racional(int n, int d) : num(n), denom(d)
{
normalizar();
}
//----------------------------------
bool Racional::fail() const
{
return (denom == 0);
}
//----------------------------------
// normalizar un numero racional
void Racional::normalizar()
{
if (denom == 0) {
// Error denominador cero
num = 0;
denom = 0;
} else if (num == 0) {
// Forma canonica

30
num = 0;
denom = 1;
} else {
// dividir numerador y denominador por MCD
int max_com_div = mcd(num, denom);
num /= max_com_div;
denom /= max_com_div;
// si denominador negativo, entonces cambiar signo
if (denom < 0) {
num = -num;
denom = -denom;
}
}
}
//-----------------------------
void Racional::sumar(const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*d2 n2*d1 (n1*d2)+(n2*d1)
// ---- + ---- == ------- + ------- == -----------------
// d1 d2 d1*d2 d2*d1 d1*d2
//-------------------------
num = ((r1.num * r2.denom) + (r2.num * r1.denom));
denom = r1.denom * r2.denom;
normalizar();
}
//-----------------------------
void Racional::restar(const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*d2 n2*d1 (n1*d2)-(n2*d1)
// ---- - ---- == ------- - ------- == -----------------
// d1 d2 d1*d2 d2*d1 d1*d2
//-------------------------
num = ((r1.num * r2.denom) - (r2.num * r1.denom));
denom = r1.denom * r2.denom;
normalizar();
}
//-----------------------------
void Racional::multiplicar(const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*n2
// ---- * ---- == -------
// d1 d2 d1*d2
//-------------------------
num = r1.num * r2.num;
denom = r1.denom * r2.denom;
normalizar();
}
//-----------------------------
void Racional::dividir(const Racional& r1, const Racional& r2)
{
//-------------------------
// n1 n2 n1*d2
// ---- / ---- == -------
// d1 d2 d1*n2
//-------------------------
int p_num = r1.num * r2.denom;
int p_denom = r1.denom * r2.num;
num = p_num;
denom = p_denom;
normalizar();
}
//-----------------------------
void Racional::escribir() const
{
if (fail()) {
cout << "Error";
} else {
cout << num << " / " << denom;
}
}
//-----------------------------

31
void Racional::leer()
{
cin >> num >> denom;
normalizar();
}
//-----------------------------
bool Racional::igual(const Racional& r1) const
{
// ambos numeros estan normalizados
return (num == r1.num && denom == r1.denom);
}
//-----------------------------
bool Racional::menor(const Racional& r1) const
{
//-------------------------
// n1 n2 n1*d2 n2*d1 (n1*d2) < (n2*d1)
// ---- < ---- ==> ------- < ------- ==> -----------------
// d1 d2 d1*d2 d2*d1 d1*d2
//-------------------------
return (num * r1.denom) < (r1.num * denom);
}
//-----------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include <cassert>
#include "racionales.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Racional& r)


{
cout << msj;
r.escribir();
cout << endl;
}
int main()
{
//-------------------------
cout << "Introduzca numerador y denominador: ";
Racional r1;
r1.leer();
escribir("R1: ", r1);
//-------------------------
cout << "Introduzca numerador y denominador: ";
Racional r2;
r2.leer();
escribir("R2: ", r2);
//-------------------------
Racional r3;
r3.sumar(r1, r2);
escribir("R3 (r1 + r2): ", r3);
//-------------------------
Racional r4;
r4.restar(r3, r2);
escribir("R4 (r3 - r2): ", r4);
if ( ! r1.igual(r4)) {
cout << "Error en suma/resta"<<endl;
}
//-------------------------
Racional r5;
r5.multiplicar(r1, r2);
escribir("R5 (r1 * r2): ", r5);
//-------------------------
Racional r6;
r6.dividir(r5, r2);
escribir("R6 (r5 / r2): ", r6);
if ( ! r1.igual(r6)) {
cout << "Error en multiplicacion/division"<<endl;
}
//-------------------------
}

32
3. TAD polinomio de grados positivos menores que 100 que defina los siguientes métodos públicos:

Solución: polinomios.hpp
#ifndef _polinomios_hpp_
#define _polinomios_hpp_
#include <tr1/array>
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6;
//----------------------------------
class Polinomio {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
// Definidos automaticamente por el compilador
//-------------------------
//~Polinomio();
//Polinomio(const Polinomio& p);
//Polinomio& operator=(const Polinomio& p);
//----------------------------
Polinomio();
unsigned max_grado() const;
// Devuelve el mayor grado del polinomio actual
// cuyo coeficiente es distinto de cero
void poner(unsigned e, double c);
// Asigna el coeficiente (c) al termino de grado (e)
// del polinomio actual
double obtener(unsigned e) const;
// Devuelve el coeficiente correspondiente al
// termino de grado (e) del polinomio actual
double evaluar(double x) const;
// Devuelve el resultado de evaluar el polinomio actual
// para un valor de (x) especificado como parametro
void derivar(const Polinomio& a);
// Asigna al polinomio actual el resultado de
// calcular la derivada del polinomio (a)
void sumar(const Polinomio& a, const Polinomio& b);
// Asigna al polinomio actual el resultado de
// sumar los polinomios (a) y (b)
void escribir() const;
// Muestra en pantalla el contenido del polinomio actual
void leer();
// Lee de teclado el valor del polinomio actual
// Lee pares de coeficiente y grado hasta que el coeficiente sea cero
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
static const unsigned MAX = 100;
typedef std::tr1::array<double, MAX> Datos;
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void inicializar();
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
unsigned mg; // maximo grado del polinomio
Datos coef; // coeficientes del polinomio (indice es el exp de la X)
//----------------------------------------------------------
};
}
#endif

Solución: polinomios.cpp
#include "polinomios.hpp"
#include <iostream>
#include <cassert>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------

33
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//----------------------------------
//-- Subprogramas Auxiliares -------
//----------------------------------
// Valor absoluto de un numero
inline double abs(double a) {
return (a >= 0) ? a : -a;
}
//-------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b) {
return abs(a-b) <= ERROR_PRECISION;
}
//-------------------------
double potencia(double base, unsigned exp)
{
double res = 1;
for (unsigned i = 0; i < exp; ++i) {
res *= base;
}
return res;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
Polinomio::Polinomio()
: mg(0), coef()
{
inicializar();
}
//--------------------------------
unsigned Polinomio::max_grado() const
{
return mg;
}
//--------------------------------
void Polinomio::poner(unsigned e, double c)
{
if (e < coef.size()) {
coef[e] = c;
if ((e > mg) && !iguales(c, 0.0)) {
mg = e;
}
}
}
//--------------------------------
double Polinomio::obtener(unsigned e) const
{
double res;
if (e < mg) {
res = coef[e];
} else {
res = 0.0;
}
return res;
}
//--------------------------------
double Polinomio::evaluar(double x) const
{
double res = 0;
for (unsigned i = 0; i <= mg; ++i) {
res += coef[i] * potencia(x, i);
}
return res;
}
//--------------------------------
void Polinomio::derivar(const Polinomio& a)

34
{
assert(this != &a);
inicializar();
for (unsigned i = 1; i <= a.mg; ++i) {
coef[i-1] = a.coef[i]*i;
}
mg = a.mg-1;
}
//--------------------------------
void Polinomio::sumar(const Polinomio& a, const Polinomio& b)
{
assert((this != &a)&&(this != &b));
inicializar();
if (a.mg >= b.mg) {
mg = a.mg;
} else {
mg = b.mg;
}
for (unsigned i = 0; i <= mg; ++i) {
coef[i] = a.coef[i] + b.coef[i];
}
}
//----------------------------------
void Polinomio::escribir() const
{
for (unsigned i = 0; i <= mg; ++i) {
if (! iguales(coef[i], 0.0)) {
cout << (coef[i] > 0.0 ? " +" : " ")
<< coef[i] << " X^" << i ;
}
}
}
//--------------------------------
void Polinomio::leer()
{
inicializar();
double c;
cout << "Introduzca coeficiente [0 -> fin]: ";
cin >> c;
while ( !cin.fail() && ! iguales(c, 0.0) ) {
unsigned e;
cout << "Introduzca exponente: ";
cin >> e;
if ( ! cin.fail() && (e < coef.size())) {
coef[e] = c;
if (e > mg) {
mg = e;
}
}
cout << "Introduzca coeficiente [0 -> fin]: ";
cin >> c;
}
}
//--------------------------------
void Polinomio::inicializar()
{
mg = 0;
for (unsigned i = 0; i < coef.size(); ++i) {
coef[i] = 0.0;
}
}
//--------------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include "polinomios.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Polinomio& p)


{
cout << msj;

35
p.escribir();
cout << endl;
}

int main()
{
Polinomio p1, p2, p3;
p1.leer();
escribir("P1: ", p1);
cout << "P1(2) = " << p1.evaluar(2.0) << endl;
p2.derivar(p1);
escribir("P2: ", p2);
p3.sumar(p1, p2);
escribir("P3: ", p3);
p1 = p3;
escribir("P1: ", p1);
}

4. Un conjunto de números enteros es una colección de elementos homogéneos (números enteros) sin repetición
y ninguna relación de orden entre ellos (no ordenados y sin repetición). Defina un TAD Conjunto de
números enteros que defina los siguientes métodos públicos:

Solución: conjuntos.hpp
#ifndef _conjuntos_hpp_
#define _conjuntos_hpp_
#include <iostream>
#include <tr1/array>
namespace umalcc {
class Conjunto {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
// Generados automaticamente por el compilador
//----------------------------------
//~Conjunto();
//Conjunto(const Conjunto& c);
//Conjunto& operator=(const Conjunto& c);
//----------------------------------
Conjunto();
// Constructor por Defecto: conjunto vacio
void clear();
// Elimina todos los elementos del conjunto actual (queda vacio)
bool es_vacio() const;
// Devuelve true si el conjunto actual esta vacio
void incluir(int e);
// Incluye el elemento (e) en el conjunto actual
void eliminar(int e);
// Elimina el elemento (e) del conjunto actual
bool pertenece(int e) const;
// Devuelve true si el elemento (e) pertenece al conjunto actual
bool es_subconjunto(const Conjunto& a) const;
// Devuelve true si el conjunto actual es subconjunto del conjunto (a)
void union_conj(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la union de los conjuntos (a) y (b)
void interseccion(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la interseccion de los conjuntos (a) y (b)
void diferencia(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la diferencia de los conjuntos (a) y (b)
void diferencia_simetrica(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la diferencia simetrica de los conjuntos (a) y (b)
void escribir() const;
// Muestra en pantalla el contenido del conjunto actual
void leer();
// Lee de teclado el valor del conjunto actual,
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------

36
//----------------------------------------------------------
static const unsigned MAX = 100;
typedef std::tr1::array<int, MAX> Datos;
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void anyadir(int e) ;
void quitar(unsigned i) ;
unsigned buscar(int e) const ;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
unsigned sz; // numero de elementos del conjunto
Datos dat; // componentes del conjunto
//----------------------------------------------------------
};
}
#endif

Solución: conjuntos.cpp
#include "conjuntos.hpp"
#include <iostream>
#include <tr1/array>
#include <cassert>
using namespace std;
namespace umalcc {
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
Conjunto::Conjunto() : sz(0), dat() {}
//--------------------------------------------------------------------------
void Conjunto::clear()
{
sz = 0;
}
//--------------------------------------------------------------------------
bool Conjunto::es_vacio() const
{
return (sz == 0);
}
//--------------------------------------------------------------------------
void Conjunto::incluir(int e)
{
if (! pertenece(e)) {
anyadir(e);
}
}
//--------------------------------------------------------------------------
void Conjunto::eliminar(int e)
{
quitar(buscar(e));
}
//--------------------------------------------------------------------------
bool Conjunto::pertenece(int e) const
{
return (buscar(e) < sz);
}
//--------------------------------------------------------------------------
bool Conjunto::es_subconjunto(const Conjunto& a) const
{
// es subconjunto si todos los elementos del conjunto actual
// pertenecen tambien al conjunto especificado como parametro
unsigned i = 0;
while ((i < sz) && a.pertenece(dat[i])) {
++i;
}
return (i == sz);
}
//--------------------------------------------------------------------------
void Conjunto::union_conj(const Conjunto& a, const Conjunto& b)
{
assert((this != &a)&&(this != &b));
clear();
for (unsigned i = 0; i < a.sz; ++i) {

37
anyadir(a.dat[i]);
}
for (unsigned i = 0; i < b.sz; ++i) {
incluir(b.dat[i]);
}
}
//--------------------------------------------------------------------------
void Conjunto::interseccion(const Conjunto& a, const Conjunto& b)
{
assert((this != &a)&&(this != &b));
clear();
for (unsigned i = 0; i < a.sz; ++i) {
if (b.pertenece(a.dat[i])) {
anyadir(a.dat[i]);
}
}
}
//--------------------------------------------------------------------------
void Conjunto::diferencia(const Conjunto& a, const Conjunto& b)
{
assert((this != &a)&&(this != &b));
clear();
for (unsigned i = 0; i < a.sz; ++i) {
if (! b.pertenece(a.dat[i])) {
anyadir(a.dat[i]);
}
}
}
//--------------------------------------------------------------------------
void Conjunto::diferencia_simetrica(const Conjunto& a, const Conjunto& b)
{
Conjunto a_b, b_a;
a_b.diferencia(a, b);
b_a.diferencia(b, a);
union_conj(a_b, b_a);
}
//--------------------------------------------------------------------------
void Conjunto::escribir() const
{
cout << "{ ";
for (unsigned i = 0; i < sz; ++i) {
cout << dat[i] << " ";
}
cout << "}";
}
//--------------------------------------------------------------------------
void Conjunto::leer()
{
clear();
int e;
cin >> e ;
while (e != 0) {
incluir(e);
cin >> e ;
}
}
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void Conjunto::anyadir(int e) {
if (sz < dat.size()) {
dat[sz] = e;
++sz;
}
}
//----------------------------
void Conjunto::quitar(unsigned i) {
// Elimina un elemento asignando el ultimo elemento a la
// posicion especificada
if (i < sz) {
if (i < sz-1) {
dat[i] = dat[sz-1];
}
--sz;

38
}
}
//----------------------------
unsigned Conjunto::buscar(int e) const {
unsigned i = 0;
while ((i < sz)&&(e != dat[i])) {
++i;
}
return i;
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include "conjuntos.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Conjunto& c)


{
cout << msj;
c.escribir();
cout << endl;
}
int main()
{
Conjunto c1, c2, c3;
cout << "Introduzca elementos del conjunto: [0 -> fin]: ";
c1.leer();
escribir("C1: ", c1);
cout << "Introduzca elementos del conjunto: [0 -> fin]: ";
c2.leer();
escribir("C2: ", c2);
cout << "Es subconjunto: " << boolalpha << c1.es_subconjunto(c2) << endl;
c3.union_conj(c1, c2);
escribir("Union: ", c3);
c3.interseccion(c1, c2);
escribir("Interseccion: ", c3);
c3.diferencia(c1, c2);
escribir("Diferencia: ", c3);
c3.diferencia_simetrica(c1, c2);
escribir("Diferencia Simetrica: ", c3);
}

5. TAD matriz matemática de números reales (de dimensiones menores de N × N ) que defina los siguientes
métodos públicos:

Solución: matriz.hpp
#ifndef _matriz_hpp_
#define _matriz_hpp_
#include <tr1/array>
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6;
//----------------------------------
class Matriz {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
// Definidos automaticamente por el compilador
//-------------------------
//~Matriz();
//Matriz(const Matriz& m);
//Matriz& operator=(const Matriz& m);
//----------------------------
Matriz() ;
// Constructor por Defecto: matriz vacia
//----------------------------
Matriz(unsigned nf, unsigned nc);

39
// Constructor especifico: crea matriz de (nfils) x (ncols) con valores 0
//----------------------------
unsigned nfils() const ;
// Devuelve el numero de filas de la matriz actual
//----------------------------
unsigned ncols() const ;
// Devuelve el numero de columnas de la matriz actual
//----------------------------
void clear(unsigned nf, unsigned nc);
// Elimina todos los elementos de la matriz actual, y asigna
// a la matriz actual una matriz de (nfils) x (ncols) con valores 0
//----------------------------
void poner(unsigned f, unsigned c, double val) ;
// PRECOND: (0 <= f && f < nfils() && 0 <= c && c < ncols())
// Asigna el valor (val) al elemento de la fila (f)
// y columna (c) de la matriz actual
//----------------------------
double obtener(unsigned f, unsigned c) const ;
// PRECOND: (f < nfils() && c < ncols())
// Devuelve el valor del elemento de la fila (f)
// y columna (c) de la matriz actual
//----------------------------
void inv(const Matriz& a);
// Asigna a la matriz actual el resultado de
// invertir la matriz (a)
//----------------------------
void sumar(const Matriz& m1, const Matriz& m2);
// Asigna a la matriz actual el resultado de
// sumar las matrices (a) y (b)
//------------------------------
void restar(const Matriz& m1, const Matriz& m2);
// Asigna a la matriz actual el resultado de
// restar las matrices (a) y (b)
//------------------------------
void multiplicar(const Matriz& m1, const Matriz& m2);
// Asigna a la matriz actual el resultado de
// multiplicar las matrices (a) y (b)
//------------------------------
void escribir() const;
// Muestra en pantalla el contenido de la matriz actual
//------------------------------
void leer();
// Lee de teclado el valor de la matriz actual,
// Lee nfils, ncols y los valores de los elementos
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
static const unsigned MAX = 10;
typedef std::tr1::array<double, MAX> Fila;
typedef std::tr1::array<Fila, MAX> Datos; // matriz maxima de 10x10
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
unsigned nfilas;
unsigned ncolumnas;
Datos dat;
//----------------------------------------------------------
};
}
#endif

Solución: matriz.cpp
#include "matriz.hpp"
#include <iostream>
#include <iomanip>
#include <cassert>
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------

40
namespace {
//----------------------------------
//-- Subprogramas Auxiliares -------
//----------------------------------
// Valor absoluto de un numero
inline double abs(double a) {
return (a >= 0) ? a : -a;
}
//-------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b) {
return abs(a-b) <= ERROR_PRECISION;
}
//-----------------------------
//-- Operaciones basicas con matrices
//-----------------------------
// Permuta las filas f1 y f2 de la matriz m
void perm_fila(Matriz& m, unsigned f1, unsigned f2)
{
if (f1 != f2) {
for (unsigned c = 0; c < m.ncols(); ++c) {
double aux = m.obtener(f1,c);
m.poner(f1,c, m.obtener(f2,c));
m.poner(f2,c, aux);
}
}
}
//--------------------------------
// multiplica una fila de la matriz m por un valor
void mult_fila(Matriz& m, double valor, unsigned fila)
{
for (unsigned c = 0; c < m.ncols(); ++c) {
m.poner(fila, c, valor * m.obtener(fila,c));
}
}
//--------------------------------
// multiplica la fila forig de la matriz m por un valor y la suma a la fila fdest
void mult_y_sumar(Matriz& m, double valor, unsigned forig, unsigned fdest)
{
for (unsigned c = 0; c < m.ncols(); ++c) {
m.poner(fdest, c, m.obtener(fdest,c) + (valor * m.obtener(forig,c)));
}
}
//--------------------------------
// Transformaciones en la matriz
//--------------------------------
// busca la fila con el mayor elemento a partir de la fila f1
void buscar_pivote_mayor(const Matriz& m, unsigned f1,
unsigned& fmay, bool& ok)
{
const unsigned col = f1;
fmay = f1;
for (unsigned f = fmay + 1; f < m.nfils(); ++f) {
if (abs(m.obtener(f,col)) > abs(m.obtener(fmay,col))) {
fmay = f;
}
}
ok = !iguales(m.obtener(fmay,col), 0.0);
}
//-----------------------------
// pone en la fila f1 la fila con mayor elemento (actua en matriz
// original e inversa)
void fila_pivote_valido(Matriz& mo, Matriz& mi, unsigned f1, bool& ok)
{
unsigned f;
buscar_pivote_mayor(mo, f1, f, ok);
if (ok) {
perm_fila(mo, f1, f);
perm_fila(mi, f1, f);
}
}
//--------------------------------
// transforma en ceros los elementos de la columna indicada por

41
// fila y en uno el pivote (actua en matriz original e inversa)
void hacer_cero_columna(Matriz& mo, Matriz& mi, unsigned fila)
{
const unsigned col = fila;

for (unsigned f = 0; f < fila; ++f) {


const double aux = mo.obtener(f,col)/mo.obtener(fila,col);
mult_y_sumar(mo, -aux, fila, f);
mult_y_sumar(mi, -aux, fila, f);
}
for (unsigned f = fila+1; f < mo.nfils(); ++f) {
const double aux = mo.obtener(f,col)/mo.obtener(fila,col);
mult_y_sumar(mo, -aux, fila, f);
mult_y_sumar(mi, -aux, fila, f);
}
// hacer 1 el pivote
const double aux = 1.0/mo.obtener(fila,col);
mult_fila(mo, aux, fila);
mult_fila(mi, aux, fila);
}
//--------------------------------
// pone en fila la fila con mayor elemento y transforma en ceros
// los elementos de la columna indicada por fila y en uno el
// pivote (actua en matriz original e inversa)
void transformar_columna(Matriz& mo, Matriz& mi, unsigned fila, bool& ok)
{
fila_pivote_valido(mo, mi, fila, ok);
if (ok) {
hacer_cero_columna(mo, mi, fila);
}
}
//--------------------------------
// Inversa de la Matriz por Gauss-Jordan
//--------------------------------
// transforma cada columna de la matriz original en ceros, salvo
// el pivote que se pone a uno. Aplica la misma trasnformacion a
// la matriz identidad.
void inv_gauss_jordan(Matriz& mo, Matriz& mi, bool& ok)
{
if ((mo.nfils() != mo.ncols())
||(mi.nfils() != mi.ncols())
||(mo.nfils() != mi.nfils())) {
ok = false;
} else {
unsigned f = 0;
ok = true;
while ((f < mo.nfils()) && ok) {
transformar_columna(mo, mi, f, ok);
++f;
}
}
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
Matriz::Matriz() : nfilas(0), ncolumnas(0), dat() {}
//--------------------------------
Matriz::Matriz(unsigned nf, unsigned nc)
: nfilas(0), ncolumnas(0)
{
if ((nf < dat.size())&&(nc < dat[0].size())) {
nfilas = nf;
ncolumnas = nc;
for (unsigned f = 0; f < nf; ++f) {
for (unsigned c = 0; c < nc; ++c) {
dat[f][c] = 0.0;
}
}
}
}

42
//----------------------------------
unsigned Matriz::nfils() const
{
return nfilas;
}
//----------------------------
unsigned Matriz::ncols() const
{
return ncolumnas;
}
//--------------------------------
void Matriz::clear(unsigned nf, unsigned nc)
{
if ((nf < dat.size())&&(nc < dat[0].size())) {
nfilas = nf;
ncolumnas = nc;
for (unsigned f = 0; f < nf; ++f) {
for (unsigned c = 0; c < nc; ++c) {
dat[f][c] = 0.0;
}
}
} else {
nfilas = 0;
ncolumnas = 0;
}
}
//----------------------------------
void Matriz::poner(unsigned f, unsigned c, double val)
{
assert(f < nfils() && c < ncols());
dat[f][c] = val;
}
//----------------------------------
double Matriz::obtener(unsigned f, unsigned c) const
{
assert(f < nfils() && c < ncols());
return dat[f][c];
}
//--------------------------------
void Matriz::inv(const Matriz& a)
{
// Copia de la matriz a para transformar
Matriz base = a;
// crear la matriz identidad
clear(a.nfils(), a.ncols());
for (unsigned f = 0; f < nfils(); ++f) {
poner(f, f, 1.0);
}
bool ok;
inv_gauss_jordan(base, *this, ok);
if ( ! ok) {
clear(0, 0);
}
}
//--------------------------------
void Matriz::sumar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));
if ((m1.nfils() != m2.nfils())||(m1.ncols() != m2.ncols())) {
clear(0, 0);
} else {
nfilas = m1.nfils();
ncolumnas = m1.ncols();
for (unsigned f = 0; f < nfils(); ++f) {
for (unsigned c = 0; c < ncols(); ++c) {
poner(f,c, m1.obtener(f,c) + m2.obtener(f,c));
}
}
}
}
//--------------------------------
void Matriz::restar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));

43
if ((m1.nfils() != m2.nfils())||(m1.ncols() != m2.ncols())) {
clear(0, 0);
} else {
nfilas = m1.nfils();
ncolumnas = m1.ncols();
for (unsigned f = 0; f < nfils(); ++f) {
for (unsigned c = 0; c < ncols(); ++c) {
poner(f,c, m1.obtener(f,c) - m2.obtener(f,c));
}
}
}
}
//--------------------------------
void Matriz::multiplicar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));
if ((m1.ncols() != m2.nfils())) {
clear(0, 0);
} else {
nfilas = m1.nfils();
ncolumnas = m2.ncols();
for (unsigned f = 0; f < nfils(); ++f) {
for (unsigned c = 0; c < ncols(); ++c) {
double suma = 0.0;
for (unsigned k = 0; k < m1.ncols(); ++k) {
suma += m1.obtener(f,k) * m2.obtener(k,c);
}
poner(f,c, suma);
}
}
}
}
//--------------------------------
void Matriz::escribir() const
{
cout << std::setprecision(4);
for (unsigned f = 0; f < nfils(); ++f) {
for (unsigned c = 0; c < ncols(); ++c) {
cout << std::setw(6) << obtener(f,c) << " ";
}
cout << std::endl;
}
}
//--------------------------------
void Matriz::leer()
{
unsigned nf, nc;
cin >> nf >> nc; // lee numero de filas y columnas
if ((nf > dat.size())||(nc > dat[0].size())) {
clear(0, 0);
} else {
nfilas = nf;
ncolumnas = nc;
for (unsigned f = 0; f < nfils(); ++f) {
for (unsigned c = 0; c < ncols(); ++c) {
double x;
cin >> x; // lee cada elemento de la matriz
poner(f,c, x);
}
}
}
}
//--------------------------------
}

Solución: main.cpp
#include <iostream>
#include "matriz.hpp"
using namespace std;
using namespace umalcc;

int main()
{
Matriz m1;

44
cout << "Introduzca dimensiones y valores de la matriz: " ;
m1.leer();

Matriz m2;
cout << "Introduzca dimensiones y valores de la matriz: " ;
m2.leer();

Matriz m3;
m3.sumar(m1, m2);
cout << "Suma: " << endl;
m3.escribir();

Matriz m4;
m4.restar(m3, m2);
cout << "Resta: " << endl;
m4.escribir();

Matriz m5;
m5.multiplicar(m1, m2);
cout << "Multiplicacion: " << endl;
m5.escribir();

Matriz m6;
m6.inv(m2);
cout << "Inversa: " << endl;
m6.escribir();

Matriz m7;
m7.multiplicar(m5, m6);
cout << "MultInv: " << endl;
m7.escribir();
}

6. Un TAD Lista de números enteros es una secuencia de elementos homogéneos (números enteros), donde
cada elemento ocupa una determinada posición dentro de la secuencia, de tal forma que se puede acceder
a cada elemento por la posición donde se encuentra. Ası́ mismo, también es posible insertar y eliminar
elementos de la secuencia en la posición indicada, manteniendo el mismo orden posicional de los elementos
en la secuencia. Diseñe e implemente el TAD que proporcione los siguientes métodos públicos:

Solución: listaint.hpp
#ifndef _listaint_hpp_
#define _listaint_hpp_
#include <tr1/array>
namespace umalcc {
class ListaInt {
public:
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
// ~ListaInt() ; // Destructor Automático
//------------------------------
ListaInt() ;
ListaInt(const ListaInt& o) ;
ListaInt& operator = (const ListaInt& o) ;
//------------------------------
bool llena() const ;
// Devuelve true si el numero de elementos almacenados
// alcanza la capacidad maxima de almacenamiento
int size() const ;
// Devuelve el numero de elementos almacenados
void clear() ;
// Elimina todos los elementos de la lista actual (queda vacia)
//------------------------------
void insertar(int pos, int dato) ;
// PRECOND: ( ! llena() && pos >= 0 && pos <= size())
// Inserta (dato) en la lista actual en la posicion (pos)
void eliminar(int pos) ;
// PRECOND: (pos >= 0 && pos < size())
// Elimina de la lista actual el elemento que ocupa la posicion (pos)
//------------------------------
int acceder(int pos) const ;

45
// PRECOND: (pos >= 0 && pos < size())
// Devuelve el elemento de la lista actual que ocupa la posicion (pos)
void modificar(int pos, int dato);
// PRECOND: (pos >= 0 && pos < size())
// Asigna (dato) al elemento de la lista actual que ocupa la posicion (pos)
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
static const int MAX = 100;
typedef std::tr1::array<int, MAX> Datos;
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void abrir_hueco(int pos) ;
void cerrar_hueco(int pos) ;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int sz; // numero de elementos de la lista
Datos v; // contiene los elementos de la lista
//----------------------------------------------------------
};
}
#endif

Solución: listaint.cpp
#include "listaint.hpp"
#include <cassert>
namespace umalcc {
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
// ListaInt::~ListaInt() {} // Destructor Automático
//----------------------------------
ListaInt::ListaInt() : sz(0), v() {} // Constructor por Defecto
//----------------------------------
ListaInt::ListaInt(const ListaInt& o) // Constructor de Copia
: sz(o.sz), v()
{
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
//----------------------------------
ListaInt& ListaInt::operator = (const ListaInt& o) // Op. de Asignación
{
if (this != &o) {
sz = o.sz ;
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
return *this ;
}
//----------------------------------
bool ListaInt::llena() const
{
return sz == int(v.size());
}
//----------------------------------
int ListaInt::size() const
{
return sz ;
}
//----------------------------------
void ListaInt::clear()
{
sz = 0 ;
}
//----------------------------------
void ListaInt::insertar(int pos, int dato)
{

46
assert( ! llena() && pos >= 0 && pos <= size()) ;
abrir_hueco(pos) ;
v[pos] = dato ;
}
//----------------------------------
void ListaInt::eliminar(int pos)
{
assert(pos >= 0 && pos < size()) ;
cerrar_hueco(pos) ;
}
//----------------------------------
int ListaInt::acceder(int pos) const
{
assert(pos >= 0 && pos < size()) ;
return v[pos] ;
}
//----------------------------------
void ListaInt::modificar(int pos, int dato)
{
assert(pos >= 0 && pos < size()) ;
v[pos] = dato;
}
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void ListaInt::abrir_hueco(int pos)
{
assert(sz < int(v.size())) ;
for (int i = sz; i > pos; --i) {
v[i] = v[i-1];
}
++sz; // Ahora hay un elemento más
}
//----------------------------------
void ListaInt::cerrar_hueco(int pos)
{
assert(sz > 0) ;
--sz; // Ahora hay un elemento menos
for (int i = pos; i < sz; ++i) {
v[i] = v[i+1];
}
}
//----------------------------------
}

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "listaint.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);
}
//---------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca un dato: " ;
cin >> dato;
}
//---------------------------------
void leer(ListaInt& lista)
{
int dato ;
lista.clear() ;
cout << "Introduzca datos (0 -> FIN): " << endl ;
cin >> dato ;

47
while ((dato != 0)&&( ! lista.llena())) {
lista.insertar(lista.size(), dato) ;
cin >> dato ;
}
}
//---------------------------------
void escribir(const ListaInt& lista)
{
cout << "Lista: " ;
for (int i = 0 ; i < lista.size() ; ++i) {
cout << lista.acceder(i) << " " ;
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const ListaInt& lista)
{
cout << "Constructor de Copia" << endl ;
ListaInt lst(lista) ;
escribir(lst) ;
cout << "Operador de Asignacion" << endl ;
lst = lista ;
escribir(lst) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Lista" << endl ;
cout << "B. Borrar Lista" << endl ;
cout << "C. Insertar Posicion" << endl ;
cout << "D. Eliminar Posicion" << endl ;
cout << "E. Acceder Posicion" << endl ;
cout << "F. Modificar Posicion" << endl ;
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
ListaInt lista ;
int dato ;
int pos ;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(lista) ;
escribir(lista) ;
break ;
case ’B’:
lista.clear() ;
escribir(lista) ;
break ;
case ’C’:
if (lista.llena()) {
cout << "Error: Lista llena" << endl ;
} else {
leer_pos(pos, lista.size()+1) ;
leer_dato(dato) ;
lista.insertar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’D’:

48
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
lista.eliminar(pos) ;
escribir(lista) ;
}
break ;
case ’E’:
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
cout << "Lista[" << pos << "]: " << lista.acceder(pos) << endl ;
escribir(lista) ;
}
break ;
case ’F’:
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
leer_dato(dato) ;
lista.modificar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’G’:
prueba_asg(lista) ;
break ;
}
} while (op != ’X’) ;
}

Tema 2.3: Tipos Abstractos de Datos Genéricos


Diseñe e implemente los siguientes TADs genéricos, dentro del espacio de nombres umalcc, ası́ como progra-
mas de utilización que permitan comprobar su funcionamiento.
1. Un TAD Vector genérico es una secuencia de elementos homogéneos, donde cada elemento ocupa una
determinada posición dentro de la secuencia, de tal forma que se puede acceder a cada elemento por la
posición donde se encuentra. Ası́ mismo, también es posible insertar al final de la secuencia, ası́ como
también eliminar el último elemento de la secuencia. Diseñe e implemente el TAD que proporcione los
siguientes métodos públicos:

Solución: vector.hpp
#ifndef _vector_hpp_
#define _vector_hpp_
#include <tr1/array>
#include <cassert>
namespace umalcc {
template <typename TipoBase, unsigned SIZE>
class Vector {
public:
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
// ~Vector() {} // Destructor Automático
//------------------------------
Vector() : sz(0), v() {} // Constructor por Defecto
//------------------------------
Vector(const Vector& o) // Constructor de Copia
: sz(o.sz), v()
{
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
//------------------------------
Vector& operator = (const Vector& o) // Operador de Asignación

49
{
if (this != &o) {
sz = o.sz ;
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
return *this ;
}
//------------------------------
// Devuelve true si el numero de elementos almacenados
// alcanza la capacidad maxima de almacenamiento
bool lleno() const
{
return sz == int(v.size());
}
//------------------------------
// Devuelve el numero de elementos almacenados
int size() const
{
return sz ;
}
//------------------------------
// Elimina todos los elementos del vector actual (queda vacio)
void clear()
{
sz = 0 ;
}
//------------------------------
// PRECOND: ( ! lleno() )
// Inserta (dato) al final de los elementos del vector actual
void insertar(const TipoBase& dato)
{
assert( ! lleno() ) ;
v[sz] = dato ;
++sz ;
}
//------------------------------
// PRECOND: ( size() > 0 )
// Elimina del vector actual el elemento que ocupa la ultima posicion
void eliminar()
{
assert( size() > 0 ) ;
--sz;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Devuelve el elemento del vector actual que ocupa la posicion (pos)
TipoBase acceder(int pos) const
{
assert(pos >= 0 && pos < size()) ;
return v[pos] ;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Asigna (dato) al elemento del vector actual que ocupa la posicion (pos)
void modificar(int pos, const TipoBase& dato)
{
assert(pos >= 0 && pos < size()) ;
v[pos] = dato;
}
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
typedef std::tr1::array<TipoBase, SIZE> Datos;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int sz; // numero de elementos del vector
Datos v; // contiene los elementos del vector
//----------------------------------------------------------
};

50
}
#endif

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "vector.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
// Instanciación de el vector genérico para almacenar hasta 100 números enteros
typedef Vector<int, 100> VectorInt;
//------------------------------------------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);
}
//---------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca un dato: " ;
cin >> dato;
}
//---------------------------------
void leer(VectorInt& vector)
{
int dato ;
vector.clear() ;
cout << "Introduzca datos (0 -> FIN): " << endl ;
cin >> dato ;
while ((dato != 0)&&( ! vector.lleno())) {
vector.insertar(dato) ;
cin >> dato ;
}
}
//---------------------------------
void escribir(const VectorInt& vector)
{
cout << "vector: " ;
for (int i = 0 ; i < vector.size() ; ++i) {
cout << vector.acceder(i) << " " ;
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const VectorInt& vector)
{
cout << "Constructor de Copia" << endl ;
VectorInt v(vector) ;
escribir(v) ;
cout << "Operador de Asignacion" << endl ;
v = vector ;
escribir(v) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Vector" << endl ;
cout << "B. Borrar Vector" << endl ;
cout << "C. Insertar" << endl ;
cout << "D. Eliminar" << endl ;
cout << "E. Acceder Posicion" << endl ;
cout << "F. Modificar Posicion" << endl ;
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;

51
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
VectorInt vector ;
int dato ;
int pos ;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(vector) ;
escribir(vector) ;
break ;
case ’B’:
vector.clear() ;
escribir(vector) ;
break ;
case ’C’:
if (vector.lleno()) {
cout << "Error: vector lleno" << endl ;
} else {
leer_dato(dato) ;
vector.insertar(dato) ;
escribir(vector) ;
}
break ;
case ’D’:
if (vector.size() == 0) {
cout << "Error: vector vacio" << endl ;
} else {
vector.eliminar() ;
escribir(vector) ;
}
break ;
case ’E’:
if (vector.size() == 0) {
cout << "Error: vector vacio" << endl ;
} else {
leer_pos(pos, vector.size()) ;
cout << "vector[" << pos << "]: " << vector.acceder(pos)<<endl;
escribir(vector) ;
}
break ;
case ’F’:
if (vector.size() == 0) {
cout << "Error: vector vacio" << endl ;
} else {
leer_pos(pos, vector.size()) ;
leer_dato(dato) ;
vector.modificar(pos, dato) ;
escribir(vector) ;
}
break ;
case ’G’:
prueba_asg(vector) ;
break ;
}
} while (op != ’X’) ;
}

2. Un TAD Lista genérica es una secuencia de elementos homogéneos, donde cada elemento ocupa una
determinada posición dentro de la secuencia, de tal forma que se puede acceder a cada elemento por la
posición donde se encuentra. Ası́ mismo, también es posible insertar y eliminar elementos de la secuencia
en la posición indicada, manteniendo el mismo orden posicional de los elementos en la secuencia. Diseñe
e implemente el TAD que proporcione los siguientes métodos públicos:

52
Solución: lista.hpp
#ifndef _lista_hpp_
#define _lista_hpp_
#include <tr1/array>
#include <cassert>
namespace umalcc {
template <typename TipoBase, unsigned SIZE>
class Lista {
public:
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
// ~Lista() {} // Destructor Automático
//------------------------------
Lista() : sz(0), v() {} // Constructor por Defecto
//------------------------------
Lista(const Lista& o) // Constructor de Copia
: sz(o.sz), v()
{
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
//------------------------------
Lista& operator = (const Lista& o) // Operador de Asignación
{
if (this != &o) {
sz = o.sz ;
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
return *this ;
}
//------------------------------
// Devuelve true si el numero de elementos almacenados
// alcanza la capacidad maxima de almacenamiento
bool llena() const
{
return sz == int(v.size());
}
//------------------------------
// Devuelve el numero de elementos almacenados
int size() const
{
return sz ;
}
//------------------------------
// Elimina todos los elementos de la lista actual (queda vacia)
void clear()
{
sz = 0 ;
}
//------------------------------
// PRECOND: ( ! llena() && 0 <= pos && pos <= size())
// Inserta (dato) en la lista actual en la posicion (pos)
void insertar(int pos, const TipoBase& dato)
{
assert( ! llena() && pos >= 0 && pos <= size()) ;
abrir_hueco(pos) ;
v[pos] = dato ;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Elimina de la lista actual el elemento que ocupa la posicion (pos)
void eliminar(int pos)
{
assert(pos >= 0 && pos < size()) ;
cerrar_hueco(pos) ;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Devuelve el elemento de la lista actual que ocupa la posicion (pos)
TipoBase acceder(int pos) const

53
{
assert(pos >= 0 && pos < size()) ;
return v[pos] ;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Asigna (dato) al elemento de la lista actual que ocupa la posicion (pos)
void modificar(int pos, const TipoBase& dato)
{
assert(pos >= 0 && pos < size()) ;
v[pos] = dato;
}
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
typedef std::tr1::array<TipoBase, SIZE> Datos;
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void abrir_hueco(int pos)
{
assert(sz < int(v.size())) ;
for (int i = sz; i > pos; --i) {
v[i] = v[i-1];
}
++sz; // Ahora hay un elemento más
}
//------------------------------
void cerrar_hueco(int pos)
{
assert(sz > 0) ;
--sz; // Ahora hay un elemento menos
for (int i = pos; i < sz; ++i) {
v[i] = v[i+1];
}
}
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int sz; // numero de elementos de la lista
Datos v; // contiene los elementos de la lista
//----------------------------------------------------------
};
}
#endif

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "lista.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
// Instanciación de la Lista genérica para almacenar hasta 100 números enteros
typedef Lista<int, 100> ListaInt;
//------------------------------------------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);
}
//---------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca un dato: " ;
cin >> dato;
}
//---------------------------------

54
void leer(ListaInt& lista)
{
int dato ;
lista.clear() ;
cout << "Introduzca datos (0 -> FIN): " << endl ;
cin >> dato ;
while ((dato != 0)&&( ! lista.llena())) {
lista.insertar(lista.size(), dato) ;
cin >> dato ;
}
}
//---------------------------------
void escribir(const ListaInt& lista)
{
cout << "Lista: " ;
for (int i = 0 ; i < lista.size() ; ++i) {
cout << lista.acceder(i) << " " ;
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const ListaInt& lista)
{
cout << "Constructor de Copia" << endl ;
ListaInt lst(lista) ;
escribir(lst) ;
cout << "Operador de Asignacion" << endl ;
lst = lista ;
escribir(lst) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Lista" << endl ;
cout << "B. Borrar Lista" << endl ;
cout << "C. Insertar Posicion" << endl ;
cout << "D. Eliminar Posicion" << endl ;
cout << "E. Acceder Posicion" << endl ;
cout << "F. Modificar Posicion" << endl ;
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
ListaInt lista ;
int dato ;
int pos ;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(lista) ;
escribir(lista) ;
break ;
case ’B’:
lista.clear() ;
escribir(lista) ;
break ;
case ’C’:
if (lista.llena()) {
cout << "Error: Lista llena" << endl ;
} else {
leer_pos(pos, lista.size()+1) ;

55
leer_dato(dato) ;
lista.insertar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’D’:
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
lista.eliminar(pos) ;
escribir(lista) ;
}
break ;
case ’E’:
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
cout << "Lista[" << pos << "]: " << lista.acceder(pos) << endl ;
escribir(lista) ;
}
break ;
case ’F’:
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
leer_dato(dato) ;
lista.modificar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’G’:
prueba_asg(lista) ;
break ;
}
} while (op != ’X’) ;
}

3. Dado el TAD lista genérica del ejercicio anterior (2), diseñe un programa de utilización que defina una
instancia del TAD lista genérica para un tipo Persona (con campos nombre, edad y teléfono), y defina
subprogramas adecuados para añadir, buscar, eliminar y ordenar los elementos de la lista de personas. La
ordenación será de menor a mayor por nombre de la persona, para el caso de nombres iguales, ordenará de
menor a mayor por su edad. En caso de igualdad de nombre y edad, ordenará de menor a mayor por
teléfono.
Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "../lista/lista.hpp"
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------
struct Persona {
string nombre;
unsigned edad;
string telefono;
};
inline bool iguales(const Persona& p1, const Persona& p2)
{
return ((p1.nombre == p2.nombre)&&(p1.edad == p2.edad)
&&(p1.telefono == p2.telefono));
}
inline bool distintos(const Persona& p1, const Persona& p2)
{
return ! iguales(p1, p2);
}
inline bool menor(const Persona& p1, const Persona& p2)
{
return ((p1.nombre < p2.nombre)
||((p1.nombre == p2.nombre)

56
&&((p1.edad < p2.edad)
||((p1.edad == p2.edad)
&&(p1.telefono < p2.telefono)))));
}
inline bool mayor(const Persona& p1, const Persona& p2)
{
return menor(p2, p1);
}
inline void escribir(const Persona& p)
{
cout << "{ " << p.nombre << " " << p.edad << " " << p.telefono << " }";
}
inline void leer(Persona& p)
{
cin >> p.nombre >> p.edad >> p.telefono;
}
//------------------------------------------------------------------
typedef Lista<Persona, 20> LPers;
//------------------------------------------------------------------
// busca la posicion donde se debe insertar el elemento de forma ordenada
int buscar_pos(const Persona& x, const LPers& lista)
{
int i = 0;
while (mayor(x, lista.acceder(i))) {
++i;
}
return i;
}
//---------------------------------
void ordenar_insercion(LPers& lista)
{
for (int i = 1; i < lista.size(); ++i) { // para cada elemento ’i’
int pos = buscar_pos(lista.acceder(i), lista); // busca su pos ord
if (pos < i) {
Persona aux = lista.acceder(i);// extrae el elem de la posicion ’i’
lista.eliminar(i); // elimina el elemento de la lista
lista.insertar(pos, aux); // lo inserta en la posicion ordenada
}
}
}
//------------------------------------------------------------------
int buscar(const Persona& x, const LPers& lista)
{
int i = 0;
while ((i < lista.size()) && distintos(x, lista.acceder(i))) {
++i;
}
return i;
}
//--------------------------------------
void eliminar_elm(const Persona& x, LPers& lista)
{
int pos = buscar(x, lista);
if (pos < lista.size()) {
lista.eliminar(pos);
cout << endl;
} else {
cout << "No encontrado: " << endl;
}
}
//--------------------------------------
void leer_dato(Persona& dato)
{
cout << "Introduzca el nombre, edad y telefono de una persona: " ;
leer(dato);
}
//---------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);

57
}
//---------------------------------
void leer(LPers& lista)
{
Persona p;
lista.clear() ;
cout << "Introduzca una lista de personas (nombre edad telefono) (fin -> FIN)" << endl;
cin >> p.nombre;
while ((p.nombre != "fin")&&( ! lista.llena())) {
cin >> p.edad >> p.telefono ;
lista.insertar(lista.size(), p);
cin >> p.nombre;
}
}
//---------------------------------
void escribir(const LPers& lista)
{
cout << "Lista: " ;
for (int i = 0 ; i < lista.size() ; ++i) {
escribir(lista.acceder(i)) ;
cout << endl ;
}
cout << endl ;
}
//---------------------------------
void ordenar(LPers& lista)
{
ordenar_insercion(lista);
escribir(lista);
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Leer" << endl;
cout << "B. Insertar Posicion" << endl;
cout << "C. Buscar Elemento" << endl;
cout << "D. Eliminar Elemento" << endl;
cout << "E. Eliminar Posicion" << endl;
cout << "F. Ordenar Insercion" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’F’))));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
LPers lista;
Persona dato;
int pos;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
leer(lista);
escribir(lista);
break;
case ’B’:
if (lista.llena()) {
cout << "Error: Lista llena" << endl ;
} else {
leer_pos(pos, lista.size()+1);
leer_dato(dato);
lista.insertar(pos, dato);
escribir(lista);
}
break;

58
case ’C’:
leer_dato(dato);
pos = buscar(dato, lista);
escribir(lista);
if (pos < lista.size()) {
cout << "Encontrado: ["<<pos<<"]: " ;
escribir(lista.acceder(pos));
cout << endl;
} else {
cout << "No encontrado: " << endl;
}
break;
case ’D’:
leer_dato(dato);
eliminar_elm(dato, lista);
escribir(lista);
break;
case ’E’:
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size());
lista.eliminar(pos);
escribir(lista);
}
break;
case ’F’:
ordenar(lista);
break;
}
} while (op != ’X’);
}

4. Un mapa genérico es un contenedor asociativo que almacena pares de elementos (clave, valor ), de tal
forma que a partir de la clave (de tipo string) se puede acceder al valor asociado almacenado (de
tipo genérico). Diseñe e implemente el TAD mapa genérico (se recomienda mantener los elementos de la
secuencia ordenados por clave, y los realizar accesos por clave con búsqueda binaria) que proporcione los
siguientes métodos públicos:

Solución: mapa.hpp
#ifndef _mapa_hpp_
#define _mapa_hpp_
#include <iostream>
#include <string>
#include <cassert>
#include <tr1/array>
//-------------------------------------------------------------------------
namespace umalcc {
template <typename TipoBase, unsigned SIZE>
class Mapa {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
// Definidos automaticamente por el compilador
//-------------------------
//~Mapa();
//----------------------------
Mapa() : sz(0), v() {}
//------------------------------
Mapa(const Mapa& o) // Constructor de Copia
: sz(o.sz), v()
{
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
//------------------------------
Mapa& operator = (const Mapa& o) // Operador de Asignación
{
if (this != &o) {

59
sz = o.sz ;
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
return *this ;
}
//------------------------------
// Devuelve true si el numero de elementos almacenados
// alcanza la capacidad maxima de almacenamiento
bool lleno() const
{
return sz == int(v.size());
}
//------------------------------
// Devuelve el numero de elementos almacenados
int size() const
{
return sz ;
}
//----------------------------
// Elimina todos los elementos del mapa actual (queda vacio)
void clear()
{
sz = 0;
}
//----------------------------
// PRECOND: ( ! lleno() )
// Inserta el valor (dato) en el mapa actual asociado a la (clave) de acceso
// En caso de que ya exista la clave, entonces modifica su valor
void insertar(const std::string& clave, const TipoBase& dato)
{
assert( ! lleno() ) ;
int pos = buscar_pos(clave);
if (pos < sz && v[pos].c == clave) {
v[pos].v = dato ;
} else {
abrir_hueco(pos) ;
v[pos].c = clave ;
v[pos].v = dato ;
}
}
//------------------------------
// Elimina del mapa actual el elemento asociado a la (clave) de acceso.
// y ok tomara el valor true. En caso de no existir, ok tomará el valor false
void eliminar(const std::string& clave, bool& ok)
{
int pos = buscar_bin(clave);
if (pos >= 0 && pos < size()) {
cerrar_hueco(pos) ;
ok = true;
} else {
ok = false;
}
}
//------------------------------
// Devuelve el valor del mapa actual asociado a la (clave) de acceso
// y ok tomará el valor true. En caso de no existir, ok tomará
// el valor false
TipoBase acceder(const std::string& clave, bool& ok) const
{
int pos = buscar_bin(clave);
if (pos >= 0 && pos < size()) {
ok = true;
} else {
ok = false;
pos = 0;
}
return v[pos].v ;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Devuelve la clave correspondiente al elemento del mapa
// actual que ocupa la posicion (pos) en la secuencia de elementos

60
std::string acceder_clave(int pos) const
{
assert(pos >= 0 && pos < size()) ;
return v[pos].c ;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Devuelve el valor correspondiente al elemento del mapa
// actual que ocupa la posicion (pos) en la secuencia de elementos
TipoBase acceder_valor(int pos) const
{
assert(pos >= 0 && pos < size()) ;
return v[pos].v ;
}
//------------------------------
// PRECOND: (0 <= pos && pos < size())
// Asigna el valor (dato) al elemento del mapa actual
// que ocupa la posicion (pos) en la secuencia de elementos
void modificar_valor(int pos, const TipoBase& dato)
{
assert(pos >= 0 && pos < size()) ;
v[pos].v = dato;
}
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
// Elem contiene cada elemento (clave, valor) del mapa
struct Elem {
std::string c;
TipoBase v;
};
typedef std::tr1::array<Elem, SIZE> Datos;
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
// busqueda binaria de un elemento
int buscar_bin(const std::string& clave) const
{
int i = 0;
int f = sz;
int m = (i + f) / 2;
while ((i < f) && (clave != v[m].c)) {
if (clave < v[m].c) {
f = m;
} else {
i = m + 1;
}
m = (i + f) / 2;
}
if (i >= f) {
m = sz;
}
return m;
}
//------------------------------
// busqueda binaria de la posición donde insertar un elemento
// int buscar_pos_bin(const std::string& clave) const
// {
// int i = 0;
// int f = sz;
// while (i < f) {
// int m = (i + f) / 2;
// if (clave <= v[m].c) {
// f = m;
// } else {
// i = m + 1;
// }
// }
// return i;
// }
//------------------------------
// busqueda secuencial de la posición donde insertar un elemento

61
int buscar_pos(const std::string& clave) const
{
int i = 0;
while ((i < sz)&&(clave > v[i].c)) {
++i;
}
return i;
}
//------------------------------
void abrir_hueco(int pos)
{
assert((sz < int(v.size()))&&(pos <= sz)) ;
for (int i = sz; i > pos; --i) {
v[i] = v[i-1];
}
++sz; // Ahora hay un elemento más
}
//------------------------------
void cerrar_hueco(int pos)
{
assert((sz > 0)&&(pos < sz)) ;
--sz; // Ahora hay un elemento menos
for (int i = pos; i < sz; ++i) {
v[i] = v[i+1];
}
}
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int sz; // numero de elementos en el mapa
Datos v; // contiene los elementos (clave, valor) del mapa
//----------------------------------------------------------
};
}
#endif

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "mapa.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
// Instanciación del mapa genérico para almacenar hasta 100 elementos
typedef Mapa<int, 100> MapaInt;
//------------------------------------------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);
}
//---------------------------------
void leer_clave(string& clave)
{
cout << "Introduzca la clave: " ;
cin >> clave;

}
//--------------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca el valor: " ;
cin >> dato;
}
//---------------------------------
void leer(MapaInt& mapa)
{
string clave;
int dato ;
mapa.clear() ;

62
cout << "Introduzca datos (fin -> FIN): " << endl ;
leer_clave(clave) ;
while ((clave != "fin")&&( ! mapa.lleno())) {
leer_dato(dato) ;
mapa.insertar(clave, dato) ;
leer_clave(clave) ;
}
}
//---------------------------------
void escribir(const MapaInt& mapa)
{
cout << "mapa: " ;
for (int i = 0 ; i < mapa.size() ; ++i) {
cout << "{ "
<< mapa.acceder_clave(i) << ", "
<< mapa.acceder_valor(i) << " } " ;
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const MapaInt& mapa)
{
cout << "Constructor de Copia" << endl ;
MapaInt m(mapa) ;
escribir(m) ;
cout << "Operador de Asignacion" << endl ;
m = mapa ;
escribir(m) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Mapa" << endl ;
cout << "B. Borrar Mapa" << endl ;
cout << "C. Insertar por Clave" << endl ;
cout << "D. Eliminar por Clave" << endl ;
cout << "E. Acceder por Clave" << endl ;
cout << "F. Modificar por Posicion" << endl ;
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
MapaInt mapa ;
int dato ;
int pos ;
string clave;
bool ok;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(mapa) ;
escribir(mapa) ;
break ;
case ’B’:
mapa.clear() ;
escribir(mapa) ;
break ;
case ’C’:
if (mapa.lleno()) {
cout << "Error: mapa lleno" << endl ;
} else {

63
leer_clave(clave) ;
leer_dato(dato) ;
mapa.insertar(clave, dato) ;
escribir(mapa) ;
}
break ;
case ’D’:
if (mapa.size() == 0) {
cout << "Error: mapa vacio" << endl ;
} else {
leer_clave(clave) ;
mapa.eliminar(clave, ok) ;
if (! ok) {
cout << "mapa[" << clave << "]: No encontrado" << endl ;
}
escribir(mapa) ;
}
break ;
case ’E’:
if (mapa.size() == 0) {
cout << "Error: mapa vacio" << endl ;
} else {
leer_clave(clave) ;
dato = mapa.acceder(clave, ok) ;
if (ok) {
cout << "mapa[" << clave << "]: " << dato << endl ;
} else {
cout << "mapa[" << clave << "]: No encontrado" << endl ;
}
escribir(mapa) ;
}
break ;
case ’F’:
if (mapa.size() == 0) {
cout << "Error: mapa vacio" << endl ;
} else {
leer_pos(pos, mapa.size()) ;
leer_dato(dato) ;
mapa.modificar_valor(pos, dato) ;
escribir(mapa) ;
}
break ;
case ’G’:
prueba_asg(mapa) ;
break ;
}
} while (op != ’X’) ;
}

5. Dado el TAD mapa genérico del ejercicio anterior (4), diseñe un programa de utilización que defina una
instancia del TAD mapa genérico para un tipo Persona (con campos nombre, edad y teléfono), y defina
subprogramas adecuados para añadir, buscar, y eliminar los elementos del mapa de personas. Compárese
la solución con la correspondiente versión que utiliza una instanciación del tipo lista genérica.
Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "../mapa/mapa.hpp"
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------
struct DatosPer {
unsigned edad;
string telefono;
};
inline void escribir(const DatosPer& p)
{
cout << "{ " << p.edad << " " << p.telefono << " } ";
}
inline void escribir(const string& nombre, const DatosPer& p)
{
cout << "{ " << nombre << " " << p.edad << " " << p.telefono << " } ";

64
}
inline void leer(string& nombre, DatosPer& p)
{
cin >> nombre >> p.edad >> p.telefono;
}
//------------------------------------------------------------------
typedef Mapa<DatosPer, 20> MPers;
//------------------------------------------------------------------
void leer_dato(DatosPer& dato)
{
cout << "Introduzca edad y telefono de una persona: " ;
cin >> dato.edad >> dato.telefono;
}
//---------------------------------
void leer_clave(string& clave)
{
cout << "Introduzca el nombre: " ;
cin >> clave;

}
//---------------------------------
void leer(MPers& mapa)
{
string nombre;
DatosPer dato;
mapa.clear() ;
cout << "Introduzca personas (nombre edad telefono) (fin -> FIN)" << endl;
leer_clave(nombre) ;
while ((nombre != "fin")&&( ! mapa.lleno())) {
leer_dato(dato) ;
mapa.insertar(nombre, dato);
leer_clave(nombre) ;
}
}
//---------------------------------
void escribir(const MPers& mapa)
{
cout << "Mapa: " ;
for (int i = 0 ; i < mapa.size() ; ++i) {
escribir(mapa.acceder_clave(i), mapa.acceder_valor(i)) ;
}
cout << endl ;
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Leer" << endl;
cout << "B. Insertar Elemento" << endl;
cout << "C. Buscar Elemento" << endl;
cout << "D. Eliminar Elemento" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’D’))));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
MPers mapa;
DatosPer dato;
string nombre;
bool ok;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
leer(mapa);

65
escribir(mapa);
break;
case ’B’:
if (mapa.lleno()) {
cout << "Error: Mapa lleno" << endl ;
} else {
leer_clave(nombre) ;
leer_dato(dato) ;
mapa.insertar(nombre, dato) ;
escribir(mapa) ;
}
break;
case ’C’:
if (mapa.size() == 0) {
cout << "Error: mapa vacio" << endl ;
} else {
leer_clave(nombre) ;
dato = mapa.acceder(nombre, ok) ;
if (ok) {
cout << "mapa[" << nombre << "]: " ;
escribir(dato) ;
cout << endl ;
} else {
cout << "mapa[" << nombre << "]: No encontrado" << endl ;
}
escribir(mapa) ;
}
break;
case ’D’:
if (mapa.size() == 0) {
cout << "Error: mapa vacio" << endl ;
} else {
leer_clave(nombre) ;
mapa.eliminar(nombre, ok) ;
if (! ok) {
cout << "mapa[" << nombre << "]: No encontrado" << endl ;
}
escribir(mapa) ;
}
break;
}
} while (op != ’X’);
}

6. Un TAD Pila genérica es una secuencia ordenada (según el orden de inserción) de elementos homogéneos
donde se pueden introducir elementos (manteniendo el orden de inserción) y sacar elementos de ella (en
orden inverso al orden de inserción), de tal forma que el primer elemento que sale de la pila es el último
elemento que ha sido introducido en ella. Diseñe e implemente el TAD que proporcione los siguientes
métodos públicos, ası́ como un programa que permita comprobar su funcionamiento:

Solución: pila.hpp
#ifndef _pila_hpp_
#define _pila_hpp_
#include <tr1/array>
#include <cassert>
namespace umalcc {
template <typename TipoBase, unsigned SIZE>
class Pila {
public:
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
// ~Pila() {} // Destructor Automático
//------------------------------
Pila() : sz(0), v() {} // Constructor por Defecto
//------------------------------
Pila(const Pila& o) // Constructor de Copia
: sz(o.sz), v()
{
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;

66
}
}
//------------------------------
Pila& operator = (const Pila& o) // Operador de Asignación
{
if (this != &o) {
sz = o.sz ;
for (int i = 0; i < sz; ++i) {
v[i] = o.v[i] ;
}
}
return *this ;
}
//------------------------------
// Devuelve true si el numero de elementos almacenados
// alcanza la capacidad maxima de almacenamiento
bool llena() const
{
return sz == int(v.size());
}
//------------------------------
// Devuelve true si la pila esta vacia
bool vacia() const
{
return sz == 0;
}
//------------------------------
// Devuelve el numero de elementos almacenados
int size() const
{
return sz ;
}
//------------------------------
// Elimina todos los elementos de la pila actual (queda vacio)
void clear()
{
sz = 0 ;
}
//------------------------------
// PRECOND: ( ! llena() )
// Inserta (dato) al final de los elementos de la pila actual
void insertar(const TipoBase& dato)
{
assert( ! llena() ) ;
v[sz] = dato ;
++sz ;
}
//------------------------------
// PRECOND: ( ! vacia() )
// Elimina de la pila actual el elemento que ocupa la ultima posicion
void eliminar()
{
assert( ! vacia() ) ;
--sz;
}
//------------------------------
// PRECOND: ( ! vacia() )
// Devuelve el elemento de la pila actual que ocupa la ultima posicion
TipoBase acceder() const
{
assert( ! vacia() ) ;
return v[sz-1] ;
}
//------------------------------
// PRECOND: ( ! vacia() )
// Asigna (dato) al elemento de la pila actual que ocupa la ultima posicion
void modificar(const TipoBase& dato)
{
assert( ! vacia() ) ;
v[sz-1] = dato;
}
//----------------------------------------------------------
private:
//----------------------------------------------------------

67
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
typedef std::tr1::array<TipoBase, SIZE> Datos;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int sz; // numero de elementos de la pila
Datos v; // contiene los elementos de la pila
//----------------------------------------------------------
};
}
#endif

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "pila.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
// Instanciación de la pila genérica para almacenar hasta 100 números enteros
typedef Pila<int, 100> PilaInt;
//------------------------------------------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca un dato: " ;
cin >> dato;
}
//---------------------------------
void leer(PilaInt& pila)
{
int dato ;
pila.clear() ;
cout << "Introduzca datos (0 -> FIN): " << endl ;
cin >> dato ;
while ((dato != 0)&&( ! pila.llena())) {
pila.insertar(dato) ;
cin >> dato ;
}
}
//---------------------------------
void escribir(const PilaInt& pila)
{
PilaInt aux = pila;
cout << "pila (orden inverso): " ;
while ( ! aux.vacia() ) {
cout << aux.acceder() << " " ;
aux.eliminar();
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const PilaInt& pila)
{
cout << "Constructor de Copia" << endl ;
PilaInt p(pila) ;
escribir(p) ;
cout << "Operador de Asignacion" << endl ;
p = pila ;
escribir(p) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Pila" << endl ;
cout << "B. Borrar Pila" << endl ;
cout << "C. Insertar" << endl ;
cout << "D. Eliminar" << endl ;
cout << "E. Acceder Cima" << endl ;
cout << "F. Modificar Cima" << endl ;

68
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
PilaInt pila ;
int dato ;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(pila) ;
escribir(pila) ;
break ;
case ’B’:
pila.clear() ;
escribir(pila) ;
break ;
case ’C’:
if (pila.llena()) {
cout << "Error: pila llena" << endl ;
} else {
leer_dato(dato) ;
pila.insertar(dato) ;
escribir(pila) ;
}
break ;
case ’D’:
if (pila.vacia()) {
cout << "Error: pila vacia" << endl ;
} else {
pila.eliminar() ;
escribir(pila) ;
}
break ;
case ’E’:
if (pila.vacia()) {
cout << "Error: pila vacia" << endl ;
} else {
cout << "pila[cima]: " << pila.acceder()<<endl;
escribir(pila) ;
}
break ;
case ’F’:
if (pila.vacia()) {
cout << "Error: pila vacia" << endl ;
} else {
leer_dato(dato) ;
pila.modificar(dato) ;
escribir(pila) ;
}
break ;
case ’G’:
prueba_asg(pila) ;
break ;
}
} while (op != ’X’) ;
}

7. Un TAD Cola genérica es una secuencia ordenada (según el orden de inserción) de elementos homogéneos
donde se pueden introducir elementos (manteniendo el orden de inserción) y sacar elementos de ella (en
el mismo orden al orden de inserción), de tal forma que el primer elemento que sale de la cola es el primer
elemento que ha sido introducido en ella. Diseñe e implemente el TAD que proporcione los siguientes
métodos públicos, ası́ como un programa que permita comprobar su funcionamiento:

69
Solución: cola.hpp
#ifndef _cola_hpp_
#define _cola_hpp_
#include <tr1/array>
#include <cassert>
namespace umalcc {
template <typename TipoBase, unsigned SIZE>
class Cola {
public:
//----------------------------------------------------------
//-- Métodos Públicos --------------------------------------
//----------------------------------------------------------
// ~Cola() {} // Destructor Automático
//------------------------------
Cola() : sz(0), pri(0), ult(0), v() {} // Constructor por Defecto
//------------------------------
Cola(const Cola& o) // Constructor de Copia
: sz(o.sz), pri(o.pri), ult(o.ult), v()
{
int idx = pri;
for (int i = 0; i < sz; ++i) {
v[idx] = o.v[idx] ;
idx = (idx + 1) % v.size();

}
}
//------------------------------
Cola& operator = (const Cola& o) // Operador de Asignación
{
if (this != &o) {
sz = o.sz ;
pri = o.pri ;
ult = o.ult ;
int idx = pri;
for (int i = 0; i < sz; ++i) {
v[idx] = o.v[idx] ;
idx = (idx + 1) % v.size();
}
}
return *this ;
}
//------------------------------
// Devuelve true si el numero de elementos almacenados
// alcanza la capacidad maxima de almacenamiento
bool llena() const
{
return sz == int(v.size());
}
//------------------------------
// Devuelve true si la cola esta vacia
bool vacia() const
{
return sz == 0;
}
//------------------------------
// Devuelve el numero de elementos almacenados
int size() const
{
return sz ;
}
//------------------------------
// Elimina todos los elementos de la cola actual (queda vacio)
void clear()
{
sz = 0 ;
}
//------------------------------
// PRECOND: ( ! llena() )
// Inserta (dato) al final de los elementos de la cola actual
void insertar(const TipoBase& dato)
{
assert( ! llena() ) ;
v[ult] = dato ;
ult = (ult + 1) % v.size();

70
++sz ;
}
//------------------------------
// PRECOND: ( ! vacia() )
// Elimina de la cola actual el elemento que ocupa la primera posicion
void eliminar()
{
assert( ! vacia() ) ;
pri = (pri + 1) % v.size();
--sz;
}
//------------------------------
// PRECOND: ( ! vacia() )
// Devuelve el elemento de la cola actual que ocupa la primera posicion
TipoBase acceder() const
{
assert( ! vacia() ) ;
return v[pri] ;
}
//------------------------------
// PRECOND: ( ! vacia() )
// Asigna (dato) al elemento de la cola actual que ocupa la primera posicion
void modificar(const TipoBase& dato)
{
assert( ! vacia() ) ;
v[pri] = dato;
}
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
typedef std::tr1::array<TipoBase, SIZE> Datos;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int sz; // numero de elementos de la cola
int pri; // primer elemento de la cola
int ult; // siguiente al ultimo elemento de la cola
Datos v; // contiene los elementos de la cola
//----------------------------------------------------------
};
}
#endif

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "cola.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
// Instanciación de la cola genérica para almacenar hasta 100 números enteros
typedef Cola<int, 100> ColaInt;
//------------------------------------------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca un dato: " ;
cin >> dato;
}
//---------------------------------
void leer(ColaInt& cola)
{
int dato ;
cola.clear() ;
cout << "Introduzca datos (0 -> FIN): " << endl ;
cin >> dato ;
while ((dato != 0)&&( ! cola.llena())) {
cola.insertar(dato) ;
cin >> dato ;
}
}
//---------------------------------

71
void escribir(const ColaInt& cola)
{
ColaInt aux = cola;
cout << "cola: " ;
while ( ! aux.vacia() ) {
cout << aux.acceder() << " " ;
aux.eliminar();
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const ColaInt& cola)
{
cout << "Constructor de Copia" << endl ;
ColaInt c(cola) ;
escribir(c) ;
cout << "Operador de Asignacion" << endl ;
c = cola ;
escribir(c) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Cola" << endl ;
cout << "B. Borrar Cola" << endl ;
cout << "C. Insertar" << endl ;
cout << "D. Eliminar" << endl ;
cout << "E. Acceder Primero" << endl ;
cout << "F. Modificar Primero" << endl ;
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
ColaInt cola ;
int dato ;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(cola) ;
escribir(cola) ;
break ;
case ’B’:
cola.clear() ;
escribir(cola) ;
break ;
case ’C’:
if (cola.llena()) {
cout << "Error: cola llena" << endl ;
} else {
leer_dato(dato) ;
cola.insertar(dato) ;
escribir(cola) ;
}
break ;
case ’D’:
if (cola.vacia()) {
cout << "Error: cola vacia" << endl ;
} else {
cola.eliminar() ;
escribir(cola) ;
}

72
break ;
case ’E’:
if (cola.vacia()) {
cout << "Error: cola vacia" << endl ;
} else {
cout << "cola[pri]: " << cola.acceder()<<endl;
escribir(cola) ;
}
break ;
case ’F’:
if (cola.vacia()) {
cout << "Error: cola vacia" << endl ;
} else {
leer_dato(dato) ;
cola.modificar(dato) ;
escribir(cola) ;
}
break ;
case ’G’:
prueba_asg(cola) ;
break ;
}
} while (op != ’X’) ;
}

73
Tema 3: Gestión de Memoria Dinámica
Tema 3.1: Listas Enlazadas
1. Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de listas
enlazadas en memoria dinámica (véase siguiente figura). El módulo deberá definir el tipo PNodo como un
puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al siguiente nodo, ası́ como
un dato de tipo int. Además, el módulo deberá definir los siguientes subprogramas. Diseñe también un
módulo principal que permita comprobar la corrección del módulo lista implementado.

lista: ◦−−→ ◦−−−−→ ◦−−−−→ 


0 1 2

Solución: lista.hpp
#ifndef _lista_hpp__
#define _lista_hpp__ 1
namespace umalcc {
//----------------------------------
struct Nodo ; // Declaración adelantada del tipo incompleto Nodo
typedef Nodo* PNodo ; // Definición de tipo Puntero a tipo incompleto Nodo
struct Nodo { // Definición del tipo Nodo
PNodo sig ; // Enlace a la siguiente estructura dinámica
int dato ; // Dato almacenado en la lista
} ;
//----------------------------------
void inicializa(PNodo& lista) ;
// Inicializa (lista) a una lista vacia
//----------------------------------
void destruir(PNodo& lista) ;
// Destruye todos los elementos de la lista, liberando
// todos los nodos de memoria dinamica. (lista) queda vacia
//----------------------------------
void insertar_principio(PNodo& lista, int dt) ;
// Inserta un elemento al principio de (lista)
//----------------------------------
void insertar_final(PNodo& lista, int dt) ;
// Inserta un elemento al final de (lista)
//----------------------------------
PNodo situar(PNodo lista, int pos) ;
// Devuelve un puntero al nodo que se encuentra en la posicion
// indicada por (pos). La posicion cero (0) indica el primer nodo.
// Si el nodo no existe, entonces devuelve NULL
//----------------------------------
void insertar_pos(PNodo& lista, int pos, int dt) ;
// Inserta en (lista) un elemento en la posicion indicada por (pos)
//----------------------------------
void eliminar_primero(PNodo& lista) ;
// Elimina el primer elemento de (lista), si existe
//----------------------------------
void eliminar_ultimo(PNodo& lista) ;
// Elimina el ultimo elemento de (lista), si existe
//----------------------------------
void eliminar_pos(PNodo& lista, int pos) ;
// Elimina de (lista) el elemento de la posicion indicada por (pos),
// si existe
//----------------------------------
PNodo duplicar(PNodo lista) ;
// Devuelve un puntero a una nueva lista resultado de duplicar en
// memoria dinamica la lista recibida como parametro
//----------------------------------
void escribir(PNodo lista) ;
// Muestra en pantalla el contenido de (lista)
//----------------------------------
PNodo leer() ;
// Devuelve una lista con los numeros leidos de teclado (en el mismo
// orden que son introducidos) hasta que lea el numero 0 (que no es
// introducido)
//----------------------------------
PNodo buscar(PNodo lista, int dt) ;

74
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
//----------------------------------
void insertar_ord(PNodo& lista, int dt) ;
// Inserta un elemento de forma ordenada en (lista), que debe estar ordenada
//----------------------------------
void eliminar_elem(PNodo& lista, int dt) ;
// Elimina de (lista) el primer elemento igual a (dt), si existe
//----------------------------------
void eliminar_mayor(PNodo& lista) ;
// Elimina el mayor elemento de (lista), si existe
//----------------------------------
void purgar(PNodo& lista, int dt) ;
// Elimina de (lista) todos los elementos que sean iguales a (dt),
// si existen
//----------------------------------
}
#endif

Solución: lista.cpp
#include "lista.hpp"
#include <iostream>
#include <cstddef>
#include <cassert>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Inicializa (lista) a una lista vacia
// sera insertado en la lista)
void inicializa(PNodo& lista)
{
lista = NULL ;
}
//--------------------------------------------------------------------------
// Destruye todos los elementos de la lista, liberando
// todos los nodos de memoria dinamica. (lista) queda vacia
void destruir(PNodo& lista)
{
while (lista != NULL) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
// Inserta un elemento al principio de (lista)
void insertar_principio(PNodo& lista, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
ptr->sig = lista ;
lista = ptr ;
}
//--------------------------------------------------------------------------
// Inserta un elemento al final de (lista)
void insertar_final(PNodo& lista, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
ptr->sig = NULL ;
if (lista == NULL) {
lista = ptr ;
} else {
PNodo act = lista ;
while (act->sig != NULL) {
act = act->sig ;
}

75
act->sig = ptr ;
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que se encuentra en la posicion
// indicada por (pos). La posicion cero (0) indica el primer nodo.
// Si el nodo no existe, entonces devuelve NULL
PNodo situar(PNodo lista, int pos)
{
PNodo ptr = lista;
while ((ptr != NULL)&&(pos > 0)) {
ptr = ptr->sig;
--pos;
}
return ptr;
}
//--------------------------------------------------------------------------
// Inserta en (lista) un elemento en la posicion indicada por (pos)
void insertar_pos(PNodo& lista, int pos, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
if (pos < 1) {
ptr->sig = lista ;
lista = ptr ;
} else {
PNodo ant = situar(lista, pos - 1);
if (ant != NULL) {
ptr->sig = ant->sig ;
ant->sig = ptr ;
}
}
}
//--------------------------------------------------------------------------
// Elimina el primer elemento de (lista), si existe
void eliminar_primero(PNodo& lista)
{
if (lista != NULL) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
// Elimina el ultimo elemento de (lista), si existe
void eliminar_ultimo(PNodo& lista)
{
if (lista != NULL) {
if (lista->sig == NULL) {
delete lista ;
lista = NULL ;
} else {
PNodo ant = lista ;
PNodo act = ant->sig ;
while (act->sig != NULL) {
ant = act ;
act = act->sig ;
}
delete act ;
ant->sig = NULL ;
}
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) el elemento de la posicion indicada por (pos),
// si existe
void eliminar_pos(PNodo& lista, int pos)
{
if (lista != NULL) {
if (pos < 1) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
} else {

76
PNodo ant = situar(lista, pos - 1) ;
if ((ant != NULL)&&(ant->sig != NULL)) {
PNodo act = ant->sig ;
ant->sig = act->sig ;
delete act ;
}
}
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero a una nueva lista resultado de duplicar en
// memoria dinamica la lista recibida como parametro
PNodo duplicar(PNodo lista)
{
PNodo nueva = NULL;
if (lista != NULL) {
nueva = new Nodo ;
nueva->dato = lista->dato ;
PNodo u = nueva ;
PNodo p = lista->sig ;
while (p != NULL) {
u->sig = new Nodo ;
u->sig->dato = p->dato ;
u = u->sig ;
p = p->sig ;
}
u->sig = NULL ;
}
return nueva;
}
//--------------------------------------------------------------------------
// Muestra en pantalla el contenido de (lista)
void escribir(PNodo lista)
{
PNodo ptr = lista;
while (ptr != NULL) {
cout << ptr->dato << " " ;
ptr = ptr->sig ;
}
cout << endl;
}
//--------------------------------------------------------------------------
// Devuelve una lista con los numeros leidos de teclado (en el mismo
// orden que son introducidos) hasta que lea el numero 0 (que no es
// introducido)
PNodo leer()
{
PNodo lista = NULL ;
int dt ;
cin >> dt ;
if (dt != 0) {
lista = new Nodo ;
lista->dato = dt ;
PNodo u = lista ;
cin >> dt ;
while (dt != 0) {
u->sig = new Nodo ;
u->sig->dato = dt ;
u = u->sig ;
cin >> dt ;
}
u->sig = NULL ;
}
return lista;
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
PNodo buscar(PNodo lista, int dt)
{
PNodo ptr = lista ;
while ((ptr != NULL)&&(ptr->dato != dt)) {
ptr = ptr->sig ;
}

77
return ptr ;
}
//--------------------------------------------------------------------------
// Inserta un elemento de forma ordenada en (lista), que debe estar
// ordenada
void insertar_ord(PNodo& lista, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
if ((lista == NULL)||(dt < lista->dato)) {
ptr->sig = lista ;
lista = ptr ;
} else {
PNodo ant = lista ;
PNodo act = ant->sig ;
while ((act!=NULL)&&(act->dato<=dt)) {
ant = act ;
act = act->sig ;
}
ptr->sig = ant->sig ;
ant->sig = ptr ;
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) el primer elemento igual a (dt), si existe
void eliminar_elem(PNodo& lista, int dt)
{
if (lista != NULL) {
if (lista->dato == dt) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
} else {
PNodo ant = lista ;
PNodo act = ant->sig ;
while ((act != NULL)&&(act->dato != dt)) {
ant = act ;
act = act->sig ;
}
if (act != NULL) {
ant->sig = act->sig ;
delete act ;
}
}
}
}
//--------------------------------------------------------------------------
// Elimina el mayor elemento de (lista), si existe
void eliminar_mayor(PNodo& lista)
{
if (lista != NULL) {
PNodo ant_may = NULL; // anterior al mayor
PNodo ptr_may = lista; // apunta al mayor
PNodo ant = lista; // anterior al ptr
PNodo ptr = lista->sig; // ptr de recorrido
while (ptr != NULL) {
if (ptr->dato > ptr_may->dato) { // si es mayor
ant_may = ant; // actualizar anterior al mayor
ptr_may = ptr; // actualizar el puntero al mayor
}
ant = ptr; // mover los punteros de recorrido
ptr = ptr->sig;
}
if (ptr_may == lista) {
lista = lista->sig;
} else {
ant_may->sig = ptr_may->sig;
}
delete ptr_may;
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) todos los elementos que sean iguales a (dt),
// si existen

78
void purgar(PNodo& lista, int dt)
{
while ((lista != NULL)&&(dt == lista->dato)) {
PNodo ptr = lista;
lista = lista->sig;
delete ptr;
}
if (lista != NULL) {
PNodo ant = lista;
PNodo act = lista->sig;
while (act != NULL) {
if (dt == act->dato) {
ant->sig = act->sig;
delete act;
} else {
ant = act;
}
act = ant->sig;
}
}
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include "lista.hpp"
#include <iostream>
using namespace std;
using namespace umalcc;

//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Destruir Lista" << endl;
cout << "B. Insertar Inicio" << endl;
cout << "C. Insertar Final" << endl;
cout << "D. Situar Posicion" << endl;
cout << "E. Insertar Posicion" << endl;
cout << "F. Eliminar Primero" << endl;
cout << "G. Eliminar Ultimo" << endl;
cout << "H. Eliminar Posicion" << endl;
cout << "I. Duplicar" << endl;
cout << "J. Escribir" << endl;
cout << "K. Leer" << endl;
cout << "L. Buscar Elemento" << endl;
cout << "M. Insertar Ordenado" << endl;
cout << "N. Eliminar Elemento" << endl;
cout << "O. Eliminar Mayor" << endl;
cout << "P. Purgar" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’P’))));
cout << endl;
return op;
}
//------------------------------------------------------------------
void leer(int& dato)
{
cout << "Introduzca dato: " ;
cin >> dato;
}
void leer(unsigned& pos)
{
cout << "Introduzca posicion: " ;
cin >> pos;
}
void prueba_duplicar(PNodo lista)
{
PNodo aux = duplicar(lista);

79
cout << "Lista original: " ;
escribir(lista);
cout << "Lista copia: " ;
escribir(aux);
destruir(aux);
}
//-------------------------------------------------------------------------
int main()
{
PNodo lista;
PNodo aux;
char op = ’ ’;
int dato;
unsigned pos;
inicializa(lista);
do {
op = menu();
switch (op) {
case ’A’:
destruir(lista);
escribir(lista);
break;
case ’B’:
leer(dato);
insertar_principio(lista, dato);
escribir(lista);
break;
case ’C’:
leer(dato);
insertar_final(lista, dato);
escribir(lista);
break;
case ’D’:
leer(pos);
aux = situar(lista, pos);
if (aux == NULL) {
cout << "Error: posicion erronea" << endl;
} else {
cout << "Elemento ["<<pos<<"]: " << aux->dato << endl;
}
escribir(lista);
break;
case ’E’:
leer(pos);
leer(dato);
insertar_pos(lista, pos, dato) ;
escribir(lista);
break;
case ’F’:
eliminar_primero(lista);
escribir(lista);
break;
case ’G’:
eliminar_ultimo(lista);
escribir(lista);
break;
case ’H’:
leer(pos);
eliminar_pos(lista, pos) ;
escribir(lista);
break;
case ’I’:
prueba_duplicar(lista);
break;
case ’J’:
escribir(lista);
break;
case ’K’:
destruir(lista);
cout << "Introduzca elementos (0 -> FIN)" << endl;
lista = leer();
escribir(lista);
break;
case ’L’:

80
leer(dato);
aux = buscar(lista, dato);
if (aux == NULL) {
cout << "Error: No encontrado" << endl;
} else {
cout << "Elemento: " << aux->dato << endl;
}
escribir(lista);
break;
case ’M’:
leer(dato);
insertar_ord(lista, dato);
escribir(lista);
break;
case ’N’:
leer(dato);
eliminar_elem(lista, dato);
escribir(lista);
break;
case ’O’:
eliminar_mayor(lista);
escribir(lista);
break;
case ’P’:
leer(dato);
purgar(lista, dato);
escribir(lista);
break;
}
} while (op != ’X’);
destruir(lista);
}

2. Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de listas
doblemente enlazadas en memoria dinámica (véase siguiente figura). El módulo deberá definir el tipo
PNodo como un puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al siguiente
nodo, un enlace al nodo anterior, ası́ como un dato de tipo int. Además, el módulo deberá definir los
siguientes subprogramas. Diseñe también un módulo principal que permita comprobar la corrección del
módulo lista implementado.

lista: ◦−−→
◦−−−
−−→ ◦−−−
−−→ 
 ←−−−−
−◦ ←−−−−
−◦
0 1 2

Solución: lista.hpp
#ifndef _listadbl_hpp__
#define _listadbl_hpp__ 1
namespace umalcc {
//----------------------------------
struct Nodo ; // Declaración adelantada del tipo incompleto Nodo
typedef Nodo* PNodo ; // Definición de tipo Puntero a tipo incompleto Nodo
struct Nodo { // Definición del tipo Nodo
PNodo sig ; // Enlace a la siguiente estructura dinámica
PNodo ant ; // Enlace a la estructura dinámica anterior
int dato ; // Dato almacenado en la lista
} ;
//----------------------------------
void inicializa(PNodo& lista) ;
// Inicializa (lista) a una lista vacia
//----------------------------------
void destruir(PNodo& lista) ;
// Destruye todos los elementos de la lista, liberando
// todos los nodos de memoria dinamica. (lista) queda vacia
//----------------------------------
void insertar_principio(PNodo& lista, int dt) ;
// Inserta un elemento al principio de (lista)
//----------------------------------
void insertar_final(PNodo& lista, int dt) ;
// Inserta un elemento al final de (lista)

81
//----------------------------------
PNodo situar(PNodo lista, int pos) ;
// Devuelve un puntero al nodo que se encuentra en la posicion
// indicada por (pos). La posicion cero (0) indica el primer nodo.
// Si el nodo no existe, entonces devuelve NULL
//----------------------------------
void insertar(PNodo& lista, int pos, int dt) ;
// Inserta en (lista) un elemento en la posicion indicada por (pos)
//----------------------------------
void eliminar_primero(PNodo& lista) ;
// Elimina el primer elemento de (lista), si existe
//----------------------------------
void eliminar_ultimo(PNodo& lista) ;
// Elimina el ultimo elemento de (lista), si existe
//----------------------------------
void eliminar(PNodo& lista, int pos) ;
// Elimina de (lista) el elemento de la posicion indicada por (pos),
// si existe
//----------------------------------
PNodo duplicar(PNodo lista) ;
// Devuelve un puntero a una nueva lista resultado de duplicar en
// memoria dinamica la lista recibida como parametro
//----------------------------------
void escribir(PNodo lista) ;
// Muestra en pantalla el contenido de (lista)
//----------------------------------
PNodo leer() ;
// Devuelve una lista con los numeros leidos de teclado (en el mismo
// orden que son introducidos) hasta que lea el numero 0 (que no es
// introducido)
//----------------------------------
PNodo buscar(PNodo lista, int dt) ;
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
//----------------------------------
void insertar_ord(PNodo& lista, int dt) ;
// Inserta un elemento de forma ordenada en (lista), que debe estar ordenada
//----------------------------------
void eliminar_elem(PNodo& lista, int dt) ;
// Elimina de (lista) el primer elemento igual a (dt), si existe
//----------------------------------
void eliminar_mayor(PNodo& lista) ;
// Elimina el mayor elemento de (lista), si existe
//----------------------------------
void purgar(PNodo& lista, int dt) ;
// Elimina de (lista) todos los elementos que sean iguales a (dt),
// si existen
//----------------------------------
}
#endif

Solución: lista.cpp
#include "listadbl.hpp"
#include <iostream>
#include <cstddef>
#include <cassert>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//----------------------------------------------------------
//-- Auxiliares --------------------------------------------
//----------------------------------------------------------
void insertar_nodo(PNodo& ptr, PNodo ant, int dt)
{
// Crea el nodo con el valor del dato, el valor del campo
// ’sig’ apuntando al nodo apuntado por ’ptr’, el valor
// del campo ’ant’ apuntando al nodo apuntado por ’ant’ y
// hace que ’ptr’ apunte a este nuevo nodo. Tambien hace
// que el anterior del siguiente apunte a este nodo
PNodo aux = new Nodo ;

82
aux->dato = dt ;
aux->ant = ant ;
aux->sig = ptr ;
ptr = aux ;
if (aux->sig != NULL) {
aux->sig->ant = aux;
}
}
//--------------------------------------------------------------------------
void eliminar_nodo(PNodo& ptr)
{
// hace que ’ptr’ apunte al nodo siguiente al que apunta
// actualmente, y hace que el anterior de este nodo
// siguiente apunte al anterior al nodo de ptr. Finalmente
// libera el nodo al que ’ptr’ apuntaba inicialmente
assert(ptr != NULL);
PNodo aux = ptr;
ptr = ptr->sig;
if (ptr != NULL) {
ptr->ant = aux->ant;
}
delete aux;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
// Inicializa (lista) a una lista vacia
// sera insertado en la lista)
void inicializa(PNodo& lista)
{
lista = NULL ;
}
//--------------------------------------------------------------------------
// Destruye todos los elementos de la lista, liberando
// todos los nodos de memoria dinamica. (lista) queda vacia
void destruir(PNodo& lista)
{
while (lista != NULL) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
// Inserta un elemento al principio de (lista)
void insertar_principio(PNodo& lista, int dt)
{
insertar_nodo(lista, NULL, dt);
}
//--------------------------------------------------------------------------
// Inserta un elemento al final de (lista)
void insertar_final(PNodo& lista, int dt)
{
if (lista == NULL) {
insertar_nodo(lista, NULL, dt);
} else {
PNodo ant = lista;
while (ant->sig != NULL) {
ant = ant->sig;
}
insertar_nodo(ant->sig, ant, dt);
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que se encuentra en la posicion
// indicada por (pos). La posicion cero (0) indica el primer nodo.
// Si el nodo no existe, entonces devuelve NULL
PNodo situar(PNodo lista, int pos)
{
PNodo ptr = lista;

83
while ((ptr != NULL)&&(pos > 0)) {
ptr = ptr->sig;
--pos;
}
return ptr;
}
//--------------------------------------------------------------------------
// Inserta en (lista) un elemento en la posicion indicada por (pos)
void insertar(PNodo& lista, int pos, int dt)
{
if (pos < 1) {
insertar_nodo(lista, NULL, dt);
} else {
PNodo ant = situar(lista, pos - 1);
if (ant != NULL) {
insertar_nodo(ant->sig, ant, dt);
}
}
}
//--------------------------------------------------------------------------
// Elimina el primer elemento de (lista), si existe
void eliminar_primero(PNodo& lista)
{
if (lista != NULL) {
eliminar_nodo(lista) ;
}
}
//--------------------------------------------------------------------------
// Elimina el ultimo elemento de (lista), si existe
void eliminar_ultimo(PNodo& lista)
{
if (lista != NULL) {
if (lista->sig == NULL) {
eliminar_nodo(lista) ;
} else {
PNodo act = lista->sig ;
while (act->sig != NULL) {
act = act->sig;
}
eliminar_nodo(act->ant->sig) ;
}
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) el elemento de la posicion indicada por (pos),
// si existe
void eliminar(PNodo& lista, int pos)
{
if (lista != NULL) {
if (pos < 1) {
eliminar_nodo(lista) ;
} else {
PNodo act = situar(lista, pos) ;
if (act != NULL) {
eliminar_nodo(act->ant->sig) ;
}
}
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero a una nueva lista resultado de duplicar en
// memoria dinamica la lista recibida como parametro
PNodo duplicar(PNodo lista)
{
PNodo nueva = NULL;
if (lista != NULL) {
insertar_nodo(nueva, NULL, lista->dato) ;
PNodo u = nueva ;
PNodo p = lista->sig ;
while (p != NULL) {
insertar_nodo(u->sig, u, p->dato) ;
u = u->sig ;
p = p->sig ;
}

84
}
return nueva;
}
//--------------------------------------------------------------------------
// Muestra en pantalla el contenido de (lista)
void escribir(PNodo lista)
{
PNodo ptr = lista;
while (ptr != NULL) {
cout << ptr->dato << " " ;
ptr = ptr->sig ;
}
cout << endl;
}
//--------------------------------------------------------------------------
// Devuelve una lista con los numeros leidos de teclado (en el mismo
// orden que son introducidos) hasta que lea el numero 0 (que no es
// introducido)
PNodo leer()
{
PNodo lista = NULL;
int dt ;
cin >> dt ;
if (dt != 0) {
insertar_nodo(lista, NULL, dt) ;
PNodo u = lista ;
cin >> dt ;
while (dt != 0) {
insertar_nodo(u->sig, u, dt) ;
u = u->sig ;
cin >> dt ;
}
}
return lista;
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
PNodo buscar(PNodo lista, int dt)
{
PNodo ptr = lista ;
while ((ptr != NULL)&&(ptr->dato != dt)) {
ptr = ptr->sig ;
}
return ptr ;
}
//--------------------------------------------------------------------------
// Inserta un elemento de forma ordenada en (lista), que debe estar
// ordenada
void insertar_ord(PNodo& lista, int dt)
{
if ((lista == NULL)||(dt < lista->dato)){
insertar_nodo(lista, NULL, dt) ;
} else {
PNodo ant = lista ;
PNodo act = ant->sig ;
while ((act!=NULL)&&(act->dato<=dt)){
ant = act ;
act = act->sig ;
}
insertar_nodo(ant->sig, ant, dt) ;
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) el primer elemento igual a (dt), si existe
void eliminar_elem(PNodo& lista, int dt)
{
if (lista != NULL) {
if (lista->dato == dt) {
eliminar_nodo(lista);
} else {
PNodo act = lista->sig ;
while ((act != NULL)&&(act->dato != dt)) {
act = act->sig;

85
}
if (act != NULL) {
eliminar_nodo(act->ant->sig);
}
}
}
}
//--------------------------------------------------------------------------
// Elimina el mayor elemento de (lista), si existe
void eliminar_mayor(PNodo& lista)
{
if (lista != NULL) {
PNodo ptr_may = lista; // apunta al mayor
PNodo ptr = lista->sig; // ptr de recorrido
while (ptr != NULL) {
if (ptr->dato > ptr_may->dato) { // si es mayor
ptr_may = ptr; // actualizar el puntero al mayor
}
ptr = ptr->sig; // mover los punteros de recorrido
}
if (ptr_may == lista) {
eliminar_nodo(lista); // eliminar el primero
} else {
eliminar_nodo(ptr_may->ant->sig); // eliminar un nodo siguiente
}
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) todos los elementos que sean iguales a (dt),
// si existen
void purgar(PNodo& lista, int dt)
{
while ((lista != NULL)&&(dt == lista->dato)) {
eliminar_nodo(lista);
}
if (lista != NULL) {
PNodo ant = lista;
PNodo act = lista->sig;
while (act != NULL) {
if (dt == act->dato) {
eliminar_nodo(ant->sig);
} else {
ant = act;
}
act = ant->sig;
}
}
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include "listadbl.hpp"
#include <iostream>
using namespace std;
using namespace umalcc;

//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Destruir Lista" << endl;
cout << "B. Insertar Inicio" << endl;
cout << "C. Insertar Final" << endl;
cout << "D. Situar Posicion" << endl;
cout << "E. Insertar Posicion" << endl;
cout << "F. Eliminar Primero" << endl;
cout << "G. Eliminar Ultimo" << endl;
cout << "H. Eliminar Posicion" << endl;
cout << "I. Duplicar" << endl;
cout << "J. Escribir" << endl;
cout << "K. Leer" << endl;

86
cout << "L. Buscar Elemento" << endl;
cout << "M. Insertar Ordenado" << endl;
cout << "N. Eliminar Elemento" << endl;
cout << "O. Eliminar Mayor" << endl;
cout << "P. Purgar" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’P’))));
cout << endl;
return op;
}
//------------------------------------------------------------------
void leer(int& dato)
{
cout << "Introduzca dato: " ;
cin >> dato;
}
void leer(unsigned& pos)
{
cout << "Introduzca posicion: " ;
cin >> pos;
}
void prueba_duplicar(PNodo lista)
{
PNodo aux = duplicar(lista);
cout << "Lista original: " ;
escribir(lista);
cout << "Lista copia: " ;
escribir(aux);
destruir(aux);
}
//-------------------------------------------------------------------------
int main()
{
PNodo lista;
PNodo aux;
char op = ’ ’;
int dato;
unsigned pos;
inicializa(lista);
do {
op = menu();
switch (op) {
case ’A’:
destruir(lista);
escribir(lista);
break;
case ’B’:
leer(dato);
insertar_principio(lista, dato);
escribir(lista);
break;
case ’C’:
leer(dato);
insertar_final(lista, dato);
escribir(lista);
break;
case ’D’:
leer(pos);
aux = situar(lista, pos);
if (aux == NULL) {
cout << "Error: posicion erronea" << endl;
} else {
cout << "Elemento ["<<pos<<"]: " << aux->dato << endl;
}
escribir(lista);
break;
case ’E’:
leer(pos);
leer(dato);
insertar(lista, pos, dato) ;
escribir(lista);

87
break;
case ’F’:
eliminar_primero(lista);
escribir(lista);
break;
case ’G’:
eliminar_ultimo(lista);
escribir(lista);
break;
case ’H’:
leer(pos);
eliminar(lista, pos) ;
escribir(lista);
break;
case ’I’:
prueba_duplicar(lista);
break;
case ’J’:
escribir(lista);
break;
case ’K’:
destruir(lista);
cout << "Introduzca elementos (0 -> FIN)" << endl;
lista = leer();
escribir(lista);
break;
case ’L’:
leer(dato);
aux = buscar(lista, dato);
if (aux == NULL) {
cout << "Error: No encontrado" << endl;
} else {
cout << "Elemento: " << aux->dato << endl;
}
escribir(lista);
break;
case ’M’:
leer(dato);
insertar_ord(lista, dato);
escribir(lista);
break;
case ’N’:
leer(dato);
eliminar_elem(lista, dato);
escribir(lista);
break;
case ’O’:
eliminar_mayor(lista);
escribir(lista);
break;
case ’P’:
leer(dato);
purgar(lista, dato);
escribir(lista);
break;
}
} while (op != ’X’);
destruir(lista);
}

3. Un conjunto de números enteros es una colección de elementos homogéneos (números enteros) sin repetición
y ninguna relación de orden entre ellos (no ordenados y sin repetición).
Un conjunto de elementos se puede representar mediante una lista enlazada, donde cada nodo contiene
un elemento del conjunto. Por ejemplo el conjunto con los elementos {1, 2, 3} puede ser representado por
la siguiente lista enlazada:

lista: ◦−−→ ◦−−−−→ ◦−−−−→ 


1 2 3

88
Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de con-
juntos de números enteros implementados mediante listas enlazadas en memoria dinámica (véase figura
anterior). El módulo deberá definir el tipo Conjunto como un registro con un campo de tipo PNodo, que se
define como un tipo puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al si-
guiente nodo, ası́ como un dato de tipo int. Además, el módulo deberá definir los siguientes subprogramas.
Diseñe también un módulo principal que permita comprobar la corrección del módulo implementado.

Nota: aunque el orden en el que se encuentran los elementos en el conjunto no es importante, y por lo
tanto, la opción más simple para incluir elementos en él podrı́a ser “insertar siempre los elementos por el
principio”, con objeto de prácticar los conceptos de listas enlazadas, en esta práctica, los elementos del
conjunto serán insertados (sin repetición) ordenados en la lista enlazada donde se encuentran almacenados.
Solución: conjuntos.hpp
#ifndef _conjuntos_hpp_
#define _conjuntos_hpp_
#include <iostream>
namespace umalcc {
//----------------------------------
struct Nodo;
typedef Nodo* PNodo;
struct Nodo {
PNodo sig;
int dato;
};
struct Conjunto {
PNodo lista;
};
//----------------------------------
void inicializar(Conjunto& c);
// Inicializa (c) a un conjunto vacio
void destruir(Conjunto& c);
// Destruye todos los elementos del conjunto, liberando
// todos los nodos de memoria dinamica. (c) queda vacio
void copiar(Conjunto& d, const Conjunto& o) ;
// Copia (duplica) el conjunto (o) en el conjunto (d)
bool es_vacio(const Conjunto& c) ;
// Devuelve true si el conjunto (c) esta vacio
void incluir(Conjunto& c, int e);
// Incluye el elemento (e) en el conjunto (c) (sin repeticion)
void eliminar(Conjunto& c, int e);
// Elimina el elemento (e) del conjunto (c)
bool pertenece(const Conjunto& c, int e) ;
// Devuelve true si el elemento (e) pertenece al conjunto (c)
bool es_subconjunto(const Conjunto& a, const Conjunto& b) ;
// Devuelve true si el conjunto (a) es subconjunto del conjunto (b)
void union_conj(Conjunto& c, const Conjunto& a, const Conjunto& b);
// Asigna al conjunto (c) el resultado de
// la union de los conjuntos (a) y (b) (duplica los nodos)
void interseccion(Conjunto& c, const Conjunto& a, const Conjunto& b);
// Asigna al conjunto (c) el resultado de
// la interseccion de los conjuntos (a) y (b) (duplica los nodos)
void diferencia(Conjunto& c, const Conjunto& a, const Conjunto& b);
// Asigna al conjunto (c) el resultado de
// la diferencia de los conjuntos (a) y (b) (duplica los nodos)
void diferencia_simetrica(Conjunto& c, const Conjunto& a, const Conjunto& b);
// Asigna al conjunto (c) el resultado de
// la diferencia simetrica de los conjuntos (a) y (b) (duplica los nodos)
void escribir(const Conjunto& c) ;
// Muestra en pantalla el contenido del conjunto (c)
void leer(Conjunto& c);
// Lee de teclado el valor del conjunto (c)
//----------------------------------
}
#endif

Solución: conjuntos.cpp
#include "conjuntos.hpp"
#include <iostream>
#include <tr1/array>
#include <cassert>
using namespace std;

89
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------------
//-- Auxiliares ------------------------------------------------------------
//--------------------------------------------------------------------------
void insertar_nodo(PNodo& ptr, int dt)
{
// Crea el nodo con el valor del dato, el valor del campo
// ’sig’ apuntando al nodo apuntado por ’ptr’, y hace que
// ’ptr’ apunte a este nuevo nodo.
PNodo aux = new Nodo ;
aux->dato = dt ;
aux->sig = ptr ;
ptr = aux ;
}
//--------------------------------------------------------------------------
void eliminar_nodo(PNodo& ptr)
{
// hace que ’ptr’ apunte al nodo siguiente al que apunta
// actualmente. Finalmente libera el nodo al que ’ptr’
// apuntaba inicialmente
assert(ptr != NULL) ;
PNodo aux = ptr ;
ptr = ptr->sig ;
delete aux ;
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
PNodo buscar_ord(PNodo lst, int dt)
{
PNodo ptr = lst ;
while ((ptr != NULL)&&(ptr->dato < dt)) {
ptr = ptr->sig ;
}
if ((ptr != NULL)&&(ptr->dato != dt)) {
ptr = NULL;
}

return ptr ;
}
//--------------------------------------------------------------------------
// Inserta un elemento de forma ordenada en (lst), que debe estar
// ordenada (sin repeticion)
void insertar_ord(PNodo& lst, int dt)
{
if ((lst == NULL) || (dt < lst->dato)) {
insertar_nodo(lst, dt) ;
} else if (dt > lst->dato) {
PNodo ant = lst ;
PNodo act = ant->sig ;
while ((act != NULL) && (act->dato < dt)) {
ant = act ;
act = act->sig ;
}
if ((act == NULL) || (act->dato != dt)) {
insertar_nodo(ant->sig, dt) ;
}
}
}
//--------------------------------------------------------------------------
// Elimina de (lst) el elemento cuyo dato es igual a (dt), si existe
// La lista (lst) se encuentra ordenada
void eliminar_ord(PNodo& lst, int dt)
{
if (lst != NULL) {
if (lst->dato == dt) {
eliminar_nodo(lst);
} else {
PNodo ant = lst ;

90
PNodo act = ant->sig ;
while ((act != NULL)&&(act->dato < dt)) {
ant = act;
act = act->sig;
}
if ((act != NULL) && (act->dato == dt)) {
eliminar_nodo(ant->sig);
}
}
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero a una nueva lst resultado de duplicar en
// memoria dinamica la lst recibida como parametro
PNodo duplicar(PNodo lst)
{
PNodo nueva = NULL;
if (lst != NULL) {
insertar_nodo(nueva, lst->dato) ;
PNodo u = nueva ;
PNodo p = lst->sig ;
while (p != NULL) {
insertar_nodo(u->sig, p->dato) ;
u = u->sig ;
p = p->sig ;
}
}
return nueva;
}
//--------------------------------------------------------------------------
// Destruye todos los elementos de la lst, liberando
// todos los nodos de memoria dinamica. (lst) queda vacia
void destruir_lista(PNodo& lst)
{
while (lst != NULL) {
PNodo ptr = lst ;
lst = lst->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Metodos Publicos ------------------------------------------------------
//--------------------------------------------------------------------------
void inicializar(Conjunto& c)
{
c.lista = NULL;
}
//--------------------------------------------------------------------------
void destruir(Conjunto& c)
{
destruir_lista(c.lista);
}
//--------------------------------------------------------------------------
void copiar(Conjunto& d, const Conjunto& o)
{
if (d.lista != o.lista) {
destruir_lista(d.lista);
d.lista = duplicar(o.lista);
}
}
//--------------------------------------------------------------------------
bool es_vacio(const Conjunto& c)
{
return (c.lista == NULL);
}
//--------------------------------------------------------------------------
void incluir(Conjunto& c, int e)

91
{
insertar_ord(c.lista, e); // inserta sin repeticion
}
//--------------------------------------------------------------------------
void eliminar(Conjunto& c, int e)
{
eliminar_ord(c.lista, e);
}
//--------------------------------------------------------------------------
bool pertenece(const Conjunto& c, int e)
{
return (buscar_ord(c.lista, e) != NULL);
}
//--------------------------------------------------------------------------
bool es_subconjunto(const Conjunto& a, const Conjunto& b)
{
PNodo ptr = a.lista ;
while ((ptr != NULL)&& pertenece(b, ptr->dato)) {
ptr = ptr->sig ;
}
return (ptr == NULL);
}
//--------------------------------------------------------------------------
void union_conj(Conjunto& c, const Conjunto& a, const Conjunto& b)
{
PNodo lst = duplicar(a.lista);
PNodo ptr = b.lista ;
while (ptr != NULL) {
insertar_ord(lst, ptr->dato); // inserta sin repeticion
ptr = ptr->sig ;
}
destruir_lista(c.lista);
c.lista = lst;
}
//--------------------------------------------------------------------------
void interseccion(Conjunto& c, const Conjunto& a, const Conjunto& b)
{
PNodo lst = NULL;
PNodo ptr = a.lista ;
while (ptr != NULL) {
if (pertenece(b, ptr->dato)) {
insertar_ord(lst, ptr->dato); // inserta sin repeticion
}
ptr = ptr->sig ;
}
destruir_lista(c.lista);
c.lista = lst;
}
//--------------------------------------------------------------------------
void diferencia(Conjunto& c, const Conjunto& a, const Conjunto& b)
{
PNodo lst = NULL;
PNodo ptr = a.lista ;
while (ptr != NULL) {
if ( ! pertenece(b, ptr->dato)) {
insertar_ord(lst, ptr->dato); // inserta sin repeticion
}
ptr = ptr->sig ;
}
destruir_lista(c.lista);
c.lista = lst;
}
//--------------------------------------------------------------------------
void diferencia_simetrica(Conjunto& c, const Conjunto& a, const Conjunto& b)
{
Conjunto a_b, b_a;
inicializar(a_b);
inicializar(b_a);
diferencia(a_b, a, b);
diferencia(b_a, b, a);
union_conj(c, a_b, b_a);
}
//--------------------------------------------------------------------------
void escribir(const Conjunto& c)

92
{
cout << "{ ";
PNodo ptr = c.lista;
while (ptr != NULL) {
cout << ptr->dato << " " ;
ptr = ptr->sig ;
}
cout << "}";
}
//--------------------------------------------------------------------------
void leer(Conjunto& c)
{
destruir(c);
int e;
cin >> e ;
while (e != 0) {
incluir(c, e);
cin >> e ;
}
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include "conjuntos.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Conjunto& c)


{
cout << msj;
escribir(c);
cout << endl;
}
int main()
{
Conjunto c1, c2, c3;
inicializar(c1);
inicializar(c2);
inicializar(c3);
cout << "Introduzca elementos del conjunto: [0 -> fin]: ";
leer(c1);
escribir("C1: ", c1);
cout << "Introduzca elementos del conjunto: [0 -> fin]: ";
leer(c2);
escribir("C2: ", c2);
cout << "Es subconjunto: " << boolalpha << es_subconjunto(c1, c2) << endl;
union_conj(c3, c1, c2);
escribir("Union: ", c3);
interseccion(c3, c1, c2);
escribir("Interseccion: ", c3);
diferencia(c3, c1, c2);
escribir("Diferencia: ", c3);
diferencia_simetrica(c3, c1, c2);
escribir("Diferencia Simetrica: ", c3);
}

Tema 3.2: Abstracción en la Gestión de Memoria Dinámica


1. Un polinomio en x de grado arbitrario se puede representar mediante una lista enlazada, donde cada nodo
contiene el coeficiente y el exponente de un término del polinomio, y donde los coeficientes con valor cero
(0) no serán almacenados. Por ejemplo el polinomio 25x − 14x5 puede ser representado por la siguiente
lista enlazada:

lista: ◦−−→
◦−−−−→ 
25 -14
1 5

93
Diseñe e implemente el TAD polinomio que defina los siguientes métodos públicos, ası́ como un programa
para comprobar su funcionamiento:

Solución: polinomios.hpp
#ifndef _polinomios_hpp_
#define _polinomios_hpp_
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6;
//----------------------------------
class Polinomio {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
~Polinomio();
// Destructor
Polinomio();
// Constructor por Defecto
Polinomio(const Polinomio& p);
// Constructor de copia
Polinomio& operator=(const Polinomio& p);
// Operador de Asignacion
int max_grado() const;
// Devuelve el mayor grado del polinomio actual
// cuyo coeficiente es distinto de cero
void poner(int e, double c);
// Asigna el coeficiente (c) al termino de grado (e)
// del polinomio actual
double obtener(int e) const;
// Devuelve el coeficiente correspondiente al
// termino de grado (e) del polinomio actual
double evaluar(double x) const;
// Devuelve el resultado de evaluar el polinomio actual
// para un valor de (x) especificado como parametro
void derivar(const Polinomio& a);
// Asigna al polinomio actual el resultado de
// calcular la derivada del polinomio (a)
void sumar(const Polinomio& a, const Polinomio& b);
// Asigna al polinomio actual el resultado de
// sumar los polinomios (a) y (b)
void escribir() const;
// Muestra en pantalla el contenido del polinomio actual
void leer();
// Lee de teclado el valor del polinomio actual
// Lee pares de coeficiente y grado hasta que el coeficiente sea cero
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Tipos Privados ----------------------------------------
//----------------------------------------------------------
struct Nodo;
typedef Nodo* PNodo;
struct Dato {
int exp;
double coef;
};
struct Nodo {
PNodo sig;
Dato dato;
};
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void insertar_nodo(PNodo& ptr, const Dato& dt) const ;
void eliminar_nodo(PNodo& ptr) const ;
PNodo buscar_ord(PNodo lst, int exp) const ;
void insertar_ord(PNodo& lst, const Dato& dt) const ;
void eliminar_ord(PNodo& lst, int exp) const ;
PNodo duplicar(PNodo lst) const ;
void destruir(PNodo& lst) const ;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------

94
//----------------------------------------------------------
int mg;
PNodo lista;
//----------------------------------------------------------
};
}
#endif

Solución: polinomios.cpp
#include "polinomios.hpp"
#include <iostream>
#include <cassert>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------------
//-- Auxiliares ------------------------------------------------------------
//--------------------------------------------------------------------------
// Valor absoluto de un numero
inline double abs(double a) {
return (a >= 0) ? a : -a;
}
//--------------------------------------------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b) {
return abs(a-b) <= ERROR_PRECISION;
}
//--------------------------------------------------------------
double potencia(double base, int exp)
{
double res = 1;
for (int i = 0; i < exp; ++i) {
res *= base;
}
return res;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
Polinomio::~Polinomio() { destruir(lista); }
//--------------------------------------------------------------
Polinomio::Polinomio() : mg(0), lista() {}
//--------------------------------------------------------------
Polinomio::Polinomio(const Polinomio& o)
: mg(o.mg), lista(duplicar(o.lista)) {}
//--------------------------------------------------------------
Polinomio& Polinomio::operator=(const Polinomio& o)
{
if (this != &o) {
destruir(lista);
mg = o.mg;
lista = duplicar(o.lista);
}
return *this;
}
//--------------------------------------------------------------
int Polinomio::max_grado() const
{
return mg;
}
//--------------------------------------------------------------
void Polinomio::poner(int e, double c)
{
// Si el coeficiente es igual a cero, entonces se debe
// eliminar el elemento correspondiente a ese exponente si es

95
// que existe. En otro caso, si ya estaba almacenado, se
// reemplaza su valor. En otro caso, se inserta ordenado por
// exponente.
if (iguales(c, 0.0)) {
if (e <= mg) {
eliminar_ord(lista, e);
}
} else {
Dato d = { e, c } ;
insertar_ord(lista, d); // insertar sin repeticion
if (e > mg) {
mg = e;
}
}
}
//--------------------------------------------------------------
double Polinomio::obtener(int e) const
{
// Si el elemento no esta en la lista entonces su valor es cero
double res = 0.0;
if (e <= mg) {
PNodo ptr = buscar_ord(lista, e);
if (ptr != NULL) {
res = ptr->dato.coef;
}
}
return res;
}
//--------------------------------------------------------------
double Polinomio::evaluar(double x) const
{
double res = 0.0;
PNodo ptr = lista;
while (ptr != NULL) {
res += ptr->dato.coef * potencia(x, ptr->dato.exp);
ptr = ptr->sig ;
}
return res;
}
//--------------------------------------------------------------
void Polinomio::derivar(const Polinomio& a)
{
PNodo lst = NULL;
PNodo ptr = a.lista;
while (ptr != NULL) {
if (ptr->dato.exp > 0) {
Dato d = { ptr->dato.exp - 1, ptr->dato.coef * ptr->dato.exp } ;
insertar_ord(lst, d) ;
}
ptr = ptr->sig ;
}
destruir(lista);
lista = lst;
mg = a.mg - 1;
}
//--------------------------------------------------------------
void Polinomio::sumar(const Polinomio& a, const Polinomio& b)
{
// Para sumar dos polinomios, consideramos el hecho de que
// ambas listas se encuentran ordenadas. Se suman los
// elementos con igual exponente, y se copian los de
// exponentes distintos.
PNodo lst = NULL;
PNodo p1 = a.lista;
PNodo p2 = b.lista;
while ((p1 != NULL)&&(p2 != NULL)) {
if (p1->dato.exp == p2->dato.exp) {
if ( ! iguales(0.0, p1->dato.coef + p2->dato.coef)) {
Dato d = { p1->dato.exp, p1->dato.coef + p2->dato.coef } ;
insertar_ord(lst, d);
}
p1 = p1->sig;
p2 = p2->sig;
} else if (p1->dato.exp < p2->dato.exp) {

96
insertar_ord(lst, p1->dato);
p1 = p1->sig;
} else {
assert(p1->dato.exp > p2->dato.exp);
insertar_ord(lst, p2->dato);
p2 = p2->sig;
}
}
while (p1 != NULL) {
insertar_ord(lst, p1->dato);
p1 = p1->sig;
}
while (p2 != NULL) {
insertar_ord(lst, p2->dato);
p2 = p2->sig;
}
//------------------------------
destruir(lista);
lista = lst;
if (a.mg > b.mg) {
mg = a.mg;
} else {
mg = b.mg;
}
}
//--------------------------------------------------------------
void Polinomio::escribir() const
{
PNodo ptr = lista;
while (ptr != NULL) {
assert(! iguales(ptr->dato.coef, 0.0));
cout << (ptr->dato.coef > 0.0 ? " +" : " ")
<< ptr->dato.coef << " X^" << ptr->dato.exp ;
ptr = ptr->sig ;
}
cout << endl;
}
//--------------------------------------------------------------
void Polinomio::leer()
{
destruir(lista);
mg = 0;
double c;
cout << "Introduzca coeficiente [0 -> fin]: ";
cin >> c;
while ( !cin.fail() && ! iguales(c, 0.0) ) {
unsigned e;
cout << "Introduzca exponente: ";
cin >> e;
if ( ! cin.fail() ) {
poner(e, c);
}
cout << "Introduzca coeficiente [0 -> fin]: ";
cin >> c;
}
}
//--------------------------------------------------------------------------
//-- Auxiliares ------------------------------------------------------------
//--------------------------------------------------------------------------
void Polinomio::insertar_nodo(PNodo& ptr, const Dato& dt) const
{
// Crea el nodo con el valor del dato, el valor del campo
// ’sig’ apuntando al nodo apuntado por ’ptr’, y hace que
// ’ptr’ apunte a este nuevo nodo.
PNodo aux = new Nodo ;
aux->dato = dt ;
aux->sig = ptr ;
ptr = aux ;
}
//--------------------------------------------------------------------------
void Polinomio::eliminar_nodo(PNodo& ptr) const
{
// hace que ’ptr’ apunte al nodo siguiente al que apunta
// actualmente. Finalmente libera el nodo al que ’ptr’

97
// apuntaba inicialmente
assert(ptr != NULL) ;
PNodo aux = ptr ;
ptr = ptr->sig ;
delete aux ;
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que contiene el exponente del elemento
// igual a (exp). Si no se encuentra, entonces devuelve NULL
Polinomio::PNodo Polinomio::buscar_ord(PNodo lst, int exp) const
{
PNodo ptr = lst ;
while ((ptr != NULL)&&(ptr->dato.exp < exp)) {
ptr = ptr->sig ;
}
if ((ptr != NULL)&&(ptr->dato.exp != exp)) {
ptr = NULL;
}
return ptr ;
}
//--------------------------------------------------------------------------
// Inserta un elemento de forma ordenada en (lst), que debe estar
// ordenada (sin repeticion)
void Polinomio::insertar_ord(PNodo& lst, const Dato& dt) const
{
if ((lst == NULL) || (dt.exp < lst->dato.exp)) {
insertar_nodo(lst, dt) ;
} else if (dt.exp == lst->dato.exp) {
lst->dato.coef = dt.coef;
} else {
PNodo ant = lst ;
PNodo act = ant->sig ;
while ((act != NULL) && (act->dato.exp < dt.exp)) {
ant = act ;
act = act->sig ;
}
if ((act != NULL) && (act->dato.exp == dt.exp)) {
act->dato.coef = dt.coef;
} else {
insertar_nodo(ant->sig, dt) ;
}
}
}
//--------------------------------------------------------------------------
// Elimina de (lst) el elemento cuyo exp es igual a (exp), si existe
// La lista (lst) se encuentra ordenada
void Polinomio::eliminar_ord(PNodo& lst, int exp) const
{
if (lst != NULL) {
if (lst->dato.exp == exp) {
eliminar_nodo(lst);
} else if (lst->dato.exp < exp) {
PNodo ant = lst ;
PNodo act = ant->sig ;
while ((act != NULL)&&(act->dato.exp < exp)) {
ant = act;
act = act->sig;
}
if ((act != NULL) && (act->dato.exp == exp)) {
eliminar_nodo(ant->sig);
}
}
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero a una nueva lst resultado de duplicar en
// memoria dinamica la lst recibida como parametro
Polinomio::PNodo Polinomio::duplicar(PNodo lst) const
{
PNodo nueva = NULL;
if (lst != NULL) {
insertar_nodo(nueva, lst->dato) ;
PNodo u = nueva ;
PNodo p = lst->sig ;

98
while (p != NULL) {
insertar_nodo(u->sig, p->dato) ;
u = u->sig ;
p = p->sig ;
}
}
return nueva;
}
//--------------------------------------------------------------------------
// Destruye todos los elementos de la lst, liberando
// todos los nodos de memoria dinamica. (lst) queda vacia
void Polinomio::destruir(PNodo& lst) const
{
while (lst != NULL) {
PNodo ptr = lst ;
lst = lst->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include "polinomios.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Polinomio& p)


{
cout << msj;
p.escribir();
cout << endl;
}

int main()
{
Polinomio p1, p2, p3;
p1.leer();
escribir("P1: ", p1);
cout << "P1(2) = " << p1.evaluar(2.0) << endl;
p2.derivar(p1);
escribir("P2: ", p2);
p3.sumar(p1, p2);
escribir("P3: ", p3);
p1 = p3;
escribir("P1: ", p1);
}

2. Un conjunto de números enteros es una colección de elementos homogéneos (números enteros) sin repetición
y ninguna relación de orden entre ellos (no ordenados y sin repetición).
Un conjunto de elementos se puede representar mediante una lista enlazada, donde cada nodo contiene
un elemento del conjunto. Por ejemplo el conjunto con los elementos {1, 2, 3} puede ser representado por
la siguiente lista enlazada:

lista: ◦−−→ ◦−−−−→ ◦−−−−→ 


1 2 3

Diseñe e implemente el siguiente TAD conjunto de números enteros que defina los siguientes métodos
públicos, ası́ como un programa para comprobar su funcionamiento:

Solución: conjuntos.hpp
#ifndef _conjuntos_hpp_
#define _conjuntos_hpp_
#include <iostream>
namespace umalcc {

99
class Conjunto {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
~Conjunto();
// Destructor
Conjunto(const Conjunto& c);
// Constructor de Copia
Conjunto& operator=(const Conjunto& c);
// Operador de Asignacion
Conjunto();
// Constructor por Defecto: conjunto vacio
void clear();
// Elimina todos los elementos del conjunto actual (queda vacio)
bool es_vacio() const;
// Devuelve true si el conjunto actual esta vacio
void incluir(int e);
// Incluye el elemento (e) en el conjunto actual
void eliminar(int e);
// Elimina el elemento (e) del conjunto actual
bool pertenece(int e) const;
// Devuelve true si el elemento (e) pertenece al conjunto actual
bool es_subconjunto(const Conjunto& a) const;
// Devuelve true si el conjunto actual es subconjunto del conjunto (a)
void union_conj(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la union de los conjuntos (a) y (b)
void interseccion(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la interseccion de los conjuntos (a) y (b)
void diferencia(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la diferencia de los conjuntos (a) y (b)
void diferencia_simetrica(const Conjunto& a, const Conjunto& b);
// Asigna al conjunto actual el resultado de
// la diferencia simetrica de los conjuntos (a) y (b)
void escribir() const;
// Muestra en pantalla el contenido del conjunto actual
void leer();
// Lee de teclado el valor del conjunto actual,
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
struct Nodo;
typedef Nodo* PNodo;
struct Nodo {
PNodo sig;
int dato;
};
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void insertar_nodo(PNodo& ptr, int dt) const ;
void eliminar_nodo(PNodo& ptr) const ;
PNodo buscar_ord(PNodo lst, int dt) const ;
void insertar_ord(PNodo& lst, int dt) const ;
void eliminar_ord(PNodo& lst, int dt) const ;
PNodo duplicar(PNodo lst) const ;
void destruir(PNodo& lst) const ;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
PNodo lista; // componentes del conjunto
//----------------------------------------------------------
};
}
#endif

Solución: conjuntos.cpp
#include "conjuntos.hpp"
#include <iostream>
#include <tr1/array>

100
#include <cassert>
using namespace std;
namespace umalcc {
//--------------------------------------------------------------------------
//-- Metodos Publicos ------------------------------------------------------
//--------------------------------------------------------------------------
Conjunto::~Conjunto() { destruir(lista); }
//--------------------------------------------------------------------------
Conjunto::Conjunto() : lista() {}
//--------------------------------------------------------------------------
Conjunto::Conjunto(const Conjunto& o)
: lista(duplicar(o.lista)) {}
//--------------------------------------------------------------------------
Conjunto& Conjunto::operator=(const Conjunto& o)
{
if (this != &o) {
destruir(lista);
lista = duplicar(o.lista);
}
return *this;
}
//--------------------------------------------------------------------------
void Conjunto::clear()
{
destruir(lista);
}
//--------------------------------------------------------------------------
bool Conjunto::es_vacio() const
{
return (lista == NULL);
}
//--------------------------------------------------------------------------
void Conjunto::incluir(int e)
{
insertar_ord(lista, e); // inserta sin repeticion
}
//--------------------------------------------------------------------------
void Conjunto::eliminar(int e)
{
eliminar_ord(lista, e);
}
//--------------------------------------------------------------------------
bool Conjunto::pertenece(int e) const
{
return (buscar_ord(lista, e) != NULL);
}
//--------------------------------------------------------------------------
bool Conjunto::es_subconjunto(const Conjunto& a) const
{
// es subconjunto si todos los elementos del conjunto actual
// pertenecen tambien al conjunto especificado como parametro
PNodo ptr = lista ;
while ((ptr != NULL)&& a.pertenece(ptr->dato)) {
ptr = ptr->sig ;
}
return (ptr == NULL);
}
//--------------------------------------------------------------------------
void Conjunto::union_conj(const Conjunto& a, const Conjunto& b)
{
PNodo lst = duplicar(a.lista);
PNodo ptr = b.lista ;
while (ptr != NULL) {
insertar_ord(lst, ptr->dato); // inserta sin repeticion
ptr = ptr->sig ;
}
destruir(lista);
lista = lst;
}
//--------------------------------------------------------------------------
void Conjunto::interseccion(const Conjunto& a, const Conjunto& b)
{
PNodo lst = NULL;
PNodo ptr = a.lista ;

101
while (ptr != NULL) {
if (b.pertenece(ptr->dato)) {
insertar_ord(lst, ptr->dato); // inserta sin repeticion
}
ptr = ptr->sig ;
}
destruir(lista);
lista = lst;
}
//--------------------------------------------------------------------------
void Conjunto::diferencia(const Conjunto& a, const Conjunto& b)
{
PNodo lst = NULL;
PNodo ptr = a.lista ;
while (ptr != NULL) {
if ( ! b.pertenece(ptr->dato)) {
insertar_ord(lst, ptr->dato); // inserta sin repeticion
}
ptr = ptr->sig ;
}
destruir(lista);
lista = lst;
}
//--------------------------------------------------------------------------
void Conjunto::diferencia_simetrica(const Conjunto& a, const Conjunto& b)
{
Conjunto a_b, b_a;
a_b.diferencia(a, b);
b_a.diferencia(b, a);
union_conj(a_b, b_a);
}
//--------------------------------------------------------------------------
void Conjunto::escribir() const
{
cout << "{ ";
PNodo ptr = lista;
while (ptr != NULL) {
cout << ptr->dato << " " ;
ptr = ptr->sig ;
}
cout << "}";
}
//--------------------------------------------------------------------------
void Conjunto::leer()
{
clear();
int e;
cin >> e ;
while (e != 0) {
incluir(e);
cin >> e ;
}
}
//--------------------------------------------------------------------------
//-- Auxiliares ------------------------------------------------------------
//--------------------------------------------------------------------------
void Conjunto::insertar_nodo(PNodo& ptr, int dt) const
{
// Crea el nodo con el valor del dato, el valor del campo
// ’sig’ apuntando al nodo apuntado por ’ptr’, y hace que
// ’ptr’ apunte a este nuevo nodo.
PNodo aux = new Nodo ;
aux->dato = dt ;
aux->sig = ptr ;
ptr = aux ;
}
//--------------------------------------------------------------------------
void Conjunto::eliminar_nodo(PNodo& ptr) const
{
// hace que ’ptr’ apunte al nodo siguiente al que apunta
// actualmente. Finalmente libera el nodo al que ’ptr’
// apuntaba inicialmente
assert(ptr != NULL) ;
PNodo aux = ptr ;

102
ptr = ptr->sig ;
delete aux ;
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
Conjunto::PNodo Conjunto::buscar_ord(PNodo lst, int dt) const
{
PNodo ptr = lst ;
while ((ptr != NULL)&&(ptr->dato < dt)) {
ptr = ptr->sig ;
}
if ((ptr != NULL)&&(ptr->dato != dt)) {
ptr = NULL;
}

return ptr ;
}
//--------------------------------------------------------------------------
// Inserta un elemento de forma ordenada en (lst), que debe estar
// ordenada (sin repeticion)
void Conjunto::insertar_ord(PNodo& lst, int dt) const
{
if ((lst == NULL) || (dt < lst->dato)) {
insertar_nodo(lst, dt) ;
} else if (dt > lst->dato) {
PNodo ant = lst ;
PNodo act = ant->sig ;
while ((act != NULL) && (act->dato < dt)) {
ant = act ;
act = act->sig ;
}
if ((act == NULL) || (act->dato != dt)) {
insertar_nodo(ant->sig, dt) ;
}
}
}
//--------------------------------------------------------------------------
// Elimina de (lst) el elemento cuyo dato es igual a (dt), si existe
// La lista (lst) se encuentra ordenada
void Conjunto::eliminar_ord(PNodo& lst, int dt) const
{
if (lst != NULL) {
if (lst->dato == dt) {
eliminar_nodo(lst);
} else {
PNodo ant = lst ;
PNodo act = ant->sig ;
while ((act != NULL)&&(act->dato < dt)) {
ant = act;
act = act->sig;
}
if ((act != NULL) && (act->dato == dt)) {
eliminar_nodo(ant->sig);
}
}
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero a una nueva lst resultado de duplicar en
// memoria dinamica la lst recibida como parametro
Conjunto::PNodo Conjunto::duplicar(PNodo lst) const
{
PNodo nueva = NULL;
if (lst != NULL) {
insertar_nodo(nueva, lst->dato) ;
PNodo u = nueva ;
PNodo p = lst->sig ;
while (p != NULL) {
insertar_nodo(u->sig, p->dato) ;
u = u->sig ;
p = p->sig ;
}
}

103
return nueva;
}
//--------------------------------------------------------------------------
// Destruye todos los elementos de la lst, liberando
// todos los nodos de memoria dinamica. (lst) queda vacia
void Conjunto::destruir(PNodo& lst) const
{
while (lst != NULL) {
PNodo ptr = lst ;
lst = lst->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include "conjuntos.hpp"
using namespace std;
using namespace umalcc;

void escribir(const string& msj, const Conjunto& c)


{
cout << msj;
c.escribir();
cout << endl;
}
int main()
{
Conjunto c1, c2, c3;
cout << "Introduzca elementos del conjunto: [0 -> fin]: ";
c1.leer();
escribir("C1: ", c1);
cout << "Introduzca elementos del conjunto: [0 -> fin]: ";
c2.leer();
escribir("C2: ", c2);
cout << "Es subconjunto: " << boolalpha << c1.es_subconjunto(c2) << endl;
c3.union_conj(c1, c2);
escribir("Union: ", c3);
c3.interseccion(c1, c2);
escribir("Interseccion: ", c3);
c3.diferencia(c1, c2);
escribir("Diferencia: ", c3);
c3.diferencia_simetrica(c1, c2);
escribir("Diferencia Simetrica: ", c3);
}

3. Hay muchas aplicaciones en las que se deben almacenar en la memoria matrices de grandes dimensiones. Si
la mayorı́a de los elementos de la matriz son ceros, ésta, en lugar de almacenarse en un array bidimensional,
se puede representar más eficientemente utilizando listas enlazadas donde los elementos nulos y filas nulas
no serán almacenadas.
Se debe diseñar e implementar el tipo abstracto de datos matriz de números reales, junto con las opera-
ciones necesarias para su gestión y tratamiento:

con las siguientes especificaciones adicionales:


El método obtener permite conocer el valor de un determinado elemento de la matriz especificado
por su fila y columna correspondiente. En caso de que no esté almacenado, entonces su valor es cero.
El método poner permite asignar un determinado valor a un determinado elemento de la matriz
especificado por su fila y columna correspondiente.
Solo se almacenarán los elementos de valor distinto de cero, considerando que si un elemento no
está almacenado, es que su valor es cero. Si una fila no tiene elementos distintos de cero, el nodo
correspondiente a dicha fila no será almacenado. Ası́ mismo, si una columna no tiene elementos
distintos de cero, el nodo correspondiente a dicha columna no será almacenado.

104
Si el valor a almacenar es distinto de cero, si el elemento ya existe, se reemplazará su valor, en otro
caso se añadirá a la estructura.
Si el valor a almacenar es cero, habrá que eliminar dicho elemento de la estructura si es que existe.
Si como consecuencia de ello, la fila o columna quedan sin elementos, habrá que eliminar el nodo
correspondiente de dicha lista.

a) Utilice la siguiente estructura para implementar la matriz dispersa, donde hay una lista enlazada
ordenada para acceder por filas, y a partir de un determinado nodo fila, se puede acceder a la lista
enlazada ordenada donde se encuentran los elementos pertenecientes a dicha fila y a la columna
especificada en el nodo cuyo valor es distinto de cero. Por ejemplo, la siguiente figura representa la
siguiente matriz:
nfil 4
ncol 4
filas

0
0 40 1 60 3 90  
40 60 0 90
 0 0 0 0 
 
 50 0 75 0 
2
0 50 2 75
0 67 83 0

3
1 67 2 83

Solución: matbase.hpp
#ifndef _matbase_hpp_
#define _matbase_hpp_
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6;
//----------------------------------
class MatrizBase {
public:
//----------------------------------------------------------
~MatrizBase();
// Destructor
MatrizBase();
// Constructor por Defecto: matriz vacia
MatrizBase(int nfils, int ncols);
// Constructor especifico: crea matriz de (nfils) x (ncols) con valores 0
MatrizBase(const MatrizBase& m);
// Constructor de copia
MatrizBase& operator=(const MatrizBase& m);
// Operador de Asignacion
void clear(int nfils, int ncols);
// Elimina todos los elementos de la matriz actual, y asigna
// a la matriz actual una matriz de (nfils) x (ncols) con valores 0
int nfils() const;
// Devuelve el numero de filas de la matriz actual
int ncols() const;
// Devuelve el numero de columnas de la matriz actual
void poner(int f, int c, double val);
// PRECOND: (0 <= f && f < nfils() && 0 <= c && c < ncols())
// Asigna el valor (val) al elemento de la fila (f)
// y columna (c) de la matriz actual
double obtener(int f, int c) const;
// PRECOND: (f < nfils() && c < ncols())
// Devuelve el valor del elemento de la fila (f)
// y columna (c) de la matriz actual
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Tipos Privados ----------------------------------------
//----------------------------------------------------------
struct Elem ;
typedef Elem* PElem;
struct Elem {
PElem sig;
int columna;

105
double valor;
};
struct Fila ;
typedef Fila* PFila;
struct Fila {
PFila sig;
int fila;
PElem elems;
};
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void insertar_nodo(PElem& ptr, int c, double v) const ;
void eliminar_nodo(PElem& ptr) const ;
void destruir(PElem& lista) const ;
PElem duplicar(PElem lista) const ;
PElem buscar_ord(PElem lista, int c) const ;
void insertar_ord(PElem& lista, int c, PElem& elem) const ;
void eliminar_ord(PElem& lista, int c) const ;
//------------------------------
void insertar_nodo(PFila& ptr, int f, PElem e) const ;
void eliminar_nodo(PFila& ptr) const ;
void destruir(PFila& lista) const ;
PFila duplicar(PFila lista) const ;
PFila buscar_ord(PFila lista, int f) const ;
void buscar_ord(PFila lista, int f, PFila& ptr, PFila& ant) const ;
void insertar_ord(PFila& lista, int f, PFila& elem) const ;
void eliminar_ord(PFila& lista, int f) const ;
//------------------------------
void eliminar_fila(PFila fil, PFila afil) ;
void eliminar(int f, int c) ;
void insertar(int f, int c, double val) ;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int nfilas;
int ncolumnas;
PFila filas;
//----------------------------------------------------------
};
}
#endif
Solución: matbase.cpp
#include "matbase.hpp"
#include <cstddef>
#include <cassert>
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//-------------------------
// Valor absoluto de un numero
inline double abs(double a)
{
return (a >= 0) ? a : -a;
}
//-------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b)
{
return abs(a-b) <= ERROR_PRECISION;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Elem ------------------------------------------------------------------

106
//--------------------------------------------------------------------------
void MatrizBase::insertar_nodo(PElem& ptr, int c, double v) const
{
PElem aux = new Elem ;
aux->columna = c ;
aux->valor = v ;
aux->sig = ptr ;
ptr = aux ;
}
//--------------------------------------------------------------------------
void MatrizBase::eliminar_nodo(PElem& ptr) const
{
assert(ptr != NULL) ;
PElem aux = ptr ;
ptr = ptr->sig ;
delete aux ;
}
//--------------------------------------------------------------------------
void MatrizBase::destruir(PElem& lista) const
{
while (lista != NULL) {
PElem ptr = lista ;
lista = lista->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
MatrizBase::PElem MatrizBase::duplicar(PElem lista) const
{
PElem nueva = NULL;
if (lista != NULL) {
insertar_nodo(nueva, lista->columna, lista->valor) ;
PElem u = nueva ;
PElem p = lista->sig ;
while (p != NULL) {
insertar_nodo(u->sig, p->columna, p->valor) ;
u = u->sig ;
p = p->sig ;
}
}
return nueva;
}
//--------------------------------------------------------------------------
MatrizBase::PElem MatrizBase::buscar_ord(PElem lista, int c) const
{
PElem ptr = lista ;
while ((ptr != NULL) && (ptr->columna < c)) {
ptr = ptr->sig ;
}
if ((ptr != NULL) && (ptr->columna != c)) {
ptr = NULL;
}
return ptr ;
}
//--------------------------------------------------------------------------
void MatrizBase::insertar_ord(PElem& lista, int c, PElem& elem) const
{
if ((lista == NULL) || (c < lista->columna)){
insertar_nodo(lista, c, 0.0) ;
elem = lista;
} else if (c == lista->columna) {
elem = lista;
} else {
PElem ant = lista ;
PElem act = ant->sig ;
while ((act != NULL) && (act->columna < c)) {
ant = act ;
act = act->sig ;
}
if ((act == NULL) || (act->columna != c)) {
insertar_nodo(ant->sig, c, 0.0) ;
}
elem = ant->sig;
}

107
}
//--------------------------------------------------------------------------
void MatrizBase::eliminar_ord(PElem& lista, int c) const
{
if (lista != NULL) {
if (lista->columna == c) {
eliminar_nodo(lista);
} else {
PElem ant = lista ;
PElem act = ant->sig ;
while ((act != NULL)&&(act->columna < c)) {
ant = act;
act = act->sig;
}
if ((act != NULL)&&(act->columna == c)) {
eliminar_nodo(ant->sig);
}
}
}
}
//--------------------------------------------------------------------------
//-- Fila ------------------------------------------------------------------
//--------------------------------------------------------------------------
void MatrizBase::insertar_nodo(PFila& ptr, int f, PElem e) const
{
PFila aux = new Fila ;
aux->fila = f ;
aux->elems = e ;
aux->sig = ptr ;
ptr = aux ;
}
//--------------------------------------------------------------------------
void MatrizBase::eliminar_nodo(PFila& ptr) const
{
assert(ptr != NULL) ;
PFila aux = ptr ;
ptr = ptr->sig ;
destruir(aux->elems);
delete aux ;
}
//--------------------------------------------------------------------------
void MatrizBase::destruir(PFila& lista) const
{
while (lista != NULL) {
PFila ptr = lista ;
lista = lista->sig ;
destruir(ptr->elems);
delete ptr ;
}
}
//--------------------------------------------------------------------------
MatrizBase::PFila MatrizBase::duplicar(PFila lista) const
{
PFila nueva = NULL;
if (lista != NULL) {
insertar_nodo(nueva, lista->fila, duplicar(lista->elems)) ;
PFila u = nueva ;
PFila p = lista->sig ;
while (p != NULL) {
insertar_nodo(u->sig, p->fila, duplicar(p->elems)) ;
u = u->sig ;
p = p->sig ;
}
}
return nueva;
}
//--------------------------------------------------------------------------
MatrizBase::PFila MatrizBase::buscar_ord(PFila lista, int f) const
{
PFila ptr = lista ;
while ((ptr != NULL) && (ptr->fila < f)) {
ptr = ptr->sig ;
}
if ((ptr != NULL) && (ptr->fila != f)) {

108
ptr = NULL;
}
return ptr ;
}
//--------------------------------------------------------------------------
void MatrizBase::buscar_ord(PFila lista, int f, PFila& ptr, PFila& ant)const
{
ant = NULL;
ptr = lista ;
while ((ptr != NULL) && (ptr->fila < f)) {
ant = ptr;
ptr = ptr->sig ;
}
if ((ptr != NULL) && (ptr->fila != f)) {
ptr = NULL;
}
}
//--------------------------------------------------------------------------
void MatrizBase::insertar_ord(PFila& lista, int f, PFila& elem) const
{
if ((lista == NULL) || (f < lista->fila)){
insertar_nodo(lista, f, NULL) ;
elem = lista;
} else if (f == lista->fila) {
elem = lista;
} else {
PFila ant = lista ;
PFila act = ant->sig ;
while ((act != NULL) && (act->fila < f)) {
ant = act ;
act = act->sig ;
}
if ((act == NULL) || (act->fila != f)) {
insertar_nodo(ant->sig, f, NULL) ;
}
elem = ant->sig;
}
}
//--------------------------------------------------------------------------
void MatrizBase::eliminar_ord(PFila& lista, int f) const
{
if (lista != NULL) {
if (lista->fila == f) {
eliminar_nodo(lista);
} else {
PFila ant = lista ;
PFila act = ant->sig ;
while ((act != NULL)&&(act->fila < f)) {
ant = act;
act = act->sig;
}
if ((act != NULL)&&(act->fila == f)) {
eliminar_nodo(ant->sig);
}
}
}
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
void MatrizBase::eliminar_fila(PFila fil, PFila afil)
{
if (fil == filas) {
eliminar_nodo(filas);
} else {
eliminar_nodo(afil->sig);
}
}
//----------------------------
// elimina el elemento [f][c] si existe.
// si fila vacia, entonces elimina fila
void MatrizBase::eliminar(int f, int c)
{
assert(f < nfils() && c < ncols());

109
PFila fil, afil;
buscar_ord(filas, f, fil, afil);
if (fil != NULL) {
eliminar_ord(fil->elems, c);
if (fil->elems == NULL) {
eliminar_fila(fil, afil);
}
}
}
//----------------------------
// insertar elemento [f][c]. Si no existe fila, la crea
// si ya existe el elemento, sustituye el valor, en
// otro caso, inserta el elemento
void MatrizBase::insertar(int f, int c, double val)
{
assert(f < nfils() && c < ncols());
PFila fil;
insertar_ord(filas, f, fil);
PElem elem;
insertar_ord(fil->elems, c, elem);
elem->valor = val;
}
//--------------------------------------------------------------------------
//-- MatrizBase ------------------------------------------------------------
//--------------------------------------------------------------------------
MatrizBase::~MatrizBase() { destruir(filas); }
//----------------------------
MatrizBase::MatrizBase() : nfilas(0), ncolumnas(0), filas(NULL) {}
//----------------------------
MatrizBase::MatrizBase(int nf, int nc)
: nfilas(nf), ncolumnas(nc), filas(NULL) {}
//-------------------------
MatrizBase::MatrizBase(const MatrizBase& m)
: nfilas(m.nfilas), ncolumnas(m.ncolumnas), filas(duplicar(m.filas)) {}
//-------------------------
MatrizBase& MatrizBase::operator=(const MatrizBase& m)
{
if (this != &m) {
destruir(filas);
nfilas = m.nfilas;
ncolumnas = m.ncolumnas;
filas = duplicar(m.filas);
}
return *this;
}
//-------------------------
void MatrizBase::clear(int nf, int nc)
{
destruir(filas);
nfilas = nf;
ncolumnas = nc;
}
//----------------------------
int MatrizBase::nfils() const
{
return nfilas;
}
//----------------------------
int MatrizBase::ncols() const
{
return ncolumnas;
}
//--------------------------------------------------------------------------
// poner un valor en [f][c], si val es cero, entonces lo elimina
void MatrizBase::poner(int f, int c, double val)
{
assert(f < nfils() && c < ncols());
if (iguales(val, 0.0)) {
eliminar(f,c);
} else {
insertar(f,c, val);
}
}
//----------------------------

110
// busca el elemento, si no existe, entonces valor cero
// en otro caso, devuelve el valor del elemento
double MatrizBase::obtener(int f, int c) const
{
assert(f < nfils() && c < ncols());
double res = 0.0;
PFila fil = buscar_ord(filas, f);
if (fil != NULL) {
PElem elem = buscar_ord(fil->elems, c);
if (elem != NULL) {
res = elem->valor;
}
}
return res;
}
//--------------------------------
}
Solución: matriz.hpp
#ifndef _matriz_hpp_
#define _matriz_hpp_
#include "matbase.hpp"
namespace umalcc {
class Matriz {
public:
//----------------------------------------------------------
// ~Matriz();
// Destructor
Matriz();
// Constructor por Defecto: matriz vacia
Matriz(int nfils, int ncols);
// Constructor especifico: crea matriz de (nfils) x (ncols) con valores 0
// Matriz(const Matriz& m);
// Constructor de copia
// Matriz& operator=(const Matriz& m);
// Operador de Asignacion
void clear(int nfils, int ncols);
// Elimina todos los elementos de la matriz actual, y asigna
// a la matriz actual una matriz de (nfils) x (ncols) con valores 0
int nfils() const;
// Devuelve el numero de filas de la matriz actual
int ncols() const;
// Devuelve el numero de columnas de la matriz actual
void poner(int f, int c, double val);
// PRECOND: (0 <= f && f < nfils() && 0 <= c && c < ncols())
// Asigna el valor (val) al elemento de la fila (f)
// y columna (c) de la matriz actual
double obtener(int f, int c) const;
// PRECOND: (f < nfils() && c < ncols())
// Devuelve el valor del elemento de la fila (f)
// y columna (c) de la matriz actual
void inv(const Matriz& a);
// Asigna a la matriz actual el resultado de
// invertir la matriz (a)
void sumar(const Matriz& a, const Matriz& b);
// Asigna a la matriz actual el resultado de
// sumar las matrices (a) y (b)
void restar(const Matriz& a, const Matriz& b);
// Asigna a la matriz actual el resultado de
// restar las matrices (a) y (b)
void multiplicar(const Matriz& a, const Matriz& b);
// Asigna a la matriz actual el resultado de
// multiplicar las matrices (a) y (b)
void escribir() const;
// Muestra en pantalla el contenido de la matriz actual
void leer();
// Lee de teclado el valor de la matriz actual,
// Lee nfils, ncols y los valores de los elementos
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
MatrizBase base;
//----------------------------------------------------------

111
};
}
#endif
Solución: matriz.cpp
#include "matriz.hpp"
#include <iostream>
#include <iomanip>
#include <cassert>
#include "matbase.hpp"
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//-------------------------
// Valor absoluto de un numero
inline double abs(double a)
{
return (a >= 0) ? a : -a;
}
//-------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b)
{
return abs(a-b) <= ERROR_PRECISION;
}
//-----------------------------
//-- Operaciones basicas con matrices
//-----------------------------
// Permuta las filas f1 y f2 de la matriz m
void perm_fila(Matriz& m, int f1, int f2)
{
if (f1 != f2) {
for (int c = 0; c < m.ncols(); ++c) {
double aux = m.obtener(f1,c);
m.poner(f1,c, m.obtener(f2,c));
m.poner(f2,c, aux);
}
}
}
//--------------------------------
// multiplica una fila de la matriz m por un valor
void mult_fila(Matriz& m, double valor, int fila)
{
for (int c = 0; c < m.ncols(); ++c) {
m.poner(fila, c, valor * m.obtener(fila,c));
}
}
//--------------------------------
// multiplica la fila forig de la matriz m por un valor y
// la suma a la fila fdest
void mult_y_sumar(Matriz& m, double valor, int forig, int fdest)
{
for (int c = 0; c < m.ncols(); ++c) {
m.poner(fdest, c, m.obtener(fdest,c)+(valor * m.obtener(forig,c)));
}
}
//--------------------------------
// Transformaciones en la matriz
//--------------------------------
// busca la fila con el mayor elemento a partir de la fila f1
void buscar_pivote_mayor(const Matriz& m, int f1,
int& fmay, bool& ok)
{
const int col = f1;
fmay = f1;
for (int f = fmay + 1; f < m.nfils(); ++f) {
if (abs(m.obtener(f,col)) > abs(m.obtener(fmay,col))) {
fmay = f;
}
}

112
ok = !iguales(m.obtener(fmay,col), 0.0);
}
//-----------------------------
// pone en la fila f1 la fila con mayor elemento (actua en matriz
// original e inversa)
void fila_pivote_valido(Matriz& mo, Matriz& mi, int f1, bool& ok)
{
int f;
buscar_pivote_mayor(mo, f1, f, ok);
if (ok) {
perm_fila(mo, f1, f);
perm_fila(mi, f1, f);
}
}
//--------------------------------
// transforma en ceros los elementos de la columna indicada por
// fila y en uno el pivote (actua en matriz original e inversa)
void hacer_cero_columna(Matriz& mo, Matriz& mi, int fila)
{
const int col = fila;

for (int f = 0; f < fila; ++f) {


const double aux = mo.obtener(f,col)/mo.obtener(fila,col);
mult_y_sumar(mo, -aux, fila, f);
mult_y_sumar(mi, -aux, fila, f);
}
for (int f = fila+1; f < mo.nfils(); ++f) {
const double aux = mo.obtener(f,col)/mo.obtener(fila,col);
mult_y_sumar(mo, -aux, fila, f);
mult_y_sumar(mi, -aux, fila, f);
}
// hacer 1 el pivote
const double aux = 1.0/mo.obtener(fila,col);
mult_fila(mo, aux, fila);
mult_fila(mi, aux, fila);
}
//--------------------------------
// pone en fila la fila con mayor elemento y transforma en ceros
// los elementos de la columna indicada por fila y en uno el
// pivote (actua en matriz original e inversa)
void transformar_columna(Matriz& mo, Matriz& mi, int fila, bool& ok)
{
fila_pivote_valido(mo, mi, fila, ok);
if (ok) {
hacer_cero_columna(mo, mi, fila);
}
}
//--------------------------------
// Inversa de la Matriz por Gauss-Jordan
//--------------------------------
// transforma cada columna de la matriz original en ceros, salvo
// el pivote que se pone a uno. Aplica la misma trasnformacion a
// la matriz identidad.
void inv_gauss_jordan(Matriz& mo, Matriz& mi, bool& ok)
{
if ((mo.nfils() != mo.ncols())
||(mi.nfils() != mi.ncols())
||(mo.nfils() != mi.nfils())) {
ok = false;
} else {
int f = 0;
ok = true;
while ((f < mo.nfils()) && ok) {
transformar_columna(mo, mi, f, ok);
++f;
}
}
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {

113
//--------------------------------------------------------------------------
// Generados automaticamente por el compilador
//----------------------------------
// ~Matriz(); // Destructor
// Matriz(const Matriz& m); // Constructor de copia
// Matriz& operator=(const Matriz& m); // Operador de Asignacion
//----------------------------------
Matriz::Matriz() : base() {}
//--------------------------------
Matriz::Matriz(int nf, int nc) : base(nf, nc) {}
//----------------------------------
int Matriz::nfils() const
{
return base.nfils();
}
//----------------------------
int Matriz::ncols() const
{
return base.ncols();
}
//--------------------------------
void Matriz::clear(int nf, int nc)
{
base.clear(nf, nc);
}
//----------------------------------
void Matriz::poner(int f, int c, double val)
{
assert(f < nfils() && c < ncols());
base.poner(f, c, val);
}
//----------------------------------
double Matriz::obtener(int f, int c) const
{
assert(f < nfils() && c < ncols());
return base.obtener(f, c);
}
//--------------------------------
void Matriz::inv(const Matriz& a)
{
// Copia de la matriz a para transformar
Matriz copia = a;
// crear la matriz identidad
clear(a.nfils(), a.ncols());
for (int f = 0; f < nfils(); ++f) {
poner(f, f, 1.0);
}
bool ok;
inv_gauss_jordan(copia, *this, ok);
if ( ! ok) {
clear(0, 0);
}
}
//--------------------------------
void Matriz::sumar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));
if ((m1.nfils() != m2.nfils())||(m1.ncols() != m2.ncols())) {
clear(0, 0);
} else {
clear(m1.nfils(), m1.ncols());
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
poner(f,c, m1.obtener(f,c) + m2.obtener(f,c));
}
}
}
}
//--------------------------------
void Matriz::restar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));
if ((m1.nfils() != m2.nfils())||(m1.ncols() != m2.ncols())) {
clear(0, 0);

114
} else {
clear(m1.nfils(), m1.ncols());
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
poner(f,c, m1.obtener(f,c) - m2.obtener(f,c));
}
}
}
}
//--------------------------------
void Matriz::multiplicar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));
if ((m1.ncols() != m2.nfils())) {
clear(0, 0);
} else {
clear(m1.nfils(), m2.ncols());
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
double suma = 0.0;
for (int k = 0; k < m1.ncols(); ++k) {
suma += m1.obtener(f,k) * m2.obtener(k,c);
}
poner(f,c, suma);
}
}
}
}
//--------------------------------
void Matriz::escribir() const
{
cout << std::setprecision(4);
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
cout << std::setw(6) << obtener(f,c) << " ";
}
cout << std::endl;
}
}
//--------------------------------
void Matriz::leer()
{
int nf, nc;
cin >> nf >> nc; // lee numero de filas y columnas
clear(nf, nc);
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
double x;
cin >> x; // lee cada elemento de la matriz
poner(f,c, x);
}
}
}
//--------------------------------
}
Solución: main.cpp
#include <iostream>
#include "matriz.hpp"
using namespace std;
using namespace umalcc;

int main()
{
Matriz m1;
cout << "Introduzca dimensiones y valores de la matriz: " ;
m1.leer();

Matriz m2;
cout << "Introduzca dimensiones y valores de la matriz: " ;
m2.leer();

Matriz m3;
m3.sumar(m1, m2);
cout << "Suma: " << endl;

115
m3.escribir();

Matriz m4;
m4.restar(m3, m2);
cout << "Resta: " << endl;
m4.escribir();

Matriz m5;
m5.multiplicar(m1, m2);
cout << "Multiplicacion: " << endl;
m5.escribir();

Matriz m6;
m6.inv(m2);
cout << "Inversa: " << endl;
m6.escribir();

Matriz m7;
m7.multiplicar(m5, m6);
cout << "MultInv: " << endl;
m7.escribir();
}

b) Utilice la siguiente estructura para implementar la matriz dispersa, donde hay una lista enlazada
ordenada para acceder por filas y otra lista enlazada ordenada para acceder por columnas, de tal
forma que los elementos de la matriz se encuentran doblemente enlazados para favorecer tanto el
acceso por filas como por columnas. La estructura de datos se compone de las dimensiones de la
matriz, la lista ordenada de acceso por filas y la lista ordenada de acceso por columnas, de tal
forma que si una determinada fila o columna no tiene elementos, entonces el nodo correspondiente
no será almacenado. Por ejemplo, la siguiente figura representa la siguiente matriz:
nfil 4
ncol 4
columnas 0 1 2 3
filas

0 0 0 0 1 0 3
40 60 90  
40 60 0 90
 0 0 0 0 
 
 50 0 75 0 
2 2 0 2 2
50 75
0 67 83 0

3 3 1 3 2
67 83

Solución: matbase.hpp
#ifndef _matbase_hpp_
#define _matbase_hpp_
namespace umalcc {
//----------------------------------
const double ERROR_PRECISION = 1e-6;
//----------------------------------
class MatrizBase {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
~MatrizBase();
// Destructor
MatrizBase();
// Constructor por Defecto: matriz vacia
MatrizBase(int nfils, int ncols);
// Constructor especifico: crea matriz de (nfils) x (ncols) con valores 0
MatrizBase(const MatrizBase& m);
// Constructor de copia
MatrizBase& operator=(const MatrizBase& m);
// Operador de Asignacion
void clear(int nfils, int ncols);
// Elimina todos los elementos de la matriz actual, y asigna
// a la matriz actual una matriz de (nfils) x (ncols) con valores 0
int nfils() const;

116
// Devuelve el numero de filas de la matriz actual
int ncols() const;
// Devuelve el numero de columnas de la matriz actual
void poner(int f, int c, double val);
// PRECOND: (0 <= f && f < nfils() && 0 <= c && c < ncols())
// Asigna el valor (val) al elemento de la fila (f)
// y columna (c) de la matriz actual
double obtener(int f, int c) const;
// PRECOND: (f < nfils() && c < ncols())
// Devuelve el valor del elemento de la fila (f)
// y columna (c) de la matriz actual
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Tipos Privados ----------------------------------------
//----------------------------------------------------------
struct Elem;
typedef Elem* PElem;
struct Elem {
int fila;
int columna;
double valor;
PElem sig_fil; // siguiente elemento en la misma fila
PElem sig_col; // siguiente elemento en la misma columna
};
//------------------------------
struct Acceso;
typedef Acceso* PAcceso;
struct Acceso {
int num;
PElem elems;
PAcceso sig;
};
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void destruir_fila(PElem& l) const ;
void destruir_acc(PAcceso& l) const ;
void desconectar_elms(PAcceso& l) const ;
void destruir() ;
void duplicar_fila(PElem l) ;
void duplicar(const MatrizBase& m) ;
void buscar_ord_en_fila(PElem l, int c, PElem& pnodo,
PElem& ant, bool& ok) const ;
void buscar_ord_en_columna(PElem l, int f, PElem& pnodo,
PElem& ant, bool& ok) const ;
void buscar_ord_en_acceso(PAcceso l, int n,
PAcceso& pnodo, PAcceso& ant,
bool& ok) const ;
void eliminar_elm(PElem& pnodo,
PElem& lf, PElem& ant_f,
PElem& lc, PElem& ant_c) const ;
void eliminar_elm(int f, int c,
PElem& lf, PElem& lc) const ;
void check_nodo_acc(PAcceso& lista,
PAcceso& pnodo, PAcceso& ant) const ;
void eliminar(int f, int c) ;
void insertar_elm(int f, int c, double v,
PElem& pnodo,
PElem& lf, PElem& ant_f, PElem& sig_f,
PElem& lc, PElem& ant_c, PElem& sig_c) const ;
void insertar_elm(int f, int c, double val,
PElem& lf, PElem& lc) const ;
void insertar_acceso(int n, PAcceso& pnodo, PAcceso& l,
PAcceso& ant, PAcceso& sig) const ;
void insertar(int f, int c, double val) ;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int nfilas;
int ncolumnas;
PAcceso filas;
PAcceso columnas;
//----------------------------------------------------------

117
};
}
#endif
Solución: matbase.cpp
#include "matbase.hpp"
#include <cstddef>
#include <cassert>
using namespace std;
using namespace umalcc;
// ---------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//---------------------------------
namespace {
//-------------------------
// Valor absoluto de un numero
inline double abs(double a)
{
return (a >= 0) ? a : -a;
}
//-------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b)
{
return abs(a-b) <= ERROR_PRECISION;
}
}
// ---------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//---------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Destruir --------------------------------------------------------------
//--------------------------------------------------------------------------
// destruye una lista de elementos (por fila)
void MatrizBase::destruir_fila(PElem& l) const
{
while (l != NULL) {
PElem aux = l;
l = l->sig_fil;
delete aux;
}
}
//-----------------------------
// Destruye una lista de acceso
void MatrizBase::destruir_acc(PAcceso& l) const
{
while (l != NULL) {
PAcceso aux = l;
l = l->sig;
destruir_fila(aux->elems);
delete aux;
}
}
//-----------------------------
// desconectar acceso a elementos
void MatrizBase::desconectar_elms(PAcceso& l) const
{
PAcceso ptr = l;
while (ptr != NULL) {
ptr->elems = NULL;
ptr = ptr->sig;
}
}
//-----------------------------
// destruye los elementos y los accesos (filas y columnas)
void MatrizBase::destruir()
{
desconectar_elms(columnas);
destruir_acc(columnas);
destruir_acc(filas);
}

118
//--------------------------------------------------------------------------
//-- Duplicar --------------------------------------------------------------
//--------------------------------------------------------------------------
// duplica una fila de elementos
void MatrizBase::duplicar_fila(PElem l)
{
assert(l != NULL);
PElem ptr = l;
while (ptr != NULL) {
assert(! iguales(ptr->valor, 0.0));
poner(ptr->fila, ptr->columna, ptr->valor);
ptr = ptr->sig_fil;
}
}
//-----------------------------
// duplica los elementos de la matriz
void MatrizBase::duplicar(const MatrizBase& m)
{
PAcceso ptr = m.filas;
while (ptr != NULL) {
duplicar_fila(ptr->elems);
ptr = ptr->sig;
}
}
//--------------------------------------------------------------------------
//-- Buscar ----------------------------------------------------------------
//--------------------------------------------------------------------------
// Devuelve:
// ok true si encontrado, false en otro caso
// si encontrado, pnodo apunta al elemento buscado
// si no encontrado, pnodo apunta al elemento siguiente
// ant = NULL si es (o debiera ser) el primero de la lista
// o el anterior
void MatrizBase::buscar_ord_en_fila(PElem l, int c, PElem& pnodo,
PElem& ant, bool& ok) const
{
ant = NULL;
pnodo = l;
while ((pnodo != NULL)&&(c > pnodo->columna)) {
ant = pnodo;
pnodo = pnodo->sig_fil;
}
ok = ((pnodo != NULL)&&(c == pnodo->columna));
}
//-----------------------------
void MatrizBase::buscar_ord_en_columna(PElem l, int f, PElem& pnodo,
PElem& ant, bool& ok) const
{
ant = NULL;
pnodo = l;
while ((pnodo != NULL)&&(f > pnodo->fila)) {
ant = pnodo;
pnodo = pnodo->sig_col;
}
ok = ((pnodo != NULL)&&(f == pnodo->fila));
}
//-----------------------------
// Devuelve:
// ok true si encontrado, false en otro caso
// si encontrado, pnodo apunta al elemento buscado
// si no encontrado, pnodo apunta al elemento siguiente
// ant = NULL si es (o debiera ser) el primero de la lista
// o el anterior
void MatrizBase::buscar_ord_en_acceso(PAcceso l, int n,
PAcceso& pnodo, PAcceso& ant,
bool& ok) const
{
ant = NULL;
pnodo = l;
while ((pnodo != NULL)&&(n > pnodo->num)) {
ant = pnodo;
pnodo = pnodo->sig;
}
ok = ((pnodo != NULL)&&(n == pnodo->num));

119
}
//--------------------------------------------------------------------------
//-- Eliminar --------------------------------------------------------------
//--------------------------------------------------------------------------
// Elimina un Nodo de una doble lista
void MatrizBase::eliminar_elm(PElem& pnodo,
PElem& lf, PElem& ant_f,
PElem& lc, PElem& ant_c) const
{
if (pnodo == lf) {
lf = lf->sig_fil; // primer elemento de la fila
} else {
assert(ant_f != NULL);
ant_f->sig_fil = pnodo->sig_fil;
}
if (pnodo == lc) {
lc = lc->sig_col; // primer elemento de la columna
} else {
assert(ant_c != NULL);
ant_c->sig_col = pnodo->sig_col;
}
delete pnodo;
}
//----------------------------
// elimina un elemento de las listas fila ’lf’ y columna ’lc’
void MatrizBase::eliminar_elm(int f, int c,
PElem& lf, PElem& lc) const
{
bool ok;
PElem pnodo_f, ant_f;
buscar_ord_en_fila(lf, c, pnodo_f, ant_f, ok);
if (ok) {
assert(pnodo_f != NULL
&& pnodo_f->fila == f && pnodo_f->columna == c);
PElem pnodo_c, ant_c;
buscar_ord_en_columna(lc, f, pnodo_c, ant_c, ok);
assert(ok && pnodo_f == pnodo_c);
eliminar_elm(pnodo_f, lf, ant_f, lc, ant_c);
}
}
//----------------------------
// Elimina un nodo de acceso si la lista de elementos esta vacia
void MatrizBase::check_nodo_acc(PAcceso& lista,
PAcceso& pnodo, PAcceso& ant) const
{
assert(pnodo != NULL) ;
if (pnodo->elems == NULL) {
if (pnodo == lista) {
lista = lista->sig; // primer elemento de lista acceso
} else {
assert(ant != NULL);
ant->sig = pnodo->sig;
}
delete pnodo;
}
}
//----------------------------
// elimina el elemento. Si fila o columna vacia, entonces
// elimina el acceso de fila o columna
void MatrizBase::eliminar(int f, int c)
{
assert(f < nfils() && c < ncols());
bool ok;
PAcceso pnodo_af, ant_af;
buscar_ord_en_acceso(filas, f, pnodo_af, ant_af, ok);
if (ok) {
assert(pnodo_af != NULL && pnodo_af->num == f);
PAcceso pnodo_ac, ant_ac;
buscar_ord_en_acceso(columnas, c, pnodo_ac, ant_ac, ok);
if (ok) {
assert(pnodo_ac != NULL && pnodo_ac->num == c);
eliminar_elm(f,c, pnodo_af->elems, pnodo_ac->elems);
check_nodo_acc(filas, pnodo_af, ant_af);
check_nodo_acc(columnas, pnodo_ac, ant_ac);

120
}
}
}
//--------------------------------------------------------------------------
//-- Insertar --------------------------------------------------------------
//--------------------------------------------------------------------------
// Insertar un Nodo de una doble lista
void MatrizBase::insertar_elm(int f, int c, double v,
PElem& pnodo,
PElem& lf, PElem& ant_f, PElem& sig_f,
PElem& lc, PElem& ant_c, PElem& sig_c) const
{
pnodo = new Elem;
pnodo->fila = f ;
pnodo->columna = c ;
pnodo->valor = v ;
pnodo->sig_fil = sig_f ;
pnodo->sig_col = sig_c ;
if (ant_f != NULL) {
ant_f->sig_fil = pnodo;
} else {
lf = pnodo; // primer elemento de lista de fila
}
if (ant_c != NULL) {
ant_c->sig_col = pnodo;
} else {
lc = pnodo; // primer elemento de lista de columna
}
}
//----------------------------
// insertar valor. Si ya existe el elemento, actualizarlo
// en otro caso, insertarlo en listas de acceso de fila y columna
void MatrizBase::insertar_elm(int f, int c, double val,
PElem& lf, PElem& lc) const
{
assert(! iguales(val, 0.0));
bool ok;
PElem pnodo_f, ant_f;
buscar_ord_en_fila(lf, c, pnodo_f, ant_f, ok);
if (ok) {
assert(pnodo_f != NULL
&& pnodo_f->fila == f && pnodo_f->columna == c);
pnodo_f->valor = val;
} else {
PElem pnodo_c, ant_c;
buscar_ord_en_columna(lc, f, pnodo_c, ant_c, ok);
assert(! ok);
PElem pnodo;
insertar_elm(f, c, val, pnodo,
lf, ant_f, pnodo_f, lc, ant_c, pnodo_c);
}
}
//----------------------------
// Insertar un Nodo de una doble lista
void MatrizBase::insertar_acceso(int n, PAcceso& pnodo, PAcceso& l,
PAcceso& ant, PAcceso& sig) const
{
pnodo = new Acceso;
pnodo->num = n ;
pnodo->elems = NULL ;
pnodo->sig = sig ;
if (ant != NULL) {
ant->sig = pnodo;
} else {
l = pnodo; // primer elemento de lista de acceso
}
}
//----------------------------
// inserta un valor. Si es necesario, inserta los nodos de acceso
// por fila y columna
void MatrizBase::insertar(int f, int c, double val)
{
assert(f < nfils() && c < ncols());
bool ok;

121
//-------------------------
PAcceso pnodo_af, ant_af;
buscar_ord_en_acceso(filas, f, pnodo_af, ant_af, ok);
if (!ok) {
PAcceso ptr;
insertar_acceso(f, ptr, filas, ant_af, pnodo_af);
pnodo_af = ptr;
}
//-------------------------
PAcceso pnodo_ac, ant_ac;
buscar_ord_en_acceso(columnas, c, pnodo_ac, ant_ac, ok);
if (!ok) {
PAcceso ptr;
insertar_acceso(c, ptr, columnas, ant_ac, pnodo_ac);
pnodo_ac = ptr;
}
//-------------------------
insertar_elm(f,c, val, pnodo_af->elems, pnodo_ac->elems);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
MatrizBase::~MatrizBase() { destruir(); }
//----------------------------
MatrizBase::MatrizBase()
: nfilas(0), ncolumnas(0), filas(NULL), columnas(NULL) {}
//----------------------------
MatrizBase::MatrizBase(int nf, int nc)
: nfilas(nf), ncolumnas(nc), filas(NULL), columnas(NULL) {}
//-------------------------
MatrizBase::MatrizBase(const MatrizBase& m)
: nfilas(m.nfilas), ncolumnas(m.ncolumnas), filas(NULL), columnas(NULL)
{ duplicar(m); }
//-------------------------
MatrizBase& MatrizBase::operator=(const MatrizBase& m)
{
if (this != &m) {
destruir();
nfilas = m.nfilas;
ncolumnas = m.ncolumnas;
duplicar(m);
}
return *this;
}
//-------------------------
void MatrizBase::clear(int nf, int nc)
{
destruir();
nfilas = nf;
ncolumnas = nc;
}
//----------------------------
int MatrizBase::nfils() const
{
return nfilas;
}
//----------------------------
int MatrizBase::ncols() const
{
return ncolumnas;
}
//--------------------------------------------------------------------------
// pone un elemento [f][c], si valor es cero, lo elimina
void MatrizBase::poner(int f, int c, double val)
{
assert(f < nfils() && c < ncols());
if (iguales(val, 0.0)) {
eliminar(f, c);
} else {
insertar(f, c, val);
}
}
//----------------------------
// obtener valor de elemento. si no existe, entonces valor cero

122
double MatrizBase::obtener(int f, int c) const
{
assert(f < nfils() && c < ncols());
double res = 0.0;
bool ok;
PAcceso pnodo_af, ant_af;
buscar_ord_en_acceso(filas, f, pnodo_af, ant_af, ok);
if (ok) {
assert(pnodo_af != NULL && pnodo_af->num == f);
PElem pnodo_f, ant_f;
buscar_ord_en_fila(pnodo_af->elems, c, pnodo_f, ant_f, ok);
if (ok) {
assert(pnodo_f != NULL
&& pnodo_f->fila == f && pnodo_f->columna == c);
res = pnodo_f->valor;
}
}
return res;
}
//--------------------------------------------------------------------------
}
Solución: matriz.hpp
#ifndef _matriz_hpp_
#define _matriz_hpp_
#include "matbase.hpp"
namespace umalcc {
class Matriz {
public:
//----------------------------------------------------------
// ~Matriz();
// Destructor
Matriz();
// Constructor por Defecto: matriz vacia
Matriz(int nfils, int ncols);
// Constructor especifico: crea matriz de (nfils) x (ncols) con valores 0
// Matriz(const Matriz& m);
// Constructor de copia
// Matriz& operator=(const Matriz& m);
// Operador de Asignacion
void clear(int nfils, int ncols);
// Elimina todos los elementos de la matriz actual, y asigna
// a la matriz actual una matriz de (nfils) x (ncols) con valores 0
int nfils() const;
// Devuelve el numero de filas de la matriz actual
int ncols() const;
// Devuelve el numero de columnas de la matriz actual
void poner(int f, int c, double val);
// PRECOND: (0 <= f && f < nfils() && 0 <= c && c < ncols())
// Asigna el valor (val) al elemento de la fila (f)
// y columna (c) de la matriz actual
double obtener(int f, int c) const;
// PRECOND: (f < nfils() && c < ncols())
// Devuelve el valor del elemento de la fila (f)
// y columna (c) de la matriz actual
void inv(const Matriz& a);
// Asigna a la matriz actual el resultado de
// invertir la matriz (a)
void sumar(const Matriz& a, const Matriz& b);
// Asigna a la matriz actual el resultado de
// sumar las matrices (a) y (b)
void restar(const Matriz& a, const Matriz& b);
// Asigna a la matriz actual el resultado de
// restar las matrices (a) y (b)
void multiplicar(const Matriz& a, const Matriz& b);
// Asigna a la matriz actual el resultado de
// multiplicar las matrices (a) y (b)
void escribir() const;
// Muestra en pantalla el contenido de la matriz actual
void leer();
// Lee de teclado el valor de la matriz actual,
// Lee nfils, ncols y los valores de los elementos
//----------------------------------------------------------
private:
//----------------------------------------------------------

123
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
MatrizBase base;
//----------------------------------------------------------
};
}
#endif
Solución: matriz.cpp
#include "matriz.hpp"
#include <iostream>
#include <iomanip>
#include <cassert>
#include "matbase.hpp"
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//-------------------------
// Valor absoluto de un numero
inline double abs(double a)
{
return (a >= 0) ? a : -a;
}
//-------------------------
// Dos numeros reales son iguales si la distancia que los
// separa es lo suficientemente pequenya
inline bool iguales(double a, double b)
{
return abs(a-b) <= ERROR_PRECISION;
}
//-----------------------------
//-- Operaciones basicas con matrices
//-----------------------------
// Permuta las filas f1 y f2 de la matriz m
void perm_fila(Matriz& m, int f1, int f2)
{
if (f1 != f2) {
for (int c = 0; c < m.ncols(); ++c) {
double aux = m.obtener(f1,c);
m.poner(f1,c, m.obtener(f2,c));
m.poner(f2,c, aux);
}
}
}
//--------------------------------
// multiplica una fila de la matriz m por un valor
void mult_fila(Matriz& m, double valor, int fila)
{
for (int c = 0; c < m.ncols(); ++c) {
m.poner(fila, c, valor * m.obtener(fila,c));
}
}
//--------------------------------
// multiplica la fila forig de la matriz m por un valor y
// la suma a la fila fdest
void mult_y_sumar(Matriz& m, double valor, int forig, int fdest)
{
for (int c = 0; c < m.ncols(); ++c) {
m.poner(fdest, c, m.obtener(fdest,c)+(valor * m.obtener(forig,c)));
}
}
//--------------------------------
// Transformaciones en la matriz
//--------------------------------
// busca la fila con el mayor elemento a partir de la fila f1
void buscar_pivote_mayor(const Matriz& m, int f1,
int& fmay, bool& ok)
{
const int col = f1;
fmay = f1;
for (int f = fmay + 1; f < m.nfils(); ++f) {

124
if (abs(m.obtener(f,col)) > abs(m.obtener(fmay,col))) {
fmay = f;
}
}
ok = !iguales(m.obtener(fmay,col), 0.0);
}
//-----------------------------
// pone en la fila f1 la fila con mayor elemento (actua en matriz
// original e inversa)
void fila_pivote_valido(Matriz& mo, Matriz& mi, int f1, bool& ok)
{
int f;
buscar_pivote_mayor(mo, f1, f, ok);
if (ok) {
perm_fila(mo, f1, f);
perm_fila(mi, f1, f);
}
}
//--------------------------------
// transforma en ceros los elementos de la columna indicada por
// fila y en uno el pivote (actua en matriz original e inversa)
void hacer_cero_columna(Matriz& mo, Matriz& mi, int fila)
{
const int col = fila;

for (int f = 0; f < fila; ++f) {


const double aux = mo.obtener(f,col)/mo.obtener(fila,col);
mult_y_sumar(mo, -aux, fila, f);
mult_y_sumar(mi, -aux, fila, f);
}
for (int f = fila+1; f < mo.nfils(); ++f) {
const double aux = mo.obtener(f,col)/mo.obtener(fila,col);
mult_y_sumar(mo, -aux, fila, f);
mult_y_sumar(mi, -aux, fila, f);
}
// hacer 1 el pivote
const double aux = 1.0/mo.obtener(fila,col);
mult_fila(mo, aux, fila);
mult_fila(mi, aux, fila);
}
//--------------------------------
// pone en fila la fila con mayor elemento y transforma en ceros
// los elementos de la columna indicada por fila y en uno el
// pivote (actua en matriz original e inversa)
void transformar_columna(Matriz& mo, Matriz& mi, int fila, bool& ok)
{
fila_pivote_valido(mo, mi, fila, ok);
if (ok) {
hacer_cero_columna(mo, mi, fila);
}
}
//--------------------------------
// Inversa de la Matriz por Gauss-Jordan
//--------------------------------
// transforma cada columna de la matriz original en ceros, salvo
// el pivote que se pone a uno. Aplica la misma trasnformacion a
// la matriz identidad.
void inv_gauss_jordan(Matriz& mo, Matriz& mi, bool& ok)
{
if ((mo.nfils() != mo.ncols())
||(mi.nfils() != mi.ncols())
||(mo.nfils() != mi.nfils())) {
ok = false;
} else {
int f = 0;
ok = true;
while ((f < mo.nfils()) && ok) {
transformar_columna(mo, mi, f, ok);
++f;
}
}
}
}
//------------------------------------------------------------------------------

125
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
// Generados automaticamente por el compilador
//----------------------------------
// ~Matriz(); // Destructor
// Matriz(const Matriz& m); // Constructor de copia
// Matriz& operator=(const Matriz& m); // Operador de Asignacion
//----------------------------------
Matriz::Matriz() : base() {}
//--------------------------------
Matriz::Matriz(int nf, int nc) : base(nf, nc) {}
//----------------------------------
int Matriz::nfils() const
{
return base.nfils();
}
//----------------------------
int Matriz::ncols() const
{
return base.ncols();
}
//--------------------------------
void Matriz::clear(int nf, int nc)
{
base.clear(nf, nc);
}
//----------------------------------
void Matriz::poner(int f, int c, double val)
{
assert(f < nfils() && c < ncols());
base.poner(f, c, val);
}
//----------------------------------
double Matriz::obtener(int f, int c) const
{
assert(f < nfils() && c < ncols());
return base.obtener(f, c);
}
//--------------------------------
void Matriz::inv(const Matriz& a)
{
// Copia de la matriz a para transformar
Matriz copia = a;
// crear la matriz identidad
clear(a.nfils(), a.ncols());
for (int f = 0; f < nfils(); ++f) {
poner(f, f, 1.0);
}
bool ok;
inv_gauss_jordan(copia, *this, ok);
if ( ! ok) {
clear(0, 0);
}
}
//--------------------------------
void Matriz::sumar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));
if ((m1.nfils() != m2.nfils())||(m1.ncols() != m2.ncols())) {
clear(0, 0);
} else {
clear(m1.nfils(), m1.ncols());
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
poner(f,c, m1.obtener(f,c) + m2.obtener(f,c));
}
}
}
}
//--------------------------------
void Matriz::restar(const Matriz& m1, const Matriz& m2)

126
{
assert((this != &m1)&&(this != &m2));
if ((m1.nfils() != m2.nfils())||(m1.ncols() != m2.ncols())) {
clear(0, 0);
} else {
clear(m1.nfils(), m1.ncols());
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
poner(f,c, m1.obtener(f,c) - m2.obtener(f,c));
}
}
}
}
//--------------------------------
void Matriz::multiplicar(const Matriz& m1, const Matriz& m2)
{
assert((this != &m1)&&(this != &m2));
if ((m1.ncols() != m2.nfils())) {
clear(0, 0);
} else {
clear(m1.nfils(), m2.ncols());
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
double suma = 0.0;
for (int k = 0; k < m1.ncols(); ++k) {
suma += m1.obtener(f,k) * m2.obtener(k,c);
}
poner(f,c, suma);
}
}
}
}
//--------------------------------
void Matriz::escribir() const
{
cout << std::setprecision(4);
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
cout << std::setw(6) << obtener(f,c) << " ";
}
cout << std::endl;
}
}
//--------------------------------
void Matriz::leer()
{
int nf, nc;
cin >> nf >> nc; // lee numero de filas y columnas
clear(nf, nc);
for (int f = 0; f < nfils(); ++f) {
for (int c = 0; c < ncols(); ++c) {
double x;
cin >> x; // lee cada elemento de la matriz
poner(f,c, x);
}
}
}
//--------------------------------
}
Solución: main.cpp
#include <iostream>
#include "matriz.hpp"
using namespace std;
using namespace umalcc;

int main()
{
Matriz m1;
cout << "Introduzca dimensiones y valores de la matriz: " ;
m1.leer();

Matriz m2;
cout << "Introduzca dimensiones y valores de la matriz: " ;
m2.leer();

127
Matriz m3;
m3.sumar(m1, m2);
cout << "Suma: " << endl;
m3.escribir();

Matriz m4;
m4.restar(m3, m2);
cout << "Resta: " << endl;
m4.escribir();

Matriz m5;
m5.multiplicar(m1, m2);
cout << "Multiplicacion: " << endl;
m5.escribir();

Matriz m6;
m6.inv(m2);
cout << "Inversa: " << endl;
m6.escribir();

Matriz m7;
m7.multiplicar(m5, m6);
cout << "MultInv: " << endl;
m7.escribir();
}

4. Se dispone de un procesador con varias etapas, y de unas determinadas tareas que se ejecutarán pasando
sucesivamente por todas las etapas del procesador, desde la primera hasta la última. Como el procesador
dispone de varias etapas, es posible que esté procesando simultáneamente diferentes tareas. Por tanto,
una tarea sólo podrá pasar a la siguiente etapa si ésta se encuentra libre. La siguiente figura muestra un
procesador con cuatro etapas y dos tareas, la tarea T1 va ejecutándose por la etapa e2 y la tarea T2 va
ejecutándose por la etapa e4.

procesador t1 t2
e1 e2 e3 e4

Se debe diseñar la estructura de datos necesaria para representar la información del estado del procesador
en cada momento. Para ello, se sigue el esquema mostrado en la siguiente figura. Como se puede observar
cada etapa se representa con dos elementos, una lista de nodos intermedios con tantos nodos como réplicas
(véase más adelante) haya de la etapa (en el ejemplo sólo se muestra una réplica por cada etapa) y los
correspondientes nodos de etapa. Cada nodo de etapa contendrá su identificador, el número de la tarea
que está ejecutando (un número cero indica que está libre, es decir, no está ejecutando ninguna tarea),
y un puntero al primer nodo de la lista de nodos intermedios de la siguiente etapa. También se puede
observar que la tarea T1 se está ejecutando en la etapa 2 y la tarea T2 se está ejecutando en la etapa 4.

procesador 1 2 3 4
0 1 0 2

Es posible que en un determinado momento interese ampliar la potencia de una determinada etapa. Para
ello, se replicará dicha etapa haciendo tantos duplicados de la misma como se desee. Ası́ mismo, es posible
que interese reducir la potencia de una determinada etapa, para ello se eliminarán réplicas de la misma.
Defina el TAD Procesador que implemente la estructura de datos especificada anteriormente, y que propor-
cione los siguientes métodos públicos, ası́ mismo, también se deberá implementar un programa principal
que permita su utilización:
a) El Constructor recibe el número de etapas que tiene un determinado procesador, y crea la estructura
base de étapas sin replicación y todas en estado inicial libre, como se muestra en la siguiente figura
para un procesador de 4 etapas:

128
procesador 1 2 3 4
0 0 0 0

b) El Destructor libera todos los recursos asociados al objeto que está siendo destruido.
c) El método Mostrar muestra en pantalla la estructura interna de un procesador. Por ejemplo, para
la figura anterior:
Etapa: 1
Replica: libre
Replica: libre
Etapa: 2
Replica: tarea 1
Replica: tarea 2
Etapa: 3
Replica: libre
Etapa: 4
Replica: libre
d ) Un método Replicar, que se usará para ampliar la potencia de una determinada etapa del procesador
actual. Recibe como parámetros el identificador de la etapa a replicar y el número de réplicas que
deseamos añadir. Las réplicas añadidas tendrán el estado inicial libre (no tienen asignadas la ejecución
de ninguna tarea).
Las réplicas se añadirán a la estructura añadiendo (en cualquier posición SALVO AL PRINCIPIO)
nodos a la lista de nodos intermedios correspondiente a la etapa y nodos de etapa apuntados desde los
nodos intermedios añadidos. Como se puede observar, todas las réplicas añadidas deben apuntar al
primer nodo intermedio de la etapa siguiente (salvo en la última etapa). La siguiente figura muestra
el resultado de añadir dos réplicas de la etapa 2 a la figura anterior:
procesador 1 2 3 4
0 1 0 2

2
0

2
0

La siguiente figura muestra el resultado de añadir una réplica de la etapa 1 a la figura anterior:
procesador 1 2 3 4
0 1 0 2

1 2
0 0

2
0

e) Un método para Compactar que se usará para disminuir el número de réplicas de una etapa del
procesador actual. Para ello, recibe como parámetro el identificador de la etapa a compactar y
elimina todas las réplicas de dicha etapa cuyo estado sea libre (no esté ejecutando ninguna tarea).
Al compactar etapas habrá que tener en cuenta que:
De cada etapa deberá quedar al menos una réplica.
Las réplicas que estén ejecutando alguna tarea no pueden ser eliminadas.
No se podrá liberar la memoria ocupada por el primer nodo de la lista de nodos intermedios de
una etapa (pues sirve de enlace entre una etapa y la siguiente). En caso de que la compactación
requiera eliminar la primera réplica de una etapa (porque esté libre), el puntero al nodo de
etapa del primer nodo de la lista de nodos intermedios pasará a apuntar a un nodo de etapa que
permanezca tras la compactación. Por ejemplo, para la siguiente figura:

129
procesador 1 2 3 4
0 0 0 0

1 2
0 1

2
2

la siguiente figura es el resultado de compactar la etapa 2 de la figura anterior:


procesador 1 3 4
0 0 0

1 2
0 1

2
2

f ) El método AvanzarEvaluacion intenta avanzar una tarea de una determinada etapa a la siguiente.
Para ello, recibe como parámetro el número de tarea (distinto de cero) que pretende avanzar a la
siguiente etapa.
Si la tarea es nueva, buscará una réplica libre de la primera etapa del procesador. Si no hay
réplicas libres en esta primera etapa, entonces lanzará una excepción.
Si la tarea se está ejecutando en la última etapa del procesador, entonces la tarea habrá terminado
su ejecución, y su número desaparecerá de la estructura.
En otro caso, intenta avanzar la tarea desde la réplica de la etapa en que se encuentra ejecutándose
hasta alguna réplica libre de la siguiente etapa. Si no hay ninguna réplica libre de la siguiente
etapa, entonces lanzará una excepción.
g) Ası́ mismo, también deberá ser posible realizar la copia y asignación (duplicación) de la estructura
de datos que representa a un determinado procesador.
Solución: procesador.hpp
#ifndef _procesador_hpp_
#define _procesador_hpp_
namespace umalcc {
class Procesador {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
~Procesador();
Procesador();
Procesador(unsigned n_etapas);
Procesador(const Procesador& o);
Procesador& operator=(const Procesador& o);
void mostrar() const;
void replicar(unsigned id_etapa, unsigned n, bool& ok);
void compactar(unsigned id_etapa, bool& ok);
void avanzar_evaluacion(unsigned tarea, bool& ok);
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
static const unsigned REPLICA_LIBRE = 0;
//-------------------------
struct Nodo;
typedef Nodo* PNodo;
struct Etapa;
typedef Etapa* PEtapa;
struct Nodo {
PEtapa et;
PNodo sig;
//---------------------
Nodo(PEtapa e, PNodo s) ;
};

130
struct Etapa {
unsigned id_etapa;
unsigned tarea;
PNodo sig;
//---------------------
Etapa(unsigned i, unsigned t, PNodo s) ;
};
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void destruir();
void destruir(PNodo& lista);
void duplicar(const Procesador& o);
void duplicar_resto(const Procesador& o);
void duplicar_base(const Procesador& o);
void duplicar(PNodo& lista, PNodo origen, PNodo sig);
PNodo buscar(unsigned id_etapa) const;
void mostrar(PNodo lista) const;
PNodo buscar_tarea(PNodo lista, unsigned tarea) const;
PNodo buscar_tarea(unsigned tarea) const;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
PNodo proc;
//----------------------------------------------------------
};
}
#endif

Solución: procesador.cpp
#include "procesador.hpp"
#include <iostream>
#include <cassert>
using namespace std;
using namespace umalcc;
namespace umalcc {
//-----------------------------
Procesador::Nodo::Nodo(PEtapa e, PNodo s) : et(e), sig(s) {}
//-----------------------------
Procesador::Etapa::Etapa(unsigned i, unsigned t, PNodo s)
: id_etapa(i), tarea(t), sig(s) {}
//-----------------------------
Procesador::~Procesador()
{
destruir();
}
//-----------------------------
Procesador::Procesador() : proc(NULL) {}
//-----------------------------
// crea un procesador con ’n’ etapas simples (sin replicas)
Procesador::Procesador(unsigned n_etapas)
: proc(NULL)
{
if (n_etapas > 0) {
proc = new Nodo(new Etapa(1, REPLICA_LIBRE, NULL), NULL);
PNodo u = proc;
for (unsigned i = 2; i <= n_etapas; ++i) {
u->et->sig = new Nodo(new Etapa(i, REPLICA_LIBRE, NULL), NULL);
u = u->et->sig;
}
}
}
//-----------------------------
// copia (duplica) un procesador
Procesador::Procesador(const Procesador& o)
: proc(NULL)
{
duplicar(o);
}
//-----------------------------
// asigna (duplica) un procesador
Procesador& Procesador::operator=(const Procesador& o)
{
if (this != &o) {

131
duplicar(o);
}
return *this;
}
//-----------------------------
// crea n replicas de una etapa
void Procesador::replicar(unsigned id_etapa, unsigned n, bool& ok)
{
PNodo ptr = buscar(id_etapa);
if (ptr == NULL) {
ok = false;
} else {
ok = true;
// Insertar n etapas con identificador ’id_etapa’ al inicio de
// ptr->sig, donde el siguiente de cada etapa es igual al
// siguiente de la etapa inicial
for (unsigned i = 0; i < n; ++i) {
ptr->sig = new Nodo(new Etapa(id_etapa, REPLICA_LIBRE,
ptr->et->sig),
ptr->sig);
}
}
}
//-----------------------------
// compacta las replicas libres de una etapa
void Procesador::compactar(unsigned id_etapa, bool& ok)
{
PNodo ptr = buscar(id_etapa);
if (ptr == NULL) {
ok = false;
} else {
ok = true;
// eliminar a partir del segundo
PNodo ant = ptr;
PNodo act = ptr->sig;
while (act != NULL) {
if (act->et->tarea == REPLICA_LIBRE) {
ant->sig = act->sig;
delete act->et;
delete act;
} else {
ant = act;
}
act = ant->sig;
}
// comprobar el primero
if ((ptr->sig != NULL)&&(ptr->et->tarea == REPLICA_LIBRE)) {
delete ptr->et;
ptr->et = ptr->sig->et;
act = ptr->sig;
ptr->sig = act->sig;
delete act;
}
}
}
//-----------------------------
// destruye una lista de nodos intermedios
void Procesador::destruir(PNodo& lista)
{
while (lista != NULL) {
PNodo aux = lista;
lista = lista->sig;
delete aux->et;
delete aux;
}
}
//-----------------------------
// destruye un procesador
void Procesador::destruir()
{
while (proc != NULL) {
assert(proc->et != NULL);
PNodo aux = proc;
proc = proc->et->sig;

132
destruir(aux);
}
}
//-----------------------------
// duplica una lista de nodos intermedios y sus etapas asociadas
// (a partir del segundo nodo)
void Procesador::duplicar(PNodo& lista, PNodo origen, PNodo sig)
{
if (origen != NULL) {
PNodo ou = origen;
lista = new Nodo(new Etapa(ou->et->id_etapa, ou->et->tarea, sig), NULL);
PNodo u = lista;
ou = ou->sig;
while (ou != NULL) {
u->sig = new Nodo(new Etapa(ou->et->id_etapa, ou->et->tarea, sig), NULL);
u = u->sig;
ou = ou->sig;
}
}
}
//-----------------------------
// duplica las listas de nodos intermedios y sus etapas asociadas
// (a partir del segundo nodo) para todas las etapas del procesador
void Procesador::duplicar_resto(const Procesador& o)
{
PNodo ptr_o = o.proc;
PNodo ptr = proc;
while (ptr != NULL) {
duplicar(ptr->sig, ptr_o->sig, ptr->et->sig);
ptr = ptr->et->sig;
ptr_o = ptr_o->et->sig;
}
}
//-----------------------------
// duplica las etapas base del procesador
void Procesador::duplicar_base(const Procesador& o)
{
if (o.proc != NULL) {
PNodo ou = o.proc;
proc = new Nodo(new Etapa(ou->et->id_etapa, ou->et->tarea, NULL), NULL);
PNodo u = proc;
ou = ou->et->sig;
while (ou != NULL) {
u->et->sig = new Nodo(new Etapa(ou->et->id_etapa, ou->et->tarea, NULL), NULL);
u = u->et->sig;
ou = ou->et->sig;
}
}
}
//-----------------------------
// duplica un procesador
void Procesador::duplicar(const Procesador& o)
{
destruir();
if (o.proc != NULL) {
duplicar_base(o);
duplicar_resto(o);
}
}
//-----------------------------
// busca una etapa en el procesador
Procesador::PNodo Procesador::buscar(unsigned id_etapa) const
{
PNodo ptr = proc;
while ((ptr != NULL)&&(id_etapa != ptr->et->id_etapa)) {
ptr = ptr->et->sig;
}
return ptr;
}
//-----------------------------
// muestra una lista de replicas de una etapa
void Procesador::mostrar(PNodo lista) const
{
if (lista != NULL) {

133
cout << "Etapa: " << lista->et->id_etapa << endl;
PNodo ptr = lista;
while (ptr != NULL) {
cout << " Replica: ";
if (ptr->et->tarea == REPLICA_LIBRE) {
cout << "libre";
} else {
cout << "tarea " << ptr->et->tarea;
}
cout << endl;
ptr = ptr->sig;
}
}
}
//-----------------------------
// muestra la lista de etapas y sus replicas
void Procesador::mostrar() const
{
cout << "--------------" << endl;
PNodo ptr = proc;
while (ptr != NULL) {
mostrar(ptr);
cout << "--------------" << endl;
ptr = ptr->et->sig;
}
}
//-----------------------------
// busca una tarea en la lista de replicas de una etapa
Procesador::PNodo Procesador::buscar_tarea(PNodo lista, unsigned t) const
{
PNodo ptr = lista;
while ((ptr != NULL)&&(t != ptr->et->tarea)) {
ptr = ptr->sig;
}
return ptr;
}
//-----------------------------
// busca una tarea en el procesador
Procesador::PNodo Procesador::buscar_tarea(unsigned t) const
{
PNodo ptr = proc;
PNodo pnodo = buscar_tarea(ptr, t);
while ((ptr != NULL)&&(pnodo == NULL)) {
ptr = ptr->et->sig;
pnodo = buscar_tarea(ptr, t);
}
return pnodo;
}
//-----------------------------
// avanza la evaluacion de una determinada tarea
void Procesador::avanzar_evaluacion(unsigned t, bool& ok)
{
assert(t != REPLICA_LIBRE);
ok = true;
PNodo orig = buscar_tarea(t);
if (orig == NULL) {
PNodo dest = buscar_tarea(proc, REPLICA_LIBRE);
if (dest == NULL) {
ok = false; // No hay replica libre
} else {
dest->et->tarea = t;
}
} else if (orig->et->sig == NULL) {
orig->et->tarea = REPLICA_LIBRE;
} else {
PNodo dest = buscar_tarea(orig->et->sig, REPLICA_LIBRE);
if (dest == NULL) {
ok = false; // No hay replica libre
}
dest->et->tarea = t;
orig->et->tarea = REPLICA_LIBRE;
}
}
//-----------------------------

134
//-----------------------------
}

5. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.


Solución: main.cpp
#include <iostream>
#include <string>
#include "procesador.hpp"
using namespace std;
using namespace umalcc;

//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Crear Procesador" << endl;
cout << "B. Replicar Etapa" << endl;
cout << "C. Compactar Etapa" << endl;
cout << "D. Avanzar Evaluacion" << endl;
cout << "E. Copiar y Asignar Procesador" << endl;
cout << "F. Mostrar" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’F’))));
cout << endl;
return op;
}
//------------------------------------------------------------------
void leer_num_etapa(unsigned& num_et)
{
cout << "Introduzca numero de etapas: ";
cin >> num_et;
}
void leer_etapa(unsigned& etapa)
{
cout << "Introduzca etapa: ";
cin >> etapa;
}
void leer_num_repl(unsigned& n)
{
cout << "Introduzca numero de replicas: ";
cin >> n;
}
void leer_tarea(unsigned& tarea)
{
cout << "Introduzca tarea: ";
cin >> tarea;
}
//------------------------------------------------------------------
void crear_procesador(Procesador& p)
{
unsigned ne;
leer_num_etapa(ne);
p = Procesador(ne);
}
void replicar_etapa(Procesador& p)
{
unsigned e, n;
bool ok;
leer_etapa(e);
leer_num_repl(n);
p.replicar(e, n, ok);
if (! ok) {
cout << "Error al replicar la etapa: " << e << endl;
}
}
void compactar_etapa(Procesador& p)
{

135
unsigned e;
bool ok;
leer_etapa(e);
p.compactar(e, ok);
if (! ok) {
cout << "Error al compactar la etapa: " << e << endl;
}
}
void avanzar_evaluacion(Procesador& p)
{
unsigned t;
bool ok;
leer_tarea(t);
p.avanzar_evaluacion(t, ok);
if (! ok) {
cout << "Error al avanzar la evaluacion de la tarea: " << t << endl;
}
}
void copiar_asig(Procesador& p)
{
Procesador copia = p;
p = copia;
}
//------------------------------------------------------------------
int main()
{
Procesador p;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
crear_procesador(p);
break;
case ’B’:
replicar_etapa(p);
break;
case ’C’:
compactar_etapa(p);
break;
case ’D’:
avanzar_evaluacion(p);
break;
case ’E’:
copiar_asig(p);
break;
case ’F’:
p.mostrar();
break;
}
} while (op != ’X’);
}

Tema 3.3: Genericidad en la Gestión de Memoria Dinámica


1. Un TAD lista genérica es una secuencia de elementos homogéneos, donde cada elemento ocupa una de-
terminada posición dentro de la secuencia, de tal forma que se puede acceder a cada elemento por la
posición donde se encuentra. Ası́ mismo, también es posible añadir y eliminar elementos de la secuencia
en la posición indicada, manteniendo el mismo orden posicional de los elementos en la secuencia. Diseñe e
implemente el TAD lista genérica, utilizando para ello listas enlazadas en memoria dinámica, que defina
los siguientes métodos públicos:

lista: ◦−−→ ◦−−−−→ ◦−−−−→ 


0 1 2

Solución: lista.hpp
#ifndef _lista_hpp_
#define _lista_hpp_

136
#include <cstddef>
#include <cassert>
namespace umalcc {
template <typename Tipo>
class Lista {
public:
//-- Métodos Públicos ----------

// Destructor
~Lista() { destruir(lista) ; }

// Constructor por Defecto


Lista() : sz(0), lista(NULL) { }

// Constructor de Copia
Lista(const Lista& o)
: sz(o.sz), lista(duplicar(o.lista)) { }

// Operador de Asignación
Lista& operator = (const Lista& o)
{
if (this != &o) {
destruir(lista) ;
sz = o.sz ;
lista = duplicar(o.lista) ;
}
return *this ;
}

// Elimina todos los elementos de la lista actual (queda vacia)


void clear()
{
destruir(lista) ;
sz = 0 ;
}

// Devuelve el numero de elementos almacenados


int size() const
{
return sz ;
}

// Devuelve true si el numero de elementos almacenados


// alcanza la capacidad maxima de almacenamiento
bool llena() const
{
return false;
}

// PRECOND: ( ! llena() && 0 <= pos && pos <= size())


// Inserta (dato) en la lista actual en la posicion (pos)
void insertar(int pos, const Tipo& d)
{
assert(! llena()
&& 0 <= pos && pos <= size()) ;
insertar_pos(lista, pos, d) ;
++sz ;
}

// PRECOND: (0 <= pos && pos < size())


// Elimina de la lista actual el elemento que ocupa la posicion (pos)
void eliminar(int pos)
{
assert(0 <= pos && pos < size()) ;
eliminar_pos(lista, pos) ;
--sz ;
}

// PRECOND: (0 <= pos && pos < size())


// Devuelve el elemento de la lista actual que ocupa la posicion (pos)
Tipo acceder(int pos) const
{
assert(0 <= pos && pos < size()) ;
PNodo ptr = situar(lista, pos) ;

137
assert(ptr != NULL) ;
return ptr->dato ;
}

// PRECOND: (0 <= pos && pos < size())


// Asigna (dato) al elemento de la lista actual que ocupa la posicion (pos)
void modificar(int pos, const Tipo& d)
{
assert(0 <= pos && pos < size()) ;
PNodo ptr = situar(lista, pos) ;
assert(ptr != NULL) ;
ptr->dato = d ;
}

private:
//-- Tipos Privados ------

struct Nodo ;
typedef Nodo* PNodo ;
struct Nodo {
PNodo sig ;
Tipo dato ;
} ;

//-- Atributos privados --

int sz ;
PNodo lista ;

//-- Métodos Privados ----------

void destruir(PNodo& lst) const


{
while (lst != NULL) {
PNodo ptr = lst ;
lst = lst->sig ;
delete ptr ;
}
}

PNodo situar(PNodo lst, int pos) const


{
PNodo ptr = lst;
while ((ptr != NULL)&&(pos > 0)) {
ptr = ptr->sig;
--pos;
}
return ptr;
}

void insertar_pos(PNodo& lst, int pos,


const Tipo& dt) const
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
if (pos < 1) {
ptr->sig = lst ;
lst = ptr ;
} else {
PNodo ant = situar(lst, pos - 1);
if (ant != NULL) {
ptr->sig = ant->sig ;
ant->sig = ptr ;
}
}
}

void eliminar_pos(PNodo& lst, int pos) const


{
if (lst != NULL) {
if (pos < 1) {
PNodo ptr = lst ;
lst = lst->sig ;
delete ptr ;

138
} else {
PNodo ant = situar(lst, pos - 1) ;
if ((ant != NULL)&&(ant->sig != NULL)) {
PNodo act = ant->sig ;
ant->sig = act->sig ;
delete act ;
}
}
}
}

PNodo duplicar(PNodo lst) const


{
PNodo nueva = NULL;
if (lst != NULL) {
nueva = new Nodo ;
nueva->dato = lst->dato ;
PNodo u = nueva ;
PNodo p = lst->sig ;
while (p != NULL) {
u->sig = new Nodo ;
u->sig->dato = p->dato ;
u = u->sig ;
p = p->sig ;
}
u->sig = NULL ;
}
return nueva;
}

} ; // class
} // namespace
#endif

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "lista.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
// Instanciación de la Lista genérica para almacenar números enteros
typedef Lista<int> ListaInt;
//------------------------------------------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);
}
//---------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca un dato: " ;
cin >> dato;
}
//---------------------------------
void leer(ListaInt& lista)
{
int dato ;
lista.clear() ;
cout << "Introduzca datos (0 -> FIN): " << endl ;
cin >> dato ;
while ((dato != 0)&&( ! lista.llena())) {
lista.insertar(lista.size(), dato) ;
cin >> dato ;
}
}
//---------------------------------
void escribir(const ListaInt& lista)
{

139
cout << "Lista: " ;
for (int i = 0 ; i < lista.size() ; ++i) {
cout << lista.acceder(i) << " " ;
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const ListaInt& lista)
{
cout << "Constructor de Copia" << endl ;
ListaInt lst(lista) ;
escribir(lst) ;
cout << "Operador de Asignacion" << endl ;
lst = lista ;
escribir(lst) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Lista" << endl ;
cout << "B. Borrar Lista" << endl ;
cout << "C. Insertar Posicion" << endl ;
cout << "D. Eliminar Posicion" << endl ;
cout << "E. Acceder Posicion" << endl ;
cout << "F. Modificar Posicion" << endl ;
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
ListaInt lista ;
int dato ;
int pos ;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(lista) ;
escribir(lista) ;
break ;
case ’B’:
lista.clear() ;
escribir(lista) ;
break ;
case ’C’:
if (lista.llena()) {
cout << "Error: Lista llena" << endl ;
} else {
leer_pos(pos, lista.size()+1) ;
leer_dato(dato) ;
lista.insertar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’D’:
if (lista.size() == 0) {
cout << "Error: Lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
lista.eliminar(pos) ;
escribir(lista) ;
}
break ;

140
case ’E’:
if (lista.size() == 0) {
cout << "Error: Lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
cout << "Lista[" << pos << "]: " << lista.acceder(pos) << endl ;
escribir(lista) ;
}
break ;
case ’F’:
if (lista.size() == 0) {
cout << "Error: Lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
leer_dato(dato) ;
lista.modificar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’G’:
prueba_asg(lista) ;
break ;
}
} while (op != ’X’) ;
}

2. Un mapa genérico es un contenedor asociativo que almacena pares de elementos (clave, valor ), de tal
forma que a partir de la clave (de tipo string) se puede acceder al valor asociado almacenado (de tipo
genérico).
El mapa se puede implementar muy eficientemente utilizando técnicas hash, el cual para una determinada
clave de tipo string, una función hash genera un determinado número en el rango del tipo int. Dicho
número se transforma mediante una operación modular (%) al rango de ı́ndices de un determinado array
de listas enlazadas donde se encuentran almacenados todos los elementos (clave, valor ) cuyo hash de la
clave proporcionó el mismo ı́ndice. Ası́, para acceder a un determinado elemento, se calcula la función
hash de la clave, de este número se obtiene la posición en el array de listas, y se busca el elemento en
dicha lista enlazada.

nelms 6
elms
0 3 pepe 30 maria 22 ana 35
1 0
2 1 juan 27
3 0
4 2 lola 23 jaime 37

Por ejemplo, una función hash para la clave de tipo string podrı́a ser la siguiente:
unsigned hash(const std::string& clave, unsigned mod)
{
unsigned valor = 0;
for (unsigned i = 0; i < clave.size(); ++i) {
valor = valor * 7 + unsigned(clave[i]);
}
return valor % mod;
}

Diseñe e implemente el TAD mapa genérico utilizando técnicas hash (utilı́cese para ello el TAD lista
genérica del ejercicio anterior 1) que proporcione los siguientes métodos públicos:

Solución: mapa.hpp
#ifndef _mapa_hpp_
#define _mapa_hpp_
#include <iostream>
#include <string>
#include <cassert>
#include <tr1/array>

141
#include "../lista/lista.hpp"
//-------------------------------------------------------------------------
namespace umalcc {
template <typename TipoBase, unsigned SIZE>
class Mapa {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
// Definidos automaticamente por el compilador
//-------------------------
//~Mapa();
//Mapa(const Mapa& c);
//Mapa& operator=(const Mapa& c);
//----------------------------
Mapa() : sz(0), dat() {}
//----------------------------
// Elimina todos los elementos del mapa actual (queda vacio)
void clear()
{
sz = 0;
for (unsigned i = 0; i < dat.size(); ++i) {
dat[i].clear();
}
}
//------------------------------
// Devuelve true si el numero de elementos almacenados
// alcanza la capacidad maxima de almacenamiento
bool lleno() const
{
return false;
}
//----------------------------
// Devuelve el numero de elementos almacenados
int size() const
{
return sz;
}
//----------------------------
// PRECOND: ( ! lleno() )
// Inserta el valor (dato) en el mapa actual asociado a la (clave) de acceso
// En caso de que ya exista la clave, entonces modifica su valor
void insertar(const std::string& clave, const TipoBase& dato)
{
assert( ! lleno() ) ;
unsigned lst = hash(clave, dat.size());
int p = buscar(clave, dat[lst]);
if (p < dat[lst].size()) {
Elem e = dat[lst].acceder(p) ;
e.v = dato;
dat[lst].modificar(p, e);
} else {
Elem e = { clave, dato } ;
dat[lst].insertar(0, e);
++sz;
}
}
//----------------------------
// Elimina del mapa actual el elemento asociado a la (clave) de acceso.
// y ok tomara el valor true. En caso de no existir, ok tomará el valor false
void eliminar(const std::string& clave, bool& ok)
{
unsigned lst = hash(clave, dat.size());
int p = buscar(clave, dat[lst]);
if (p < dat[lst].size()) {
dat[lst].eliminar(p);
--sz;
ok = true;
} else {
ok = false;
}
}
//----------------------------
// Devuelve el valor del mapa actual asociado a la (clave) de acceso

142
// y ok tomará el valor true. En caso de no existir, ok tomará
// el valor false
TipoBase acceder(const std::string& clave, bool& ok) const
{
Elem e;
unsigned lst = hash(clave, dat.size());
int p = buscar(clave, dat[lst]);
if (p < dat[lst].size()) {
e = dat[lst].acceder(p) ;
ok = true;
} else {
ok = false;
}
return e.v ;
}
//----------------------------
// PRECOND: (0 <= pos && pos < size())
// Devuelve la clave correspondiente al elemento del mapa
// actual que ocupa la posicion (pos) en la secuencia de elementos
std::string acceder_clave(int pos) const
{
assert(0 <= pos && pos < size());
int lst, p;
situar(pos, lst, p);
assert(lst < int(dat.size()) && p < dat[lst].size());
Elem e = dat[lst].acceder(p) ;
return e.c ;
}
//----------------------------
// PRECOND: (0 <= pos && pos < size())
// Devuelve el valor correspondiente al elemento del mapa
// actual que ocupa la posicion (pos) en la secuencia de elementos
TipoBase acceder_valor(int pos) const
{
assert(0 <= pos && pos < size());
int lst, p;
situar(pos, lst, p);
assert(lst < int(dat.size()) && p < dat[lst].size());
Elem e = dat[lst].acceder(p) ;
return e.v ;
}
//----------------------------
// PRECOND: (0 <= pos && pos < size())
// Asigna el valor (dato) al elemento del mapa actual
// que ocupa la posicion (pos) en la secuencia de elementos
void modificar_valor(int pos, const TipoBase& dato)
{
assert(0 <= pos && pos < size());
int lst, p;
situar(pos, lst, p);
assert(lst < dat.size() && p < dat[lst].size());
Elem e = dat[lst].acceder(p) ;
e.v = dato;
dat[lst].modificar(p, e);
}
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
// Elem contiene cada elemento (clave, valor) del mapa
struct Elem {
std::string c;
TipoBase v;
};
typedef Lista<Elem> ListaElem;
typedef std::tr1::array<ListaElem, SIZE> Datos;
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
unsigned hash(const std::string& clave, unsigned mod) const
{
unsigned valor = 0;
for (unsigned i = 0; i < clave.size(); ++i) {

143
valor = valor * 7 + unsigned(clave[i]);
}
return valor % mod;
}
//-------------------------
bool iguales(const std::string& c, const Elem& e) const
{
return c == e.c ;
}
//-------------------------
int buscar(const std::string& c, const ListaElem& lista) const
{
int i = 0;
while ((i < lista.size()) && ! iguales(c, lista.acceder(i))) {
++i;
}
return i;
}
//-------------------------
void situar(int pos, int& lst, int& p) const {
assert(pos < size());
// Para una determinada posicion ’pos’,
// ’lst’ es el indice dentro del array a la lista
// ’p’ es el indice dentro de la lista
p = pos;
lst = 0;
while ((lst < int(dat.size()))&&(p >= dat[lst].size())) {
p -= dat[lst].size();
++lst;
}
}
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
int sz; // numero de elementos del mapa
Datos dat; // elementos del mapa
//----------------------------------------------------------
};
}
#endif

Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "mapa.hpp"
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------
struct DatosPer {
unsigned edad;
string telefono;
};
inline void escribir(const DatosPer& p)
{
cout << "{ " << p.edad << " " << p.telefono << " } ";
}
inline void escribir(const string& nombre, const DatosPer& p)
{
cout << "{ " << nombre << " " << p.edad << " " << p.telefono << " } ";
}
inline void leer(string& nombre, DatosPer& p)
{
cin >> nombre >> p.edad >> p.telefono;
}
//------------------------------------------------------------------
typedef Mapa<DatosPer, 20> MPers;
//------------------------------------------------------------------
void leer_dato(DatosPer& dato)
{
cout << "Introduzca edad y telefono de una persona: " ;
cin >> dato.edad >> dato.telefono;
}
//---------------------------------

144
void leer_clave(string& clave)
{
cout << "Introduzca el nombre: " ;
cin >> clave;

}
//---------------------------------
void leer(MPers& mapa)
{
string nombre;
DatosPer dato;
mapa.clear() ;
cout << "Introduzca personas (nombre edad telefono) (fin -> FIN)" << endl;
leer_clave(nombre) ;
while ((nombre != "fin")&&( ! mapa.lleno())) {
leer_dato(dato) ;
mapa.insertar(nombre, dato);
leer_clave(nombre) ;
}
}
//---------------------------------
void escribir(const MPers& mapa)
{
cout << "Mapa: " ;
for (int i = 0 ; i < mapa.size() ; ++i) {
escribir(mapa.acceder_clave(i), mapa.acceder_valor(i)) ;
}
cout << endl ;
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Leer" << endl;
cout << "B. Insertar Elemento" << endl;
cout << "C. Buscar Elemento" << endl;
cout << "D. Eliminar Elemento" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’D’))));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
MPers mapa;
DatosPer dato;
string nombre;
bool ok;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
leer(mapa);
escribir(mapa);
break;
case ’B’:
if (mapa.lleno()) {
cout << "Error: Mapa lleno" << endl ;
} else {
leer_clave(nombre) ;
leer_dato(dato) ;
mapa.insertar(nombre, dato) ;
escribir(mapa) ;
}
break;
case ’C’:
if (mapa.size() == 0) {

145
cout << "Error: mapa vacio" << endl ;
} else {
leer_clave(nombre) ;
dato = mapa.acceder(nombre, ok) ;
if (ok) {
cout << "mapa[" << nombre << "]: " ;
escribir(dato) ;
cout << endl ;
} else {
cout << "mapa[" << nombre << "]: No encontrado" << endl ;
}
escribir(mapa) ;
}
break;
case ’D’:
if (mapa.size() == 0) {
cout << "Error: mapa vacio" << endl ;
} else {
leer_clave(nombre) ;
mapa.eliminar(nombre, ok) ;
if (! ok) {
cout << "mapa[" << nombre << "]: No encontrado" << endl ;
}
escribir(mapa) ;
}
break;
}
} while (op != ’X’);
}

146
Tema 4: Colecciones
1. Diseñe un programa que utilice el tipo vector de la biblioteca estándar para almacenar las personas de
una agenda, donde por cada persona se almacena el nombre y el teléfono. La agenda se gestiona mediante
la siguientes operaciones:
Añadir los datos de una persona a la agenda, de tal forma que si la persona ya existe, entonces se
actualiza su número de teléfono, y en otro caso, se añade la nueva persona y su teléfono a la agenda.
Mostrar todos los datos de la agenda.
Mostrar los datos de una determinada persona, a partir de su nombre recibido como parámetro. En
caso de que no exista en la agenda ninguna persona con dicho nombre, entonces se mostrará un
mensaje de error.
Borrar los datos de una determinada persona, a partir de su nombre recibido como parámetro. En
caso de que no exista en la agenda ninguna persona con dicho nombre, entonces se mostrará un
mensaje de error.
Solución: agenda.hpp
#ifndef agenda_hpp__
#define agenda_hpp__
#include <string>
#include <vector>
namespace umalcc {
class Agenda {
public:
//------------------------------
//~Agenda(); // Generado automáticamente
//Agenda(); // Generado automáticamente
//Agenda(const Agenda& o); // Generado automáticamente
//Agenda& operator=(const Agenda& o); // Generado automáticamente
//------------------------------
void anyadir(const std::string& n, const std::string& t);
void mostrar(const std::string& n, bool& ok) const;
void mostrar() const;
void borrar(const std::string& n, bool& ok);
//------------------------------
private:
struct Persona {
std::string nombre;
std::string tfn;
};
typedef std::vector<Persona> Datos;
//-- Atributos -----------------
Datos ag;
//------------------------------
int buscar(const std::string& n) const;
void mostrar(const Persona& p) const;
};
}
#endif

Solución: agenda.cpp
#include "agenda.hpp"
#include <iostream>
using namespace std;
namespace umalcc {
//--------------------------------------------------------------------------
void Agenda::anyadir(const std::string& n, const std::string& t)
{
int pos = buscar(n);
if (0 <= pos && pos < int(ag.size())) {
ag[pos].tfn = t;
} else {
Persona p = { n, t };
ag.push_back(p);
}
}
//--------------------------------------------------------------------------
void Agenda::mostrar(const std::string& n, bool& ok) const
{
int pos = buscar(n);
ok = (0 <= pos && pos < int(ag.size()));

147
if (ok) {
mostrar(ag[pos]);
}
}
//--------------------------------------------------------------------------
void Agenda::mostrar() const
{
cout << "--------------------" << endl;
for (int i = 0; i < int(ag.size()); ++i) {
mostrar(ag[i]);
cout << "--------------------" << endl;
}
}
//--------------------------------------------------------------------------
void Agenda::borrar(const std::string& n, bool& ok)
{
int pos = buscar(n);
ok = (0 <= pos && pos < int(ag.size()));
if (ok) {
if (pos < int(ag.size()) - 1) {
ag[pos] = ag[ag.size() - 1];
}
ag.pop_back();
}
}
//--------------------------------------------------------------------------
int Agenda::buscar(const std::string& n) const
{
int i = 0;
while ((i < int(ag.size())) && (ag[i].nombre != n)) {
++i;
}
return i;
}
//--------------------------------------------------------------------------
void Agenda::mostrar(const Persona& p) const
{
cout << "Nombre: " << p.nombre << endl;
cout << "Teléfono: " << p.tfn << endl;
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "agenda.hpp"
using namespace std;
using namespace umalcc;
//-------------------------------------------------------------------------
void leer_nombre(string& nombre)
{
cout << "Introduza nombre (sin espacios): ";
cin >> nombre;
}
//---------------------------------
void leer_persona(string& nombre, string& tfn)
{
leer_nombre(nombre);
cout << "Introduza telefono: ";
cin >> tfn;
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "A. A~nadir datos de persona" << endl;
cout << "B. Mostrar toda la agenda" << endl;
cout << "C. Mostrar datos de persona" << endl;
cout << "D. Borrar persona" << endl;
cout << "X. Fin" << endl;
do {

148
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!(((op >= ’A’)&&(op <= ’D’))||(op == ’X’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
Agenda ag;
string n, t;
bool ok;
char op;
do {
op = menu();
switch (op) {
case ’A’:
leer_persona(n, t);
ag.anyadir(n, t);
break;
case ’B’:
ag.mostrar();
break;
case ’C’:
leer_nombre(n);
ag.mostrar(n, ok);
if (!ok) {
cout << "Error, persona no encontrada" << endl;
}
break;
case ’D’:
leer_nombre(n);
ag.borrar(n, ok);
if (!ok) {
cout << "Error, persona no encontrada" << endl;
}
break;
}
} while (op != ’X’);
}

2. Modifique el programa de la agenda anterior para que por cada persona de la agenda permita almacenar
una cola de mensajes para dicha persona, utilizando para ello el tipo queue de la biblioteca estándar. Ası́,
se deberán modificar las operaciones para mostrar los datos de una persona para que también muestren los
contenidos de todos los mensajes destinados a dicha persona. Además, se deberán incorporar la siguientes
operaciones:
Añadir un mensaje para una determinada persona de la agenda, a partir de su nombre recibido como
parámetro. En caso de que no exista en la agenda ninguna persona con dicho nombre, entonces se
mostrará un mensaje de error.
Borrar todos los mensajes que tiene almacenados una determinada persona de la agenda, a partir
de su nombre recibido como parámetro. En caso de que no exista en la agenda ninguna persona con
dicho nombre, entonces se mostrará un mensaje de error.
Solución: agendamsj.hpp
#ifndef agendamsj_hpp__
#define agendamsj_hpp__
#include <string>
#include <vector>
#include <queue>
namespace umalcc {
class Agenda {
public:
//------------------------------
//~Agenda(); // Generado automáticamente
//Agenda(); // Generado automáticamente
//Agenda(const Agenda& o); // Generado automáticamente
//Agenda& operator=(const Agenda& o); // Generado automáticamente
//------------------------------
void anyadir(const std::string& n, const std::string& t);

149
void
mostrar(const std::string& n, bool& ok) const;
void
mostrar() const;
void
borrar(const std::string& n, bool& ok);
void
anyadir_mensaje(const std::string& n, const std::string& m,
bool& ok);
void borrar_mensajes(const std::string& n, bool& ok);
//------------------------------
private:
typedef std::queue<std::string> CMsj;
struct Persona {
std::string nombre;
std::string tfn;
CMsj msj;
};
typedef std::vector<Persona> Datos;
//-- Atributos -----------------
Datos ag;
//------------------------------
int buscar(const std::string& n) const;
void mostrar(const Persona& p) const;
void mostrar(const CMsj& c) const;
};
}
#endif

Solución: agendamsj.cpp
#include "agendamsj.hpp"
#include <iostream>
using namespace std;
namespace umalcc {
//--------------------------------------------------------------------------
void Agenda::anyadir(const std::string& n, const std::string& t)
{
int pos = buscar(n);
if (0 <= pos && pos < int(ag.size())) {
ag[pos].tfn = t;
} else {
Persona p = { n, t, CMsj() };
ag.push_back(p);
}
}
//--------------------------------------------------------------------------
void Agenda::mostrar(const std::string& n, bool& ok) const
{
int pos = buscar(n);
ok = (0 <= pos && pos < int(ag.size()));
if (ok) {
mostrar(ag[pos]);
}
}
//--------------------------------------------------------------------------
void Agenda::mostrar() const
{
cout << "--------------------" << endl;
for (int i = 0; i < int(ag.size()); ++i) {
mostrar(ag[i]);
cout << "--------------------" << endl;
}
}
//--------------------------------------------------------------------------
void Agenda::borrar(const std::string& n, bool& ok)
{
int pos = buscar(n);
ok = (0 <= pos && pos < int(ag.size()));
if (ok) {
if (pos < int(ag.size()) - 1) {
ag[pos] = ag[ag.size() - 1];
}
ag.pop_back();
}
}
//--------------------------------------------------------------------------
int Agenda::buscar(const std::string& n) const
{

150
int i = 0;
while ((i < int(ag.size())) && (ag[i].nombre != n)) {
++i;
}
return i;
}
//--------------------------------------------------------------------------
void Agenda::mostrar(const Persona& p) const
{
cout << "Nombre: " << p.nombre << endl;
cout << "Teléfono: " << p.tfn << endl;
cout << "Mensajes: " << endl;
mostrar(p.msj);
}
//--------------------------------------------------------------------------
void Agenda::mostrar(const CMsj& c) const
{
CMsj cc = c;
while (! cc.empty()) {
cout << " " << cc.front() << endl;
cc.pop();
}
}
//--------------------------------------------------------------------------
void Agenda::anyadir_mensaje(const std::string& n, const std::string& m,
bool& ok)
{
int pos = buscar(n);
ok = (0 <= pos && pos < int(ag.size()));
if (ok) {
ag[pos].msj.push(m);
}
}
//--------------------------------------------------------------------------
void Agenda::borrar_mensajes(const std::string& n, bool& ok)
{
int pos = buscar(n);
ok = (0 <= pos && pos < int(ag.size()));
if (ok) {
ag[pos].msj = CMsj();
}
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "agendamsj.hpp"
using namespace std;
using namespace umalcc;
//-------------------------------------------------------------------------
void leer_nombre(string& nombre)
{
cout << "Introduza nombre (sin espacios): ";
cin >> nombre;
}
//---------------------------------
void leer_persona(string& nombre, string& tfn)
{
leer_nombre(nombre);
cout << "Introduza telefono: ";
cin >> tfn;
}
//-------------------------------------------------------------------------
void leer_mensaje(string& msj)
{
cout << "Introduza mensaje: ";
cin >> ws;
getline(cin, msj);
}
//-------------------------------------------------------------------------
char menu()

151
{
char op;
cout << endl;
cout << "A. A~nadir datos de persona" << endl;
cout << "B. Mostrar toda la agenda" << endl;
cout << "C. Mostrar datos de persona" << endl;
cout << "D. Borrar persona" << endl;
cout << "E. A~nadir mensaje" << endl;
cout << "F. Borrar mensajes" << endl;
cout << "X. Fin" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!(((op >= ’A’)&&(op <= ’F’))||(op == ’X’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
Agenda ag;
string n, t, m;
bool ok;
char op;
do {
op = menu();
switch (op) {
case ’A’:
leer_persona(n, t);
ag.anyadir(n, t);
break;
case ’B’:
ag.mostrar();
break;
case ’C’:
leer_nombre(n);
ag.mostrar(n, ok);
if (!ok) {
cout << "Error, persona no encontrada" << endl;
}
break;
case ’D’:
leer_nombre(n);
ag.borrar(n, ok);
if (!ok) {
cout << "Error, persona no encontrada" << endl;
}
break;
case ’E’:
leer_nombre(n);
leer_mensaje(m);
ag.anyadir_mensaje(n, m, ok);
if (!ok) {
cout << "Error, persona no encontrada" << endl;
}
break;
case ’F’:
leer_nombre(n);
ag.borrar_mensajes(n, ok);
if (!ok) {
cout << "Error, persona no encontrada" << endl;
}
break;
}
} while (op != ’X’);
}

3. Diseñe un programa que lea una expresión aritmética que contiene llaves {}, paréntesis () y corchetes []
y compruebe si éstos se encuentran correctamente balanceados o no. Por ejemplo la siguiente expresión
3 + 5 ∗ ([4] − 67) + [(7)] se encuentra correctamente balanceada, donde los posibles casos de error son los
siguientes:

152
Un sı́mbolo de apertura no se corresponde con su correspondiente sı́mbolo de cierre. Por ejemplo:
3 + 5 ∗ ([4] − 67] + [(7)]
Para un determinado sı́mbolo de apertura, no existe el correspondiente sı́mbolo de cierre. Por ejemplo:
(3 + 5 ∗ ([4] − 67 + [(7)])
Para un determinado sı́mbolo de cierre, no existe el correspondiente sı́mbolo de apertura. Por ejemplo:
(3 + 5 ∗ [4] − 67) + [(7)])
Solución
#include <iostream>
#include <string>
#include <stack>
using namespace std;
//------------------------------------------------------------------------------
enum Cod_Error {
CORRECTO,
NO_CORRESPONDE,
FALTA_APERTURA,
FALTA_CIERRE
};
//------------------------------------------------------------------------------
typedef stack<char> PChar;
//------------------------------------------------------------------------------
void leer_expr(string& exp)
{
cout << "Introduzca la expresión aritmética: ";
cin >> ws;
getline(cin, exp);
}
//------------------------------------------------------------------------------
bool es_apertura(char c)
{
return (c == ’(’ || c == ’[’ || c == ’{’);
}
//------------------------------------------------------------------------------
bool es_cierre(char c)
{
return (c == ’)’ || c == ’]’ || c == ’}’);
}
//------------------------------------------------------------------------------
bool corresponde(char a, char c)
{
bool ok = false;
switch (a) {
case ’(’: ok = (c == ’)’); break;
case ’[’: ok = (c == ’]’); break;
case ’{’: ok = (c == ’}’); break;
}
return ok;
}
//------------------------------------------------------------------------------
Cod_Error esta_balanceado(const string& exp)
{
PChar p;
Cod_Error ok = CORRECTO;
for (int i = 0; (ok == CORRECTO) && (i < int(exp.size())); ++i) {
if (es_apertura(exp[i])) {
p.push(exp[i]);
} else if (es_cierre(exp[i])) {
if (p.empty()) {
ok = FALTA_APERTURA;
} else if (corresponde(p.top(), exp[i])) {
p.pop();
} else {
ok = NO_CORRESPONDE;
}
}
}
if (ok == CORRECTO && ! p.empty() ) {
ok = FALTA_CIERRE;
}
return ok;
}
//------------------------------------------------------------------------------

153
int main()
{
string exp;
leer_expr(exp);
Cod_Error ok = esta_balanceado(exp);
switch (ok) {
case CORRECTO:
cout << "Está balanceado" << endl;
break;
case NO_CORRESPONDE:
cout << "Sı́mbolos no corresponden" << endl;
break;
case FALTA_APERTURA:
cout << "Falta sı́mbolo de apertura" << endl;
break;
case FALTA_CIERRE:
cout << "Falta sı́mbolo de cierre" << endl;
break;
}
}
//------------------------------------------------------------------------------

4. Diseñe un programa que lea y almacene en un vector de la biblioteca estándar las ventas realizadas por
unos agentes de ventas. Leerá el nombre y el total de ventas que ha realizado, hasta leer un nombre de
agente vacı́o. Posteriormente se eliminarán del vector aquellos agentes cuyas ventas sean inferiores a la
media de las ventas realizadas. Finalmente se mostrará el contenido del vector.
Solución
#include <iostream>
#include <vector>
#include <string>
using namespace std ;

struct Agente {
string nombre ;
double ventas ;
} ;

typedef vector<Agente> VAgentes ;

void leer (VAgentes& v)


{
v.clear() ;
Agente a ;
cout << "Introduzca Nombre: " ;
getline(cin, a.nombre) ;
while (( ! cin.fail()) && (a.nombre.size() > 0)) {
cout << "Introduzca Ventas: " ;
cin >> a.ventas ;
cin.ignore(1000, ’\n’) ;
v.push_back(a) ;
cout << "Introduzca Nombre: " ;
getline(cin, a.nombre) ;
}
}

double media(const VAgentes& v)


{
double suma=0.0 ;
for (int i = 0 ; i < int(v.size()) ; ++i) {
suma += v[i].ventas ;
}
return suma/double(v.size()) ;
}

void purgar(VAgentes& v, double media)


{
// altera el orden secuencial de los elementos
int i = 0 ;
while (i < int(v.size())) {
if (v[i].ventas < media) {
v[i] = v[v.size()-1] ;
v.pop_back() ;

154
} else {
++i ;
}
}
}

void purgar_ordenado(VAgentes& v, double media)


{
// mantiene el orden secuencial de los elementos
int k = 0 ;
while ((k < int(v.size()))&&(v[k].ventas >= media)) {
++k;
}
for (int i = k ; i < int(v.size()) ; ++i) {
if(v[i].ventas >= media) {
v[k] = v[i] ;
++k ;
}
}
v.resize(k) ;
}

void imprimir(const VAgentes& v)


{
for (int i = 0 ; i < int(v.size()) ; ++i) {
cout << v[i].nombre << " " << v[i].ventas << endl ;
}
}

int main ()
{
VAgentes v ;
leer(v) ;
purgar(v, media(v)) ;
imprimir(v) ;
}

5. Diseñe un programa que lea dos matrices de números reales de tamaños arbitrarios (primero leerá las
dimensiones y posteriormente los elementos de la matriz) y muestre el resultado de multiplicar ambas
matrices. Las matrices se almacenarán en vectores de dos dimensiones (utilizando el tipo vector de la
biblioteca estándar).
Solución
#include <vector>
#include <iostream>
#include <iomanip>
using namespace std ;

typedef vector <double> Fila ;


typedef vector <Fila> Matriz ;

void imprimir(const Matriz& m)


{
for (int f = 0 ; f < int(m.size()) ; ++f) {
for (int c = 0 ; c < int(m[f].size()) ; ++c) {
cout << setw(10) << setprecision(4)
<< m[f][c] << " " ;
}
cout << endl ;
}
}

void leer(Matriz& m)
{
int nf, nc ;
cout << "Introduzca el numero de filas: " ;
cin >> nf ;
cout << "Introduzca el numero de columnas: " ;
cin >> nc ;
m = Matriz(nf, Fila (nc)) ; // copia de la matriz completa
cout << "Introduzca los elementos: " << endl ;
for (int f = 0 ; f < int(m.size()) ; ++f) {
for (int c = 0 ; c < int(m[f].size()) ; ++c) {

155
cin >> m[f][c] ;
}
}
}

// otra opción más eficiente para la lectura de vectores


void leer_2(Matriz& m)
{
int nf, nc ;
cout << "Introduzca el numero de filas: " ;
cin >> nf ;
cout << "Introduzca el numero de columnas: " ;
cin >> nc ;
Matriz aux(nf, Fila (nc)) ;
cout << "Introduzca los elementos: " << endl ;
for (int f = 0 ; f < int(aux.size()) ; ++f) {
for (int c = 0 ; c < int(aux[f].size()) ; ++c) {
cin >> aux[f][c] ;
}
}
m.swap(aux) ; // evita la copia de la matriz completa
}

void multiplicar(const Matriz& m1, const Matriz& m2, Matriz& m3)


{
m3.clear() ;
if ((m1.size() > 0) && (m2.size() > 0) && (m2[0].size() > 0)
&& (m1[0].size() == m2.size())){
Matriz aux(m1.size(), Fila(m2[0].size())) ;
for (int f = 0 ; f < int(aux.size()) ; ++f) {
for (int c = 0 ; c < int(aux[f].size()) ; ++c) {
double suma = 0.0 ;
for (int k = 0 ; k < int(m2.size()) ; ++k) {
suma += m1[f][k] * m2[k][c] ;
}
aux[f][c] = suma ;
}
}
m3.swap(aux) ; // evita la copia de la matriz completa
}
}

int main()
{
Matriz m1, m2, m3 ;
leer(m1) ;
leer(m2) ;
multiplicar(m1, m2, m3) ;
if (m3.size() == 0) {
cout << "Error en la multiplicación de Matrices" << endl ;
} else {
imprimir(m3) ;
}
}

6. En una expresión en postfija, el operador binario aparece justo después de sus dos operandos. Por ejemplo
la expresión 12 34 + es la notación postfija equivalente a la expresión infija 12 + 34. Otros ejemplos:

expresiones
infija ⇔ postfija
12 + 34 * 56 ⇔ 12 34 56 * +
12 + (34 * 56) ⇔ 12 34 56 * +
(12 + 34) * 56 ⇔ 12 34 + 56 *

Una gran ventaja de las expresiones postfijas es que no son necesarias reglas de precedencia y de asocia-
tividad ni paréntesis, ésto permite que los algoritmos que las evalúan sean relativamente fáciles, utilizando
para ello una pila. Por ejemplo, para evaluar las siguientes expresiones en notación postfija:

156
12 34 56 * + 12 34 + 56 *
34*56 12+1904 12+34 46*56

56
34 34 1904 34 56
12 12 12 12 1916 12 12 46 46 2576

Diseñe un programa que lea de teclado una expresión en notación postfija, la evalúe y muestre el resultado
de dicha evaluación. Utilice para ello el tipo stack de la biblioteca estándar, teniendo en cuenta las
siguientes consideraciones:
La expresión postfija es correcta.
Los operadores son +, -, * y / (todos binarios).
Los operandos son números naturales separados por operadores y espacios en blanco.
Solución
#include <iostream>
#include <string>
#include <stack>
#include <cassert>
using namespace std;

typedef stack<int> PilaInt;


//------------------------------------------------------------------
bool es_operador(char c)
{
return ((c == ’+’)||(c == ’-’)||(c == ’*’)||(c == ’/’));
}
//---------------------------------
bool es_operando(char c)
{
return ((c >= ’0’)&&(c <= ’9’));
}
//---------------------------------
// extrae el valor numerico que hay en el string a partir de la posicion ’i’
// ’i’ se devuelve con la posicion siguiente al numero extraido
void extraer_numero(const string& expr_postfija, unsigned& i, int& val)
{
val = 0;
while ((i < expr_postfija.size()) && es_operando(expr_postfija[i])) {
val = val * 10 + (int(expr_postfija[i]) - int(’0’));
++i;
}
}
//---------------------------------
// evalua una operacion (+, -, *, /) con dos operandos
int evaluar_op(char op, int op1, int op2, bool& ok)
{
int val = 0;
if (ok) {
switch (op) {
case ’+’: val = op1 + op2; break;
case ’-’: val = op1 - op2; break;
case ’*’: val = op1 * op2; break;
case ’/’:
if (op2 == 0) {
ok = false; // Division por Cero
} else {
val = op1 / op2;
}
break;
}
}
return val;
}
//------------------------------------------------------------------
// evalua una expresion postfija
int evaluar_expr_postfija(const string& expr_postfija, bool& ok)
{
PilaInt pila;
int res = 0;
ok = true;
unsigned i = 0;

157
while ( ok && (i < expr_postfija.size())) {
if (expr_postfija[i] == ’ ’) {
// saltar los espacios
++i;
} else if (es_operador(expr_postfija[i]) && (pila.size() >= 2)) {
// si es operador, sacar dos operandos de la pila,
// evaluar la operacion y almacenar el resultado en la pila
int op2 = pila.top();
pila.pop();
int op1 = pila.top();
pila.pop();
int val = evaluar_op(expr_postfija[i], op1, op2, ok);
pila.push(val);
++i;
} else if (es_operando(expr_postfija[i])) {
// si es operando, almacenar su valor en la pila
int val;
extraer_numero(expr_postfija, i, val);
pila.push(val);
} else {
ok = false ; // Error en expresion: simbolo no esperado
}
}
// el resultado de la expresion esta en la pila
if (pila.empty()) {
ok = false;
}
if (ok) {
res = pila.top();
pila.pop();
if ( ! pila.empty()) {
ok = false;
}
}
return res;
}
//------------------------------------------------------------------
int main()
{
bool ok;
string expr_postfija;
cout << "Introduza una expresion en notacion postfija: " << endl;
getline(cin, expr_postfija);
int val = evaluar_expr_postfija(expr_postfija, ok);
if (! ok) {
cout << "Error " << endl;
} else {
cout << "Resultado: " << val << endl;
}
}

7. Ası́ mismo, también es posible convertir una expresión en notación infija a la correspondiente expresión en
notación postfija, considerando tanto la precedencia como asociatividad de los operadores y los paréntesis,
utilizando para ello una pila. Por ejemplo, para las siguientes expresiones en notación infija:

Entrada 12 + 34 / 5 * 6 - 7

Stack / / * *
+ + + + + + - -

Salida 12 34 5 / 6 *+ 7 -

Entrada 12 + ( 34 * 56 )

* *
Stack ( ( ( (
+ + + + + +

Salida 12 34 56 * +

158
Entrada ( 12 + 34 ) * 56

Stack + +
( ( ( ( * *

Salida 12 34 + 56 *

Diseñe un programa que lea una expresión en infija y muestre la correspondiente expresión en notación
postfija. Utilice para ello el tipo stack de la biblioteca estándar, teniendo en cuenta las siguientes consid-
eraciones:
La expresión infija es correcta.
Los operadores son +, -, *, / (todos binarios), y los paréntesis.
La prioridad de *, / es mayor que que la prioridad de +, -.
Los operandos son números naturales separados por operadores, paréntesis y espacios en blanco.
Solución
#include <iostream>
#include <string>
#include <stack>
#include <cassert>
using namespace std;
//------------------------------------------------------------------
typedef stack<char> PilaOp;
//---------------------------------
// indica si se espera que lo proximo sea operando u operador
enum Proximo {
OPERANDO, OPERADOR
};
//------------------------------------------------------------------
const unsigned PRIO_PAR = 0; // prioridad de parentesis
const unsigned PRIO_SUM = 1; // prioridad de + y -
const unsigned PRIO_MUL = 2; // prioridad de * y /
//------------------------------------------------------------------
bool es_operando(char c)
{
return ((c >= ’0’)&&(c <= ’9’));
}
//---------------------------------
bool es_operador(char c)
{
return ((c == ’+’)||(c == ’-’)||(c == ’*’)||(c == ’/’));
}
//---------------------------------
// si la pila esta vacia, entonces Error
void check_no_vacia(const PilaOp& pila, bool& ok)
{
if (pila.empty()) {
ok = false; // Error en expresion: falta ’abre parentesis’
}
}
//---------------------------------
// si la pila no esta vacia, entonces Error
void check_vacia(const PilaOp& pila, bool& ok)
{
if (! pila.empty()) {
ok = false; // Error en expresion: falta ’cierra parentesis’
}
}
//---------------------------------
// comprueba si se esperaba operando, en caso contrario se esperaba
// operador
void check_operando_esperado(Proximo proximo, bool& ok)
{
if (proximo != OPERANDO) {
ok = false; // Error en expresion: Operador esperado
}
}
//---------------------------------
// comprueba si se esperaba operador, en caso contrario se esperaba
// operando
void check_operador_esperado(Proximo proximo, bool& ok)

159
{
if (proximo != OPERADOR) {
ok = false; // Error en expresion: Operando esperado
}
}
//---------------------------------
// devuelve la prioridad del operador
unsigned prioridad(char op)
{
unsigned prio = 0;
switch (op) {
case ’+’:
case ’-’:
prio = PRIO_SUM;
break;
case ’*’:
case ’/’:
prio = PRIO_MUL;
break;
case ’(’:
case ’)’:
prio = PRIO_PAR;
break;
}
return prio;
}
//---------------------------------
// extrae el numero de la expresion infija y la pasa a la expresion
// postfija
void extraer_numero(const string& expr_infija, unsigned& i,
string& expr_postfija)
{
while ((i < expr_infija.size())&&es_operando(expr_infija[i])) {
expr_postfija += expr_infija[i];
++i;
}
expr_postfija += " ";
}
//---------------------------------
// extrae los operadores de la pila con prioridad mayor o igual
// al operador actual
void extraer_operadores(PilaOp& pila, char op, string& expr_postfija)
{
while ( (! pila.empty()) && (prioridad(op) <= prioridad(pila.top()))) {
expr_postfija += pila.top();
expr_postfija += " ";
pila.pop();
}
}
//---------------------------------
// extrae los operadores de la pila hasta extraer el abre parentesis
void extraer_hasta_aparent(PilaOp& pila, string& expr_postfija, bool& ok)
{
check_no_vacia(pila, ok);
while (ok && (pila.top() != ’(’)) {
expr_postfija += pila.top();
expr_postfija += " ";
pila.pop();
check_no_vacia(pila, ok);
}
if (ok) {
pila.pop();
}
}
//------------------------------------------------------------------
// convierte de infija a postfija
void convertir_infija_postfija(const string& expr_infija,
string& expr_postfija, bool& ok)
{
ok = true;
expr_postfija = "";
PilaOp pila;
Proximo proximo = OPERANDO;
unsigned i = 0;

160
// pone en la pila un abre parentesis inicial
pila.push(’(’);
while (ok && (i < expr_infija.size())) {
if (expr_infija[i] == ’ ’) {
// saltar los espacios
++i;
} else if (expr_infija[i] == ’(’) {
// poner el abre parentesis en la pila
check_operando_esperado(proximo, ok);
if (ok) {
pila.push(expr_infija[i]);
++i;
}
} else if (expr_infija[i] == ’)’) {
// extrae todos los operadores de la pila
// hasta abre parentesis
check_operador_esperado(proximo, ok);
if (ok) {
extraer_hasta_aparent(pila, expr_postfija, ok);
++i;
}
} else if (es_operador(expr_infija[i])) {
// extrae todos los operadores de la pila que tengan
// prioridad mayor o igual, y despues pone el operador
// actual en la pila
check_operador_esperado(proximo, ok);
if (ok) {
proximo = OPERANDO;
extraer_operadores(pila, expr_infija[i], expr_postfija);
pila.push(expr_infija[i]);
++i;
}
} else if (es_operando(expr_infija[i])) {
// extrae el operando de la expresion infija y lo
// pone en la expresion postfija
check_operando_esperado(proximo, ok);
if (ok) {
proximo = OPERADOR;
extraer_numero(expr_infija, i, expr_postfija);
}
} else {
ok = false; // Error en expresion: simbolo no esperado
}
}
// extrae todos los operadores de la pila hasta el abre parentesis inicial
check_operador_esperado(proximo, ok);
extraer_hasta_aparent(pila, expr_postfija, ok);
check_vacia(pila, ok);
}
//------------------------------------------------------------------
int main()
{
bool ok;
string expr_infija;
string expr_postfija;
cout << "Introduza una expresion en notacion infija: " << endl;
getline(cin, expr_infija);
convertir_infija_postfija(expr_infija, expr_postfija, ok);
if (!ok) {
cout << "Error en conversion" << endl;
} else {
cout << "Expresion postfija: " << expr_postfija << endl;
}
}

161
Prácticas de Laboratorio
Práctica 1: Almacenamiento Persistente de Datos
1. Diseñe un programa que gestione una agenda que almacene información sobre personas (nombre y edad),
permita añadir, modificar y eliminar personas desde el teclado, ası́ como mostrar el contenido de la agenda.
Adicionalmente, el programa permitirá cargar el contenido de la agenda desde un fichero, ası́ como guardar
el contenido de la agenda a un fichero.
Se diseñarán versiones para trabajar con tres formatos de ficheros diferentes (donde los sı́mbolos y ←-

representan el espacio en blanco y el fin de lı́nea respectivamente):

Opción (A) Opción (B) Opción (C)


nombre apellidos ←-
nombre apellidos edad ←- edad ←- nombre apellidos edad ←-
nombre apellidos edad ←- nombre apellidos ←- nombre apellidos edad ←-
··· edad ←- ···
···

Nótese que en la primera opción (A), el nombre de la persona no contiene espacios en blanco, mientras
que en las siguientes opciones (B y C), si es posible que el nombre de la persona contenga espacios en
blanco.
El programa mostrará un menú iterativo para poder seleccionar las acciones deseadas por el usuario del
programa, tal como:

Solución (A)
#include <iostream>
#include <fstream>
#include <string>
#include <tr1/array>
#include <cctype>
using namespace std;
using namespace std::tr1;
//-------------------------------------------------------------------------
struct Persona {
string nombre;
unsigned edad;
};
const unsigned MAX = 100;
typedef array<Persona, MAX> APers;
struct Agenda {
unsigned nelms;
APers elm;
};
//-------------------------------------------------------------------------
// Inicializa la agenda a vacia
void inic_agenda(Agenda& ag)
{
ag.nelms = 0;
}
//---------------------------------
// anyade una persona al final de la agenda
void anyadir_persona(Agenda& ag, const Persona& p, bool& ok)
{
if (ag.nelms < ag.elm.size()) {
ag.elm[ag.nelms] = p;
++ag.nelms;
ok = true;
} else {
ok = false;
}
}
//-------------------------------------------------------------------------
void leer_persona(Persona& p)
{
cout << "Introduza nombre (sin espacios): ";
cin >> p.nombre;
cout << "Introduza edad: ";
cin >> p.edad;
}
//---------------------------------

162
// lee una persona y la anyade a la agenda (supone lectura correcta)
void nueva_persona(Agenda& ag)
{
bool ok;
Persona p;
leer_persona(p);
anyadir_persona(ag, p, ok);
if (!ok) {
cout << "Error al introducir la nueva persona" << endl;
}
}
//-------------------------------------------------------------------------
void escribir_persona(const Persona& p)
{
cout << "Nombre: " << p.nombre << endl;
cout << "Edad: " << p.edad << endl;
}
//---------------------------------
// escribe la agenda en pantalla
void escribir_agenda(const Agenda& ag)
{
for (unsigned i = 0; i < ag.nelms; ++i) {
cout << "----------------------------------------" << endl;
escribir_persona(ag.elm[i]);
}
cout << "----------------------------------------" << endl;
}
//-------------------------------------------------------------------------
// FORMATO DEL FICHERO DE ENTRADA:
//
// <nombre> <edad> <RC>
// <nombre> <edad> <RC>
// ...
//-------------------------------------------------------------------------
void leer_persona(ifstream& fich, Persona& p)
{
fich >> p.nombre >> p.edad;
}
//----------------------------------------------
void leer_agenda(const string& nombre_fich, Agenda& ag, bool& ok)
{
ifstream fich; // flujo de entrada de fichero
Persona p;

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {
ok = false; // error de apertura del fichero
} else {
ok = true;
inic_agenda(ag); // inicializa la agenda
leer_persona(fich, p); // lee una persona
while (!fich.fail() && ok) { // mientras lectura correcta
anyadir_persona(ag, p, ok); // anyade persona a la agenda
leer_persona(fich, p); // lee otra persona
}
ok = ok && fich.eof(); // correcto si alcanzado Fin-De-Fichero
fich.close(); // cierra la vinculacion
}
}
void cargar_agenda(Agenda& ag)
{
bool ok;
string nombre_fich;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
leer_agenda(nombre_fich, ag, ok);
if (!ok) {
cout << "Error al cargar el fichero" << endl;
}
}
//-------------------------------------------------------------------------
// FORMATO DEL FICHERO DE SALIDA:
//
// <nombre> <edad> <RC>

163
// <nombre> <edad> <RC>
// ...
//-------------------------------------------------------------------------
void escribir_persona(ofstream& fich, const Persona& p)
{
fich << p.nombre << " " << p.edad << endl;
}
void escribir_agenda(const string& nombre_fich, const Agenda& ag, bool& ok)
{
ofstream fich; // flujo de salida de fichero

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {
ok = false; // error de apertura del fichero
} else {
unsigned i = 0; // Para cada elemento
while ((i < ag.nelms) && (!fich.fail())) { // mientras fich en buen estado
escribir_persona(fich, ag.elm[i]); // escribir persona en fichero
++i;
}
ok = ! fich.fail(); // correcto si flujo en buen estado
fich.close(); // cierra la vinculacion
}
}
void guardar_agenda(const Agenda& ag)
{
bool ok;
string nombre_fich;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
escribir_agenda(nombre_fich, ag, ok);
if (!ok) {
cout << "Error al guardar el fichero" << endl;
}
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "C. Cargar Agenda" << endl;
cout << "M. Mostrar Agenda" << endl;
cout << "N. Nueva Persona" << endl;
cout << "G. Guardar Agenda" << endl;
cout << "X. Fin" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’C’)||(op == ’M’)||(op == ’N’)||(op == ’G’)||(op == ’X’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
Agenda ag;
char op;
inic_agenda(ag);
do {
op = menu();
switch (op) {
case ’C’:
cargar_agenda(ag);
break;
case ’M’:
escribir_agenda(ag);
break;
case ’N’:
nueva_persona(ag);
break;
case ’G’:
guardar_agenda(ag);
break;

164
}
} while (op != ’X’);
}

Solución (B)
#include <iostream>
#include <fstream>
#include <string>
#include <tr1/array>
#include <cctype>
using namespace std;
using namespace std::tr1;
//-------------------------------------------------------------------------
struct Persona {
string nombre;
unsigned edad;
};
const unsigned MAX = 100;
typedef array<Persona, MAX> APers;
struct Agenda {
unsigned nelms;
APers elm;
};
//-------------------------------------------------------------------------
// Inicializa la agenda a vacia
void inic_agenda(Agenda& ag)
{
ag.nelms = 0;
}
//---------------------------------
// anyade una persona al final de la agenda
void anyadir_persona(Agenda& ag, const Persona& p, bool& ok)
{
if (ag.nelms < ag.elm.size()) {
ag.elm[ag.nelms] = p;
++ag.nelms;
ok = true;
} else {
ok = false;
}
}
//-------------------------------------------------------------------------
void leer_persona(Persona& p)
{
cout << "Introduza nombre (puede tener espacios): ";
cin >> ws; // salta espacios y saltos de linea
getline(cin, p.nombre); // lee una linea completa
cout << "Introduza edad: ";
cin >> p.edad; // lee la edad
}
//---------------------------------
// lee una persona y la anyade a la agenda (supone lectura correcta)
void nueva_persona(Agenda& ag)
{
bool ok;
Persona p;
leer_persona(p);
anyadir_persona(ag, p, ok);
if (!ok) {
cout << "Error al introducir la nueva persona" << endl;
}
}
//-------------------------------------------------------------------------
void escribir_persona(const Persona& p)
{
cout << "Nombre: " << p.nombre << endl;
cout << "Edad: " << p.edad << endl;
}
//---------------------------------
// escribe la agenda en pantalla
void escribir_agenda(const Agenda& ag)
{
for (unsigned i = 0; i < ag.nelms; ++i) {
cout << "----------------------------------------" << endl;

165
escribir_persona(ag.elm[i]);
}
cout << "----------------------------------------" << endl;
}
//-------------------------------------------------------------------------
// FORMATO DEL FICHERO DE ENTRADA:
//
// <nombre> <RC> <edad> <RC>
// <nombre> <RC> <edad> <RC>
// ...
//-------------------------------------------------------------------------
void leer_persona(ifstream& fich, Persona& p)
{
fich >> ws; // salta espacios y saltos de linea
getline(fich, p.nombre);// lee una linea completa
fich >> p.edad; // lee la edad
}
//------------------------------------
// Otra posible implementacion alternativa
// void leer_persona(ifstream& fich, Persona& p)
// {
// getline(fich, p.nombre); // lee una linea completa
// fich >> p.edad; // lee la edad
// fich.ignore(1000, ’\n’); // elimina hasta el salto de linea
// }
//----------------------------------------------
void leer_agenda(const string& nombre_fich, Agenda& ag, bool& ok)
{
ifstream fich; // flujo de entrada de fichero
Persona p;

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {
ok = false; // error de apertura del fichero
} else {
ok = true;
inic_agenda(ag); // inicializa la agenda
leer_persona(fich, p); // lee una persona
while (!fich.fail() && ok) { // mientras lectura correcta
anyadir_persona(ag, p, ok); // anyade persona a la agenda
leer_persona(fich, p); // lee otra persona
}
ok = ok && fich.eof(); // correcto si alcanzado Fin-De-Fichero
fich.close(); // cierra la vinculacion
}
}
void cargar_agenda(Agenda& ag)
{
bool ok;
string nombre_fich;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
leer_agenda(nombre_fich, ag, ok);
if (!ok) {
cout << "Error al cargar el fichero" << endl;
}
}
//-------------------------------------------------------------------------
// FORMATO DEL FICHERO DE SALIDA:
//
// <nombre> <RC> <edad> <RC>
// <nombre> <RC> <edad> <RC>
// ...
//-------------------------------------------------------------------------
void escribir_persona(ofstream& fich, const Persona& p)
{
fich << p.nombre << endl << p.edad << endl;
}
void escribir_agenda(const string& nombre_fich, const Agenda& ag, bool& ok)
{
ofstream fich; // flujo de salida de fichero

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {

166
ok = false; // error de apertura del fichero
} else {
unsigned i = 0; // Para cada elemento
while ((i < ag.nelms) && (!fich.fail())) { // mientras fich en buen estado
escribir_persona(fich, ag.elm[i]); // escribir persona en fichero
++i;
}
ok = ! fich.fail(); // correcto si flujo en buen estado
fich.close(); // cierra la vinculacion
}
}
void guardar_agenda(const Agenda& ag)
{
bool ok;
string nombre_fich;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
escribir_agenda(nombre_fich, ag, ok);
if (!ok) {
cout << "Error al guardar el fichero" << endl;
}
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "C. Cargar Agenda" << endl;
cout << "M. Mostrar Agenda" << endl;
cout << "N. Nueva Persona" << endl;
cout << "G. Guardar Agenda" << endl;
cout << "X. Fin" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’C’)||(op == ’M’)||(op == ’N’)||(op == ’G’)||(op == ’X’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
Agenda ag;
char op;
inic_agenda(ag);
do {
op = menu();
switch (op) {
case ’C’:
cargar_agenda(ag);
break;
case ’M’:
escribir_agenda(ag);
break;
case ’N’:
nueva_persona(ag);
break;
case ’G’:
guardar_agenda(ag);
break;
}
} while (op != ’X’);
}

Solución (C)
#include <iostream>
#include <fstream>
#include <string>
#include <tr1/array>
#include <cctype>
using namespace std;
using namespace std::tr1;
//-------------------------------------------------------------------------

167
struct Persona {
string nombre;
unsigned edad;
};
const unsigned MAX = 100;
typedef array<Persona, MAX> APers;
struct Agenda {
unsigned nelms;
APers elm;
};
//-------------------------------------------------------------------------
// Inicializa la agenda a vacia
void inic_agenda(Agenda& ag)
{
ag.nelms = 0;
}
//---------------------------------
// anyade una persona al final de la agenda
void anyadir_persona(Agenda& ag, const Persona& p, bool& ok)
{
if (ag.nelms < ag.elm.size()) {
ag.elm[ag.nelms] = p;
++ag.nelms;
ok = true;
} else {
ok = false;
}
}
//-------------------------------------------------------------------------
void leer_persona(Persona& p)
{
cout << "Introduza nombre (puede tener espacios): ";
cin >> ws; // salta espacios y saltos de linea
getline(cin, p.nombre); // lee una linea completa
cout << "Introduza edad: ";
cin >> p.edad; // lee la edad
}
//---------------------------------
// lee una persona y la anyade a la agenda (si lectura correcta)
void nueva_persona(Agenda& ag)
{
bool ok;
Persona p;
leer_persona(p);
anyadir_persona(ag, p, ok);
if (!ok) {
cout << "Error al introducir la nueva persona" << endl;
}
}
//-------------------------------------------------------------------------
void escribir_persona(const Persona& p)
{
cout << "Nombre: " << p.nombre << endl;
cout << "Edad: " << p.edad << endl;
}
//---------------------------------
// escribe la agenda en pantalla
void escribir_agenda(const Agenda& ag)
{
for (unsigned i = 0; i < ag.nelms; ++i) {
cout << "----------------------------------------" << endl;
escribir_persona(ag.elm[i]);
}
cout << "----------------------------------------" << endl;
}
//-------------------------------------------------------------------------
// FORMATO DEL FICHERO DE ENTRADA:
//
// <nombre> <edad> <RC>
// <nombre> <edad> <RC>
// ...
//-------------------------------------------------------------------------
// busca el indice del primer digito en una linea (string)
unsigned buscar_digitos(const string& linea)

168
{
unsigned i = 0;
while ((i < linea.size()) && ! isdigit(linea[i])) {
++i;
}
return i;
}
//------------------------------------
// extrae un numero de una cadena a partir de la posicion i
unsigned extraer_num(const string& linea, unsigned& i)
{
unsigned num = 0;
while ((i < linea.size()) && isdigit(linea[i])) {
num = num * 10 + unsigned(linea[i] - ’0’);
++i;
}
return num;
}
//------------------------------------
// elimina espacios iniciales y finales en una cadena
void eliminar_espacios(string& str)
{
if (str.size() > 0) {
unsigned i = 0;
while ((i < str.size())&&(str[i] == ’ ’)) {
++i;
}
if (i >= str.size()) {
str = "";
} else {
unsigned j = unsigned(str.size()) - 1;
while (str[j] == ’ ’) {
--j;
}
str = str.substr(i, j-i+1);
}
}
}
//------------------------------------
// dada una linea leida segun el formato, extrae el nombre y la edad
void extraer(const string& linea, string& nombre, unsigned& edad, bool& ok)
{
unsigned i = buscar_digitos(linea);
if (i >= linea.size()) {
ok = false;
} else {
nombre = linea.substr(0, i);
eliminar_espacios(nombre);
edad = extraer_num(linea, i);
ok = (nombre.size() > 0) && (i >= linea.size());
}
}
//------------------------------------
void leer_persona(ifstream& fich, Persona& p, bool& ok)
{
string linea;
fich >> ws; // salta espacios y saltos de linea
getline(fich, linea); // lee una linea completa
extraer(linea, p.nombre, p.edad, ok); // extrae nombre y edad
}
//----------------------------------------------
void leer_agenda(const string& nombre_fich, Agenda& ag, bool& ok)
{
ifstream fich; // flujo de entrada de fichero
Persona p;

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {
ok = false; // error de apertura del fichero
} else {
bool ok1 = true;
bool ok2 = true;
inic_agenda(ag); // inicializa la agenda
leer_persona(fich, p, ok1); // lee una persona

169
while (!fich.fail() && ok1 && ok2) {// mientras lectura correcta
anyadir_persona(ag, p, ok2); // anyade persona a la agenda
leer_persona(fich, p, ok1); // lee otra persona
}
ok = ok1 && ok2 && fich.eof(); // correcto si alcanzado Fin-De-Fichero
fich.close(); // cierra la vinculacion
}
}
void cargar_agenda(Agenda& ag)
{
bool ok;
string nombre_fich;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
leer_agenda(nombre_fich, ag, ok);
if (!ok) {
cout << "Error al cargar el fichero" << endl;
}
}
//-------------------------------------------------------------------------
// FORMATO DEL FICHERO DE SALIDA:
//
// <nombre> <edad> <RC>
// <nombre> <edad> <RC>
// ...
//-------------------------------------------------------------------------
void escribir_persona(ofstream& fich, const Persona& p)
{
fich << p.nombre << " " << p.edad << endl;
}
void escribir_agenda(const string& nombre_fich, const Agenda& ag, bool& ok)
{
ofstream fich; // flujo de salida de fichero

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {
ok = false; // error de apertura del fichero
} else {
unsigned i = 0; // Para cada elemento
while ((i < ag.nelms) && (!fich.fail())) { // mientras fich en buen estado
escribir_persona(fich, ag.elm[i]); // escribir persona en fichero
++i;
}
ok = ! fich.fail(); // correcto si flujo en buen estado
fich.close(); // cierra la vinculacion
}
}
void guardar_agenda(const Agenda& ag)
{
bool ok;
string nombre_fich;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
escribir_agenda(nombre_fich, ag, ok);
if (!ok) {
cout << "Error al guardar el fichero" << endl;
}
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "C. Cargar Agenda" << endl;
cout << "M. Mostrar Agenda" << endl;
cout << "N. Nueva Persona" << endl;
cout << "G. Guardar Agenda" << endl;
cout << "X. Fin" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’C’)||(op == ’M’)||(op == ’N’)||(op == ’G’)||(op == ’X’)));
cout << endl;

170
return op;
}
//-------------------------------------------------------------------------
int main()
{
Agenda ag;
char op;
inic_agenda(ag);
do {
op = menu();
switch (op) {
case ’C’:
cargar_agenda(ag);
break;
case ’M’:
escribir_agenda(ag);
break;
case ’N’:
nueva_persona(ag);
break;
case ’G’:
guardar_agenda(ag);
break;
}
} while (op != ’X’);
}

Práctica 2: Tipos Abstractos de Datos


1. Diseñe e implemente un TAD (Cuenta) bancaria que represente una cuenta bancaria de un cliente de un
banco. Ası́, almacena el nombre del cliente (cadena de caracteres), el número de cuenta (número natural)
y el saldo de la cuenta (número real), de tal forma que gestione su manipulación segura, especialmente
considerando que no se puede quedar con saldo negativo.
El tipo abstracto de datos deberá permitir realizar ingresos y retirar efectivo de la cuenta bancaria.
Además, también permitirá aplicar un determinado interés al saldo, ası́ como cobrar unos gastos de
comisión.
El TAD Cuenta se definirá en el espacio de nombres umalcc_1, y deberá proporcionar métodos públicos
que soporten adecuadamente las siguientes operaciones. Diseñe también un programa que permita su
utilización.
El constructor por defecto creara una cuenta con valores por defecto. Habrá un constructor especı́fico
que reciba la información necesaria para crear adecuadamente una cuenta bancaria (nombre y número
de cuenta, de tal forma que el saldo será cero).
Mostrar en pantalla toda la información asociada a la cuenta bancaria.
Acceder a los valores almacenados en la estructura de datos (nombre, número de cuenta y saldo).
Ingresar una determinada cantidad de dinero, para añadir al saldo de la cuenta.
Retirar una determinada cantidad de dinero del saldo de la cuenta, considerando que el saldo nunca
podrá ser negativo.
Ingresar intereses, donde incrementará el saldo de la cuenta en un determinado porcentaje especificado
como parámetro.
Cargar gastos de comisión, retirando del saldo una cantidad que será especificada como parámetro,
considerando que el saldo nunca podrá ser negativo.
Solución: cuenta.hpp
#ifndef _cuenta_hpp_
#define _cuenta_hpp_
#include <string>
namespace umalcc_1 {
class Cuenta {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
//~Cuenta();

171
//Cuenta(const Cuenta& o);
//Cuenta& operator=(const Cuenta& o);
//-------------------------
Cuenta() ;
Cuenta(const std::string& n, unsigned c, double s) ;
void mostrar() const ;
std::string obtener_nombre() const ;
unsigned obtener_cuenta() const ;
double obtener_saldo() const ;
void ingresar_saldo(double inc, bool& ok);
void retirar_saldo(double inc, bool& ok);
void ingresar_interes(double porcentaje, bool& ok);
void retirar_comision(double inc, bool& ok);
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
std::string nombre;
unsigned num_cuenta;
double saldo;
//----------------------------------------------------------
};
}
#endif

Solución: cuenta.cpp
#include "cuenta.hpp"
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
namespace umalcc_1 {
//--------------------------------------------------------------------------
Cuenta::Cuenta() : nombre(), num_cuenta(0), saldo(0.0) {}
//--------------------------------------------------------------------------
Cuenta::Cuenta(const string& n, unsigned c, double s)
: nombre(n), num_cuenta(c), saldo(s) {}
//--------------------------------------------------------------------------
string Cuenta::obtener_nombre() const
{
return nombre;
}
//--------------------------------------------------------------------------
unsigned Cuenta::obtener_cuenta() const
{
return num_cuenta;
}
//--------------------------------------------------------------------------
double Cuenta::obtener_saldo() const
{
return saldo;
}
//--------------------------------------------------------------------------
void Cuenta::mostrar() const
{
cout << " Nombre: " << nombre << endl;
cout << " Cuenta: " << num_cuenta << endl;
cout << " Saldo: " << saldo << endl;
}
//--------------------------------------------------------------------------
void Cuenta::ingresar_saldo(double inc, bool& ok)
{
if ((inc < 0.0) || (saldo + inc < 0.0)) {
ok = false;
} else {
saldo += inc;
ok = true;
}
}
//--------------------------------------------------------------------------
void Cuenta::retirar_saldo(double inc, bool& ok)
{
if ((inc < 0.0) || (saldo - inc < 0.0)) {

172
ok = false;
} else {
saldo -= inc;
ok = true;
}
}
//--------------------------------------------------------------------------
void Cuenta::ingresar_interes(double porcentaje, bool& ok)
{
assert(porcentaje > 0.0);
double cantidad = saldo * porcentaje / 100.0;
ingresar_saldo(cantidad, ok);
}
//--------------------------------------------------------------------------
void Cuenta::retirar_comision(double cantidad, bool& ok)
{
if (saldo >= cantidad) {
retirar_saldo(cantidad, ok);
} else {
retirar_saldo(saldo, ok);
}
}
//--------------------------------------------------------------------------
}

2. Diseñe un programa que permita comprobar el funcionamiento del TAD especificado en el ejercicio anterior.
3. Diseñe e implemente un TAD (Banco) que represente las cuentas bancarias de los clientes de un banco,
utilizando para ello el TAD Cuenta bancaria diseñado en el apartado anterior.
Ası́, el TAD Banco se encargará de la gestión del global de cuentas a nivel del banco y considerando
múltiples clientes, utilizando para cada uno de ellos el TAD Cuenta, el cual se encargará de la manipulación
de la cuenta de un usuario, controlando que operaciones son válidas y cuales no.
Ası́, para cada cliente, se almacena su nombre (cadena de caracteres), el número de cuenta (número natu-
ral) y el saldo de la cuenta (número real), teniendo en cuenta que nunca podrá existir un saldo negativo. El
tipo abstracto de datos deberá permitir tanto crear nuevas cuentas bancarias como eliminarlas. Ası́ mismo,
deberá permitir realizar ingresos y retirar efectivo de una determinada cuenta bancaria, ası́ como realizar
transferencias de efectivo de una cuenta a otra. Además, también permitirá aplicar un determinado interés
a los saldos de las cuentas bancarias existentes, cobrar unos gastos de comisión, y mostrar los datos de
las cuentas bancarias almacenadas (del banco completo, para un determinado nombre de cliente, y para
un determinado número de cuenta). Ası́ mismo, también permitirá guardar y cargar los datos de ficheros.
Diseñe también un programa que permita su utilización.
El TAD Banco se definirá en el espacio de nombres umalcc_2, y deberá proporcionar métodos públicos
que soporten adecuadamente las siguientes operaciones:
Mostrar toda la información almacenada en la estructura de datos.
Crear una cuenta bancaria para una determinada persona, utilizando su nombre.
Eliminar una cuenta bancaria a partir de un número de cuenta.
Ingresar una determinada cantidad de dinero a un determinado número de cuenta.
Retirar una determinada cantidad de dinero de un determinado número de cuenta, considerando que
el saldo nunca podrá ser negativo.
Transferir una determinada cantidad de dinero desde un número de cuenta origen a otro número de
cuenta destino, considerando que el saldo nunca podrá ser negativo.
Ingresar intereses en todas las cuentas del banco, para un determinado porcentaje especificado como
parámetro.
Cargar gastos de comisión en todas las cuentas del banco, una cantidad que será especificada como
parámetro.
Mostrar en pantalla toda la información asociada a la cuenta bancaria cuyo número de cuenta se
especifica como parámetro.
Mostrar en pantalla toda la información asociada a todas las cuentas bancarias de de una persona
cuyo nombre se especifica como parámetro.

173
Guardar toda la información almacenada en la estructura de datos en el fichero cuyo nombre se especi-
fica como parámetro, en un formato adecuado para posteriormente poder recuperar la información
almacenada.
Cargar toda la información de la estructura de datos desde el fichero cuyo nombre se especifica como
parámetro, siguiendo el formato utilizado en la operación anterior.
Solución: banco.hpp
#ifndef _banco_hpp_
#define _banco_hpp_
#include <string>
#include <tr1/array>
#include "cuenta.hpp"
namespace umalcc_2 {
class Banco {
public:
//----------------------------------------------------------
//-- Metodos Publicos --------------------------------------
//----------------------------------------------------------
//~Banco();
//Banco(const Banco& o);
//Banco& operator=(const Banco& o);
//-------------------------
Banco();
void crear_cuenta(const std::string& nombre, unsigned& num, bool& ok);
void eliminar_cuenta(unsigned num, bool& ok);
void ingreso(unsigned num, double cantidad, bool& ok);
void retirar(unsigned num, double cantidad, bool& ok);
void transferir(unsigned dest, unsigned origen, double cantidad,
bool& ok);
void ingresar_intereses(double porcentaje);
void gastos_comision(double cantidad);
void mostrar_cuenta(unsigned num, bool& ok) const;
void mostrar_cuentas(const std::string& nombre) const;
void mostrar_cuentas() const ;
void cargar_cuentas_fich(const std::string& nombre_fich, bool& ok) ;
void guardar_cuentas_fich(const std::string& nombre_fich, bool& ok) const;
//----------------------------------------------------------
private:
//----------------------------------------------------------
//-- Ctes y Tipos Privados ---------------------------------
//----------------------------------------------------------
static const unsigned MAX = 256;
typedef std::tr1::array<umalcc_1::Cuenta, MAX> Datos;
//----------------------------------------------------------
//-- Metodos Privados --------------------------------------
//----------------------------------------------------------
void anyadir_cuenta(const umalcc_1::Cuenta& cuenta, bool& ok);
void elim_cuenta(unsigned p, bool& ok);
unsigned buscar(unsigned num) const;
void mostrar_cuenta_pos(unsigned pos) const;
//----------------------------------------------------------
//-- Atributos Privados ------------------------------------
//----------------------------------------------------------
unsigned cnt_cuentas;
unsigned ncuentas;
Datos cuentas;
//----------------------------------------------------------
};
}
#endif

Solución: banco.cpp
#include "banco.hpp"
#include <iostream>
#include <fstream>
#include <cassert>
using namespace std;
using namespace umalcc_1;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------------

174
//-- Subprogramas Auxiliares -----------------------------------------------
//--------------------------------------------------------------------------
void leer_cuenta(ifstream& fich, Cuenta& c)
{
string nombre;
unsigned cuenta;
double saldo;
fich >> ws; // salta espacios y saltos de linea
getline(fich, nombre); // lee el nombre (una linea completa)
fich >> cuenta; // lee la cuenta
fich >> saldo; // lee el saldo
c = Cuenta(nombre, cuenta, saldo);
}
//-----------------------------
void escribir_cuenta(ofstream& fich, const Cuenta& c)
{
fich << c.obtener_nombre() << endl;
fich << c.obtener_cuenta() << endl;
fich << c.obtener_saldo() << endl;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc_2.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc_2 {
//--------------------------------------------------------------------------
Banco::Banco()
: cnt_cuentas(1), ncuentas(0), cuentas() {}
//-----------------------------
void Banco::crear_cuenta(const std::string& nombre, unsigned& num, bool& ok)
{
anyadir_cuenta(Cuenta(nombre, cnt_cuentas, 0.0), ok);
if (ok) {
num = cnt_cuentas;
++cnt_cuentas;
}
}
//-----------------------------
void Banco::eliminar_cuenta(unsigned num, bool& ok)
{
unsigned p = buscar(num);
if (p < ncuentas) {
if (cuentas[p].obtener_saldo() > 0.0) {
ok = false;
} else {
elim_cuenta(p, ok);
}
} else {
ok = false;
}
}
//-----------------------------
void Banco::ingreso(unsigned num, double cantidad, bool& ok)
{
assert(cantidad > 0.0);
unsigned p = buscar(num);
if (p < ncuentas) {
cuentas[p].ingresar_saldo(cantidad, ok);
} else {
ok = false;
}
}
//-----------------------------
void Banco::retirar(unsigned num, double cantidad, bool& ok)
{
assert(cantidad > 0.0);
unsigned p = buscar(num);
if (p < ncuentas) {
cuentas[p].retirar_saldo(cantidad, ok);
} else {
ok = false;
}
}

175
//-----------------------------
void Banco::transferir(unsigned dest, unsigned origen, double cantidad,
bool& ok)
{
assert(cantidad > 0.0);
unsigned pd = buscar(dest);
if (pd < ncuentas) {
unsigned po = buscar(origen);
if (po < ncuentas) {
// primero se intenta retirar saldo, para comprobar
// que hay suficiente saldo
cuentas[po].retirar_saldo(cantidad, ok);
if (ok) {
cuentas[pd].ingresar_saldo(cantidad, ok);
if (! ok) {
cuentas[po].ingresar_saldo(cantidad, ok);
ok = false;
}
}
} else {
ok = false;
}
} else {
ok = false;
}
}
//-----------------------------
void Banco::ingresar_intereses(double porcentaje)
{
assert(porcentaje > 0.0);
bool ok;
for (unsigned i = 0; i < ncuentas; ++i) {
cuentas[i].ingresar_interes(porcentaje, ok);
if (! ok) {
cout << "Error al ingresar intereses a cuenta: "
<< cuentas[i].obtener_cuenta() << endl;
}
}
}
//-----------------------------
void Banco::gastos_comision(double cantidad)
{
assert(cantidad > 0.0);
bool ok;
for (unsigned i = 0; i < ncuentas; ++i) {
cuentas[i].retirar_comision(cantidad, ok);
if (! ok) {
cout << "Error al retirar comisiones a cuenta: "
<< cuentas[i].obtener_cuenta() << endl;
}
}
}
//-----------------------------
void Banco::mostrar_cuenta(unsigned num, bool& ok) const
{
unsigned p = buscar(num);
if (p < ncuentas) {
mostrar_cuenta_pos(p);
ok = true;
} else {
ok = false;
}
}
//-----------------------------
void Banco::mostrar_cuentas(const std::string& nombre) const
{
for (unsigned i = 0; i < ncuentas; ++i) {
if (nombre == cuentas[i].obtener_nombre()) {
mostrar_cuenta_pos(i);
}
}
cout << "-------------------------------------" << endl;
}
//-----------------------------

176
void Banco::mostrar_cuentas() const
{
for (unsigned i = 0; i < ncuentas; ++i) {
mostrar_cuenta_pos(i);
}
cout << "-------------------------------------" << endl;
}
//-----------------------------
void Banco::mostrar_cuenta_pos(unsigned pos) const
{
assert(pos < ncuentas);
cout << "-------------------------------------" << endl;
cuentas[pos].mostrar();
}
//-----------------------------
// FORMATO DEL FICHERO DE ENTRADA:
//
// <cnt_cuentas> <RC>
// <nombre> <RC> <cuenta> <RC> <saldo> <RC>
// <nombre> <RC> <cuenta> <RC> <saldo> <RC>
// ...
//-----------------------------
void Banco::cargar_cuentas_fich(const std::string& nombre_fich, bool& ok)
{
ifstream fich; // flujo de entrada de fichero
Cuenta c;

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {
ok = false; // error de apertura del fichero
} else {
ok = true;
ncuentas = 0; // inicializa las cuentas bancarias
fich >> cnt_cuentas; // lee el contador de cuentas
leer_cuenta(fich, c); // lee una cuenta
while (!fich.fail() && ok) { // mientras lectura correcta
anyadir_cuenta(c, ok); // anyade cuenta al banco
leer_cuenta(fich, c); // lee otra cuenta
}
ok = ok && fich.eof(); // correcto si alcanzado Fin-De-Fichero
fich.close(); // cierra la vinculacion
}
}
//-----------------------------
// FORMATO DEL FICHERO DE SALIDA:
//
// <cnt_cuentas> <RC>
// <nombre> <RC> <cuenta> <RC> <saldo> <RC>
// <nombre> <RC> <cuenta> <RC> <saldo> <RC>
// ...
//-----------------------------
void Banco::guardar_cuentas_fich(const std::string& nombre_fich, bool& ok) const
{
ofstream fich; // flujo de salida de fichero

fich.open(nombre_fich.c_str()); // vincula la vble de flujo con el fichero


if (fich.fail()) {
ok = false; // error de apertura del fichero
} else {
fich << cnt_cuentas << endl; // ecribe el contador de cuentas
unsigned i = 0; // Para cada elemento
while ((i < ncuentas) && (! fich.fail())) { // mientras fich en buen estado
escribir_cuenta(fich, cuentas[i]); // escribir persona en fichero
++i;
}
ok = ! fich.fail(); // correcto si flujo en buen estado
fich.close(); // cierra la vinculacion
}
}
//-----------------------------
void Banco::anyadir_cuenta(const Cuenta& cuenta, bool& ok)
{
if (ncuentas < cuentas.size()) {
cuentas[ncuentas] = cuenta;

177
++ncuentas;
ok = true;
} else {
ok = false;
}
}
//-----------------------------
void Banco::elim_cuenta(unsigned p, bool& ok)
{
if (p < ncuentas) {
if (p < ncuentas-1) {
cuentas[p] = cuentas[ncuentas-1];
}
--ncuentas;
ok = true;
} else {
ok = false;
}
}
//-----------------------------
// busca un numero de cuenta
unsigned Banco::buscar(unsigned num) const
{
unsigned i = 0;
while ((i < ncuentas)
&&(num != cuentas[i].obtener_cuenta())) {
++i;
}
return i;
}
//-----------------------------
}

4. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.


Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "banco.hpp"
using namespace std;
using namespace umalcc_2;
//-------------------------------------------------------------------------
void leer_nombre(string& n)
{
cout << "Introduza nombre: ";
cin >> ws;
getline(cin, n);
}
//---------------------------------
void leer_num_cuenta(unsigned& n)
{
cout << "Introduza numero de cuenta: ";
cin >> n;
}
void leer_num_cuenta_o(unsigned& n)
{
cout << "Introduza numero de cuenta origen: ";
cin >> n;
}
void leer_num_cuenta_d(unsigned& n)
{
cout << "Introduza numero de cuenta destino: ";
cin >> n;
}
//---------------------------------
void leer_cantidad(double& n)
{
do {
cout << "Introduza cantidad (euros): ";
cin >> n;
} while ( !(n > 0.0) );
}
//---------------------------------

178
void leer_porcentaje(double& n)
{
do {
cout << "Introduza porcentaje: ";
cin >> n;
} while ( !(n > 0.0 && n <= 100) );
}
//---------------------------------
void cargar_cuentas_bancarias(Banco& bnk)
{
string nombre_fich;
bool ok;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
bnk.cargar_cuentas_fich(nombre_fich, ok);
if (!ok) {
cout << "Error al cargar datos desde el fichero: "
<< nombre_fich << endl;
}
}
void guardar_cuentas_bancarias(const Banco& bnk)
{
string nombre_fich;
bool ok;
cout << "Introduce el nombre del fichero: ";
cin >> nombre_fich;
bnk.guardar_cuentas_fich(nombre_fich, ok);
if (!ok) {
cout << "Error al guardar datos al fichero: " << nombre_fich << endl;
}
}
void nueva_cuenta(Banco& bnk)
{
string n;
bool ok;
unsigned ncuenta;
leer_nombre(n);
bnk.crear_cuenta(n, ncuenta, ok);
if (ok) {
cout << "Nueva Cuenta: " << endl;
bnk.mostrar_cuenta(ncuenta, ok);
if (! ok) {
cout << "Error al mostrar cuenta: " << n << endl;
}
} else {
cout << "Error al crear nueva cuenta para: " << n << endl;
}
}
void eliminar_cuenta(Banco& bnk)
{
unsigned n;
bool ok;
leer_num_cuenta(n);
bnk.eliminar_cuenta(n, ok);
if (! ok) {
cout << "Error al eliminar cuenta: " << n << endl;
}
}
void ingresar_efectivo(Banco& bnk)
{
unsigned n;
bool ok;
double c;
leer_num_cuenta(n);
leer_cantidad(c);
bnk.ingreso(n, c, ok);
if (! ok) {
cout << "Error al ingresar efectivo en cuenta: " << n << endl;
}
}
void retirar_efectivo(Banco& bnk)
{
unsigned n;
bool ok;

179
double c;
leer_num_cuenta(n);
leer_cantidad(c);
bnk.retirar(n, c, ok);
if (! ok) {
cout << "Error al retirar efectivo de cuenta: " << n << endl;
}
}
void transferir_efectivo(Banco& bnk)
{
unsigned nd, no;
bool ok;
double c;
leer_num_cuenta_d(nd);
leer_num_cuenta_o(no);
leer_cantidad(c);
bnk.transferir(nd, no, c, ok);
if (! ok) {
cout << "Error al transferir efectivo desde: " << no
<< " hasta: " << nd << endl;
}
}
void intereses(Banco& bnk)
{
double p;
leer_porcentaje(p);
bnk.ingresar_intereses(p);
}
void comisiones(Banco& bnk)
{
double c;
leer_cantidad(c);
bnk.gastos_comision(c);
}
void mostrar_ncuenta(const Banco& bnk)
{
unsigned n;
bool ok;
leer_num_cuenta(n);
bnk.mostrar_cuenta(n, ok);
if (! ok) {
cout << "Error al mostrar cuenta: " << n << endl;
}
}
void mostrar_nombre(const Banco& bnk)
{
string n;
leer_nombre(n);
bnk.mostrar_cuentas(n);
}
void mostrar_completo(const Banco& bnk)
{
bnk.mostrar_cuentas();
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "A. Cargar Cuentas Bancarias" << endl;
cout << "B. Crear Cuenta Bancaria" << endl;
cout << "C. Eliminar Cuenta Bancaria" << endl;
cout << "D. Ingreso de Efectivo en Cuenta Bancaria" << endl;
cout << "E. Retirada de Efectivo de Cuenta Bancaria" << endl;
cout << "F. Transferencia de Efectivo de Cuenta Bancaria" << endl;
cout << "G. Ingresar Intereses en Cuentas Bancarias" << endl;
cout << "H. Gastos de Comision de Cuentas Bancarias" << endl;
cout << "I. Mostrar Cuentas Bancarias (num-cuenta)" << endl;
cout << "J. Mostrar Cuentas Bancarias (nombre)" << endl;
cout << "K. Mostrar Cuentas Bancarias (completo)" << endl;
cout << "L. Guardar Cuentas Bancarias" << endl;
cout << "X. Fin" << endl;
do {
cout << endl << " Opcion: ";

180
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||(op >= ’A’ && op <= ’L’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
Banco bnk;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
cargar_cuentas_bancarias(bnk);
break;
case ’B’:
nueva_cuenta(bnk);
break;
case ’C’:
eliminar_cuenta(bnk);
break;
case ’D’:
ingresar_efectivo(bnk);
break;
case ’E’:
retirar_efectivo(bnk);
break;
case ’F’:
transferir_efectivo(bnk);
break;
case ’G’:
intereses(bnk);
break;
case ’H’:
comisiones(bnk);
break;
case ’I’:
mostrar_ncuenta(bnk);
break;
case ’J’:
mostrar_nombre(bnk);
break;
case ’K’:
mostrar_completo(bnk);
break;
case ’L’:
guardar_cuentas_bancarias(bnk);
break;
}
} while (op != ’X’);
}

Práctica 3: Tipos Abstractos de Datos Genéricos


1. Se pretende gestionar las ofertas de paquetes turı́sticos en una agencia de viajes, de tal forma que se dispone
de una estrutura de datos donde se utiliza un TAD mapa genérico (Tema 2.3 Ej. 4) para almacenar la
información relativa a los paquetes turı́sticos, cuya clave de acceso es el nombre del paquete turı́stico, y
donde se almacena como valor asociado a la clave el número de plazas y el precio por cada plaza.

egipto-1 grecia-1 grecia-2 holanda-1


1 750 2 1000 3 200 2 135

Implemente el TAD genérico especificado anteriormente (mapa), y defina un nuevo TAD PaqTur que
soporte adecuadamente la estructura de datos especificada anteriormente, instanciando el TAD genérico
a los tipos de datos necesarios. Ası́ mismo, el TAD PaqTur deberá proporcionar los siguientes métodos
públicos:
Constructor por defecto que permita construir una estructura de datos vacı́a.

181
Destructor que libera los recursos asociados al objeto.
Mostrar muestra en pantalla toda la información de la estructura de datos.
Añadir paquete turı́stico, que recibirá como parámetros la información suficiente para ello y la
añadirá en el mapa, utilizando como clave el nombre del paquete turı́stico.
Eliminar paquete turı́stico, que recibe el nombre del paquete turı́stico, y lo elimina del mapa de
paquetes turı́sticos.
Guardar toda la información almacenada en la estructura de datos en el fichero cuyo nombre se especi-
fica como parámetro, en un formato adecuado para posteriormente poder recuperar la información
almacenada. Por ejemplo, para la información de la figura anterior:
paqtur.txt

1 750 egipto-1
2 1000 grecia-1
3 200 grecia-2
2 135 holanda-1

Cargar toda la información de la estructura de datos desde el fichero cuyo nombre se especifica como
parámetro, siguiendo el formato utilizado en la operación anterior.
Solución: paqtur.hpp
#ifndef _paqtur_hpp_
#define _paqtur_hpp_
#include <iostream>
#include <string>
#include <tr1/array>
#include "../../t23/mapa/mapa.hpp"
namespace umalcc {
class PaqTur {
public:
// ~PaqTur(); // Generado Automaticamente
// PaqTur(); // Generado Automaticamente
// PaqTur(const PaqTur& o); // Generado Automaticamente
// PaqTur& operator=(const PaqTur& o); // Generado Automaticamente
void mostrar() const ;
void anyadir_pq(const std::string& nombre, int plazas, int precio,
bool& ok);
void eliminar_pq(const std::string& nombre, bool& ok);
void guardar(const std::string& nombre_pq, bool& ok) const ;
void cargar(const std::string& nombre_pq, bool& ok) ;
private:
//------------------------------
//-- Ctes & Tipos --------------
//------------------------------
static const int MAX_PAQTUR = 20;
struct PTur {
// std::string nombre; // Es la Clave del Mapa
int plazas;
int precio;
};
typedef Mapa<PTur, MAX_PAQTUR> MPTur;
//------------------------------
//-- Metodos -------------------
//------------------------------
void mostrar_pq(const MPTur& pq) const;
void guardar_pq(const std::string& nombre_pq, bool& ok) const;
void cargar_pq(const std::string& nombre_pq, bool& ok);
//------------------------------
//-- Atributos -----------------
//------------------------------
MPTur paqtur;
};
}
#endif

Solución: paqtur.cpp
#include "paqtur.hpp"
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cassert>
using namespace std;

182
using namespace umalcc;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------------
//-- Subprogramas Auxiliares -----------------------------------------------
//--------------------------------------------------------------------------
struct PqT {
string n;
int pl;
int pr;
};
void leer_pq(ifstream& fich, PqT& p)
{
fich >> p.pl; // lee plazas
fich >> p.pr; // lee precio
fich >> ws; // salta espacios y saltos de linea
getline(fich, p.n); // lee el nombre (una linea completa)
}
void escribir_pq(ofstream& fich, const PqT& p)
{
fich << p.pl << " " << p.pr << " " << p.n << endl;
}
//-----------------------------
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Publicos --------------------------------------------------------------
//--------------------------------------------------------------------------
void PaqTur::mostrar() const
{
mostrar_pq(paqtur);
}
//--------------------------------------------------------------------------
void PaqTur::anyadir_pq(const string& nombre, int plazas, int precio,
bool& ok)
{
if (paqtur.lleno()) {
ok = false;
} else {
ok = true;
PTur p = { plazas, precio};
paqtur.insertar(nombre, p);
}
}
//--------------------------------------------------------------------------
void PaqTur::eliminar_pq(const string& nombre, bool& ok)
{
paqtur.eliminar(nombre, ok);
}
//--------------------------------------------------------------------------
void PaqTur::guardar(const string& nombre_pq, bool& ok) const
{
guardar_pq(nombre_pq, ok);
}
//--------------------------------------------------------------------------
void PaqTur::cargar(const string& nombre_pq, bool& ok)
{
cargar_pq(nombre_pq, ok);
}
//--------------------------------------------------------------------------
//-- Privados --------------------------------------------------------------
//--------------------------------------------------------------------------
void PaqTur::mostrar_pq(const MPTur& pq) const
{
cout << "Paquetes Turisticos:" << endl;
for (int i = 0; i < pq.size(); ++i) {
string n = pq.acceder_clave(i);

183
PTur p = pq.acceder_valor(i);
cout << "Nombre: " << setw(20) << n
<< " Plazas: " << setw(5) << p.plazas
<< " Precio: " << setw(5) << p.precio
<< endl;
}
}
//--------------------------------------------------------------------------
void PaqTur::guardar_pq(const string& nombre_pq, bool& ok) const
{
ofstream fich;
fich.open(nombre_pq.c_str());
if (fich.fail()) {
ok = false;
} else {
for (int i = 0; (i < paqtur.size()) && (! fich.fail()); ++i) {
string n = paqtur.acceder_clave(i);
PTur p = paqtur.acceder_valor(i);
PqT pq = { n, p.plazas, p.precio };
escribir_pq(fich, pq);
}
ok = ! fich.fail();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::cargar_pq(const string& nombre_pq, bool& ok)
{
ifstream fich;
fich.open(nombre_pq.c_str());
if (fich.fail()) {
ok = false;
} else {
ok = true;
paqtur.clear();
PqT pq;
leer_pq(fich, pq);
while (!fich.fail() && ok) {
anyadir_pq(pq.n, pq.pl, pq.pr, ok);
leer_pq(fich, pq);
}
ok = ok && fich.eof();
fich.close();
}
}
//--------------------------------------------------------------------------
}

2. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.


Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "paqtur.hpp"
using namespace std;
using namespace umalcc;
//-------------------------------------------------------------------------
void leer_nombre(const string& msj, string& n)
{
cout << msj;
cin >> ws;
getline(cin, n);
}
//---------------------------------
void leer_num(const string& msj, int& n)
{
do {
cout << msj;
cin >> n;
} while ( !(n > 0) );
}
//---------------------------------
void cargar(PaqTur& pq)

184
{
string nombre_pq, nombre_cl;
bool ok;
cout << "Introduce el nombre del fichero de paquetes turisticos: ";
cin >> nombre_pq;
pq.cargar(nombre_pq, ok);
if (!ok) {
cout << "Error al cargar datos desde el fichero: "
<< nombre_pq << endl;
}
}
void guardar(const PaqTur& pq)
{
string nombre_pq, nombre_cl;
bool ok;
cout << "Introduce el nombre del fichero de paquetes turisticos: ";
cin >> nombre_pq;
pq.guardar(nombre_pq, ok);
if (!ok) {
cout << "Error al guardar datos al fichero: "
<< nombre_pq << endl;
}
}
//--------------------------------------
void nuevo_paqtur(PaqTur& pq)
{
string n;
int plazas, precio;
bool ok;
leer_nombre("Introduzca nombre del paquete turistico: ", n);
leer_num("Introduzca numero de plazas: ", plazas);
leer_num("Introduzca precio: ", precio);
pq.anyadir_pq(n, plazas, precio, ok);
if (!ok) {
cout << "Error al crear nuevo paquete turistico: " << n << endl;
}
}
void eliminar_paqtur(PaqTur& pq)
{
string n;
bool ok;
leer_nombre("Introduzca nombre del paquete turistico: ", n);
pq.eliminar_pq(n, ok);
if (! ok) {
cout << "Error al eliminar paquete turistico: " << n << endl;
}
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Mostrar Informacion" << endl;
cout << "B. A~nadir Paquete Turistico" << endl;
cout << "C. Eliminar Paquete Turistico" << endl;
cout << "D. Guardar en Fichero" << endl;
cout << "E. Cargar de Fichero" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||(op >= ’A’ && op <= ’E’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
PaqTur pq;
char op = ’ ’;
do {
op = menu();
switch (op) {

185
case ’A’:
pq.mostrar();
break;
case ’B’:
nuevo_paqtur(pq);
break;
case ’C’:
eliminar_paqtur(pq);
break;
case ’E’:
guardar(pq);
break;
case ’F’:
cargar(pq);
break;
}
} while (op != ’X’);
}

3. Además de la gestión de ofertas de paquetes turı́sticos del ejercicio anterior, se pretente también gestionar
los posibles clientes de una agencia de viajes, de tal forma que a la estructura de datos del ejercicio
anterior (que contiene un mapa genérico para almacenar los paquetes turı́sticos) se le añadirá un TAD
vector genérico (Tema 2.3 Ej. 1) para almacenar la información de los clientes, de tal forma que, para
cada cliente, almacena el nombre del cliente y el dinero que tiene disponible.

egipto-1 grecia-1 grecia-2 holanda-1 juan luis victor ana


1 750 2 1000 3 200 2 135 1100 800 500 300

Implemente los TADs genéricos especificados anteriormente (mapa, y vector), y defina un nuevo TAD
PaqTur que soporte adecuadamente la estructura de datos especificada anteriormente, instanciando los
TADs genéricos a los tipos de datos necesarios. Ası́ mismo, el TAD PaqTur deberá proporcionar los
siguientes métodos públicos:

Constructor por defecto que permita construir una estructura de datos vacı́a.
Destructor que libera los recursos asociados al objeto.
Mostrar muestra en pantalla toda la información de la estructura de datos.
Añadir paquete turı́stico, que recibirá como parámetros la información suficiente para ello y la
añadirá en el mapa, utilizando como clave el nombre del paquete turı́stico.
Eliminar paquete turı́stico, que recibe el nombre del paquete turı́stico, y lo elimina del mapa de
paquetes turı́sticos.
Añadir cliente, que recibe el nombre del cliente y la cantidad de dinero que tiene disponible.
Eliminar cliente, que recibe el nombre del cliente, y lo elimina del vector de clientes.
Guardar toda la información almacenada en la estructura de datos los ficheros cuyos nombres se
especifican como parámetros, en un formato adecuado para posteriormente poder recuperar la infor-
mación almacenada. Se utilizaran dos ficheros, uno para almacenar la información de los paquetes
turı́sticos, y otro fichero para almacenar la información de los clientes. Por ejemplo, para la informa-
ción de la figura anterior:
paqtur.txt clientes.txt

1 750 egipto-1 juan


2 1000 grecia-1 1100
3 200 grecia-2 luis
2 135 holanda-1 800
victor
500
ana
300

Cargar toda la información de la estructura de datos desde los ficheros cuyos nombres se especifican
como parámetros, siguiendo el formato utilizado en la operación anterior.

186
Solución: paqtur.hpp
#ifndef _paqtur_hpp_
#define _paqtur_hpp_
#include <iostream>
#include <string>
#include <tr1/array>
#include "../../t23/mapa/mapa.hpp"
#include "../../t23/vector/vector.hpp"
namespace umalcc {
class PaqTur {
public:
// ~PaqTur(); // Generado Automaticamente
// PaqTur(); // Generado Automaticamente
// PaqTur(const PaqTur& o); // Generado Automaticamente
// PaqTur& operator=(const PaqTur& o); // Generado Automaticamente
void mostrar() const ;
void anyadir_pq(const std::string& nombre, int plazas, int precio,
bool& ok);
void eliminar_pq(const std::string& nombre, bool& ok);
void anyadir_cli(const std::string& nombre, int dinero, bool& ok);
void eliminar_cli(const std::string& nombre, bool& ok);
void guardar(const std::string& nombre_pq,
const std::string& nombre_cli, bool& ok) const ;
void cargar(const std::string& nombre_pq,
const std::string& nombre_cli, bool& ok) ;
private:
//------------------------------
//-- Ctes & Tipos --------------
//------------------------------
static const int MAX_PAQTUR = 20;
static const int MAX_CLIENTES = 10;
struct Cliente {
std::string nombre;
int dinero;
};
typedef Vector<Cliente, MAX_CLIENTES> VClientes;
struct PTur {
// std::string nombre; // Es la Clave del Mapa
int plazas;
int precio;
};
typedef Mapa<PTur, MAX_PAQTUR> MPTur;
//------------------------------
//-- Metodos -------------------
//------------------------------
void mostrar_pq(const MPTur& pq) const;
void mostrar_cli(const VClientes& c) const;
bool iguales(const std::string& nombre, const Cliente& c) const;
int buscar(const VClientes& vcl, const std::string& nombre) const;
void guardar_pq(const std::string& nombre_pq, bool& ok) const;
void cargar_pq(const std::string& nombre_pq, bool& ok);
void escribir_cli(std::ofstream& fich, const Cliente& c) const;
void guardar_cli(const std::string& nombre_cli, bool& ok) const;
void leer_cli(std::ifstream& fich, Cliente& c) const;
void cargar_cli(const std::string& nombre_cli, bool& ok);
//------------------------------
//-- Atributos -----------------
//------------------------------
MPTur paqtur;
VClientes clientes;
};
}
#endif

Solución: paqtur.cpp
#include "paqtur.hpp"
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cassert>
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la

187
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------------
//-- Subprogramas Auxiliares -----------------------------------------------
//--------------------------------------------------------------------------
struct PqT {
string n;
int pl;
int pr;
};
void leer_pq(ifstream& fich, PqT& p)
{
fich >> p.pl; // lee plazas
fich >> p.pr; // lee precio
fich >> ws; // salta espacios y saltos de linea
getline(fich, p.n); // lee el nombre (una linea completa)
}
void escribir_pq(ofstream& fich, const PqT& p)
{
fich << p.pl << " " << p.pr << " " << p.n << endl;
}
//-----------------------------
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Publicos --------------------------------------------------------------
//--------------------------------------------------------------------------
void PaqTur::mostrar() const
{
mostrar_pq(paqtur);
mostrar_cli(clientes);
}
//--------------------------------------------------------------------------
void PaqTur::anyadir_pq(const string& nombre, int plazas, int precio,
bool& ok)
{
if (paqtur.lleno()) {
ok = false;
} else {
ok = true;
PTur p = { plazas, precio};
paqtur.insertar(nombre, p);
}
}
//--------------------------------------------------------------------------
void PaqTur::eliminar_pq(const string& nombre, bool& ok)
{
paqtur.eliminar(nombre, ok);
}
//--------------------------------------------------------------------------
void PaqTur::anyadir_cli(const string& nombre, int dinero, bool& ok)
{
int i = buscar(clientes, nombre);
if ((i < clientes.size())||(clientes.lleno())) {
ok = false;
} else {
ok = true;
Cliente c = { nombre, dinero };
clientes.insertar(c);
}
}
//--------------------------------------------------------------------------
void PaqTur::eliminar_cli(const string& nombre, bool& ok)
{
int i = buscar(clientes, nombre);
if (i < clientes.size()) {
if (i < clientes.size()-1) {
clientes.modificar(i, clientes.acceder(clientes.size()-1));
}

188
clientes.eliminar();
ok = true;
} else {
ok = false;
}
}
//--------------------------------------------------------------------------
void PaqTur::guardar(const string& nombre_pq,
const string& nombre_cli, bool& ok) const
{
guardar_pq(nombre_pq, ok);
if (ok) {
guardar_cli(nombre_cli, ok);
}
}
//--------------------------------------------------------------------------
void PaqTur::cargar(const string& nombre_pq,
const string& nombre_cli, bool& ok)
{
cargar_pq(nombre_pq, ok);
if (ok) {
cargar_cli(nombre_cli, ok);
}
}
//--------------------------------------------------------------------------
//-- Privados --------------------------------------------------------------
//--------------------------------------------------------------------------
void PaqTur::mostrar_pq(const MPTur& pq) const
{
cout << "Paquetes Turisticos:" << endl;
for (int i = 0; i < pq.size(); ++i) {
string n = pq.acceder_clave(i);
PTur p = pq.acceder_valor(i);
cout << "Nombre: " << setw(20) << n
<< " Plazas: " << setw(5) << p.plazas
<< " Precio: " << setw(5) << p.precio
<< endl;
}
}
//--------------------------------------------------------------------------
void PaqTur::mostrar_cli(const VClientes& vcli) const
{
cout << "Clientes:" << endl;
for (int i = 0; i < vcli.size(); ++i) {
Cliente c = vcli.acceder(i);
cout << "Nombre: " << setw(20) << c.nombre
<< " Dinero: " << setw(5) << c.dinero ;
cout << endl;
}
}
//--------------------------------------------------------------------------
bool PaqTur::iguales(const string& nombre, const Cliente& c) const
{
return nombre == c.nombre;
}
//--------------------------------------------------------------------------
int PaqTur::buscar(const VClientes& vcl, const string& nombre) const
{
int i = 0;
while ((i < vcl.size())&& !iguales(nombre, vcl.acceder(i))) {
++i;
}
return i;
}
//--------------------------------------------------------------------------
void PaqTur::guardar_pq(const string& nombre_pq, bool& ok) const
{
ofstream fich;
fich.open(nombre_pq.c_str());
if (fich.fail()) {
ok = false;
} else {
for (int i = 0; (i < paqtur.size()) && (! fich.fail()); ++i) {
string n = paqtur.acceder_clave(i);

189
PTur p = paqtur.acceder_valor(i);
PqT pq = { n, p.plazas, p.precio };
escribir_pq(fich, pq);
}
ok = ! fich.fail();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::cargar_pq(const string& nombre_pq, bool& ok)
{
ifstream fich;
fich.open(nombre_pq.c_str());
if (fich.fail()) {
ok = false;
} else {
ok = true;
paqtur.clear();
PqT pq;
leer_pq(fich, pq);
while (!fich.fail() && ok) {
anyadir_pq(pq.n, pq.pl, pq.pr, ok);
leer_pq(fich, pq);
}
ok = ok && fich.eof();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::escribir_cli(ofstream& fich, const Cliente& c) const
{
fich << c.nombre << endl;
fich << c.dinero << " " << endl;
}
//--------------------------------------------------------------------------
void PaqTur::guardar_cli(const string& nombre_cli, bool& ok) const
{
ofstream fich;
fich.open(nombre_cli.c_str());
if (fich.fail()) {
ok = false;
} else {
for (int i = 0; (i < clientes.size()) && (! fich.fail()); ++i) {
escribir_cli(fich, clientes.acceder(i));
}
ok = ! fich.fail();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::leer_cli(ifstream& fich, Cliente& c) const
{
fich >> ws; // salta espacios y saltos de linea
getline(fich, c.nombre); // lee el nombre (una linea completa)
fich >> c.dinero; // lee dinero disponible
}
//--------------------------------------------------------------------------
void PaqTur::cargar_cli(const string& nombre_cli, bool& ok)
{
ifstream fich;
fich.open(nombre_cli.c_str());
if (fich.fail()) {
ok = false;
} else {
ok = true;
clientes.clear();
Cliente c;
leer_cli(fich, c);
while (!fich.fail() && ok) {
anyadir_cli(c.nombre, c.dinero, ok);
leer_cli(fich, c);
}
ok = ok && fich.eof();
fich.close();

190
}
}
//--------------------------------------------------------------------------
}

4. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.


Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "paqtur.hpp"
using namespace std;
using namespace umalcc;
//-------------------------------------------------------------------------
void leer_nombre(const string& msj, string& n)
{
cout << msj;
cin >> ws;
getline(cin, n);
}
//---------------------------------
void leer_num(const string& msj, int& n)
{
do {
cout << msj;
cin >> n;
} while ( !(n > 0) );
}
//---------------------------------
void cargar(PaqTur& pq)
{
string nombre_pq, nombre_cl;
bool ok;
cout << "Introduce el nombre del fichero de paquetes turisticos: ";
cin >> nombre_pq;
cout << "Introduce el nombre del fichero de clientes: ";
cin >> nombre_cl;
pq.cargar(nombre_pq, nombre_cl, ok);
if (!ok) {
cout << "Error al cargar datos desde el fichero: "
<< nombre_pq << " " << nombre_cl << endl;
}
}
void guardar(const PaqTur& pq)
{
string nombre_pq, nombre_cl;
bool ok;
cout << "Introduce el nombre del fichero de paquetes turisticos: ";
cin >> nombre_pq;
cout << "Introduce el nombre del fichero de clientes: ";
cin >> nombre_cl;
pq.guardar(nombre_pq, nombre_cl, ok);
if (!ok) {
cout << "Error al guardar datos al fichero: "
<< nombre_pq << " " << nombre_cl << endl;
}
}
//--------------------------------------
void nuevo_paqtur(PaqTur& pq)
{
string n;
int plazas, precio;
bool ok;
leer_nombre("Introduzca nombre del paquete turistico: ", n);
leer_num("Introduzca numero de plazas: ", plazas);
leer_num("Introduzca precio: ", precio);
pq.anyadir_pq(n, plazas, precio, ok);
if (!ok) {
cout << "Error al crear nuevo paquete turistico: " << n << endl;
}
}
void eliminar_paqtur(PaqTur& pq)
{

191
string n;
bool ok;
leer_nombre("Introduzca nombre del paquete turistico: ", n);
pq.eliminar_pq(n, ok);
if (! ok) {
cout << "Error al eliminar paquete turistico: " << n << endl;
}
}
//--------------------------------------
void nuevo_cliente(PaqTur& pq)
{
string n;
int dinero;
bool ok;
leer_nombre("Introduzca nombre del cliente: ", n);
leer_num("Introduzca cantidad de dinero disponible: ", dinero);
pq.anyadir_cli(n, dinero, ok);
if (!ok) {
cout << "Error al crear nuevo cliente: " << n << endl;
}
}
void eliminar_cliente(PaqTur& pq)
{
string n;
bool ok;
leer_nombre("Introduzca nombre del cliente: ", n);
pq.eliminar_cli(n, ok);
if (! ok) {
cout << "Error al eliminar cliente: " << n << endl;
}
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Mostrar Informacion" << endl;
cout << "B. A~nadir Paquete Turistico" << endl;
cout << "C. Eliminar Paquete Turistico" << endl;
cout << "D. A~nadir Cliente" << endl;
cout << "E. Eliminar Cliente" << endl;
cout << "F. Guardar en Fichero" << endl;
cout << "G. Cargar de Fichero" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||(op >= ’A’ && op <= ’G’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
PaqTur pq;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
pq.mostrar();
break;
case ’B’:
nuevo_paqtur(pq);
break;
case ’C’:
eliminar_paqtur(pq);
break;
case ’D’:
nuevo_cliente(pq);
break;
case ’E’:
eliminar_cliente(pq);

192
break;
case ’F’:
guardar(pq);
break;
case ’G’:
cargar(pq);
break;
}
} while (op != ’X’);
}

5. Una vez finalizada la práctica anterior, realice la práctica propuesta en el ejercicio ?? de la sección 1 de
Problemas de Examen (Gestión de Agencia de Viajes).

Práctica 4: Gestión de Memoria Dinámica


1. Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de listas
enlazadas en memoria dinámica (véase siguiente figura). El módulo deberá definir el tipo PNodo como un
puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al siguiente nodo, ası́ como
un dato de tipo int. Además, el módulo deberá definir los siguientes subprogramas. Diseñe también un
módulo principal que permita comprobar la corrección del módulo lista implementado.

lista: ◦−−→ ◦−−−−→ ◦−−−−→ 


0 1 2

Solución: lista.hpp
#ifndef _lista_hpp__
#define _lista_hpp__ 1
namespace umalcc {
//----------------------------------
struct Nodo ; // Declaración adelantada del tipo incompleto Nodo
typedef Nodo* PNodo ; // Definición de tipo Puntero a tipo incompleto Nodo
struct Nodo { // Definición del tipo Nodo
PNodo sig ; // Enlace a la siguiente estructura dinámica
int dato ; // Dato almacenado en la lista
} ;
//----------------------------------
void inicializa(PNodo& lista) ;
// Inicializa (lista) a una lista vacia
//----------------------------------
void destruir(PNodo& lista) ;
// Destruye todos los elementos de la lista, liberando
// todos los nodos de memoria dinamica. (lista) queda vacia
//----------------------------------
void insertar_principio(PNodo& lista, int dt) ;
// Inserta un elemento al principio de (lista)
//----------------------------------
void insertar_final(PNodo& lista, int dt) ;
// Inserta un elemento al final de (lista)
//----------------------------------
PNodo situar(PNodo lista, int pos) ;
// Devuelve un puntero al nodo que se encuentra en la posicion
// indicada por (pos). La posicion cero (0) indica el primer nodo.
// Si el nodo no existe, entonces devuelve NULL
//----------------------------------
void insertar_pos(PNodo& lista, int pos, int dt) ;
// Inserta en (lista) un elemento en la posicion indicada por (pos)
//----------------------------------
void eliminar_primero(PNodo& lista) ;
// Elimina el primer elemento de (lista), si existe
//----------------------------------
void eliminar_ultimo(PNodo& lista) ;
// Elimina el ultimo elemento de (lista), si existe
//----------------------------------
void eliminar_pos(PNodo& lista, int pos) ;
// Elimina de (lista) el elemento de la posicion indicada por (pos),
// si existe

193
//----------------------------------
PNodo duplicar(PNodo lista) ;
// Devuelve un puntero a una nueva lista resultado de duplicar en
// memoria dinamica la lista recibida como parametro
//----------------------------------
void escribir(PNodo lista) ;
// Muestra en pantalla el contenido de (lista)
//----------------------------------
PNodo leer() ;
// Devuelve una lista con los numeros leidos de teclado (en el mismo
// orden que son introducidos) hasta que lea el numero 0 (que no es
// introducido)
//----------------------------------
PNodo buscar(PNodo lista, int dt) ;
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
//----------------------------------
void insertar_ord(PNodo& lista, int dt) ;
// Inserta un elemento de forma ordenada en (lista), que debe estar ordenada
//----------------------------------
void eliminar_elem(PNodo& lista, int dt) ;
// Elimina de (lista) el primer elemento igual a (dt), si existe
//----------------------------------
void eliminar_mayor(PNodo& lista) ;
// Elimina el mayor elemento de (lista), si existe
//----------------------------------
void purgar(PNodo& lista, int dt) ;
// Elimina de (lista) todos los elementos que sean iguales a (dt),
// si existen
//----------------------------------
}
#endif

Solución: lista.cpp
#include "lista.hpp"
#include <iostream>
#include <cstddef>
#include <cassert>
using namespace std;
using namespace umalcc ;
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Inicializa (lista) a una lista vacia
// sera insertado en la lista)
void inicializa(PNodo& lista)
{
lista = NULL ;
}
//--------------------------------------------------------------------------
// Destruye todos los elementos de la lista, liberando
// todos los nodos de memoria dinamica. (lista) queda vacia
void destruir(PNodo& lista)
{
while (lista != NULL) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
// Inserta un elemento al principio de (lista)
void insertar_principio(PNodo& lista, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
ptr->sig = lista ;
lista = ptr ;
}

194
//--------------------------------------------------------------------------
// Inserta un elemento al final de (lista)
void insertar_final(PNodo& lista, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
ptr->sig = NULL ;
if (lista == NULL) {
lista = ptr ;
} else {
PNodo act = lista ;
while (act->sig != NULL) {
act = act->sig ;
}
act->sig = ptr ;
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que se encuentra en la posicion
// indicada por (pos). La posicion cero (0) indica el primer nodo.
// Si el nodo no existe, entonces devuelve NULL
PNodo situar(PNodo lista, int pos)
{
PNodo ptr = lista;
while ((ptr != NULL)&&(pos > 0)) {
ptr = ptr->sig;
--pos;
}
return ptr;
}
//--------------------------------------------------------------------------
// Inserta en (lista) un elemento en la posicion indicada por (pos)
void insertar_pos(PNodo& lista, int pos, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
if (pos < 1) {
ptr->sig = lista ;
lista = ptr ;
} else {
PNodo ant = situar(lista, pos - 1);
if (ant != NULL) {
ptr->sig = ant->sig ;
ant->sig = ptr ;
}
}
}
//--------------------------------------------------------------------------
// Elimina el primer elemento de (lista), si existe
void eliminar_primero(PNodo& lista)
{
if (lista != NULL) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
}
}
//--------------------------------------------------------------------------
// Elimina el ultimo elemento de (lista), si existe
void eliminar_ultimo(PNodo& lista)
{
if (lista != NULL) {
if (lista->sig == NULL) {
delete lista ;
lista = NULL ;
} else {
PNodo ant = lista ;
PNodo act = ant->sig ;
while (act->sig != NULL) {
ant = act ;
act = act->sig ;
}
delete act ;
ant->sig = NULL ;

195
}
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) el elemento de la posicion indicada por (pos),
// si existe
void eliminar_pos(PNodo& lista, int pos)
{
if (lista != NULL) {
if (pos < 1) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
} else {
PNodo ant = situar(lista, pos - 1) ;
if ((ant != NULL)&&(ant->sig != NULL)) {
PNodo act = ant->sig ;
ant->sig = act->sig ;
delete act ;
}
}
}
}
//--------------------------------------------------------------------------
// Devuelve un puntero a una nueva lista resultado de duplicar en
// memoria dinamica la lista recibida como parametro
PNodo duplicar(PNodo lista)
{
PNodo nueva = NULL;
if (lista != NULL) {
nueva = new Nodo ;
nueva->dato = lista->dato ;
PNodo u = nueva ;
PNodo p = lista->sig ;
while (p != NULL) {
u->sig = new Nodo ;
u->sig->dato = p->dato ;
u = u->sig ;
p = p->sig ;
}
u->sig = NULL ;
}
return nueva;
}
//--------------------------------------------------------------------------
// Muestra en pantalla el contenido de (lista)
void escribir(PNodo lista)
{
PNodo ptr = lista;
while (ptr != NULL) {
cout << ptr->dato << " " ;
ptr = ptr->sig ;
}
cout << endl;
}
//--------------------------------------------------------------------------
// Devuelve una lista con los numeros leidos de teclado (en el mismo
// orden que son introducidos) hasta que lea el numero 0 (que no es
// introducido)
PNodo leer()
{
PNodo lista = NULL ;
int dt ;
cin >> dt ;
if (dt != 0) {
lista = new Nodo ;
lista->dato = dt ;
PNodo u = lista ;
cin >> dt ;
while (dt != 0) {
u->sig = new Nodo ;
u->sig->dato = dt ;
u = u->sig ;
cin >> dt ;

196
}
u->sig = NULL ;
}
return lista;
}
//--------------------------------------------------------------------------
// Devuelve un puntero al nodo que contiene el elemento
// igual a (dt). Si no se encuentra, entonces devuelve NULL
PNodo buscar(PNodo lista, int dt)
{
PNodo ptr = lista ;
while ((ptr != NULL)&&(ptr->dato != dt)) {
ptr = ptr->sig ;
}
return ptr ;
}
//--------------------------------------------------------------------------
// Inserta un elemento de forma ordenada en (lista), que debe estar
// ordenada
void insertar_ord(PNodo& lista, int dt)
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
if ((lista == NULL)||(dt < lista->dato)) {
ptr->sig = lista ;
lista = ptr ;
} else {
PNodo ant = lista ;
PNodo act = ant->sig ;
while ((act!=NULL)&&(act->dato<=dt)) {
ant = act ;
act = act->sig ;
}
ptr->sig = ant->sig ;
ant->sig = ptr ;
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) el primer elemento igual a (dt), si existe
void eliminar_elem(PNodo& lista, int dt)
{
if (lista != NULL) {
if (lista->dato == dt) {
PNodo ptr = lista ;
lista = lista->sig ;
delete ptr ;
} else {
PNodo ant = lista ;
PNodo act = ant->sig ;
while ((act != NULL)&&(act->dato != dt)) {
ant = act ;
act = act->sig ;
}
if (act != NULL) {
ant->sig = act->sig ;
delete act ;
}
}
}
}
//--------------------------------------------------------------------------
// Elimina el mayor elemento de (lista), si existe
void eliminar_mayor(PNodo& lista)
{
if (lista != NULL) {
PNodo ant_may = NULL; // anterior al mayor
PNodo ptr_may = lista; // apunta al mayor
PNodo ant = lista; // anterior al ptr
PNodo ptr = lista->sig; // ptr de recorrido
while (ptr != NULL) {
if (ptr->dato > ptr_may->dato) { // si es mayor
ant_may = ant; // actualizar anterior al mayor
ptr_may = ptr; // actualizar el puntero al mayor
}

197
ant = ptr; // mover los punteros de recorrido
ptr = ptr->sig;
}
if (ptr_may == lista) {
lista = lista->sig;
} else {
ant_may->sig = ptr_may->sig;
}
delete ptr_may;
}
}
//--------------------------------------------------------------------------
// Elimina de (lista) todos los elementos que sean iguales a (dt),
// si existen
void purgar(PNodo& lista, int dt)
{
while ((lista != NULL)&&(dt == lista->dato)) {
PNodo ptr = lista;
lista = lista->sig;
delete ptr;
}
if (lista != NULL) {
PNodo ant = lista;
PNodo act = lista->sig;
while (act != NULL) {
if (dt == act->dato) {
ant->sig = act->sig;
delete act;
} else {
ant = act;
}
act = ant->sig;
}
}
}
//--------------------------------------------------------------------------
}

Solución: main.cpp
#include "lista.hpp"
#include <iostream>
using namespace std;
using namespace umalcc;

//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Destruir Lista" << endl;
cout << "B. Insertar Inicio" << endl;
cout << "C. Insertar Final" << endl;
cout << "D. Situar Posicion" << endl;
cout << "E. Insertar Posicion" << endl;
cout << "F. Eliminar Primero" << endl;
cout << "G. Eliminar Ultimo" << endl;
cout << "H. Eliminar Posicion" << endl;
cout << "I. Duplicar" << endl;
cout << "J. Escribir" << endl;
cout << "K. Leer" << endl;
cout << "L. Buscar Elemento" << endl;
cout << "M. Insertar Ordenado" << endl;
cout << "N. Eliminar Elemento" << endl;
cout << "O. Eliminar Mayor" << endl;
cout << "P. Purgar" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’P’))));
cout << endl;
return op;
}

198
//------------------------------------------------------------------
void leer(int& dato)
{
cout << "Introduzca dato: " ;
cin >> dato;
}
void leer(unsigned& pos)
{
cout << "Introduzca posicion: " ;
cin >> pos;
}
void prueba_duplicar(PNodo lista)
{
PNodo aux = duplicar(lista);
cout << "Lista original: " ;
escribir(lista);
cout << "Lista copia: " ;
escribir(aux);
destruir(aux);
}
//-------------------------------------------------------------------------
int main()
{
PNodo lista;
PNodo aux;
char op = ’ ’;
int dato;
unsigned pos;
inicializa(lista);
do {
op = menu();
switch (op) {
case ’A’:
destruir(lista);
escribir(lista);
break;
case ’B’:
leer(dato);
insertar_principio(lista, dato);
escribir(lista);
break;
case ’C’:
leer(dato);
insertar_final(lista, dato);
escribir(lista);
break;
case ’D’:
leer(pos);
aux = situar(lista, pos);
if (aux == NULL) {
cout << "Error: posicion erronea" << endl;
} else {
cout << "Elemento ["<<pos<<"]: " << aux->dato << endl;
}
escribir(lista);
break;
case ’E’:
leer(pos);
leer(dato);
insertar_pos(lista, pos, dato) ;
escribir(lista);
break;
case ’F’:
eliminar_primero(lista);
escribir(lista);
break;
case ’G’:
eliminar_ultimo(lista);
escribir(lista);
break;
case ’H’:
leer(pos);
eliminar_pos(lista, pos) ;
escribir(lista);

199
break;
case ’I’:
prueba_duplicar(lista);
break;
case ’J’:
escribir(lista);
break;
case ’K’:
destruir(lista);
cout << "Introduzca elementos (0 -> FIN)" << endl;
lista = leer();
escribir(lista);
break;
case ’L’:
leer(dato);
aux = buscar(lista, dato);
if (aux == NULL) {
cout << "Error: No encontrado" << endl;
} else {
cout << "Elemento: " << aux->dato << endl;
}
escribir(lista);
break;
case ’M’:
leer(dato);
insertar_ord(lista, dato);
escribir(lista);
break;
case ’N’:
leer(dato);
eliminar_elem(lista, dato);
escribir(lista);
break;
case ’O’:
eliminar_mayor(lista);
escribir(lista);
break;
case ’P’:
leer(dato);
purgar(lista, dato);
escribir(lista);
break;
}
} while (op != ’X’);
destruir(lista);
}

Práctica 5: Abstracción en la Gestión de Memoria Dinámica


1. Un TAD lista genérica es una secuencia de elementos homogéneos, donde cada elemento ocupa una de-
terminada posición dentro de la secuencia, de tal forma que se puede acceder a cada elemento por la
posición donde se encuentra. Ası́ mismo, también es posible añadir y eliminar elementos de la secuencia
en la posición indicada, manteniendo el mismo orden posicional de los elementos en la secuencia. Diseñe e
implemente el TAD lista genérica, utilizando para ello listas enlazadas en memoria dinámica, que defina
los siguientes métodos públicos:

lista: ◦−−→ ◦−−−−→ ◦−−−−→ 


0 1 2

Solución: lista.hpp
#ifndef _lista_hpp_
#define _lista_hpp_
#include <cstddef>
#include <cassert>
namespace umalcc {
template <typename Tipo>
class Lista {

200
public:
//-- Métodos Públicos ----------

// Destructor
~Lista() { destruir(lista) ; }

// Constructor por Defecto


Lista() : sz(0), lista(NULL) { }

// Constructor de Copia
Lista(const Lista& o)
: sz(o.sz), lista(duplicar(o.lista)) { }

// Operador de Asignación
Lista& operator = (const Lista& o)
{
if (this != &o) {
destruir(lista) ;
sz = o.sz ;
lista = duplicar(o.lista) ;
}
return *this ;
}

// Elimina todos los elementos de la lista actual (queda vacia)


void clear()
{
destruir(lista) ;
sz = 0 ;
}

// Devuelve el numero de elementos almacenados


int size() const
{
return sz ;
}

// Devuelve true si el numero de elementos almacenados


// alcanza la capacidad maxima de almacenamiento
bool llena() const
{
return false;
}

// PRECOND: ( ! llena() && 0 <= pos && pos <= size())


// Inserta (dato) en la lista actual en la posicion (pos)
void insertar(int pos, const Tipo& d)
{
assert(! llena()
&& 0 <= pos && pos <= size()) ;
insertar_pos(lista, pos, d) ;
++sz ;
}

// PRECOND: (0 <= pos && pos < size())


// Elimina de la lista actual el elemento que ocupa la posicion (pos)
void eliminar(int pos)
{
assert(0 <= pos && pos < size()) ;
eliminar_pos(lista, pos) ;
--sz ;
}

// PRECOND: (0 <= pos && pos < size())


// Devuelve el elemento de la lista actual que ocupa la posicion (pos)
Tipo acceder(int pos) const
{
assert(0 <= pos && pos < size()) ;
PNodo ptr = situar(lista, pos) ;
assert(ptr != NULL) ;
return ptr->dato ;
}

// PRECOND: (0 <= pos && pos < size())

201
// Asigna (dato) al elemento de la lista actual que ocupa la posicion (pos)
void modificar(int pos, const Tipo& d)
{
assert(0 <= pos && pos < size()) ;
PNodo ptr = situar(lista, pos) ;
assert(ptr != NULL) ;
ptr->dato = d ;
}

private:
//-- Tipos Privados ------

struct Nodo ;
typedef Nodo* PNodo ;
struct Nodo {
PNodo sig ;
Tipo dato ;
} ;

//-- Atributos privados --

int sz ;
PNodo lista ;

//-- Métodos Privados ----------

void destruir(PNodo& lst) const


{
while (lst != NULL) {
PNodo ptr = lst ;
lst = lst->sig ;
delete ptr ;
}
}

PNodo situar(PNodo lst, int pos) const


{
PNodo ptr = lst;
while ((ptr != NULL)&&(pos > 0)) {
ptr = ptr->sig;
--pos;
}
return ptr;
}

void insertar_pos(PNodo& lst, int pos,


const Tipo& dt) const
{
PNodo ptr = new Nodo ;
ptr->dato = dt ;
if (pos < 1) {
ptr->sig = lst ;
lst = ptr ;
} else {
PNodo ant = situar(lst, pos - 1);
if (ant != NULL) {
ptr->sig = ant->sig ;
ant->sig = ptr ;
}
}
}

void eliminar_pos(PNodo& lst, int pos) const


{
if (lst != NULL) {
if (pos < 1) {
PNodo ptr = lst ;
lst = lst->sig ;
delete ptr ;
} else {
PNodo ant = situar(lst, pos - 1) ;
if ((ant != NULL)&&(ant->sig != NULL)) {
PNodo act = ant->sig ;
ant->sig = act->sig ;

202
delete act ;
}
}
}
}

PNodo duplicar(PNodo lst) const


{
PNodo nueva = NULL;
if (lst != NULL) {
nueva = new Nodo ;
nueva->dato = lst->dato ;
PNodo u = nueva ;
PNodo p = lst->sig ;
while (p != NULL) {
u->sig = new Nodo ;
u->sig->dato = p->dato ;
u = u->sig ;
p = p->sig ;
}
u->sig = NULL ;
}
return nueva;
}

} ; // class
} // namespace
#endif

2. Diseñe un programa principal que permita comprobar el funcionamiento del TAD Lista Genérica diseñado
en el ejercicio anterior.
Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "lista.hpp"
using namespace std ;
using namespace umalcc ;
//------------------------------------------------------------------
// Instanciación de la Lista genérica para almacenar números enteros
typedef Lista<int> ListaInt;
//------------------------------------------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);
}
//---------------------------------
void leer_dato(int& dato)
{
cout << "Introduzca un dato: " ;
cin >> dato;
}
//---------------------------------
void leer(ListaInt& lista)
{
int dato ;
lista.clear() ;
cout << "Introduzca datos (0 -> FIN): " << endl ;
cin >> dato ;
while ((dato != 0)&&( ! lista.llena())) {
lista.insertar(lista.size(), dato) ;
cin >> dato ;
}
}
//---------------------------------
void escribir(const ListaInt& lista)
{
cout << "Lista: " ;
for (int i = 0 ; i < lista.size() ; ++i) {

203
cout << lista.acceder(i) << " " ;
}
cout << endl ;
}
//---------------------------------
void prueba_asg(const ListaInt& lista)
{
cout << "Constructor de Copia" << endl ;
ListaInt lst(lista) ;
escribir(lst) ;
cout << "Operador de Asignacion" << endl ;
lst = lista ;
escribir(lst) ;
}
//-------------------------------------------------------------------------
char menu()
{
char op ;
cout << endl ;
cout << "X. Fin" << endl ;
cout << "A. Leer Lista" << endl ;
cout << "B. Borrar Lista" << endl ;
cout << "C. Insertar Posicion" << endl ;
cout << "D. Eliminar Posicion" << endl ;
cout << "E. Acceder Posicion" << endl ;
cout << "F. Modificar Posicion" << endl ;
cout << "G. Prueba Copia y Asignacion" << endl ;
do {
cout << endl << " Opcion: " ;
cin >> op ;
op = char(toupper(op)) ;
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’G’)))) ;
cout << endl ;
return op ;
}
//-------------------------------------------------------------------------
int main()
{
ListaInt lista ;
int dato ;
int pos ;
char op = ’ ’ ;
do {
op = menu() ;
switch (op) {
case ’A’:
leer(lista) ;
escribir(lista) ;
break ;
case ’B’:
lista.clear() ;
escribir(lista) ;
break ;
case ’C’:
if (lista.llena()) {
cout << "Error: Lista llena" << endl ;
} else {
leer_pos(pos, lista.size()+1) ;
leer_dato(dato) ;
lista.insertar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’D’:
if (lista.size() == 0) {
cout << "Error: Lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
lista.eliminar(pos) ;
escribir(lista) ;
}
break ;
case ’E’:
if (lista.size() == 0) {

204
cout << "Error: Lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
cout << "Lista[" << pos << "]: " << lista.acceder(pos) << endl ;
escribir(lista) ;
}
break ;
case ’F’:
if (lista.size() == 0) {
cout << "Error: Lista vacia" << endl ;
} else {
leer_pos(pos, lista.size()) ;
leer_dato(dato) ;
lista.modificar(pos, dato) ;
escribir(lista) ;
}
break ;
case ’G’:
prueba_asg(lista) ;
break ;
}
} while (op != ’X’) ;
}

3. Dado el TAD lista genérica del ejercicio anterior, diseñe un programa de utilización que defina una instancia
del TAD lista genérica para un tipo Persona (con campos nombre, edad y teléfono), y defina subprogramas
adecuados para añadir, buscar, eliminar y ordenar los elementos de la lista de personas. La ordenación
será de menor a mayor por nombre de la persona, para el caso de nombres iguales, ordenará de menor a
mayor por su edad. En caso de igualdad de nombre y edad, ordenará de menor a mayor por teléfono.
Solución: main.cpp
#include <iostream>
#include <cctype>
#include <cassert>
#include "../lista/lista.hpp"
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------
struct Persona {
string nombre;
unsigned edad;
string telefono;
};
inline bool iguales(const Persona& p1, const Persona& p2)
{
return ((p1.nombre == p2.nombre)&&(p1.edad == p2.edad)
&&(p1.telefono == p2.telefono));
}
inline bool distintos(const Persona& p1, const Persona& p2)
{
return ! iguales(p1, p2);
}
inline bool menor(const Persona& p1, const Persona& p2)
{
return ((p1.nombre < p2.nombre)
||((p1.nombre == p2.nombre)
&&((p1.edad < p2.edad)
||((p1.edad == p2.edad)
&&(p1.telefono < p2.telefono)))));
}
inline bool mayor(const Persona& p1, const Persona& p2)
{
return menor(p2, p1);
}
inline void escribir(const Persona& p)
{
cout << "{ " << p.nombre << " " << p.edad << " " << p.telefono << " }";
}
inline void leer(Persona& p)
{
cin >> p.nombre >> p.edad >> p.telefono;
}
//------------------------------------------------------------------

205
typedef Lista<Persona> LPers;
//------------------------------------------------------------------
// busca la posicion donde se debe insertar el elemento de forma ordenada
int buscar_pos(const Persona& x, const LPers& lista)
{
int i = 0;
while (mayor(x, lista.acceder(i))) {
++i;
}
return i;
}
//---------------------------------
void ordenar_insercion(LPers& lista)
{
for (int i = 1; i < lista.size(); ++i) { // para cada elemento ’i’
int pos = buscar_pos(lista.acceder(i), lista); // busca su pos ord
if (pos < i) {
Persona aux = lista.acceder(i);// extrae el elem de la posicion ’i’
lista.eliminar(i); // elimina el elemento de la lista
lista.insertar(pos, aux); // lo inserta en la posicion ordenada
}
}
}
//------------------------------------------------------------------
int buscar(const Persona& x, const LPers& lista)
{
int i = 0;
while ((i < lista.size()) && distintos(x, lista.acceder(i))) {
++i;
}
return i;
}
//--------------------------------------
void eliminar_elm(const Persona& x, LPers& lista)
{
int pos = buscar(x, lista);
if (pos < lista.size()) {
lista.eliminar(pos);
cout << endl;
} else {
cout << "No encontrado: " << endl;
}
}
//--------------------------------------
void leer_dato(Persona& dato)
{
cout << "Introduzca el nombre, edad y telefono de una persona: " ;
leer(dato);
}
//---------------------------------
void leer_pos(int& pos, int limite)
{
assert(limite > 0);
do {
cout << "Introduzca posicion ( < " << limite << " ): " ;
cin >> pos;
} while (pos < 0 || pos >= limite);
}
//---------------------------------
void leer(LPers& lista)
{
Persona p;
lista.clear() ;
cout << "Introduzca una lista de personas (nombre edad telefono) (fin -> FIN)" << endl;
cin >> p.nombre;
while ((p.nombre != "fin")&&( ! lista.llena())) {
cin >> p.edad >> p.telefono ;
lista.insertar(lista.size(), p);
cin >> p.nombre;
}
}
//---------------------------------
void escribir(const LPers& lista)
{

206
cout << "Lista: " ;
for (int i = 0 ; i < lista.size() ; ++i) {
escribir(lista.acceder(i)) ;
cout << endl ;
}
cout << endl ;
}
//---------------------------------
void ordenar(LPers& lista)
{
ordenar_insercion(lista);
escribir(lista);
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Leer" << endl;
cout << "B. Insertar Posicion" << endl;
cout << "C. Buscar Elemento" << endl;
cout << "D. Eliminar Elemento" << endl;
cout << "E. Eliminar Posicion" << endl;
cout << "F. Ordenar Insercion" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’F’))));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
LPers lista;
Persona dato;
int pos;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
leer(lista);
escribir(lista);
break;
case ’B’:
if (lista.llena()) {
cout << "Error: Lista llena" << endl ;
} else {
leer_pos(pos, lista.size()+1);
leer_dato(dato);
lista.insertar(pos, dato);
escribir(lista);
}
break;
case ’C’:
leer_dato(dato);
pos = buscar(dato, lista);
escribir(lista);
if (pos < lista.size()) {
cout << "Encontrado: ["<<pos<<"]: " ;
escribir(lista.acceder(pos));
cout << endl;
} else {
cout << "No encontrado: " << endl;
}
break;
case ’D’:
leer_dato(dato);
eliminar_elm(dato, lista);
escribir(lista);
break;

207
case ’E’:
if (lista.size() == 0) {
cout << "Error: lista vacia" << endl ;
} else {
leer_pos(pos, lista.size());
lista.eliminar(pos);
escribir(lista);
}
break;
case ’F’:
ordenar(lista);
break;
}
} while (op != ’X’);
}

4. Una vez finalizada la práctica anterior, realice la práctica propuesta en el ejercicio 1 de la sección 2 de
Problemas de Examen (Juego de Ajedrez).

Práctica 6: Biblioteca Estándar


1. Defina e implemente la clase Hotel (dentro del espacio de nombres umalcc) que defina las siguientes estruc-
turas de datos y proporcione los siguientes métodos públicos para gestionar la ocupación de habitaciones
y reservas de un hotel:
Estructura de Datos 1: La estructura que contendrá los datos de ocupación de las habitaciones se
definirá mediante un tipo vector de dos dimensiones, donde cada fila almacena los datos de cada planta
del hotel, y para cada fila hay una estructura que almacena los datos de cada habitación, que se compone
del nombre del cliente (cadena de caracteres) que se encuentra alojado.
Hotel

habitaciones
0 1 2 3 4
0 nombre ··· ··· ··· ···
1 ··· ··· ··· ··· ···
2 ··· ··· ··· ··· ···

Métodos Públicos 1:
a) Hotel(⇓ n plantas, ⇓ n habs):
Inicializará la estructura de datos Hotel a un estado vacı́o, es decir, sin habitaciones ocupadas, de tal
forma que el vector que representa las habitaciones del hotel se creará con tantas filas como indique el
parámetro n_plantas, y tantos elementos por fila como indique el parámetro n_habs.
b) ~Hotel():
Liberará y destruirá todos los recursos asociados a la estructura de datos Hotel.
c) Mostrar():
Muestra en pantalla toda la información almacenada en las estructuras de datos del Hotel, en un formato
adecuadamente legible.
d ) Alojar(⇓ nombre, ⇑ planta, ⇑ n hab, ⇑ ok):
Comprueba si hay habitaciones disponibles, en cuyo caso alojará adecuadamente a dicho cliente en alguna
habitación que no esté ocupada. ok tomará el valor true, y planta y n hab tomarán el valor de la planta
y número de habitación donde se alojará el cliente.
Cuando se aloja un determinado cliente en una habitación (planta y número de habitación), ésta pasará a
estar ocupada por dicho cliente, almacenando su nombre.
Si todas las habitaciones están ocupadas, ok tomará el valor false.
e) Desalojar(⇓ nombre, ⇑ ok):
Busca el cliente cuyo nombre se especifica como parámetro, y en caso de encontrarlo en una determinada
habitación, lo desalojará de ella borrando su nombre (a ""), y ok tomará el valor true. En otro caso, ok
tomará el valor false.
Estructura de Datos 2: Modifique la estructura de datos 1 especificada anteriormente para que cada
habitación también almacene una cola de mensajes para dicha habitación, la cual almacena mensajes (cadena
de caracteres) para una determinada habitación en el mismo orden de llegada. Se definirá utilizando un
contenedor de tipo queue de la biblioteca estándar.
Métodos Públicos 2:

208
a) Modifique los constructores y destructores adecuadamente para inicializar y destruir la nueva estructura
de datos.
b) Modifique el método Mostrar() especificado anteriormente para que también muestre en pantalla la
información relativa a los mensajes disponibles para cada habitación. Nota: Esta operación debe realizarse
conservando sin alteración la cola de mensajes de cada habitación.
c) Modifique los métodos Alojar() y Desalojar() especificados anteriormente para que ambos inicialicen a
vacı́a la cola de mensajes de la habitación que sea alojada o desalojada respectivamente.
d ) Anyadir Mensaje(⇓ nombre, ⇓ msj, ⇑ ok):
Busca el cliente especificado como parámetro entre los clientes alojados en el hotel, y si lo encuentra,
entonces añade el mensaje especificado como parámetro en la cola de mensajes de la habitación ocupada
por dicho cliente y ok tomará el valor true. En otro caso, ok tomará el valor false.
e) Mostrar Mensajes(⇓ nombre, ⇑ ok):
Busca el cliente especificado como parámetro entre los clientes alojados en el hotel, y si lo encuentra,
entonces muestra por pantalla los mensajes (según el orden de llegada) que tenga disponibles en la cola
de mensajes de su habitación, que se quedará vacı́a después de realizar esta operación, y ok tomará el
valor true. En otro caso, si el nombre no se ha encontrado, ok tomará el valor false.
Notas:
a) ⇓ Especifica un parámetro de entrada y ⇑ especifica un parámetro de salida.
b) Los parámetros nombre y msj son de tipo “cadena de caracteres”.
c) El parámetro ok es de tipo “lógico” (“boolean”).
d) Los parámetros planta y n hab representan la planta y número de habitación donde se alojará el cliente
en caso de que la operación haya sido correcta, y se representan mediante números de tipo “natural”.
Solución: hotel.hpp
#ifndef hotel_hpp__
#define hotel_hpp__ 1
#include <iostream>
#include <string>
#include <queue>
#include <vector>
namespace umalcc {
//--------------------------------------------------------------------------
class Hotel {
public:
//------------------------------
//-- Metodos Publicos 1 --------
//------------------------------
// ~Hotel() {} // Destruccion por defecto
Hotel(unsigned n_plantas, unsigned n_habs);
void Mostrar() const;
void Alojar(const std::string& nombre,
unsigned& planta, unsigned& n_hab, bool& ok);
void Desalojar(const std::string& nombre, bool& ok);
//------------------------------
//-- Metodos Publicos 2 --------
//------------------------------
void Anyadir_Mensaje(const std::string& nombre, const std::string& msj,
bool& ok);
void Mostrar_Mensajes(const std::string& nombre, bool& ok);
private:
//- tipos ----------------------
typedef std::queue<std::string> CMsj; // Estructura de datos 2
struct Habitacion {
std::string nombre;
CMsj m; // Estructura de datos 2
};
typedef std::vector<Habitacion> Fila;
typedef std::vector<Fila> Habitaciones;
//- atributos ------------------
Habitaciones hab;
//- metodos 1 ------------------
void mostrar(const Habitacion& h) const;
void mostrar_hab() const;
void buscar(const std::string& nombre, unsigned& f, unsigned& c,
bool& ok) const;
void buscar_hab_libre(unsigned& f, unsigned& c, bool& ok) const;
//- metodos 2 ------------------
void mostrar_vaciar(CMsj& c) const;
void mostrar_cola(const CMsj& c) const;
//------------------------------

209
};
}
#endif

Solución: hotel.cpp
#include "hotel.hpp"
#include <iostream>
#include <cassert>
#include <fstream>
using namespace std;
using namespace umalcc;

//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Metodos 1 -------------------------------------------------------------
//--------------------------------------------------------------------------
Hotel::Hotel(unsigned n_plantas, unsigned n_habs)
: hab(n_plantas, Fila(n_habs)) {}
//--------------------------------------------------------------------------
void Hotel::mostrar(const Habitacion& h) const
{
if (h.nombre == "") {
cout << "vacia" << endl;
} else {
cout << h.nombre << endl;
mostrar_cola(h.m); // Metodos 2
}
}
//----------------------------------
void Hotel::mostrar_hab() const
{
for (unsigned f = 0; f < hab.size(); ++f) {
for (unsigned c = 0; c < hab[f].size(); ++c) {
cout << f << "." << c << ": ";
mostrar(hab[f][c]);
}
}
}
//----------------------------------
void Hotel::Mostrar() const
{
cout << "Habitaciones:" << endl;
mostrar_hab();
cout << endl;
}
//--------------------------------------------------------------------------
void Hotel::buscar(const std::string& nombre, unsigned& f, unsigned& c,
bool& ok) const
{
f = 0;
c = 0;
while ((f < hab.size())&&(hab[f][c].nombre != nombre)) {
++c;
if (c >= hab[f].size()) {
c = 0;
++f;
}
}
ok = (f < hab.size());
}
//----------------------------------
void Hotel::buscar_hab_libre(unsigned& f, unsigned& c, bool& ok) const
{
buscar("", f, c, ok);
}
//--------------------------------------------------------------------------
void Hotel::Alojar(const std::string& nombre,
unsigned& planta, unsigned& n_hab, bool& ok)
{
buscar_hab_libre(planta, n_hab, ok);
if (ok) {
hab[planta][n_hab].nombre = nombre;
hab[planta][n_hab].m = CMsj(); // Metodos 2

210
}
}
//--------------------------------------------------------------------------
void Hotel::Desalojar(const std::string& nombre, bool& ok)
{
unsigned f, c;
buscar(nombre, f, c, ok);
if (ok) {
hab[f][c].nombre = "";
hab[f][c].m = CMsj(); // Metodos 2
}
}
//--------------------------------------------------------------------------
//-- Metodos 2 -------------------------------------------------------------
//--------------------------------------------------------------------------
void Hotel::Anyadir_Mensaje(const std::string& nombre,
const std::string& msj,
bool& ok)
{
unsigned f, c;
buscar(nombre, f, c, ok);
if (ok) {
hab[f][c].m.push(msj);
}
}
//--------------------------------------------------------------------------
void Hotel::mostrar_vaciar(CMsj& c) const
{
while ( ! c.empty()) {
cout << " " << c.front() << endl;
c.pop();
}
}
//----------------------------------
void Hotel::Mostrar_Mensajes(const std::string& nombre, bool& ok)
{
unsigned f, c;
buscar(nombre, f, c, ok);
if (ok) {
mostrar_vaciar(hab[f][c].m);
}
}
//--------------------------------------------------------------------------
void Hotel::mostrar_cola(const CMsj& c) const
{
CMsj copia(c); // Para que se vacie la copia
mostrar_vaciar(copia);
}
//--------------------------------------------------------------------------
}

2. Diseñe un programa que utilice la clase Hotel definida anteriormente y permita gestionar las habitaciones
de un hotel.
Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "hotel.hpp"
using namespace std;
using namespace umalcc;

void leer_num(unsigned& num, const string& msj)


{
cout << msj ;
cin >> num;
}
void leer_str(string& str, const string& msj)
{
cout << msj ;
cin >> str;
}
void leer_cliente(string& nombre)
{

211
leer_str(nombre, "Introduzca el nombre del cliente: ");
}
void check_ok(bool ok, const string& msj)
{
if (ok) {
cout << "Operacion Correcta. [" << msj << "]" << endl;
} else {
cout << "Operacion Erronea. [" << msj << "]" << endl;
}
}
char menu()
{
char op;
cout << "X: Fin" << endl;
cout << "A: Mostrar Datos Hotel" << endl;
cout << "B: Alojar Nuevo Cliente" << endl;
cout << "C: Desalojar Cliente" << endl;
cout << "D: Anyadir Mensaje" << endl;
cout << "E: Mostrar Mensajes" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’E’))));
cout << endl;
return op;
}
int main()
{
unsigned n_plantas, n_habs;
leer_num(n_plantas, "Introduzca el numero de plantas del hotel: ");
leer_num(n_habs, "Introduzca el numero de habitaciones por planta: ");
Hotel hotel(n_plantas, n_habs);
bool ok;
string nombre, msj;
unsigned planta, n_hab;
char op = ’ ’;
do {
op = menu();
switch(op) {
case ’A’:
hotel.Mostrar();
break;
case ’B’:
leer_cliente(nombre);
hotel.Alojar(nombre, planta, n_hab, ok);
check_ok(ok, "Alojar");
if (ok) {
cout << " Alojar: " << nombre
<< " Planta: " << planta
<< " Hab: " << n_hab << endl;
}
break;
case ’C’:
leer_str(nombre, "Introduzca el nombre del cliente: ");
hotel.Desalojar(nombre, ok);
check_ok(ok, "Desalojar");
break;
case ’D’:
leer_str(nombre, "Introduzca el nombre del cliente: ");
leer_str(msj, "Introduzca el mensaje: ");
hotel.Anyadir_Mensaje(nombre, msj, ok);
check_ok(ok, "Anyadir Mansaje");
break;
case ’E’:
leer_str(nombre, "Introduzca el nombre del cliente: ");
hotel.Mostrar_Mensajes(nombre, ok);
check_ok(ok, "Mostrar Mensajes");
break;
}
} while (op != ’X’);
}

212
3. Una vez finalizada la práctica anterior, realice la práctica propuesta en el ejercicio 1 de la sección 3 de
Problemas de Examen (Gestión de Hotel).

213
Problemas de Examen
Examen 1: Gestión de Agencia de Viajes
1. Se pretende gestionar las reservas de clientes de paquetes turı́sticos en una agencia de viajes, de tal forma
que se dispone de una estrutura de datos donde se utiliza un TAD mapa genérico (Tema 2.3 Ej. 4) para
almacenar la información relativa a los paquetes turı́sticos, cuya clave de acceso es el nombre del paquete
turı́stico, y donde se almacena como valor asociado a la clave el número de plazas y el precio por cada
plaza. Además, la estructura de datos también utiliza un TAD vector genérico (Tema 2.3 Ej. 1) para
almacenar la información de los clientes, de tal forma que, para cada cliente, almacena el nombre del
cliente, el dinero que tiene disponible, y un TAD lista genérica (Tema 2.3 Ej. 2) con los nombres de
paquetes turı́sticos que tenga reservados.

egipto-1
1 750 juan luis victor ana
1100 800 500 300
grecia-1
2 1000 egipto-1 egipto-1 grecia-1 holanda-1
grecia-1 grecia-2 holanda-1 –
grecia-2 holanda-1 – – –
3 200 – – – –
– – – –
holanda-1
2 135

Implemente los TADs genéricos especificados anteriormente (mapa, vector y lista), y defina un nuevo
TAD PaqTur que soporte adecuadamente la estructura de datos especificada anteriormente, instanciando
los TADs genéricos a los tipos de datos necesarios. Ası́ mismo, el TAD PaqTur deberá proporcionar los
siguientes métodos públicos:
Constructor por defecto que permita construir una estructura de datos vacı́a.
Destructor que libera los recursos asociados al objeto.
Mostrar muestra en pantalla toda la información de la estructura de datos.
Añadir paquete turı́stico, que recibirá como parámetros la información suficiente para ello y la
añadirá en el mapa, utilizando como clave el nombre del paquete turı́stico.
Eliminar paquete turı́stico, que recibe el nombre del paquete turı́stico, y lo elimina del mapa de
paquetes turı́sticos.
Además, también eliminará dicho paquete turı́stico de todas las reservas de los clientes donde
aparezca.
Añadir cliente, que recibe el nombre del cliente y la cantidad de dinero que tiene disponible.
Eliminar cliente, que recibe el nombre del cliente, y lo elimina del vector de clientes.
Añadir reserva, que recibe el nombre del cliente y el nombre del paquete turı́stico que desea reservar.
En caso de que el cliente ya tenga reservada dicho paquete turı́stico, o el cliente haya alcanzado ya
su capacidad máxima de reservas, se indicará un error.
Eliminar reserva, que recibe el nombre del cliente y del paquete turı́stico, y elimina dicha reserva.
Guardar toda la información almacenada en la estructura de datos los ficheros cuyos nombres se
especifican como parámetros, en un formato adecuado para posteriormente poder recuperar la infor-
mación almacenada. Se utilizaran dos ficheros, uno para almacenar la información de los paquetes
turı́sticos, y otro fichero para almacenar la información de los clientes y sus reservas. Por ejemplo,
para la información de la figura anterior:
paqtur.txt clientes.txt

1 750 egipto-1 juan


2 1000 grecia-1 1100 3
3 200 grecia-2 egipto-1
2 135 holanda-1 grecia-1
holanda-1
luis
800 2
egipto-1
grecia-2
victor
500 2
grecia-1
holanda-1
ana
300 1
holanda-1

214
Cargar toda la información de la estructura de datos desde los ficheros cuyos nombres se especifican
como parámetros, siguiendo el formato utilizado en la operación anterior.
Ajustar gastos, para cada cliente, eliminará todas aquellas reservas para las que no tenga dinero
disponible suficiente, considerando que la posición de la reserva en la lista indica su grado de prefer-
encia, de tal forma que la suma del precio de todas las reservas que mantenga no supere el dinero
total disponible.
Ası́, considerando el ejemplo de la figura, para el cliente juan eliminará la reserva grecia_1, para
luis eliminará la reserva grecia_2, para victor eliminará la reserva grecia_1 y para ana no
eliminará ninguna reserva.
Solución: paqtur.hpp
#ifndef _paqtur_hpp_
#define _paqtur_hpp_
#include <iostream>
#include <string>
#include <tr1/array>
#include "../t23/mapa/mapa.hpp"
#include "../t23/vector/vector.hpp"
#include "../t23/lista/lista.hpp"
namespace umalcc {
class PaqTur {
public:
// ~PaqTur(); // Generado Automaticamente
// PaqTur(); // Generado Automaticamente
// PaqTur(const PaqTur& o); // Generado Automaticamente
// PaqTur& operator=(const PaqTur& o); // Generado Automaticamente
void mostrar() const ;
void anyadir_pq(const std::string& nombre, int plazas, int precio,
bool& ok);
void eliminar_pq(const std::string& nombre, bool& ok);
void anyadir_cli(const std::string& nombre, int dinero, bool& ok);
void eliminar_cli(const std::string& nombre, bool& ok);
void anyadir_rsv(const std::string& p, const std::string& c, bool& ok);
void eliminar_rsv(const std::string& p, const std::string& c, bool& ok);
void ajustar_gastos() ;
void guardar(const std::string& nombre_pq,
const std::string& nombre_cli, bool& ok) const ;
void cargar(const std::string& nombre_pq,
const std::string& nombre_cli, bool& ok) ;
private:
//------------------------------
//-- Ctes & Tipos --------------
//------------------------------
static const int MAX_PAQTUR = 20;
static const int MAX_CLIENTES = 10;
static const int MAX_RESERVAS = 5;
typedef Lista<std::string, MAX_RESERVAS> LReservas;
struct Cliente {
std::string nombre;
int dinero;
LReservas rsv;
};
typedef Vector<Cliente, MAX_CLIENTES> VClientes;
struct PTur {
// std::string nombre; // Es la Clave del Mapa
int plazas;
int precio;
};
typedef Mapa<PTur, MAX_PAQTUR> MPTur;
//------------------------------
//-- Metodos -------------------
//------------------------------
void mostrar_pq(const MPTur& pq) const;
void mostrar_rsv(const LReservas& lr) const;
void mostrar_cli(const VClientes& c) const;
void eliminar_rsv(const std::string& p, int icliente, bool& ok);
void eliminar_rsv(const std::string& p);
bool iguales(const std::string& nombre, const Cliente& c) const;
int buscar(const VClientes& vcl, const std::string& nombre) const;
int buscar(const LReservas& l, const std::string& nombre) const;
void guardar_pq(const std::string& nombre_pq, bool& ok) const;
void cargar_pq(const std::string& nombre_pq, bool& ok);
void escribir_rsv(std::ofstream& fich, const LReservas& lr) const;

215
void guardar_cli(const std::string& nombre_cli, bool& ok) const;
void leer_rsv(std::ifstream& fich, const std::string& nmcli, int n,
bool& ok);
void cargar_cli(const std::string& nombre_cli, bool& ok);
void ajustar_gastos(int icliente);
//------------------------------
//-- Atributos -----------------
//------------------------------
MPTur paqtur;
VClientes clientes;
};
}
#endif

Solución: paqtur.cpp
#include "paqtur.hpp"
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cassert>
using namespace std;
using namespace umalcc;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo. Es una parte privada de la
// implementacion. No es accesible desde fuera del modulo
//------------------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------------
//-- Subprogramas Auxiliares -----------------------------------------------
//--------------------------------------------------------------------------
struct PqT {
string n;
int pl;
int pr;
};
void leer_pq(ifstream& fich, PqT& p)
{
fich >> p.pl; // lee plazas
fich >> p.pr; // lee precio
fich >> ws; // salta espacios y saltos de linea
getline(fich, p.n); // lee el nombre (una linea completa)
}
void escribir_pq(ofstream& fich, const PqT& p)
{
fich << p.pl << " " << p.pr << " " << p.n << endl;
}
//-----------------------------
struct Cli {
string n;
int d;
int r;
};
void leer_cli(ifstream& fich, Cli& c)
{
fich >> ws; // salta espacios y saltos de linea
getline(fich, c.n); // lee el nombre (una linea completa)
fich >> c.d; // lee dinero disponible
fich >> c.r; // lee numero de reservas
}
void escribir_cli(ofstream& fich, const Cli& c)
{
fich << c.n << endl;
fich << c.d << " " << c.r << endl;
}
}
//------------------------------------------------------------------------------
// Espacio de nombres umalcc.
// Aqui reside la implementacion de la parte publica del modulo
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Publicos --------------------------------------------------------------
//--------------------------------------------------------------------------
void PaqTur::mostrar() const

216
{
mostrar_pq(paqtur);
mostrar_cli(clientes);
}
//--------------------------------------------------------------------------
void PaqTur::anyadir_pq(const string& nombre, int plazas, int precio,
bool& ok)
{
if (paqtur.lleno()) {
ok = false;
} else {
ok = true;
PTur p = { plazas, precio};
paqtur.insertar(nombre, p);
}
}
//--------------------------------------------------------------------------
void PaqTur::eliminar_pq(const string& nombre, bool& ok)
{
paqtur.eliminar(nombre, ok);
if (ok) {
eliminar_rsv(nombre);
}
}
//--------------------------------------------------------------------------
void PaqTur::anyadir_cli(const string& nombre, int dinero, bool& ok)
{
int i = buscar(clientes, nombre);
if ((i < clientes.size())||(clientes.lleno())) {
ok = false;
} else {
ok = true;
Cliente c;
c.nombre = nombre;
c.dinero = dinero;
c.rsv.clear();
clientes.insertar(c);
}
}
//--------------------------------------------------------------------------
void PaqTur::eliminar_cli(const string& nombre, bool& ok)
{
int i = buscar(clientes, nombre);
if (i < clientes.size()) {
if (i < clientes.size()-1) {
clientes.modificar(i, clientes.acceder(clientes.size()-1));
}
clientes.eliminar();
ok = true;
} else {
ok = false;
}
}
//--------------------------------------------------------------------------
void PaqTur::anyadir_rsv(const string& p, const string& c, bool& ok)
{
PTur pq = paqtur.acceder(p, ok);
if (ok) {
int i = buscar(clientes, c);
if (i < clientes.size()) {
Cliente cli = clientes.acceder(i);
int j = buscar(cli.rsv, p);
if (j < cli.rsv.size()) {
ok = false;
} else {
cli.rsv.insertar(cli.rsv.size(), p);
clientes.modificar(i, cli);
ok = true;
}
} else {
ok = false;
}
}
}

217
//--------------------------------------------------------------------------
void PaqTur::eliminar_rsv(const string& p, const string& c,
bool& ok)
{
int icliente = buscar(clientes, c);
eliminar_rsv(p, icliente, ok);
}
//--------------------------------------------------------------------------
void PaqTur::ajustar_gastos()
{
for (int i = 0; i < clientes.size(); ++i) {
ajustar_gastos(i);
}
}
//--------------------------------------------------------------------------
void PaqTur::guardar(const string& nombre_pq,
const string& nombre_cli, bool& ok) const
{
guardar_pq(nombre_pq, ok);
if (ok) {
guardar_cli(nombre_cli, ok);
}
}
//--------------------------------------------------------------------------
void PaqTur::cargar(const string& nombre_pq,
const string& nombre_cli, bool& ok)
{
cargar_pq(nombre_pq, ok);
if (ok) {
cargar_cli(nombre_cli, ok);
}
}
//--------------------------------------------------------------------------
//-- Privados --------------------------------------------------------------
//--------------------------------------------------------------------------
void PaqTur::mostrar_pq(const MPTur& pq) const
{
cout << "Paquetes Turisticos:" << endl;
for (int i = 0; i < pq.size(); ++i) {
string n = pq.acceder_clave(i);
PTur p = pq.acceder_valor(i);
cout << "Nombre: " << setw(20) << n
<< " Plazas: " << setw(5) << p.plazas
<< " Precio: " << setw(5) << p.precio
<< endl;
}
}
//--------------------------------------------------------------------------
void PaqTur::mostrar_rsv(const LReservas& lr) const
{
cout << " [ ";
for (int i = 0; i < lr.size(); ++i) {
cout << lr.acceder(i) << " ";
}
cout << "]";
}
//--------------------------------------------------------------------------
void PaqTur::mostrar_cli(const VClientes& vcli) const
{
cout << "Clientes:" << endl;
for (int i = 0; i < vcli.size(); ++i) {
Cliente c = vcli.acceder(i);
cout << "Nombre: " << setw(20) << c.nombre
<< " Dinero: " << setw(5) << c.dinero ;
mostrar_rsv(c.rsv);
cout << endl;
}
}
//--------------------------------------------------------------------------
void PaqTur::eliminar_rsv(const string& p, int icliente, bool& ok)
{
if (icliente < clientes.size()) {
Cliente c = clientes.acceder(icliente);
int j = buscar(c.rsv, p);

218
if (j < c.rsv.size()) {
c.rsv.eliminar(j);
clientes.modificar(icliente, c);
ok = true;
} else {
ok = false;
}
} else {
ok = false;
}
}
//--------------------------------------------------------------------------
void PaqTur::eliminar_rsv(const string& p)
{
bool ok;
for (int i = 0; i < clientes.size(); ++i) {
eliminar_rsv(p, i, ok);
}
}
//--------------------------------------------------------------------------
bool PaqTur::iguales(const string& nombre, const Cliente& c) const
{
return nombre == c.nombre;
}
//--------------------------------------------------------------------------
int PaqTur::buscar(const VClientes& vcl, const string& nombre) const
{
int i = 0;
while ((i < vcl.size())&& !iguales(nombre, vcl.acceder(i))) {
++i;
}
return i;
}
//--------------------------------------------------------------------------
int PaqTur::buscar(const LReservas& l, const string& nombre) const
{
int i = 0;
while ((i < l.size())&&(nombre != l.acceder(i))) {
++i;
}
return i;
}
//--------------------------------------------------------------------------
void PaqTur::guardar_pq(const string& nombre_pq, bool& ok) const
{
ofstream fich;
fich.open(nombre_pq.c_str());
if (fich.fail()) {
ok = false;
} else {
for (int i = 0; (i < paqtur.size()) && (! fich.fail()); ++i) {
string n = paqtur.acceder_clave(i);
PTur p = paqtur.acceder_valor(i);
PqT pq = { n, p.plazas, p.precio };
escribir_pq(fich, pq);
}
ok = ! fich.fail();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::cargar_pq(const string& nombre_pq, bool& ok)
{
ifstream fich;
fich.open(nombre_pq.c_str());
if (fich.fail()) {
ok = false;
} else {
ok = true;
paqtur.clear();
PqT pq;
leer_pq(fich, pq);
while (!fich.fail() && ok) {
anyadir_pq(pq.n, pq.pl, pq.pr, ok);

219
leer_pq(fich, pq);
}
ok = ok && fich.eof();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::escribir_rsv(ofstream& fich, const LReservas& lr) const
{
for (int i = 0; i < lr.size(); ++i) {
fich << lr.acceder(i) << endl;
}
}
//--------------------------------------------------------------------------
void PaqTur::guardar_cli(const string& nombre_cli, bool& ok) const
{
ofstream fich;
fich.open(nombre_cli.c_str());
if (fich.fail()) {
ok = false;
} else {
for (int i = 0; (i < clientes.size()) && (! fich.fail()); ++i) {
Cliente c = clientes.acceder(i);
Cli cl = { c.nombre, c.dinero, c.rsv.size() };
escribir_cli(fich, cl);
escribir_rsv(fich, c.rsv);
}
ok = ! fich.fail();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::leer_rsv(ifstream& fich, const string& nmcli, int n, bool& ok)
{
for (int i = 0; ok && (i < n)&& ! fich.fail(); ++i) {
string npq;
fich >> ws;
getline(fich, npq);
if (! fich.fail()) {
anyadir_rsv(npq, nmcli, ok);
}
}
}
//--------------------------------------------------------------------------
void PaqTur::cargar_cli(const string& nombre_cli, bool& ok)
{
ifstream fich;
fich.open(nombre_cli.c_str());
if (fich.fail()) {
ok = false;
} else {
ok = true;
clientes.clear();
Cli c;
leer_cli(fich, c);
while (!fich.fail() && ok) {
anyadir_cli(c.n, c.d, ok);
leer_rsv(fich, c.n, c.r, ok);
leer_cli(fich, c);
}
ok = ok && fich.eof();
fich.close();
}
}
//--------------------------------------------------------------------------
void PaqTur::ajustar_gastos(int icliente)
{
assert(icliente < clientes.size());
bool ok;
int total = 0;
Cliente c = clientes.acceder(icliente);
int j = 0;
while (j < c.rsv.size()) {
PTur pq = paqtur.acceder(c.rsv.acceder(j), ok);

220
if (ok && (total + pq.precio <= c.dinero)) {
total += pq.precio;
++j;
} else {
c.rsv.eliminar(j);
}
}
clientes.modificar(icliente, c);
}
//--------------------------------------------------------------------------
}

2. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.


Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "paqtur.hpp"
using namespace std;
using namespace umalcc;
//-------------------------------------------------------------------------
void leer_nombre(const string& msj, string& n)
{
cout << msj;
cin >> ws;
getline(cin, n);
}
//---------------------------------
void leer_num(const string& msj, int& n)
{
do {
cout << msj;
cin >> n;
} while ( !(n > 0) );
}
//---------------------------------
void cargar(PaqTur& pq)
{
string nombre_pq, nombre_cl;
bool ok;
cout << "Introduce el nombre del fichero de paquetes turisticos: ";
cin >> nombre_pq;
cout << "Introduce el nombre del fichero de clientes: ";
cin >> nombre_cl;
pq.cargar(nombre_pq, nombre_cl, ok);
if (!ok) {
cout << "Error al cargar datos desde el fichero: "
<< nombre_pq << " " << nombre_cl << endl;
}
}
void guardar(const PaqTur& pq)
{
string nombre_pq, nombre_cl;
bool ok;
cout << "Introduce el nombre del fichero de paquetes turisticos: ";
cin >> nombre_pq;
cout << "Introduce el nombre del fichero de clientes: ";
cin >> nombre_cl;
pq.guardar(nombre_pq, nombre_cl, ok);
if (!ok) {
cout << "Error al guardar datos al fichero: "
<< nombre_pq << " " << nombre_cl << endl;
}
}
//--------------------------------------
void nuevo_paqtur(PaqTur& pq)
{
string n;
int plazas, precio;
bool ok;
leer_nombre("Introduzca nombre del paquete turistico: ", n);
leer_num("Introduzca numero de plazas: ", plazas);
leer_num("Introduzca precio: ", precio);

221
pq.anyadir_pq(n, plazas, precio, ok);
if (!ok) {
cout << "Error al crear nuevo paquete turistico: " << n << endl;
}
}
void eliminar_paqtur(PaqTur& pq)
{
string n;
bool ok;
leer_nombre("Introduzca nombre del paquete turistico: ", n);
pq.eliminar_pq(n, ok);
if (! ok) {
cout << "Error al eliminar paquete turistico: " << n << endl;
}
}
//--------------------------------------
void nuevo_cliente(PaqTur& pq)
{
string n;
int dinero;
bool ok;
leer_nombre("Introduzca nombre del cliente: ", n);
leer_num("Introduzca cantidad de dinero disponible: ", dinero);
pq.anyadir_cli(n, dinero, ok);
if (!ok) {
cout << "Error al crear nuevo cliente: " << n << endl;
}
}
void eliminar_cliente(PaqTur& pq)
{
string n;
bool ok;
leer_nombre("Introduzca nombre del cliente: ", n);
pq.eliminar_cli(n, ok);
if (! ok) {
cout << "Error al eliminar cliente: " << n << endl;
}
}
//--------------------------------------
void nueva_reserva(PaqTur& pq)
{
string np, nc;
bool ok;
leer_nombre("Introduzca nombre del cliente: ", nc);
leer_nombre("Introduzca nombre del paquete turistico: ", np);
pq.anyadir_rsv(np, nc, ok);
if (!ok) {
cout << "Error al crear nueva reserva: " << nc << " " << np << endl;
}
}
void eliminar_reserva(PaqTur& pq)
{
string np, nc;
bool ok;
leer_nombre("Introduzca nombre del cliente: ", nc);
leer_nombre("Introduzca nombre del paquete turistico: ", np);
pq.eliminar_rsv(np, nc, ok);
if (!ok) {
cout << "Error al eliminar reserva: " << nc << " " << np << endl;
}
}
//-------------------------------------------------------------------------
char menu()
{
char op;
cout << endl;
cout << "X. Fin" << endl;
cout << "A. Mostrar Informacion" << endl;
cout << "B. A~nadir Paquete Turistico" << endl;
cout << "C. Eliminar Paquete Turistico" << endl;
cout << "D. A~nadir Cliente" << endl;
cout << "E. Eliminar Cliente" << endl;
cout << "F. A~nadir Reserva" << endl;
cout << "G. Eliminar Reserva" << endl;

222
cout << "H. Guardar en Fichero" << endl;
cout << "I. Cargar de Fichero" << endl;
cout << "J. Ajustar Gastos" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op == ’X’)||(op >= ’A’ && op <= ’J’)));
cout << endl;
return op;
}
//-------------------------------------------------------------------------
int main()
{
PaqTur pq;
char op = ’ ’;
do {
op = menu();
switch (op) {
case ’A’:
pq.mostrar();
break;
case ’B’:
nuevo_paqtur(pq);
break;
case ’C’:
eliminar_paqtur(pq);
break;
case ’D’:
nuevo_cliente(pq);
break;
case ’E’:
eliminar_cliente(pq);
break;
case ’F’:
nueva_reserva(pq);
break;
case ’G’:
eliminar_reserva(pq);
break;
case ’H’:
guardar(pq);
break;
case ’I’:
cargar(pq);
break;
case ’J’:
pq.ajustar_gastos();
break;
}
} while (op != ’X’);
}

Examen 2: Juego de Ajedrez


1. Defina e implemente la clase Ajedrez (dentro del espacio de nombres umalcc) que defina las siguientes
estructuras de datos y proporcione los siguientes métodos públicos para permitir jugar al ajedrez :
Estructura de Datos 1: La estructura de datos contiene un tablero de ajedrez (array de 2 dimensiones de
8 × 8 caracteres) con las piezas en sus posiciones correspondientes, ası́ como el turno de juego.
Métodos Públicos 1:
• El destructor libera todos los recursos de la clase.
• El constructor por defecto inicializa la estructura de datos y coloca las piezas en las posiciones adecuadas
(especificadas en la figura adjunta), ası́ como inicializa el turno de juego.
Nótese que al jugador con las piezas minúsculas le corresponde el turno de juego impar, y al jugador con
las piezas mayúsculas le corresponde el turno de juego par.

223
0 1 2 3 4 5 6 7

0 t c a d r a c t
1 p p p p p p p p
2
turno
3
1
4

P P P P P P P P
6

T C A D R A C T
7

• El método dibujar muestra en pantalla el estado actual del juego (como se muestra en la figura anterior).
• El método movimiento recibe como parámetros las coordenadas (fila y columna) de la posición origen
y posición destino del movimiento, y realiza el movimiento solicitado si dicho movimiento es correcto y
realizado en el turno de juego correspondiente (alternativo para cada jugador).
En caso de movimiento erróneo, será notificado al entorno exterior mediante un parámetro de salida que
indique un error en la operación.
El movimiento consiste en mover la pieza de la casilla origen a la casilla destino, dejando posteriormente
la casilla origen vacı́a. Un movimiento es correcto si se cumplen las siguientes condiciones:
◦ Las coordenadas de la casilla origen y destino son válidas (dentro del rango de filas y columnas de
la matriz tablero).
◦ El turno de cada jugador es alternativo, y la pieza que se encuentra en la casilla origen del movimiento
pertenece al jugador que le toca en ese momento.
◦ La casilla destino esta vacı́a, o contiene una pieza perteneciente al jugador contrario.
◦ Todas las casillas del camino lineal desde la casilla origen a la casilla destino están vacı́as (excluyendo
ambas casillas), salvo que la pieza que se esté moviendo sea el caballo (’C’ o ’c’).
• El método cargar recibe como parámetro el nombre del fichero desde donde leer y ejecutar los movimien-
tos a realizar. Los movimientos se encuentran en el siguiente formato en el fichero, donde el sı́mbolo esp
representa el carácter de espacio en blanco, y el sı́mbolo ←- representa el carácter de fin de lı́nea:
fila orig esp columna orig esp fila dest esp columna dest ←-

fila orig esp columna orig esp fila dest esp columna dest ←-
···············

Estructura de Datos 2: Modifique la estructura de datos anterior para que también incluya como nuevo
atributo una lista enlazada, utilizando para ello el TAD lista genérica implementado en memoria dinámica
(Tema 3.3 Ej. 1) que contendrá la secuencia de movimientos realizados desde el inicio del juego. Donde cada
movimiento se compone de las coordenadas (fila y columna) tanto del origen como del destino del movimiento,
ası́ como del carácter correspondiente a la pieza capturada (en caso de no haber capturado ninguna pieza, se
utilizará el carácter x).
Métodos Públicos 2:
• Modifique el constructor anterior para que inicialice la secuencia de movimientos a un estado vacı́o, y
también modifique el destructor anterior para que destruya adecuadamente dicha secuencia de movimien-
tos.
• Modifique el método movimiento anterior para que, en caso de haber realizado un movimiento correcto,
añada dicho movimiento (coordenadas origen y destino) y la pieza capturada (en caso de no capturar
ninguna pieza, se utilizará el carácter x) al nuevo atributo que contiene la secuencia de movimientos
definido en el apartado anterior.
• El método deshacer pone el estado del juego al estado anterior al último movimiento realizado que no
haya sido deshecho previamente, restaurando si es necesario las piezas eliminadas (nótese que el carácter
x representa que un determinado movimiento no capturó ninguna pieza), actualizando adecuadamente
tanto el turno de juego como la secuencia de movimientos realizados.
En caso de no existir movimiento alguno que deshacer, será notificado al entorno exterior mediante un
parámetro de salida que indique un error en la operación.
• El método guardar recibe como parámetro el nombre del fichero donde guardar los movimientos realizados
desde el inicio del juego hasta el momento actual (sin considerar los movimientos deshechos), en el mismo
orden secuencial en que fueron realizados, según el formato especificado en el método de carga especificado
anteriormente.
Métodos Públicos 3:
• Modifique el método movimiento anterior para que añada las siguientes condiciones a la hora de decidir
si un movimiento es correcto. En caso de que la pieza origen del movimiento de la casilla (fo , co ) a la
casilla (fd , cd ) sea:
◦ Rey (R,r): (abs(fd − fo ) ≤ 1) && (abs(cd − co ) ≤ 1)
◦ Dama (D,d): torre(fo , co , fd , cd ) || alfil(fo , co , fd , cd )

224
◦ Torre (T,t): (fd == fo ) || (cd == co )
◦ Alfil (A,a): (abs(fd − fo ) == abs(cd − co ))
◦ Caballo (C,c): ((abs(fd −fo ) == 2) && (abs(cd −co ) == 1)) || ((abs(fd −fo ) == 1) && (abs(cd −co ) == 2))
◦ Peón (P): El movimiento será correcto si se cumple alguna de las siguientes condiciones:
 (cd == co ) && (fd − fo == −1) && libre(fd , cd ))
 (cd == co ) && (fd − fo == −2) && (fo == 6) && libre(fd , cd ))
 (abs(cd − co ) == 1) && (fd − fo == −1) && ocupada(fd , cd ))
 No se considerará el caso del peón comido al-paso.
◦ Peón (p): El movimiento será correcto si se cumple alguna de las siguientes condiciones:
 (cd == co ) && (fd − fo == +1) && libre(fd , cd ))
 (cd == co ) && (fd − fo == +2) && (fo == 1) && libre(fd , cd ))
 (abs(cd − co ) == 1) && (fd − fo == +1) && ocupada(fd , cd ))
 No se considerará el caso del peón comido al-paso.
Solución: ajedrez.hpp
#ifndef _ajedrez_hpp_
#define _ajedrez_hpp_
#include <iostream>
#include <string>
#include <tr1/array>
#include "../t33/lista/lista.hpp"
namespace umalcc {
//-----------------------------
// EXTERNO, para que se pueda utilizar desde Main
struct Coord {
int fil, col;
};
//-----------------------------
class Ajedrez {
public:
//-------------------------
// Metodos Publicos 1
//-------------------------
//~Ajedrez() {}
Ajedrez();
void dibujar() const;
void movimiento(const Coord& org, const Coord& dst, bool& ok);
void cargar(const std::string& nombre_fich, bool& ok);
//-------------------------
// Metodos Publicos 2
//-------------------------
void deshacer(bool& ok);
void guardar(const std::string& nombre_fich, bool& ok) const;
//----------------------------------------------------------
private:
//-------------------------
// Estructura de Datos 1
//-------------------------
static const int NFILS = 8;
static const int NCOLS = 8;
typedef std::tr1::array<char, NCOLS> Fila;
typedef std::tr1::array<Fila, NFILS> Tablero;
//-------------------------
// Estructura de Datos 2
//-------------------------
struct Mov {
Coord org, dst;
char pz;
};
typedef Lista<Mov> LMov;
//-------------------------
// Metodos Privados 1
//-------------------------
void inc(int& x, int y) const;
bool linea_vacia(const Coord& org, const Coord& dst) const;
bool mov_correcto(const Coord& org, const Coord& dst) const;
//-------------------------
// Metodos Privados 3
//-------------------------
bool mov_pieza_correcto(const Coord& org, const Coord& dst) const;
//- Atributos 1 -------------
int turno;
Tablero tbl;

225
//- Atributos 2 -------------
LMov mov;
//----------------------------------------------------------
};
}
#endif

Solución: ajedrez.cpp
#include "ajedrez.hpp"
#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
#include <tr1/array>
#include <cassert>
using namespace std;
//------------------------------------------------------------------------------
// Espacio de nombres anonimo para subprogramas auxiliares
namespace {
const int FILA_INICIAL_P = 6;
const int FILA_INICIAL_p = 1;
const char VACIO = ’ ’;
const char VACIO_X = ’x’;
inline int abs(int x)
{
if (x < 0) {
x = -x;
}
return x;
}
inline bool esta_vacia(char pz)
{
return pz == VACIO;
}
inline bool esta_ocupada(char pz)
{
return ! esta_vacia(pz);
}
inline int direccion(char pz)
{
int d;
switch (pz) {
case ’P’:
d = -1;
break;
case ’p’:
d = +1;
break;
default:
d = 0;
break;
}
return d;
}
inline int fila_inicial(char pz)
{
int d;
switch (pz) {
case ’P’:
d = FILA_INICIAL_P;
break;
case ’p’:
d = FILA_INICIAL_p;
break;
default:
d = 0;
break;
}
return d;
}
}
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------

226
// Metodos Publicos 1
//--------------------------------------------------------------------------
Ajedrez::Ajedrez()
: turno(1), tbl(), // Estructura de Datos 1
mov() // Estructura de Datos 2
{
//-------------------------
for (int f = 0; f < int(tbl.size()); ++f) {
for (int c = 0; c < int(tbl[f].size()); ++c) {
tbl[f][c] = VACIO;
}
}
//---------------------
tbl[0][0] = ’t’;
tbl[0][1] = ’c’;
tbl[0][2] = ’a’;
tbl[0][3] = ’d’;
tbl[0][4] = ’r’;
tbl[0][5] = ’a’;
tbl[0][6] = ’c’;
tbl[0][7] = ’t’;
//---------------------
tbl[7][0] = ’T’;
tbl[7][1] = ’C’;
tbl[7][2] = ’A’;
tbl[7][3] = ’D’;
tbl[7][4] = ’R’;
tbl[7][5] = ’A’;
tbl[7][6] = ’C’;
tbl[7][7] = ’T’;
//---------------------
for (int i = 0; i < NCOLS; ++i) {
tbl[FILA_INICIAL_p][i] = ’p’;
tbl[FILA_INICIAL_P][i] = ’P’;
}
//---------------------
}
//----------------------------------------------------------
void Ajedrez::dibujar() const
{
cout << " ";
for (int c = 0; c < int(tbl[0].size()); ++c) {
cout << c << " ";
}
cout << endl;
cout << " +-----------------+"<< endl;
for (int f = 0; f < int(tbl.size()); ++f) {
cout << f << "| ";
for (int c = 0; c < int(tbl[f].size()); ++c) {
cout << tbl[f][c] << " " ;
}
cout << "| " << f << endl;
}
cout << " +-----------------+"<< endl;
cout << " ";
for (int c = 0; c < int(tbl[0].size()); ++c) {
cout << c << " ";
}
cout << endl;
cout << endl
<< "Turno: " << turno << " "
<< ((turno % 2 == 1) ? "[M]" : "[m]")
<< endl;
}
//----------------------------------------------------------
void Ajedrez::movimiento(const Coord& org, const Coord& dst, bool& ok)
{
if ( ! mov_correcto(org, dst)) {
ok = false;
} else {
ok = true;
char pz = tbl[dst.fil][dst.col];
tbl[dst.fil][dst.col] = tbl[org.fil][org.col];
tbl[org.fil][org.col] = VACIO;

227
++turno;
//-------------------------
// Metodos Publicos 2
//-------------------------
if (mov.llena()) {
ok = false;
} else {
Mov mv;
mv.org = org;
mv.dst = dst;
mv.pz = pz;
if (esta_vacia(mv.pz)) {
mv.pz = VACIO_X;
}
mov.insertar(mov.size(), mv);
}
}
}
//----------------------------------------------------------
void Ajedrez::cargar(const string& nombre_fich, bool& ok)
{
ifstream fent;
fent.open(nombre_fich.c_str());
if (! fent.fail()) {
ok = true;
Coord org, dst;
fent >> org.fil >> org.col
>> dst.fil >> dst.col ;
while ( ! fent.fail() && ok ) {
movimiento(org, dst, ok);
fent >> org.fil >> org.col
>> dst.fil >> dst.col ;
}
fent.close();
}
ok = ( ok && fent.fail() && fent.eof() ) ;
}
//--------------------------------------------------------------------------
// Metodos Publicos 2
//--------------------------------------------------------------------------
void Ajedrez::deshacer(bool& ok)
{
if (mov.size() == 0) {
ok = false;
} else {
ok = true;
Mov mv = mov.acceder(mov.size()-1);
mov.eliminar(mov.size()-1);
if (mv.pz == VACIO_X) {
mv.pz = VACIO;
}
tbl[mv.org.fil][mv.org.col] = tbl[mv.dst.fil][mv.dst.col];
tbl[mv.dst.fil][mv.dst.col] = mv.pz;
--turno;
//-------------------------
// Metodos Publicos 3
//-------------------------
}
}
//----------------------------------------------------------
void Ajedrez::guardar(const string& nombre_fich, bool& ok) const
{
ofstream fsal;
fsal.open(nombre_fich.c_str());
if (fsal.fail()) {
ok = false;
} else {
for (int i = 0; (i < mov.size()) && (!fsal.fail()); ++i) {
Mov mv = mov.acceder(i);
fsal << mv.org.fil << " " << mv.org.col << " "
<< mv.dst.fil << " " << mv.dst.col << endl ;
}
ok = ! fsal.fail();
fsal.close();

228
}
}
//--------------------------------------------------------------
void Ajedrez::inc(int& x, int y) const
{
if (x < y) {
++x;
} else if (x > y) {
--x;
}
}
//--------------------------------------------------------------
bool Ajedrez::linea_vacia(const Coord& org, const Coord& dst) const
{
Coord x = org;
do {
inc(x.fil, dst.fil);
inc(x.col, dst.col);
} while (esta_vacia(tbl[x.fil][x.col])
&& !(x.fil == dst.fil && x.col == dst.col));
return (x.fil == dst.fil && x.col == dst.col);
}
//----------------------------------------------------------
bool Ajedrez::mov_correcto(const Coord& org, const Coord& dst) const
{
bool ok = (org.fil < NFILS && org.col < NCOLS
&& dst.fil < NFILS && dst.col < NCOLS)
&& (((turno % 2 == 1) && isupper(tbl[org.fil][org.col]))
|| islower(tbl[org.fil][org.col]))
&& (esta_vacia(tbl[dst.fil][dst.col])
||(((turno % 2 == 1) && islower(tbl[dst.fil][dst.col]))
|| isupper(tbl[dst.fil][dst.col])));
if (toupper(tbl[org.fil][org.col]) != ’C’) {
ok = ok && linea_vacia(org, dst);
}
//-------------------------
// Metodos Privados 3
//-------------------------
ok = ok && mov_pieza_correcto(org, dst);
return ok;
}
//--------------------------------------------------------------------------
// Metodos Privados 3
//--------------------------------------------------------------------------
bool Ajedrez::mov_pieza_correcto(const Coord& org, const Coord& dst) const
{
bool ok = false;
int dir, fini;
char pz = tbl[org.fil][org.col];
switch (pz) {
case ’R’:
case ’r’:
ok = ((abs(dst.fil - org.fil) <= 1)&&(abs(dst.col - org.col) <= 1));
break;
case ’D’:
case ’d’:
ok = (((dst.fil == org.fil) || (dst.col == org.col))
|| (abs(dst.fil - org.fil) == abs(dst.col - org.col)));
break;
case ’T’:
case ’t’:
ok = ((dst.fil == org.fil) || (dst.col == org.col));
break;
case ’A’:
case ’a’:
ok = (abs(dst.fil - org.fil) == abs(dst.col - org.col));
break;
case ’C’:
case ’c’:
ok = (((abs(dst.fil-org.fil) == 2)&&(abs(dst.col-org.col) == 1))
||((abs(dst.fil-org.fil) == 1)&&(abs(dst.col-org.col) == 2)));
break;
case ’P’:
case ’p’:

229
dir = direccion(pz);
fini = fila_inicial(pz);
ok = (((dst.col == org.col) && (dst.fil - org.fil == dir)
&& esta_vacia(tbl[dst.fil][dst.col]))
||((dst.col == org.col) && (dst.fil - org.fil == 2*dir)
&& (org.fil == fini)
&& esta_vacia(tbl[dst.fil][dst.col]))
||((abs(dst.col - org.col) == 1) && (dst.fil - org.fil == dir)
&& esta_ocupada(tbl[dst.fil][dst.col]))
);
break;
}
return ok;
}
//--------------------------------------------------------------------------
}

2. Diseñe un programa que utilice la clase Ajedrez definida anteriormente y permita jugar a dicho juego.
Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "ajedrez.hpp"
using namespace std;
using namespace umalcc;

void leer(Coord& o, Coord& d)


{
cout << "Fila y Columna Origen: ";
cin >> o.fil >> o.col;
cout << "Fila y Columna Destino: ";
cin >> d.fil >> d.col;
}
void leer_nombre_fich(string& nombre_fich)
{
cout << "Introduzca Nombre de Fichero: ";
cin >> nombre_fich;
}
char menu()
{
char op;
cout << "M: Movimiento" << endl;
cout << "D: Deshacer" << endl;
cout << "C: Cargar" << endl;
cout << "G: Guardar" << endl;
cout << "F: Fin" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;
op = char(toupper(op));
} while (!((op==’M’)||(op==’D’)||(op==’C’)||(op==’G’)||(op==’F’)));
cout << endl;
return op;
}
int main()
{
Ajedrez aj;
Coord o, d;
char op = ’ ’;
string nombre_fich;
bool ok;
do {
aj.dibujar();
op = menu();
switch(op) {
case ’M’:
leer(o, d);
aj.movimiento(o, d, ok);
if (! ok) {
cout << "Movimiento Erroneo" << endl;
}
break;
case ’D’:

230
aj.deshacer(ok);
if (! ok) {
cout << "Movimiento Erroneo" << endl;
}
break;
case ’C’:
leer_nombre_fich(nombre_fich);
aj.cargar(nombre_fich, ok);
if (! ok) {
cout << "Error al cargar fichero: " << nombre_fich << endl;
}
break;
case ’G’:
leer_nombre_fich(nombre_fich);
aj.guardar(nombre_fich, ok);
if (! ok) {
cout << "Error al guardar fichero: " << nombre_fich << endl;
}
break;
}
} while (op != ’F’);
}

Examen 3: Gestión de Hotel


1. Defina e implemente la clase Hotel (dentro del espacio de nombres umalcc) que defina las siguientes estruc-
turas de datos y proporcione los siguientes métodos públicos para gestionar la ocupación de habitaciones
y reservas de un hotel:
Estructura de Datos 1: La estructura que contendrá los datos de ocupación de las habitaciones se
definirá mediante un tipo array de dos dimensiones (3 × 5), donde cada fila almacena los datos de cada
planta del hotel, y para cada fila hay una estructura que almacena los datos de cada habitación, que se com-
ponen del nombre del cliente (cadena de caracteres), de las fechas de entrada y salida (números naturales).
Hotel
habitaciones
0 1 2 3 4

nombre
0 f ent ··· ··· ··· ···
f sal

1 ··· ··· ··· ··· ···


2 ··· ··· ··· ··· ···

Métodos Públicos 1:
a) Hotel():
Inicializará la estructura de datos Hotel a un estado vacı́o, es decir, sin habitaciones ocupadas.
b) ~Hotel():
Liberará y destruirá todos los recursos asociados a la estructura de datos Hotel.
c) Mostrar():
Muestra en pantalla toda la información almacenada en las estructuras de datos del Hotel, en un formato
adecuadamente legible.
d ) Alojar(⇓ nombre, ⇓ f ent, ⇓ f sal, ⇑ planta, ⇑ n hab, ⇑ ok):
Comprueba si hay habitaciones disponibles en el rango de dı́as determinado por las fechas de entrada y
salida, en cuyo caso alojará adecuadamente a dicho cliente en alguna habitación que no esté ocupada. ok
tomará el valor true, y planta y n hab tomarán el valor de la planta y número de habitación donde se
alojará el cliente.
Si no hay habitaciones disponibles para las fechas especificadas, ok tomará el valor false.
Notas:
• Cuando se aloja un determinado cliente en una habitación (planta y número de habitación), ésta
pasará a estar ocupada por dicho cliente, almacenándose su nombre y fechas de ocupación.
• Hay habitaciones disponibles en un rango de fechas determinado por [f ent, f sal) si el número de
habitaciones totales del hotel es mayor que la suma del número de habitaciones ocupadas que
incluyen ese rango de fechas.
• Una habitación ocupada cuyas fechas de ocupación están delimitadas por [fe, fs) se encuentra
ocupada en un rango de fechas determinado por [f ent, f sal) si se cumple la siguiente condición
(fe < f sal) && (fs > f ent).

231
e) Desalojar(⇓ nombre, ⇑ ok):
Busca el cliente cuyo nombre se especifica como parámetro, y en caso de encontrarlo en una determinada
habitación, lo desalojará de ella borrando su nombre (a ""), fechas de ocupación (a cero), y ok tomará el
valor true. En otro caso, ok tomará el valor false.
f ) Guardar(⇓ nombre fich, ⇑ ok):
Almacena en el fichero de memoria secundaria, cuyo nombre se especifica como parámetro, los datos
de las habitaciones ocupadas (planta, número de habitación, fechas de entrada y salida, y nombre del
cliente). El formato de almacenamiento es el siguiente:
planta  habitación  f ent  f sal  nombre ←-
planta  habitación  f ent  f sal  nombre ←-
...........................................
Si la operación se realizó correctamente, ok tomará el valor true, y false en caso contrario.
g) Cargar(⇓ nombre fich, ⇑ ok):
Carga desde el fichero de memoria secundaria, cuyo nombre se especifica como parámetro, los datos
de las habitaciones ocupadas (planta, número de habitación, nombre del cliente y fechas de entrada y
salida). El formato de almacenamiento es como se especificó en la operación de Guardar.
Si la operación se realizó correctamente, ok tomará el valor true, y false en caso contrario.
Estructura de Datos 2: Modifique la estructura de datos 1 especificada anteriormente para que cada
habitación también almacene una cola de mensajes para dicha habitación, la cual almacena mensajes (cadena
de caracteres) para una determinada habitación en el mismo orden de llegada. Se definirá utilizando un
contenedor de tipo queue de la biblioteca estándar.
Métodos Públicos 2:
a) Modifique los constructores y destructores adecuadamente para inicializar y destruir la nueva estructura
de datos.
b) Modifique el método Mostrar() especificado anteriormente para que también muestre en pantalla la
información relativa a los mensajes disponibles para cada habitación. Nota: Esta operación debe realizarse
conservando sin alteración la cola de mensajes de cada habitación.
c) Modifique los métodos Alojar() y Desalojar() especificados anteriormente para que ambos inicialicen a
vacı́a la cola de mensajes de la habitación que sea alojada o desalojada respectivamente.
d ) Anyadir Mensaje(⇓ nombre, ⇓ msj, ⇑ ok):
Busca el cliente especificado como parámetro entre los clientes alojados en el hotel, y si lo encuentra,
entonces añade el mensaje especificado como parámetro en la cola de mensajes de la habitación ocupada
por dicho cliente y ok tomará el valor true. En otro caso, ok tomará el valor false.
e) Mostrar Mensajes(⇓ nombre, ⇑ ok):
Busca el cliente especificado como parámetro entre los clientes alojados en el hotel, y si lo encuentra,
entonces muestra por pantalla los mensajes (según el orden de llegada) que tenga disponibles en la cola
de mensajes de su habitación, que se quedará vacı́a después de realizar esta operación, y ok tomará el
valor true. En otro caso, si el nombre no se ha encontrado, ok tomará el valor false.
Estructura de Datos 3: Modifique la estructura de datos 1 (ó 2) especificada anteriormente para añadir una
secuencia de reservas que contiene los datos de las reservas realizadas, donde, cada una de ellas se compone
del nombre del cliente (cadena de caracteres) y de las fechas de entrada y salida (números naturales) de la
reserva. Se definirá mediante un tipo vector de la biblioteca estándar.
Métodos Públicos 3:
a) Modifique los constructores y destructores adecuadamente para inicializar y destruir la nueva estructura
de datos.
b) Modifique el método Mostrar() especificado anteriormente para que también muestre en pantalla la
información relativa a los reservas disponibles. Nota: Esta operación debe realizarse conservando sin
alteración la secuencia de reservas.
c) Reservar(⇓ nombre, ⇓ f ent, ⇓ f sal, ⇑ ok):
Si hay habitaciones disponibles en el rango de fechas determinado por [f ent, f sal), según las notas que
aparecen en la siguiente modificación del método Alojar, entonces añade la reserva (con nombre y rango
de fechas de ocupación) a la secuencia de reservas y ok tomará el valor true. En otro caso, si no hay
habitaciones disponibles para ese rango de fechas, ok tomará el valor false.
Nótese que al realizar la reserva no se hace ninguna asignación de habitación, sólo se comprueba que
habrá habitaciones disponibles para la fecha de la reserva. Cuando se aloje el huesped, será en dicho
momento cuando se asigne una de las habitaciones disponibles.
d ) Anular Reserva(⇓ nombre, ⇓ f ent, ⇓ f sal, ⇑ ok):
Busca la reserva especificada por el nombre y rango de fechas, y si la encuentra, la eliminará de la
secuencia de reservas y ok tomará el valor true. En otro caso, ok tomará el valor false.
e) Modifique el método Alojar() especificado anteriormente para que primero considere el siguiente caso
adicional:

232
1) Si el cliente ya tenı́a una reserva para ese rango de fechas, entonces se eliminará su reserva y se
procederá a alojarlo adecuadamente en alguna habitación que no esté ocupada. ok tomará el valor
true, y planta y n hab tomarán el valor de la planta y número de habitación donde se alojará el
cliente.
2) En otro caso, procederá como en la especificación original.
Notas: Las notas de alojamiento originales se ven afectadas por la secuencia de reservas de la siguiente
forma:
• Hay habitaciones disponibles en un rango de fechas determinado por [f ent, f sal) si el número de
habitaciones totales del hotel es mayor que la suma del número de habitaciones ocupadas que
incluyen ese rango de fechas y de reservas que incluyan también ese rango de fechas.
• Una reserva cuyas fechas de ocupación están delimitadas por [fe, fs) se encuentra ocupada en un rango
de fechas determinado por [f ent, f sal) si se cumple la siguiente condición (fe < f sal) && (fs > f ent).
Notas:
a) ⇓ Especifica un parámetro de entrada y ⇑ especifica un parámetro de salida.
b) Los parámetros nombre, msj y nombre fich son de tipo “cadena de caracteres”.
c) El parámetro ok es de tipo “lógico” (“boolean”).
d) Los parámetros planta y n hab representan la planta y número de habitación donde se alojará el cliente
en caso de que la operación haya sido correcta, y se representan mediante números de tipo “natural”.
e) Los parámetros f ent y f sal representan la fecha de entrada y de salida respectivamente, y se representan
mediante un número de tipo “natural” donde el dos de Mayo de 2011 se representa como el número
20110502, de tal forma que es posible comparar fechas directamente como la comparación de números
naturales. En caso de que la fecha de entrada sea mayor o igual a la fecha de salida, entonces la operación
involucrada será errónea y ok tomará el valor false.
Solución: hotel.hpp
#ifndef hotel_hpp__
#define hotel_hpp__ 1
#include <iostream>
#include <fstream>
#include <string>
#include <queue>
#include <vector>
#include <tr1/array>
namespace umalcc {
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
class Hotel {
public:
//------------------------------
//-- Metodos Publicos 1 --------
//------------------------------
// ~Hotel() {} // Destruccion por defecto
// Hotel() {} // Construccion por defecto
void Mostrar() const;
void Alojar(const std::string& nombre, unsigned fe, unsigned fs,
unsigned& planta, unsigned& n_hab, bool& ok);
void Desalojar(const std::string& nombre, bool& ok);
void Guardar(const std::string& nfich, bool& ok) const;
void Cargar(const std::string& nfich, bool& ok);
//------------------------------
//-- Metodos Publicos 2 --------
//------------------------------
void Anyadir_Mensaje(const std::string& nombre, const std::string& msj,
bool& ok);
void Mostrar_Mensajes(const std::string& nombre, bool& ok);
//------------------------------
//-- Metodos Publicos 3 --------
//------------------------------
void Reservar(const std::string& nombre, unsigned fe, unsigned fs,
bool& ok);
void Anular_Reserva(const std::string& nombre, unsigned fe, unsigned fs,
bool& ok);
private:
//- tipos ----------------------
typedef std::queue<std::string> CMsj; // Estructura de datos 2
struct Datos {
std::string nombre;
unsigned f_ent;
unsigned f_sal;

233
};
struct Habitacion {
Datos p;
CMsj m; // Estructura de datos 2
};
static const unsigned NFIL = 3;
static const unsigned NCOL = 5;
typedef std::tr1::array<Habitacion, NCOL> Fila;
typedef std::tr1::array<Fila, NFIL> Habitaciones;
typedef std::vector<Datos> Reservas; // Estructura de datos 3
//- atributos ------------------
Habitaciones hab;
Reservas rsv; // Estructura de datos 3
//- metodos 1 ------------------
void mostrar(const Datos& d) const;
void mostrar(const Habitacion& h) const;
void mostrar_hab() const;
bool esta_ocupada(const Datos& d, unsigned f_ent, unsigned f_sal) const;
unsigned cnt_ocupadas_hab(unsigned f_ent, unsigned f_sal) const;
bool disponibles(unsigned f_ent, unsigned f_sal) const;
void buscar(const std::string& nombre, unsigned& f, unsigned& c,
bool& ok) const;
void buscar_hab_libre(unsigned& f, unsigned& c, bool& ok) const;
//- metodos 2 ------------------
void mostrar_vaciar(CMsj& c) const;
void mostrar_cola(const CMsj& c) const;
//- metodos 3 ------------------
bool iguales(const Datos& d1, const Datos& d2) const;
int buscar_rsv(const Datos& d) const;
void mostrar_rsv() const;
unsigned cnt_ocupadas_rsv(unsigned f_ent, unsigned f_sal) const;
//------------------------------
};
}
#endif

Solución: hotel.cpp
#include "hotel.hpp"
#include <iostream>
#include <cassert>
#include <fstream>
using namespace std;
using namespace umalcc;

//------------------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------------
// Auxiliar: para facilitar la lectura de fichero
struct Dat_Resv {
unsigned planta;
unsigned n_hab;
unsigned f_ent;
unsigned f_sal;
std::string nombre;
};
void leer_hab(ifstream& fich, Dat_Resv& d)
{
fich >> d.planta >> d.n_hab >> d.f_ent >> d.f_sal >> d.nombre;
}
}
//------------------------------------------------------------------------------
namespace umalcc {
//--------------------------------------------------------------------------
//-- Metodos 1 -------------------------------------------------------------
//--------------------------------------------------------------------------
void Hotel::mostrar(const Datos& d) const
{
if (d.nombre == "") {
cout << "vacia";
} else {
cout << d.nombre << " " << d.f_ent << " " << d.f_sal;
}
}
//----------------------------------

234
void Hotel::mostrar(const Habitacion& h) const
{
mostrar(h.p);
cout << endl;
mostrar_cola(h.m); // Metodos 2
}
//----------------------------------
void Hotel::mostrar_hab() const
{
for (unsigned f = 0; f < hab.size(); ++f) {
for (unsigned c = 0; c < hab[f].size(); ++c) {
cout << f << "." << c << ": ";
mostrar(hab[f][c]);
}
}
}
//----------------------------------
void Hotel::Mostrar() const
{
cout << "Habitaciones:" << endl;
mostrar_hab();
cout << "Reservas:" << endl; // Metodos 3
mostrar_rsv(); // Metodos 3
}
//--------------------------------------------------------------------------
bool Hotel::esta_ocupada(const Datos& d, unsigned f_ent, unsigned f_sal)
const
{
return (d.f_ent < f_sal) && (d.f_sal > f_ent);
}
//----------------------------------
unsigned Hotel::cnt_ocupadas_hab(unsigned f_ent, unsigned f_sal) const
{
unsigned cnt = 0;
for (unsigned f = 0; f < hab.size(); ++f) {
for (unsigned c = 0; c < hab[f].size(); ++c) {
if (esta_ocupada(hab[f][c].p, f_ent, f_sal)) {
++cnt;
}
}
}
return cnt;
}
//----------------------------------
bool Hotel::disponibles(unsigned f_ent, unsigned f_sal) const
{
return (NFIL * NCOL) > (cnt_ocupadas_hab(f_ent, f_sal)
+ cnt_ocupadas_rsv(f_ent, f_sal)); // Metodos 3
}
//----------------------------------
void Hotel::buscar(const std::string& nombre, unsigned& f, unsigned& c,
bool& ok) const
{
f = 0;
c = 0;
while ((f < hab.size())&&(hab[f][c].p.nombre != nombre)) {
++c;
if (c >= hab[f].size()) {
c = 0;
++f;
}
}
ok = (f < hab.size());
}
//----------------------------------
void Hotel::buscar_hab_libre(unsigned& f, unsigned& c, bool& ok) const
{
buscar("", f, c, ok);
}
//----------------------------------
void Hotel::Alojar(const std::string& nombre, unsigned fe, unsigned fs,
unsigned& planta, unsigned& n_hab, bool& ok)
{
Anular_Reserva(nombre, fe, fs, ok); // Metodos 3

235
if ( ! ok ) {
ok = disponibles(fe, fs);
}
if (ok) {
buscar_hab_libre(planta, n_hab, ok);
assert(ok);
hab[planta][n_hab].p.nombre = nombre;
hab[planta][n_hab].p.f_ent = fe;
hab[planta][n_hab].p.f_sal = fs;
hab[planta][n_hab].m = CMsj(); // Metodos 2
}
}
//--------------------------------------------------------------------------
void Hotel::Desalojar(const std::string& nombre, bool& ok)
{
unsigned f, c;
buscar(nombre, f, c, ok);
if (ok) {
hab[f][c].p.nombre = "";
hab[f][c].p.f_ent = 0;
hab[f][c].p.f_sal = 0;
hab[f][c].m = CMsj(); // Metodos 2
}
}
//--------------------------------------------------------------------------
void Hotel::Guardar(const std::string& nfich, bool& ok) const
{
ofstream fich;
fich.open(nfich.c_str());
if (fich.fail()) {
ok = false;
} else {
for (unsigned f = 0; ! fich.fail() && (f < hab.size()); ++f) {
for (unsigned c = 0; ! fich.fail() && (c < hab[f].size()); ++c){
if (hab[f][c].p.nombre != "") {
fich << f << " " << c << " "
<< hab[f][c].p.f_ent << " "
<< hab[f][c].p.f_sal << " "
<< hab[f][c].p.nombre << endl;
}
}
}
ok = ! fich.fail();
fich.close();
}
}
//--------------------------------------------------------------------------
void Hotel::Cargar(const std::string& nfich, bool& ok)
{
ifstream fich;
fich.open(nfich.c_str());
if (fich.fail()) {
ok = false;
} else {
Dat_Resv d;
leer_hab(fich, d);
while (! fich.fail()) {
hab[d.planta][d.n_hab].p.nombre = d.nombre;
hab[d.planta][d.n_hab].p.f_ent = d.f_ent;
hab[d.planta][d.n_hab].p.f_sal = d.f_sal;
hab[d.planta][d.n_hab].m = CMsj(); // Metodos 2
leer_hab(fich, d);
}
ok = fich.eof();
fich.close();
}
}
//--------------------------------------------------------------------------
//-- Metodos 2 -------------------------------------------------------------
//--------------------------------------------------------------------------
void Hotel::Anyadir_Mensaje(const std::string& nombre,
const std::string& msj,
bool& ok)
{

236
unsigned f, c;
buscar(nombre, f, c, ok);
if (ok) {
hab[f][c].m.push(msj);
}
}
//--------------------------------------------------------------------------
void Hotel::mostrar_vaciar(CMsj& c) const
{
while ( ! c.empty()) {
cout << "\t" << c.front() << endl;
c.pop();
}
}
//----------------------------------
void Hotel::Mostrar_Mensajes(const std::string& nombre, bool& ok)
{
unsigned f, c;
buscar(nombre, f, c, ok);
if (ok) {
mostrar_vaciar(hab[f][c].m);
}
}
//--------------------------------------------------------------------------
void Hotel::mostrar_cola(const CMsj& c) const
{
CMsj copia(c); // Para que se vacie la copia
mostrar_vaciar(copia);
}
//--------------------------------------------------------------------------
//-- Metodos 3 -------------------------------------------------------------
//--------------------------------------------------------------------------
void Hotel::Reservar(const std::string& nombre, unsigned fe, unsigned fs,
bool& ok)
{
if (disponibles(fe, fs)) {
Datos d = { nombre, fe, fs };
rsv.push_back(d);
ok = true;
} else {
ok = false;
}
}
//--------------------------------------------------------------------------
bool Hotel::iguales(const Datos& d1, const Datos& d2) const
{
return ((d1.nombre == d2.nombre)
&& (d1.f_ent == d2.f_ent)
&& (d1.f_sal == d2.f_sal));
}
//----------------------------------
int Hotel::buscar_rsv(const Datos& d) const
{
int i = 0;
while ((i < int(rsv.size())) && ! iguales(d, rsv[i])) {
++i;
}
return i;
}
//----------------------------------
void Hotel::Anular_Reserva(const std::string& nombre,
unsigned fe, unsigned fs, bool& ok)
{
Datos d = { nombre, fe, fs };
int i = buscar_rsv(d);
if (i < int(rsv.size())) {
if (i < int(rsv.size())-1) {
rsv[i] = rsv[rsv.size()-1];
}
rsv.pop_back();
ok = true;
} else {
ok = false;
}

237
}
//--------------------------------------------------------------------------
void Hotel::mostrar_rsv() const
{
for (int i = 0; i < int(rsv.size()); ++i) {
mostrar(rsv[i]);
cout << endl;
}
}
//--------------------------------------------------------------------------
unsigned Hotel::cnt_ocupadas_rsv(unsigned f_ent, unsigned f_sal) const
{
unsigned cnt = 0;
for (int i = 0; i < int(rsv.size()); ++i) {
if (esta_ocupada(rsv[i], f_ent, f_sal)) {
++cnt;
}
}
return cnt;
}
//--------------------------------------------------------------------------
}

2. Diseñe un programa que utilice la clase Hotel definida anteriormente y permita gestionar las habitaciones
de un hotel.
Solución: main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "hotel.hpp"
using namespace std;
using namespace umalcc;

void leer_num(unsigned& num, const string& msj)


{
cout << msj ;
cin >> num;
}
void leer_str(string& str, const string& msj)
{
cout << msj ;
cin >> str;
}
void leer_cliente(string& nombre, unsigned& f_ent, unsigned& f_sal)
{
leer_str(nombre, "Introduzca el nombre del cliente: ");
leer_num(f_ent, "Introduzca la fecha de entrada (20110507): ");
leer_num(f_sal, "Introduzca la fecha de salida (20110507): ");
}
void check_ok(bool ok, const string& msj)
{
if (ok) {
cout << "Operacion Correcta. [" << msj << "]" << endl;
} else {
cout << "Operacion Erronea. [" << msj << "]" << endl;
}
}
char menu()
{
char op;
cout << "X: Fin" << endl;
cout << "A: Mostrar Datos Hotel" << endl;
cout << "B: Alojar Nuevo Cliente" << endl;
cout << "C: Desalojar Cliente" << endl;
cout << "D: Guardar Datos" << endl;
cout << "E: Cargar Datos" << endl;
cout << "F: Anyadir Mensaje" << endl;
cout << "G: Mostrar Mensajes" << endl;
cout << "H: Reservar Habitacion" << endl;
cout << "I: Anular Reserva" << endl;
do {
cout << endl << " Opcion: ";
cin >> op;

238
op = char(toupper(op));
} while (!((op == ’X’)||((op >= ’A’)&&(op <= ’I’))));
cout << endl;
return op;
}
int main()
{
Hotel hotel;
bool ok;
string nombre, msj;
unsigned f_ent, f_sal, planta, n_hab;
char op = ’ ’;
do {
op = menu();
switch(op) {
case ’A’:
hotel.Mostrar();
break;
case ’B’:
leer_cliente(nombre, f_ent, f_sal);
hotel.Alojar(nombre, f_ent, f_sal, planta, n_hab, ok);
check_ok(ok, "Alojar");
if (ok) {
cout << " Alojar: " << nombre
<< " Planta: " << planta
<< " Hab: " << n_hab << endl;
}
break;
case ’C’:
leer_str(nombre, "Introduzca el nombre del cliente: ");
hotel.Desalojar(nombre, ok);
check_ok(ok, "Desalojar");
break;
case ’D’:
leer_str(nombre, "Introduzca el nombre del fichero: ");
hotel.Guardar(nombre, ok);
check_ok(ok, "Guardar");
break;
case ’E’:
leer_str(nombre, "Introduzca el nombre del fichero: ");
hotel.Cargar(nombre, ok);
check_ok(ok, "Cargar");
break;
case ’F’:
leer_str(nombre, "Introduzca el nombre del cliente: ");
leer_str(msj, "Introduzca el mensaje: ");
hotel.Anyadir_Mensaje(nombre, msj, ok);
check_ok(ok, "Anyadir Mansaje");
break;
case ’G’:
leer_str(nombre, "Introduzca el nombre del cliente: ");
hotel.Mostrar_Mensajes(nombre, ok);
check_ok(ok, "Mostrar Mensajes");
break;
case ’H’:
leer_cliente(nombre, f_ent, f_sal);
hotel.Reservar(nombre, f_ent, f_sal, ok);
check_ok(ok, "Reservar");
break;
case ’I’:
leer_cliente(nombre, f_ent, f_sal);
hotel.Anular_Reserva(nombre, f_ent, f_sal, ok);
check_ok(ok, "Anular Reserva");
break;
}
} while (op != ’X’);
}

3. Modifique la implementación de la clase Hotel del ejercicio anterior (estructura de datos 3) para que
utilice, para almacenar la secuencia de reservas, el TAD lista genérica con implementación en memoria
dinámica (Tema 3.3 Ej. 1), en vez del tipo vector de la biblioteca estándar.

239

Вам также может понравиться