Estructuras de Datos y Algoritmos (ITIS)

Anuncio
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
Estructuras de Datos y Algoritmos (ITIS)
Ingeniería Técnica en Informática de Sistemas, Curso 2º
PRÁCTICA 3
TAD TREE
Árbol binario de búsqueda. Tabla de frecuencias.
Uno de los mecanismos más sencillos que se utilizan en los sistemas de recuperación de información
textual consiste en asociar a cada documento de texto una Tabla de Frecuencias, tabla en la que
aparecen de forma ordenada las palabras encontradas en el texto junto con su frecuencia de aparición
(número de ocurrencias). Uno de los objetivos de esta práctica será el utilizar un ABB para establecer
un mecanismo eficiente de asociación entre un conjunto de datos de entrada (cadenas de caracteres y su
correspondiente tabla de frecuencias) y hace eficiente el proceso de búsqueda sobre dicha árbol.
Por ejemplo:
En este caso, los objetos son del tipo Cstring,
clase que heredará de Ccomparable y que
encapsulará la funcionalidad del tipo string
{“uno”, “uno”, “dos”, “tres”,
“uno”, “uno”, “dos”,...}
PALABRA
“dos”
“tres”
“uno”
…
FRECUENCIA
2
1
4
…
Listado ordenado
En esta práctica vamos a utilizar un ABB “genérico” (utilizando herencia) para establecer un
mecanismo eficiente de asociación entre un conjunto de datos de entrada (conjunto de objetos
comparables generados de forma aleatoria) y su correspondiente tabla de frecuencias.
• Utilizando Cstring (hereda de Comparable, que es una clase abstracta con todas sus funciones
virtuales puras), generar n objetos Cstring comparables aleatorios e insertarlos en un ABB.
• Mostrar en pantalla el contenido de la estructura realizando los distintos recorridos vistos clase.
Como mínimo: anchura y profundidad (Preorden, Inorden y Postorden).
• Mostrar estructura de ABB resultante, si n es relativamente pequeño.
Página 1
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
• Generar una cadena de caracteres (string) que represente el objeto árbol (método string
toString()). Introducir la posibilidad de construir un árbol a partir de una cadena de caracteres,
resultado del método toString() de otro árbol (método void fromString(string))
• Mostrar por pantalla la tabla de frecuencias asociada.
A continuación se muestran las cabeceras de las clases (declaración) a implementar, junto con la
implementación (definición) de algunas de las funciones miembro declaradas.
// Comparable.hpp
#ifndef __CCOMPARABLE_HPP__
#define __CCOMPARABLE_HPP__
#include <string>
using namespace std;
class Ccomparable
{
public:
virtual bool operator==(Ccomparable*) = 0;
virtual bool operator<(Ccomparable*) = 0;
virtual bool operator>(Ccomparable*) = 0;
virtual string toString() = 0;
virtual void fromString(string) = 0;
};
#endif
// Cstring.hpp
#ifndef __CSTRING_HPP__
#define __CSTRING_HPP__
#include <string>
#include <sstream>
#include "Ccomparable.hpp"
using namespace std;
class Cstring : public Ccomparable{
string cad;
public:
Cstring(string);
Cstring();
bool operator==(Ccomparable*);
bool operator<(Ccomparable*);
bool operator>(Ccomparable*);
string getCad();
void setCad(string);
string toString();
void fromString(string);
};
#endif
// Cstring.cpp
#include "Cstring.hpp"
Cstring::Cstring(string cadena)
{
cad = cadena;
}
Página 2
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
bool Cstring::operator<(Ccomparable* ele)
{
return( cad < ((Cstring*)ele)->getCad() );
}
string Cstring::toString()
{
ostringstream os;
os << cad;
return os.str();
}
void Cstring::fromString(string cadena)
{
cad = cadena;
}
// ***** Implementar restantes funciones miembro de la clase Cstring
// Cnodo.hpp
#ifndef __CNODO_HPP__
#define __CNODO_HPP__
#include "Ccomparable.hpp"
using namespace std;
class Cnodo
{
Ccomparable* info;
Cnodo* izq,* dch;
int frecuencia;
public:
Cnodo();
~Cnodo();
Cnodo(Comparable*);
Ccomparable* getInfo();
Cnodo* getIzq();
Cnodo* getDch();
string toString();
void setIzq(Cnodo*);
void setDch(Cnodo*);
void setFrecuencia(int);
int getFrecuencia();
void setInfo(Ccomparable*);
};
#endif
// Cnodo.cpp
#include "Cnodo.hpp"
Cnodo::Cnodo(Ccomparable* valor)
{
info = valor;
izq = NULL;
dch = NULL;
frecuencia = 0;
}
Página 3
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
Cnodo::Cnodo()
{
info=NULL;
izq=NULL;
dch=NULL;
frecuencia=0;
}
Cnodo::~Cnodo()
{
delete info;
}
string Cnodo::toString()
{
return info->toString();
}
// ***** Implementar restantes funciones miembro de la clase Cnodo
// CarbolBB.hpp
#ifndef __CARBOLBB_HPP__
#define __CARBOLBB_HPP__
#include "Cnodo.hpp"
#include "Cstring.hpp"
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <math.h>
using namespace std;
class CarbolBB {
Cnodo* raiz;
void preOrden(Cnodo*);
void inOrden(Cnodo*);
void postOrden(Cnodo*);
void eliminarArbol(Cnodo*);
void mostrarFrecuencias(Cnodo*);
Cnodo* eliminar(Ccomparable*, Cnodo*);
void mostrarArbol(Cnodo*, int);
int altura(Cnodo*);
Cnodo* buscarMinimo(Cnodo*);
Cnodo* eliminarMinimo(Cnodo*);
Cnodo* buscar(Ccomparable*, Cnodo*);
Cnodo* insertar(Ccomparable*, Cnodo*);
string toString(string, Cnodo*);
public:
CarbolBB();
~CarbolBB();
int altura();
void preOrden();
void inOrden();
void postOrden();
void anchura();
void insertar(Ccomparable*);
void mostrarArbol();
void mostrarFrecuencias();
Página 4
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
void eliminar(Ccomparable*);
Cnodo* buscar(Ccomparable*);
string toString();
void fromString(string);
};
#endif
// CarbolBB.cpp
#include "CarbolBB.hpp"
CarbolBB::CarbolBB()
{
raiz = NULL;
}
CarbolBB::~CarbolBB()
{
eliminarArbol(raiz);
}
void CarbolBB::eliminarArbol(Cnodo* r)
{
if (!r) return;
eliminarArbol(r->getIzq());
eliminarArbol(r->getDch());
delete r;
}
void CarbolBB::preOrden()
{
preOrden( raiz );
}
void CarbolBB::preOrden(Cnodo* r)
{
if( r )
{
cout << r->toString() <<"
";
preOrden(r->getIzq());
preOrden(r->getDch());
}
}
Cnodo* CarbolBB::buscar(Ccomparable* x)
{
return buscar(x, raiz);
}
Cnodo* CarbolBB::buscar(Ccomparable* x, Cnodo* a)
{
if(!a)
return NULL;
if(*x == a->getInfo())
return a;
if(*x > a->getInfo())
return buscar(x, a->getDch());
else
return buscar(x, a->getIzq());
}
Página 5
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
void CarbolBB::insertar(Ccomparable* x)
{
raiz = insertar(x, raiz);
}
Cnodo* CarbolBB::insertar(Ccomparable* x, Cnodo* nb)
{
if(!nb)
nb = new Cnodo(x);
if(*x < nb->getInfo())
nb->setIzq(insertar(x, nb->getIzq()));
if(*x > nb->getInfo())
nb->setDch(insertar(x, nb->getDch()));
if(*x == nb->getInfo())
nb->setFrecuencia(nb->getFrecuencia() + 1);
return nb;
}
string CarbolBB::toString()
{
string cadena = "";
return toString(cadena, raiz);
}
string CarbolBB::toString(string cad, Cnodo* nb)
{
if(!nb)
return cad;
cad += (nb->getInfo())->toString() + " ";
cad = toString(cad, nb->getIzq());
cad = toString(cad, nb->getDch());
return cad;
}
// ***** Implementar restantes funciones miembro de la clase CarbolBB
Página 6
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <string>
#include "CarbolB.hpp"
#include "Cstring.hpp"
using namespace std;
int main()
{
int n;
CarbolB* arbol = NULL;
Ccomparable* nuevoElemento = NULL;
// Generar n objetos Cstring comparables aleatorios e insertarlos en un ABB
cout << "INTRODUZCA EL NUMERO DE ELEMENTOS A INSERTAR: ";
cin >> n;
arbol = new CarbolB();
for(int i = 0; (i < n); i++)
{
int num = rand() % 10;
string palabra = "";
char p;
for (int j = 0; (j < num); j++)
{
p = (rand() % 25) + 65;
palabra = palabra + p;
}
nuevoElemento = new Cstring(palabra);
arbol->insertar(nuevoElemento);
}
if (arbol->altura() < 15)
arbol->mostrarArbol();
else
cout <<"ERROR -> Demasiados alturar para mostrar." << endl;
// Realizar las pruebas de las funciones que se piden sobre el ABB
delete arbol;
}
Página 7
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
Árbol binario de búsqueda. Validación de accesos a un ordenador (login).
Planteamiento del problema. Consideremos el problema de organizar una colección de
identificadores de usuario de un ordenador (nombre de usuario o username) y sus claves de acceso
(palabras clave o password). Cada vez que un usuario accede al sistema introduciendo su username y
su password (secreto), el sistema debe comprobar la validez de ambos para verificar que es un usuario
legítimo. Puesto que dicha validación debe hacerse en un gran número de veces al día, es necesario
estructurar esa información de forma que se pueda encontrar rápidamente. Además, debe ser una
estructura dinámica porque se pueden ir añadiendo sistemáticamente usuarios al sistema.
Diseño. Uno de los objetos de este problema es la información de usuario (username y password) que
van a ser cadenas de caracteres. Para ello construiremos una clase InfoUsuario. Necesitamos también
una colección de objetos InfoUsuario. Usaremos un ABB para ello, porque en él se pueden buscar
rápidamente y también se trata de una estructura dinámica. Por tanto, las principales operaciones que
necesitamos nos la proporciona la clase ABB:
• Construir un ABB de objetos InfoUsuario
• Buscar en el ABB un objeto InfoUsuario introducido desde teclado
• Mostrar un mensaje indicando si el usuario es válido
La clase InfoUsuario tendrá como atributos el username y el password del usuario (ambos cadenas
de caracteres). Puesto que la búsqueda y la inserción en un ABB requiere poder comparar los valores
almacenados en él con < y ==, debemos sobrecargar dichos operadores para InfoUsuario. Y también
necesitamos una operación de entrada, por lo que también sobrecargaremos >> para este caso.
class InfoUsuario
{
public:
string id() const { return username; }
void leer(istream & in)
{
in >> username >> password;
}
bool operator==(const InfoUsuario & user) const
{ return username == user.username && password == user.password; }
bool operator<(const InfoUsuario & user) const
{ return username < user.username || username == user.username &&
password < user.password; }
private:
string username, password;
};
istream & operator>>(istream & in, InfoUsuario & user)
{
user.leer(in);
}
El algoritmo que usa estos objetos y operaciones para la validación de acceso a un ordenador (login) es
el siguiente (Algoritmo para leer username y password, comprobando su validez):
// Inicialmente crea el ABB de objetos InfoUsuario
1.
Abrir un stream a un archivo que contiene la información válida sobre username-password de
usuarios
Página 8
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
2.
Crear un ABB con elementos del tipo InfoUsuario
3.
Leer los objetos InfoUsuario del archivo e insertarlos en el ABB
// Proceso de verificación
4.
Repetir lo siguiente hasta que se detenga el sistema
a. Leer un objeto InfoUsuario
b. Buscar en el ABB dicho objeto
c. Si se encuentra, mostrar el mensaje “acceso permitido”,
si no, mostrar el mensaje “acceso denegado”
Fin del bucle repetir
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
// Abrir un stream a un archivo con los pares username-password de usuarios
string archivoUsuario;
cout << "Introduzca el nombre completo de archivo (con username-password): ";
getline(cin, archivoUsuario);
ifstream canalEntrada (archivoUsuario.data());
if (!canalEntrada.is_open())
{
cerr << "No se puede abrir " << archivoUsuario << "\n";
exit(1);
}
// Crear un ABB con elementos del tipo InfoUsuario
// ABB<InfoUsuario> abbUsuarios;
// Leer los objetos InfoUsuario del archivo e insertarlos en el ABB
InfoUsuario usuario;
for(;;)
{
canalEntrada >> usuario;
if (canalEntrada.eof()) break;
// abbUsuarios.insertar(usuario);
}
canalEntrada.close();
// Proceso de verificación: validación de accesos (logins)
cout << "Introduzca S S para salir (detener el procesamiento).\n";
for (;;)
{
cout << "\nUsuario username & password: ";
cin >> usuario;
if (usuario.id() == "S") break;
//if (abbUsuarios.buscar(usuario))
// cout << "Acceso permitido\n";
//else
// cout << "Acceso degenado\n";
}
}
Página 9
Estructuras de Datos y Algoritmos (ITIS). TAD Tree
Ejercicios prácticos relacionados con los árboles vistos en clase.
Ejercicio 1. Escribir una función que a partir de un ABB y dos números enteros n1 y n2, tal que n1 ≤
n2, visualice en orden todos los elementos e del árbol tal que n1 ≤ e ≤ n2.
Ejercicio 2. Realizar una función que devuelva la posición de un elemento en un ABB (desde 1 hasta
el número de nodos del árbol) según un recorrido en inorden sobre el mismo. Si el elemento no está en
dicho árbol, devolverá 0.
Ejercicio 3. Dado un heap mínimo, implementar una función que devuelva ordenado (de menor a
mayor) el contenido de dicho heap (i.e. implementar el heapsort).
Ejercicio 4. Para hacer uso de la implementación de árboles AVL estudiada. Inserte en un árbol AVL
vacío los siguientes valores clave (por orden): 3, 8, 9, 2 1, 5, 4, 6, 10 y 7. Indique las rotaciones y los
cambios en los factores de equilibrio que aplica el algoritmo de inserción en cada caso. Muestre el
árbol anterior y siguiente a cada inserción. Después de haber dejado cerrado el árbol AVL tras las
sucesivas inserciones, extraiga los siguientes valores de clave de dicho árbol: 8, 10 y 1, indicando
también las rotaciones y los cambios en los factores de equilibrio que aplica el algoritmo de extracción
en cada caso. Muestre el árbol anterior y siguiente a cada extracción.
Ejercicio 5. Para hacer uso de la implementación de árboles Roji-Negros estudiada. Inserte en un árbol
Roji-Negro vacío los siguientes valores clave (por orden): 8, 4, 6, 2, 14, 12 y 10. Indique las rotaciones
y las recolocaciones que aplica el algoritmo de inserción en cada caso. Muestre el árbol anterior y
siguiente a cada inserción. Después de haber dejado cerrado el árbol Roji-Negro tras las sucesivas
inserciones, extraiga los siguientes valores de clave de dicho árbol: 6, 14 y 8, indicando también las
reestructuraciones que aplica el algoritmo de extracción en cada caso. Muestre el árbol anterior y
siguiente a cada extracción.
Ejercicio 6 (Opcional). Estudiar detenidamente la implementación en C++ de un contenedor usando
un árbol B (ArbolB) que se muestra en el libro “Fundamentos de Estructuras de Datos” (paginas 394405), sabiendo que éste extiende de Multirrama, y éste a su vez de ContenedorPersistente.
Implementar un pequeño programa prueba que inserte una serie de elementos y elimine alguno de ellos,
comprobando cómo quedaría el árbol después de todas las inserciones y extracciones. Puede elegir el
orden del árbol B, y todas las decisiones que sean necesarias para la prueba, ... pero justificándolo todo.
Página 10
Descargar