Tema 4

Anuncio
EDA 02/03
Tema 6.
Tema 6. Estructuras de datos lineales
Estructuras de datos lineales
INTRODUCCIÓN.......................................................................................................1
IMPLEMENTACIÓN DE ESTRUCTURAS DE DATOS CON PUNTEROS...............2
PILAS.........................................................................................................................3
ESPECIFICACIÓN ALGEBRAICA (PILA NO ACOTADA).......................................................3
ESPECIFICACIÓN ALGEBRAICA (PILA ACOTADA)............................................................4
POSIBLES IMPLEMENTACIONES ...................................................................................5
EJEMPLO: IMPLEMENTACIÓN ESTÁTICA (IMPERATIVA) ...................................................6
EJEMPLO: IMPLEMENTACIÓN ESTÁTICA (GENÉRICA, POO) ...........................................7
EJEMPLO: IMPLEMENTACIÓN DINÁMICA .......................................................................8
APLICACIÓN:
EVALUACIÓN DE EXPRESIONES EN NOTACIÓN POLACA .....................10
COLAS.....................................................................................................................14
ESPECIFICACIÓN ALGEBRAICA (COLA NO ACOTADA)....................................................14
POSIBLES IMPLEMENTACIONES .................................................................................15
EJEMPLO: IMPLEMENTACIÓN ESTÁTICA .....................................................................15
LISTAS ....................................................................................................................18
ESPECIFICACIÓN ALGEBRAICA ..................................................................................19
INTERFAZ DE UNA CLASE LISTA .................................................................................20
POSIBLES IMPLEMENTACIONES .................................................................................21
MODIFICACIÓN DE LA DEFINICIÓN DEL TAD: INSERTAR EN ORDEN ...............................23
OTROS TIPOS DE LISTAS ..........................................................................................27
LISTA CON PUNTO DE INTERÉS ..................................................................................29
ESPECIFICACIÓN ALGEBRAICA DE LA LISTA CON PUNTO DE INTERÉS.............................30
Página 0
EDA 02/03
Tema 6.
Tema 6. Estructuras de datos lineales
Estructuras de datos lineales
Introducción
Es necesario el uso de TADs que permitan almacenar una colección de
elementos de un determinado tipo.
Estructuras de datos lineales:
para almacenar secuencias: cada
elemento tiene un único sucesor (excepto el último) y un único
predecesor (excepto el primero).
Operaciones necesarias: crear, insertar un elemento, eliminar un
elemento, consultar, etc.
Según cómo se realicen las operaciones consideradas, tendremos TADs
distintos.
Podemos considerar estructuras de datos:
- Estáticas: hay un número máximo de elementos; implementadas con
un vector.
- Dinámicas: sin número máximo de elementos, si suponemos
memoria infinita; implementadas con punteros.
Para un TAD con un comportamiento determinado pueden existir
varias implementaciones distintas.
Si los costes de las operaciones en distintas implementaciones son
diferentes, hay que seleccionar la más adecuada según la aplicación
donde se vaya a utilizar.
Página 1
EDA 02/03
Tema 6. Estructuras de datos lineales
Implementación de estructuras de datos con punteros
Ventajas:
- No es necesario determinar la dimensión máxima de la estructura
(aunque la memoria no es infinita, realmente)
- El programador no tiene que gestionar el espacio libre (aunque el SO
puede hacerlo de manera ineficiente)
El problema de la asignación:
p
q
- Un único objeto está identificado por dos o más punteros; no hay copia
- Solución: todo TAD debe ofrecer una operación duplica que se usará en
lugar de la asignación
El problema de la comparación:
p=q
- Se comparan direcciones, no los objetos
- Solución: todo TAD debe ofrecer una operación igual para comparar
El problema de los parámetros de entrada
- Cuando usamos un parámetro de entrada, se pasa una copia del puntero,
que no variará, pero sí pueden modificarse los datos de la estructura
- Solución: Prohibido modificar los parámetros de entrada; si es
necesario, hacer una copia con duplica
El problema de las variables auxiliares
- Se debe liberar toda la memoria utilizada por las estructuras con
punteros creadas como variables auxiliares
- Solución: llamar a una función destruir que libere todo el espacio (o
confiar en el recolector de basura, si existe)
El problema de la reinicialización
- Si aplicamos la operación de creación sobre una estructura con
información, dejaremos espacio inaccesible en la memoria
- Solución: Llamar a la operación destruir cuando la información de la
estructura ya no es necesaria
Página 2
EDA 02/03
Tema 6. Estructuras de datos lineales
Pilas
Pila: Stack: LIFO (Last In, First Out)
Ejemplo informático: gestión de memoria; almacenamiento de las
llamadas a procedimientos y variables locales.
Operaciones básicas: Crear (create), Apilar (push), Desapilar (pop),
Cima (top, peek), ¿Vacía? (empty)
Si tenemos un espacio limitado en la pila, serán necesarias operaciones
para saber si la pila está llena y su profundidad (número de elementos).
Especificación algebraica (pila no acotada)
TAD Pila
TIPO: Pila (elemento)
SINTAXIS:
crear ()
→
vacía (Pila)
→
cima (Pila)
→
apilar (Pila, Elemento)
desapilar (Pila)
SEMÁNTICA:
Pila
Boolean
Elemento
→ Pila
→ Pila
∀ P ∈ Pila; ∀ i ∈ Elemento
vacía (crear)
vacía (apilar (P,i))
⇒
⇒
Cierto
Falso
cima (crear)
cima (apilar (P,i))
⇒
⇒
Error
i
desapilar (crear)
desapilar (apilar (P,i))
⇒
⇒
crear
P
{o Error}
Página 3
EDA 02/03
Tema 6. Estructuras de datos lineales
Especificación algebraica (pila acotada)
TAD Pila
TIPO: Pila (elemento, Máximo)
SINTAXIS:
crear ( )
→ Pila
vacía (Pila)
→ Boolean
cima (Pila)
→ Elemento
apilar (Pila, Elemento) → Pila
desapilar (Pila)
→ Pila
profundidad (Pila)
→ Natural
llena (Pila)
→ Boolean
SEMÁNTICA:
∀ P ∈ Pila; ∀ i ∈ Elemento
vacía (crear)
vacía (apilar (P,i))
⇒
⇒
Cierto
Falso
cima (crear)
cima (apilar (P,i))
⇒
⇒
Error
i
desapilar (crear)
desapilar (apilar (P,i))
⇒
⇒
crear
P
profundidad (crear)
⇒
profundidad (apilar(P, i)) ⇒
{o Error}
cero
sucesor( profundidad (P) )
llena (P)
⇒
igual( profundidad(P), Máximo)
apilar (P, i)
⇒
si llena(P) entonces Error
si no apilar (P, i)
Página 4
EDA 02/03
Tema 6. Estructuras de datos lineales
Posibles implementaciones
Implementación estática
1
A
2
B
3
C
4
?
...
...
n
?
Cima: 3
- Inserciones y extracciones por el final del vector, indicado por Cima.
- Sin el atributo Cima el coste de todas las operaciones sería lineal.
Implementación dinámica
Cima
A
B
C
- Inserciones y extracciones por el principio, apuntado por Cima.
Página 5
EDA 02/03
Tema 6. Estructuras de datos lineales
Ejemplo: Implementación estática (imperativa)
// pila.h
// Definición de la interfaz del TAD Pila
// Implementación estática
#define Max 100
// Nº máximo de elementos;
typedef int dato; // Tipo de los elementos
typedef dato vector[Max];
typedef struct pila {
vector elemento;
int cont;
};
// Tipo de la pila
// Operaciones
void crear (pila &p);
boolean estavacia (pila p);
boolean estallena (pila p);
void apilar (pila &p, dato e);
void cima (pila p, dato &e);
void eliminar (pila &p);
// pila.cpp
#include "pila.h"
void crear (pila &p) {
p.cont = 0;
}
boolean estavacia (pila p) {
if (p.cont == 0)
return(true);
else
return(false);
}
boolean estallena (pila p) {
return( (p.cont == Max));
}
void apilar (pila &p, dato e) {
// error si la pila estuviera llena
p.elemento[p.cont] = e;
p.cont++;
}
void cima (pila p, dato &e) {
// error si la pila estuviera vacía
e = p.elemento[p.cont-1];
}
void eliminar (pila &p) {
// error si la pila estuviera vacía
p.cont--;
}
Página 6
EDA 02/03
Tema 6. Estructuras de datos lineales
Ejemplo: Implementación estática (genérica, POO)
# include <iostream.h>
template <class Tipo>
class pila {
Tipo *datos;
int top, num;
public:
pila(int max=100);
void insertar(Tipo c);
Tipo cima(void);
int vacia(void);
void borrar(void);
~pila();
};
template <class Tipo>
pila<Tipo>::pila(int max){
datos=new Tipo[max];
top=-1;
num=max;
}
template <class Tipo>
pila<Tipo>::~pila(){ delete [] datos;
}
template <class Tipo>
Tipo pila<Tipo>::cima(void) {
if (top == -1) return 0;
else return datos[top] ;
}
template <class Tipo>
void pila<Tipo>::borrar(void) {
if (top == -1) cout << "pila vacía \n";
else top--;
}
template <class Tipo>
int pila<Tipo>::vacia(void) {
return (top == -1);
};
template <class Tipo>
void pila<Tipo>::insertar(Tipo c) {
if (top == num-1)
cout << " pila llena \n";
else {
top++;
datos[top] = c;
}
};
Página 7
EDA 02/03
Tema 6. Estructuras de datos lineales
Ejemplo: Implementación dinámica
Data Structures and Algorithm Analysis in C++ (Second Edition). Mark Allen Weiss.
http://www.cs.fiu.edu/~weiss/dsaa_c++/code/
// Stack class -- linked list implementation
//
// CONSTRUCTION: with no parameters
//
//**PUBLIC OPERATIONS***************
// void push( x )
--> Insert x
// void pop( ) --> Remove most recent item
// Object top( ) --> Return most recent item
// Object topAndPop( ) --> Return and remove
// bool isEmpty( )
--> Return true if empty//
bool isFull( )
--> Return true if full// void
makeEmpty( )
--> Remove all items
//
******************ERRORS*************
// Overflow and Underflow thrown as needed
template <class Object>
class Stack
{
public:
Stack( );
Stack( const Stack & rhs );
~Stack( );
bool isEmpty( ) const;
bool isFull( ) const;
const Object & top( ) const;
void makeEmpty( );
void pop( );
void push( const Object & x );
Object topAndPop( );
const Stack & operator=( const Stack & rhs );
private:
struct ListNode
{
Object element;
ListNode *next;
ListNode( const Object &
theElement, ListNode * n = NULL )
: element( theElement ), next( n ) { }
};
ListNode *topOfStack;
};
/**
* Construct the stack.
*/
template <class Object>
Stack<Object>::Stack( )
{
topOfStack = NULL;
}
/**
* Copy constructor.
*/
template <class Object>
Stack<Object>::Stack( const Stack<Object> &
rhs )
{
topOfStack = NULL;
*this = rhs;
}
/**
* Destructor.
*/
template <class Object>
Stack<Object>::~Stack( )
{
makeEmpty( );
}
/**
* Test if the stack is logically full.
* Return false always, in this implementation.
*/
template <class Object>
bool Stack<Object>::isFull( ) const
{
return false;
}
/**
* Test if the stack is logically empty.
* Return true if empty, false otherwise.
*/
template <class Object>
bool Stack<Object>::isEmpty( ) const
{
return topOfStack == NULL;
}
Página 8
EDA 02/03
/**
* Make the stack logically empty.
*/
template <class Object>
void Stack<Object>::makeEmpty( )
{
while( !isEmpty( ) )
pop( );
}
/**
* Get the most recently inserted item.
* Return the most recently inserted item
* or throw an exception if empty.
*/
template <class Object>
const Object & Stack<Object>::top( ) const
{
if( isEmpty( ) )
throw Underflow( );
return topOfStack->element;
}
/**
* Remove the most recently inserted item from
the stack.
* Exception Underflow if the stack is empty.
*/
template <class Object>
void Stack<Object>::pop( )
{
if( isEmpty( ) )
throw Underflow( );
ListNode *oldTop = topOfStack;
topOfStack = topOfStack->next;
delete oldTop;
}
/**
* Return and remove the most recently inserted
* item from the stack.
Tema 6. Estructuras de datos lineales
*/
template <class Object>
Object Stack<Object>::topAndPop( )
{
Object topItem = top( );
pop( );
return topItem;
}
/**
* Insert x into the stack.
*/
template <class Object>
void Stack<Object>::push( const Object & x )
{
topOfStack = new ListNode( x, topOfStack );
}
/**
* Deep copy.
*/
template <class Object>
const Stack<Object> & Stack<Object>::
operator=( const Stack<Object> & rhs )
{
if( this != &rhs )
{
makeEmpty( );
if( rhs.isEmpty( ) )
return *this;
ListNode *rptr = rhs.topOfStack;
ListNode *ptr = new ListNode( rptr>element );
topOfStack = ptr;
for( rptr = rptr->next; rptr != NULL; rptr =
rptr->next )
ptr = ptr->next = new ListNode( rptr>element );
}
return *this;
}
Página 9
EDA 02/03
Tema 6. Estructuras de datos lineales
Aplicación: Evaluación de expresiones en notación polaca
Notación infija
Notación polaca (postfija)
2+3*5
235*+
(2 + 3) * 5
23+5*
9 / 6 ^ (1 + 3) * 4 –5 * 9
9613+^/4*59*-
Con una fórmula en notación polaca no es necesario considerar ni los
paréntesis ni la prioridad de los operadores.
Para evaluar una expresión en notación polaca, se recorre de izquierda a
derecha, aplicando los operadores sobre los dos últimos operandos.
¿Cómo se evalúa una expresión en notación polaca?
Clase Expresión: Secuencia de elementos de la clase Símbolo.
Operaciones: Obtener primero, Eliminar primero, Insertar al final,
Vacía?
Clase Símbolo: Para almacenar números y operadores. Operaciones:
EsOperador?, Valor (para los números), Operador (para los
operadores)
Usaremos una Pila de enteros para ir almacenando los resultados.
Página 10
EDA 02/03
Tema 6. Estructuras de datos lineales
Función Evaluación (ent e: Expresión): entero;
var
p: PilaEnteros;
s: Símbolo;
r: entero;
Inicio
p.CrearPila;
Mientras no e.Vacía? hacer
s e.Primero;
Si no s.EsOperador? entonces
p.InsPila(s.Valor)
Si no
r Eval(p, s.Operador);
p.InsPila(r)
Fin Si
e.Eliminar
Fin Mientras
retorna (p.CimaPila)
Fin
Función Eval(ent/sal p: PilaEnteros; ent op: Operador): entero;
var
v1, v2, ev: entero;
Inicio
v1 p.CimaPila; p.BorPila;
v2 p.CimaPila;
Opcion op de
suma:
resta:
producto:
división:
FinOpción
retorna (ev)
p.BorPila;
ev
ev
ev
ev
v2 + v1;
v2 - v1;
v2 * v1;
v2 / v1;
Fin
Página 11
EDA 02/03
Tema 6. Estructuras de datos lineales
Transformación de una expresión infija en notación polaca
Infija
2 * (3 + 2) * 5
* (3 + 2) * 5
(3 + 2) * 5
3 + 2) * 5
+ 2) * 5
2) * 5
)*5
*5
5


Pila
λ
λ
*
*(
*(
*(+
*(+
*
*
*
λ
Postfija

2
2
2
2 3
2 3
2 3
2 3
2 3
2 3
2 3
2
2
2
2
2
+
+ *
+ * 5
+ * 5*
Los operandos se van añadiendo al final de la expresión postfija.
Antes de apilar un nuevo operador, se sacan de la pila aquellos
operadores cuya prioridad sea mayor o igual que la prioridad del
operador que se está tratando.
El paréntesis abierto siempre se mete en la pila, y hay que sacarlo
siempre que lo encontremos en la pila.
El paréntesis cerrado nunca se mete en la pila; se trata explícitamente en
el algoritmo: se sacan todos los operadores hasta encontrar el paréntesis
abierto.
Tenemos que considerar distintas prioridades en la pila y en la expresión
para poder manejar correctamente los paréntesis.
El paréntesis abierto tendrá una prioridad mínima (0) en la pila, y
máxima en la entrada (10).
Página 12
EDA 02/03
Tema 6. Estructuras de datos lineales
Procedimiento InfijaPostfija (ent e1: Expresión; sal e2: Expresión);
var
p: PilaOperaciones;
s: Símbolo;
op: Operador;
Inicio
p.CrearPila;
p.InsPila(OperadorNulo);
e1.Añadir(OperadorNulo);
e2.Crear;
Mientras no e1.Vacía? hacer
s e1.Primero;
Si no s.EsOperador? entonces
e2.Añadir(s)
Si no
op s.Operador;
Si op = = p_cerrado entonces
Mientras no (p.CimaPila == p_abierto) hacer
e2.Añadir(p.CimaPila);
p.BorPila;
Fin Mientras;
p.BorPila {el paréntesis abierto}
Si no
Mientras p.CimaPila.PriorPila >= op.PriorExp hacer
e2.Añadir(p.CimaPila);
p.BorPila
Fin Mientras
p.InsPila(op)
Fin Si
Fins Si
e1. Eliminar
Fin Mientras
Fin
En la pila, insertamos inicialmente un OperadorNulo, que tendrá una
prioridad de pila tal que nunca saldrá de la pila (la más baja posible -1;
así no hace falta comprobar esta condición).
También insertamos el OperadorNulo al final de la expresión infija, que
tendrá una prioridad de entrada tal que obligue a sacar de la pila todos
los símbolos que haya, excepto el nulo (prioridad 0, por ejemplo).
Página 13
EDA 02/03
Tema 6. Estructuras de datos lineales
Colas
Cola: Queue: FIFO (First In, First Out)
Ejemplo informático: cola de procesos en un sistema operativo, en una
impresora; en un servidor.
Operaciones básicas: Crear, Insertar, Eliminar, Consultar, ¿está
vacía?
Si tenemos un espacio limitado en la cola, serán necesarias operaciones
para saber si está llena y, a veces, su longitud (número de elementos).
Especificación algebraica (cola no acotada)
TAD Cola
TIPO: Cola (elemento)
SINTAXIS:
crear ( )
vacía (Cola)
insertar (Cola, Elemento)
borrar (Cola)
frente (Cola)
SEMÁNTICA:
→
→
→
→
→
Cola
Boolean
Cola
Cola
Elemento
∀ C ∈ Cola; ∀ i ∈ Elemento
vacía (crear)
vacía (insertar (C,i))
⇒
⇒
Cierto
Falso
frente (crear)
frente (insertar (C,i))
⇒
⇒
Error
Si vacía (C) i
si no frente (C )
borrar (crear)
borrar (insertar (C,i))
⇒
⇒
Error
Si vacía (C ) entonces crear
si no insertar (borrar (C ), i)
Problema: Escribir la especificación algebraica de una cola acotada y de
una cola con prioridad
Página 14
EDA 02/03
Tema 6. Estructuras de datos lineales
Posibles implementaciones
Implementación estática
1
2
3
4
5
...
MaxCola
?
B
C
D
?
...
?
Frente
Frente: 2
Final: 5
Final
Cont: 3
Inserciones por Final y extracciones por Frente.
Añadimos el contador para diferenciar entre cola llena y vacía.
Implementación dinámica
Frente
Final
A
B
C
Inserciones por Final y extracciones por Frente.
Ejemplo: Implementación estática
// Queue class -- array implementation
// CONSTRUCTION: with or without a
// capacity; default is 10
// bool isEmpty( )
--> Return true if empty
// bool isFull( )
--> Return true if full
// void makeEmpty( ) --> Remove all items
// **PUBLIC OPERATIONS*************
// void enqueue( x )
--> Insert x
// void dequeue( )
--> Return and remove
// Object getFront( )
--> Return
// **ERRORS**************************
// Overflow and Underflow thrown as needed
template <class Object>
class Queue
Página 15
EDA 02/03
{
public:
explicit Queue( int capacity = 10 );
bool isEmpty( ) const;
bool isFull( ) const;
const Object & getFront( ) const;
void makeEmpty( );
Object dequeue( );
void enqueue( const Object & x );
private:
vector<Object> theArray;
int
currentSize;
int
front;
int
back;
void increment( int & x );
Tema 6. Estructuras de datos lineales
template <class Object>
const Object & Queue<Object>::getFront( )
const
{
if( isEmpty( ) )
throw Underflow( );
return theArray[ front ];
}
/** Return and remove the least recently
* inserted item from the queue.
* Throw Underflow if empty.
*/
template <class Object>
Object Queue<Object>::dequeue( )
{
if( isEmpty( ) )
throw Underflow( );
};
#include "QueueAr.h"
/** Construct the queue.
*/
template <class Object>
Queue<Object>::Queue( int capacity ) :
theArray( capacity )
{
makeEmpty( );
}
/** Test if the queue is logically empty.
* Return true if empty, false otherwise.
*/
template <class Object>
bool Queue<Object>::isEmpty( ) const
{
return currentSize == 0;
}
/** Test if the queue is logically full.
* Return true if full, false otherwise.
*/
template <class Object>
bool Queue<Object>::isFull( ) const
{
return currentSize == theArray.size( );
}
/** Make the queue logically empty.
*/
template <class Object>
void Queue<Object>::makeEmpty( )
{
currentSize = 0;
front = 0;
back = -1;
}
/** Get the least recently inserted item.
* Return the least recently inserted item
* or throw Underflow if empty.
*/
currentSize--;
Object frontItem = theArray[ front ];
increment( front );
return frontItem;
}
/** Insert x into the queue.
* Throw Overflow if queue is full
*/
template <class Object>
void Queue<Object>::enqueue(const Object & x
){
if( isFull( ) )
throw Overflow( );
increment( back );
theArray[ back ] = x;
currentSize++;
}
/**
* Internal method to increment x with
wraparound.
*/
template <class Object>
void Queue<Object>::increment( int & x )
{
if( ++x == theArray.size( ) )
x = 0;
}
Página 16
EDA 02/03
Tema 6. Estructuras de datos lineales
Ejemplo: Implementación dinámica (Genérica, POO)
template<class Tipo>
class ColaPolimorfica
{
typedef struct Nodo
{
Tipo Info;
Nodo *Suce;
}tiponodo;
tiponodo *frente, *fin;
int ElementosCola;
public:
ColaPolimorfica();
void Insertar(Tipo Dato);
void BorradoTotal();
void BorrarFrente();
Tipo FrenteCola();
int Vacia();
};
template<class Tipo>ColaPolimorfica<Tipo>::ColaPolimorfica(){
frente=fin=NULL;
ElementosCola=0;
}
template <class Tipo>void ColaPolimorfica<Tipo>::Insertar(Tipo Dato){
tiponodo *nuevo;
nuevo=(tiponodo*)malloc(sizeof(tiponodo));
nuevo->Info=Dato;
nuevo->Suce=NULL;
if (Vacia())
frente=nuevo;
else fin->Suce=nuevo;
fin=nuevo;
ElementosCola++;
}
template <class Tipo>void ColaPolimorfica<Tipo>::BorrarFrente() {
Nodo* paux;
paux=frente;
frente=frente->Suce;
free(paux);
ElementosCola--;
}
template <class Tipo>void ColaPolimorfica<Tipo>::BorradoTotal(){
while (!Vacia())
{
BorrarFrente();
if (Vacia())
fin=NULL;
}
}
template <class Tipo>Tipo ColaPolimorfica<Tipo>::FrenteCola(){
return (frente->Info);
}
template <class Tipo> int ColaPolimorfica<Tipo>::Vacia(){
return(frente==NULL);
}
Página 17
EDA 02/03
Tema 6. Estructuras de datos lineales
Listas
Definición: Una lista es una secuencia de 0 ó más elementos de un tipo
dado (DatosLista). Podemos representar una lista por a1, ..., an; con n>=0, y
tipo de ai es DatosLista.
Hay que tener en cuenta que:
Si n >= 1, entonces a1 es el primer elemento y an es el último.
Si n = 0, entonces la lista está vacía.
Diremos que ai está en la posición i. Por la linealidad de la estructura,
diremos que:
ai precede a ai+1
i = 1, 2, ..., n-1
ai sucede a ai-1
i = 2, 3, ..., n
El primer elemento sólo tienen sucesor. El último sólo tiene predecesor.
Definición recursiva de lista:
( ) Lista vacía
Lista =
(elemento, lista)
Posibles operaciones:
-
Creación de la lista vacía
-
Inserción de un elemento: al principio, al final, en orden
-
Borrado de un elemento: el primero, el último, uno determinado
-
Obtención de un elemento: el primero, uno determinado
-
...
Página 18
EDA 02/03
Tema 6. Estructuras de datos lineales
Especificación algebraica
TAD Lista
TIPO: Lista (elemento)
SINTAXIS:
CrearLista ()
ListaVacia (Lista)
Primero (Lista)
InsLista (Lista, Elemento)
Resto (Lista)
Modificar (Lista, Elemento)
SupLista (Lista, Elemento)
SEMÁNTICA:
Lista
Boolean
Elemento
Lista
Lista
Lista
Lista
∀ L ∈ Lista; ∀ x,y ∈ Elemento
ListaVacia (CrearLista)
⇒
Cierto
ListaVacia (InsLista (L,x))
⇒
Falso
Primero (CrearLista)
⇒
Error
Primero (InsLista (L,x))
⇒
x
Resto (CrearLista)
⇒
Error
Resto (InsLista (L,x))
⇒
L
Modificar (CrearLista)
⇒
Error
Modificar (InsLista (L,y),x)
⇒
InsLista (L,x)
SupLista (CrearLista, x)
⇒
CrearLista
SupLista (InsLista (L,y),x)
⇒
Si x = y → L
si no → InsLista (SupLista(L,x), y)
Página 19
EDA 02/03
Tema 6. Estructuras de datos lineales
Interfaz de una clase Lista
typedef ............ tipodatos;
class Lista {
struct nodol {
tipodatos info;
struct nodol * suce;
};
};
struct nodol * E_Lista;
public:
Lista ();
boolean ListaVacia();
void InsLista (tipodatos A);
tipodatos Primero ();
Lista Resto ();
void Modificar (tipodatos A);
void SupLista (tipodatos A);
void MostrarTodo();
Función para ver si un elemento pertenece o no a una lista:
boolean Pertenece (Lista L, tipodatos x) {
boolean enc=False;
tipodatos y;
while (!L.ListaVacia() && (!enc))
{
y = L.primero();
if (x == y)//Habría que sobrecargar el operador ==
enc=True;
else
L=L.Resto();
}
return(enc);
}
Página 20
EDA 02/03
Tema 6. Estructuras de datos lineales
Posibles implementaciones
Posibles estructuras de datos usadas para implementar listas:
Implementación dinámica.
L
A
B
C
Implementación estática.
Con un vector.
Posibles representaciones (implementación estática):
Secuencial: los elementos consecutivos en la secuencia se almacenan
seguidos en el vector.
Encadenada: existe un elemento que indica dónde se almacena el
siguiente elemento de la secuencia.
Posibles implementaciones:
Vector; representación secuencial
Vector con indicador de último elemento (o primero libre);
representación secuencial.
Vector; representación encadenada (campo adicional); es necesario un
apuntador al primero y otro al último
Igual que el anterior, con los espacios libres formando una lista (pila)
Estructura dinámica
Página 21
EDA 02/03
Tema 6. Estructuras de datos lineales
Una implementación estática encadenada:
Tendremos 2 listas en una: Libres y ocupados.
Una lista vacía será:
Libre = 1
CursorLista = 0
Si insertamos un elemento (por el principio):
Libre = 2
CursorLista = 1
Si insertamos más elementos:
Libre = 4
CursorLista = 3
Página 22
EDA 02/03
Tema 6. Estructuras de datos lineales
Implementación del procedimiento InsOrden para inserciones en una lista
ordenada (desde fuera de la clase Lista):
Lista InsOrden (Lista L, tipodatos x);
{
tipodatos y;
if (L.ListaVacia())
L.InsLista(x);
else
{
y = L.Primero();
if (x < y)
L.InsLista(x)
else
{
L=L.Resto();
L = InsOrden(L, x);
L.InsLista(y);
}
}
return L;
}
Modificación de la definición del TAD: Insertar en orden
Especificación de la operación:
InsOrden (CrearLista, x)
⇒ InsLista (CrearLista, x)
InsOrden (InsLista (L,y),x)
⇒
Si x <= y → InsLista (InsLista (L,y), x)
si no
→ InsLista (InsOrden (L,x),y)
Ejercicio: Implementar la operación InsOrden utilizando las operaciones
del TAD y/o trabajando directamente con algunas de las implementaciones.
Página 23
EDA 02/03
Tema 6. Estructuras de datos lineales
Ejemplo: Implementación dinámica en C++ (Genérica, POO)
#include <conio.h>
#include <iostream.h>
#include <alloc.h>
#include <process.h>
// Definición de la clase genérica lista
template <class tipodato>
class lista {
struct nodolista { // Estructura de un nodo
tipodato datos; // Datos del nodo.
nodolista *suce; // Apunta a otro nodo de la lista.
};
nodolista *E_Lista;// Apunta a un nodo de la lista.
int num_elementos; // Mantiene el n£mero de elementos de la lista.
public:
lista(void);
// Constructor
~lista(void);
// Destructor
int vacia(void);
// Comprueba si la lista está vacía.
tipodato frente(void);// Devuelve el elemento apuntado de la lista
void insertar(tipodato dat);// Inserta un nuevo dato dat en lista.
void borrar(void);
// Elimina el frente de la lista
int cuantos_elem();
// Devuelve número de elementos
void modificar(tipodato dat); // Modifica el dato frente de lista
void borrartodo(); // Elimina toda la lista
tipodato devolver(int pos);// Devuelve un elemento de la lista.
void sup_lista(tipodato dat); // Elimina un dato dat de la lista
void resto();
// Devuelve el resto de la lista
};
// lista.
/* =========IMPLEMENTACION DE LAS FUNCIONES DEL TAD LISTA ========= */
template <class tipodato>
lista<tipodato>::lista() {
// Constructor de la lista. Crea una nueva lista vacía y por tanto con
// cero elementos.
num_elementos = 0;
E_Lista = NULL;
}
template <class tipodato>
int lista<tipodato>::vacia() {
// Comprueba si la lista está vacía, devuelve 0 si no está vacía y
// otro valor si lo está .
return (E_Lista == NULL);
}
template <class tipodato>
tipodato lista<tipodato>::frente() {
// Devuelve el primer elemento de la lista, el accesible desde el
// atributo miembro E_Lista.
return E_Lista->datos;
}
template <class tipodato>
void lista<tipodato>::insertar(tipodato dat) {
// Inserta un nuevo elemento en la lista, la inserción se realiza al
// principio de la lista.
nodolista *p;
p = new nodolista;
// Crea un nuevo nodo.
if(p==NULL) exit(1);
p->datos = dat;
Página 24
EDA 02/03
Tema 6. Estructuras de datos lineales
p->suce = E_Lista;
E_Lista = p;
num_elementos++;
// Incrementa el número de elementos
}
template <class tipodato>
void lista<tipodato>::borrar(void){
nodolista *p;
if (!vacia()) {
p = E_Lista;
E_Lista=E_Lista->suce;
num_elementos--;
delete(p);
}
}
template <class tipodato>
tipodato lista<tipodato>::devolver(int pos) {
// Devuelve el elemento de la lista que está‚ en la posición dada por
// el parámetro pos.
register int i = 1;
nodolista *aux = E_Lista;
if (!E_Lista) exit(1);
while (i != pos) {
if (aux != NULL) {
aux = aux->suce;
i++;
}
else exit(1); // Se pretende acceder a una posición que desborda
//la lista.
}
return aux->datos;
}
template <class tipodato>
int lista<tipodato>::cuantos_elem() {
// Devuelve el número de elementos de la lista.
return num_elementos;
}
template <class tipodato>
void lista<tipodato>::sup_lista(tipodato dat) {
// Elimina un elemento de la lista, el elemento a eliminar es el que
// viene dado por el parámetro dat.
nodolista *ant, *p;
int enc = 0;
p = E_Lista;
while ((!enc) && (p!=NULL)) {
// Mientras no encontremos el nodo a eliminar o no lleguemos al
// final de la lista, buscamos el nodo.
if (p->datos == dat) enc = 1;
else {
ant = p;
p = p->suce;
}
}
if (enc) {
// Puedeo ourrir que sea el primer elemento o que sea otro
if (p == E_Lista) E_Lista = E_Lista->suce;
else ant->suce = p->suce;
delete (p);
num_elementos--;
// Decrementamos el numero de elementos
}
}
Página 25
EDA 02/03
Tema 6. Estructuras de datos lineales
template <class tipodato>
void lista<tipodato>::resto() {
// Si la lista no está vacía adelanta el puntero de la lista.
if (!vacia())
E_Lista = E_Lista->suce;
}
template <class tipodato>
void lista<tipodato>::modificar(tipodato dat) {
// Modifica un dato de la lista, el dato a modificar ser
// por el atributo miembro E_Lista, y el nuevo valor ser
// pasemos por parámetro.
if (!vacia())
E_Lista->datos = dat;
}
el accesible
el que le
template <class tipodato>
lista<tipodato>::~lista() {
// Destructor de la clase lista. Libera la memoria asignada por los
// nodos de la lista. Esto sólo habrá que hacerlo mientras la lista no
// esté vacía.
while (!vacia()) {
nodolista *p;
p = E_Lista;
E_Lista = E_Lista->suce;
delete (p);
num_elementos--;
}
}
template <class tipodato>
void lista<tipodato>::borrartodo() {
// Es una variante del destructor de la clase lista y sirve para
// eliminar un tipo de lista que almacena como datos de la lista
// punteros otros elementos asignados dinámicamente.
nodolista *aux = E_Lista;
while ( E_Lista != NULL ) {
aux = E_Lista;
E_Lista = E_Lista->suce;
//delete aux->datos;// Libera el puntero al que apunta el dato.
delete aux;
// Libera el nodo.
num_elementos--;
// Decrementa el número de elementos.
}
}
Página 26
EDA 02/03
Tema 6. Estructuras de datos lineales
Otros tipos de listas
Listas circulares
Listas doblemente enlazadas
Listas circulares doblemente enlazadas
Listas circulares.El sucesor del último elemento será el primer elemento.
LC
void ListaCircular::InsLista (tipodatos x)
// Suponiendo que tenemos el objeto ListaCircular
{
struct nodol *p;
p = new nodol;
p->dato = x;
if (ListaVacia()) {
p->suce = p;
E_lista = p;
}
else {
p->suce = E_lista->suce;
E_lista->suce = p;
}
}
// Obviamente se complica si queremos que la lista esté ordenada
En la eliminación de elementos hay que tener en cuenta:
Si la lista tiene un único elemento.
Si la componente que se va a eliminar es la indicada por la variable
externa o es otra cualquiera.
Página 27
EDA 02/03
Tema 6. Estructuras de datos lineales
Recorrido de una lista circular
p = LC;
if (! LC.VaciaLista ())
do {
p = p->suce;
<< tratar los datos de la componente p->info >>
} while (p != LC);
Listas doblemente enlazadas.-
En la inserción:
- ¿Por el principio?
- ¿Por el final?
- ¿Quiero que esté ordenada la lista?
En la eliminación de elementos hay que tener en cuenta:
- Si queda un único elemento
- Se trata de eliminar el primer elemento
- Se trata de eliminar el último elemento
- Se elimina un elemento intermedio
Listas circulares doblemente enlazadas.-
Página 28
EDA 02/03
Tema 6. Estructuras de datos lineales
Lista con punto de interés
TAD
ListaPI es Crear, Vacía, Inicio, Avanza, Fin, Insertar, Consultar, Borrar
Operaciones
Crear
retorna
ListaPI
efecto
Devuelve una LPI vacía
Vacía (L:ListaPI) retorna
Boolean
efecto
Devuelve True si la LPI está vacía y False
en caso contrario.
Inicio (L:ListaPI) modifica
L
efecto
Modifica la LPI de manera que el punto de
interés queda apuntando al primer elemento
de la lista. Si la lista está vacía, el PI
apuntará a NIL.
Avanza(L:ListaPI) modifica
L
efecto
Modifica la LPI de manera que el punto de
interés queda apuntando al siguiente
elemento al que apuntaba. Si estuviera
apuntando al último antes de ejecutar esta
operación, acabará apuntando a NIL.
Fin (L:ListaPI)
retorna
Boolean
efecto
Devuelve True si el punto de interés está
apuntando a NIL (es decir, si ha llegado al
final de la LPI) y False en caso contrario (es
decir, si apunta a un elemento de la lista).
Insertar (L:ListaPI; D: TipoDato)
modifica
L
efecto
Inserta D en la LPI, a continuación del nodo
al que apunta el punto de interés. Después
de la inserción, el PI queda apuntando al
nuevo elemento. Si la lista está vacía, el
nuevo nodo se inserta. Si el PI está
apuntando a NIL pero la lista no está vacía,
esta operación no hace nada.
Consultar(L:ListaPI)
retorna
TipoDato
efecto
Devuelve el dato al que apunta el punto de
interés. Si PI apunta a NIL, no hace nada.
Borrar (L:ListaPI)modifica
L
efecto
Elimina el nodo al que apunta el punto de
interés.
(Se podría añadir una operación para retroceder el punto de interés.)
Página 29
EDA 02/03
Tema 6. Estructuras de datos lineales
Especificación algebraica de la Lista con punto de interés
TAD ListaPI
TIPO: ListaPI (Elemento)
SINTAXIS:
crear ( )
insertar (ListaPI, Elemento)
borrar (ListaPI)
inicio (ListaPI)
avanza (ListaPI)
consultar (ListaPI)
fin (ListaPI)
vacía (ListaPI)
Privada:
<_, _>: (Pila,Pila)
SEMÁNTICA:
ListaPI
ListaPI
ListaPI
ListaPI
ListaPI
Elemento
Boolean
Boolean
ListaPI
∀ p, p1, p2 ∈ Pila; ∀ v ∈ Elemento
crear
⇒ <Pila.crear, Pila.crear>
insertar (<p1, p2>, v)
⇒ <Pila.apilar(p1, v), p2>
borrar (<p1, Pila.crear>)
⇒ Error
borrar (<p1, Pila.apilar(p2, v)>) ⇒ <p1, p2>
inicio (<p1, p2>)
⇒ <Pila.crear, concatenar(p1, p2)>
avanza (<p1, Pila.crear>)
⇒ Error
avanza (<p1, Pila.apilar(p2, v)>)⇒ <Pila.apilar(p1, v), p2>
consultar (<p1, Pila.crear>)
⇒ Error
consultar (<p1, Pila.apilar(p2, v)>)⇒ v
fin (<p1, p2>)
⇒ Pila.vacía(p2)
vacía(<p1, p2>)
⇒ Pila.vacía(p2) ∧ Pila.vacía(p2)
Ejercicios:
Especificar la concatenación de pilar; posibles
implementaciones; recorrido de una lista con punto de interés;
búsqueda de un elemento en una lista con punto de interés
Página 30
Descargar