Curso de Estructuras de Datos UNAM
Curso de Estructuras de Datos UNAM
Introducción
Las Estructura más simples alojadas en la memoria principal se estudian por
dos razones fundamentales: la primera porque de ellas se forman otras
estructuras más complejas y la segunda por que varios compiladores
actualmente tiene incluidos los tipos de datos estándar.
1
Las Estructuras de Datos son objetos con los cuales se representa el manejo y
ubicación de la información en la memoria interna de la Computadora. Por lo
cual se administra el espacio en dicha memoria.
Objetivo particular
Al finalizar el tema el alumno conocerá las estructuras de datos simples, así
como su importancia en la representación de la información en la memoria
principal de la computadora.
Temario detallado
1.1 Definición de estructura de datos
1.2 Tipos de datos
1.3 Tipos de datos abstractos
Introducción
Las estructuras simples de datos son la base del manejo de información en la
programación, conocer las operaciones y el manejo en memoria de éstas nos
permite construir programas más eficaces y, sobre todo, empleado a los
nuevos lenguajes, nos permite desarrollar aplicaciones más seguras y
robustas.
1
Cf. Luis Joyanes Aguilar, Fundamentos de programación, 2ª ed., México, McGraw Hill, 1996,
pp. 439-440, 496.
2
tipos de información determinada almacenada y manejada en las páginas de
memoria interna del ordenador los cuales se utilizarán para una aplicación.
Entero
Carácter
Estándar Real
Flot (Punto Flotante)
Lógico
Enumerativo
Definidos por el Programador
Subrango
A los Datos Estándar también se les conoce como Datos Primitivos; y a los
Definidos por el Programador como Datos Incorporados.
Entero
El tipo entero es un subconjunto de los números enteros, que dependiendo del
lenguaje que estemos usando podrá ser mayor o menor a 16 bits
3
(2^16=32768), es decir se pueden representar desde el -32768 hasta el 32767.
Para representar un número entero fuera de este rango se tendría que usar un
tipo real.
Real
El tipo real define un conjunto de números que puede ser representado con la
notación de punto-flotante, por lo que nos permite representar datos muy
grandes o muy específicos.
Carácter
Cualquier signo tipográfico. Puede ser una letra, un número, un signo de
puntuación o un espacio. Generalmente este tipo de dato está definido por el
conjunto ASCII.
Lógicos
Este tipo de dato, también llamado booleano, permite almacenar valores de
lógica booleana o binaria, es decir, representaciones de verdadero o falso.
Float
Este tipo de Datos se emplean en aquellos datos fraccionarios que requieren
de cifras a la derecha del punto decimal o de entero con varios decimales. De
estos se desprende la necesidad de crear el formato de representación
científica (10 E +12).
4
Subrango
En este tipo de datos nosotros delimitamos el rango, menor y mayor de los
posibles valores que puede tomar una variable, de esta manera podemos
asegurar que la entrada o salida de nuestro programa esté controlada.
Ejemplos: type Tipo1 = 1.. 30; type Tipo2 = 1.. 10;
Enumerativo
Los tipos de dato enumerativo son tipos que pueden tomar valores dentro de
un rango en el que se especifica ordenadamente cada uno de dichos valores,
al igual que el tipo de subrango nos permite restringir los valores de una
variable.
program l'1 1;
{$APPTYPE CONSOLE}
{$R+} uses
SysUti1s,
Math;
type sub1000 = 1..1000;
type mOd2 .(par, impar); var
num1_int, nurn2_int:sub1000; res_int:integer; res_rea:real;
n1_mod,n2_mod,espar:mOd2; res_boo:boolean;
repetir_char:char;
function divlnt(i1_int, i2_int: integer}: real; begin
Result := i1 int / i2 int;
if ((i1_int mod i2_intJ > O) then
raise [Link]('La division de estos numeros
arrojaría un numero real') divlnt := i1_int div i2_int;
--~
5
end;
end;
begin
espar:=par;
repeat
writeln(repetir_char) ;
write('Intoduce un entero positivo menor a 1000: ');
readln(num1_intl);
write('Intoduce otro entero positivo menor a 1000: ');
readln(num2_intl);
writeln('Tenemos 2 enteros'); n1_mod:=impar; n2 mod:=impar;
if (num1_int mod 2)=0) then nl_mod:=par;
if (num2 int mod 2)=0) then n2 mod:=par;
writeln('Üsando tipos enumerados, veamos si el primer
entero `, num1_int);
res boo:= n1_mod = espar;
writeln('es un numero par:', res_boo);
writeln('y hagamos lo mismo con el segundo:', n2_mod E
espar);
writeln;
6
writeln('Ahora, a estos numeros los podemos Sumar: ',numl
int, '+',num2 int);
res_int:=num1_int + nurn2_int;
writeln('y el resultado guardarlo en un entero:
',res_int);
writeln;
writeln('Los podemos restar:' ,numl int, '-',num2 int);
res int:=numl int - num2 int;
writeln('y el resultado guardarlo en un entero: ',res_int);
writeln¡
writeln('Los podemos multiplicar: ',numl_int,
'*',num2_int);
res int:=numl int * num2 int;
writeln('y el-resultado guardarlo en un entero: ',res_int);
writeln;
writeln('Podemos tambien sacar el residuo de la divison:
',numl_int, , mod num2 int);
res int:=numl int mod num2 int¡
writeln('y el-resultado gUardarlo en un entero: ',res_int);
writeln;
writeln('y podemos hacer una división entera: ',numl_int, ,
div ',num2_int); res_int:=numl_int div num2_int¡
writeln('y el resultado guardar lo en otro entero:
',res_int);
writeln;
writeln('pero si los tratamos de dividir, el resultado
solo');
writeln('podria guardarse en un entero si la division');
write('fuera exacta');
try
res int:=[Link](divInt(numl int, num2 int));
writeln(' como en este caso en Que el resultado') ¡
7
writeln('de `, numl_int, '/', num2_int, ' es entero:
',res_int) ¡
except
On EMathError do writeReal(numl_int, num2_int);
//writeln('como en este caso en que el resultado es
entero: ',res_int);
end¡
writeln;
writeln('para salir del programa presiona <q> y <enter>');
writeln('o presiona <enter> para volver a correr el
programa'}; read(repetir_char) ;
until (repetir_char='q') or (repetir_char.'Q');
end.
8
“Estructura de datos, material en línea, disponible en:
[Link]
Actividades de Aprendizaje.
Cuestionario de autoevaluación
9
8. La letra “A” se puede emplear en operaciones de comparación si le
asigno un ___________, el cual también tiene un valor ya asignado en el
Código _________.
9. Enlista los tipos de Datos Simples empleados por los compiladores
Pascal, C y C++.
10. Anota la instrucción en Lenguaje Pascal y en Lenguaje C++ para definir
un tipo de dato subrango.
Examen de autoevaluación
10
TEMA 2. ESTRUCTURAS DE DATOS FUNDAMENTALES
Objetivo Particular
Al finalizar el tema el alumno conocerá y comprenderá a los tipos de datos
complejos, no solo como una integración de los tipos de datos simples sino
también como una representación abstracta para desarrollar aplicaciones.
Temario detallado
2.1. Introducción a los tipos de datos abstractos
2.2. Arreglos
2. 2.1 Unidimensionales
2.2.2 Multidimensionales
2.2.3 Operaciones sobre arreglos
2.3.- Pilas
2.3.1.-Definición del tipo de dato abstracto pila
2.3.2.-Definición de las operaciones sobre pilas
2.3.3.-Implantación de una pila
2.4.-Colas
2.4.1.-Definición del tipo de dato abstracto cola
2.4.2.-Definición de las operaciones sobre colas
2.4.3.-Bicolas
2.4.4.-Implantación de una cola
2.5.-Listas
2.5.1.-Definición del tipo de dato abstracto lista
2.5.2.-Definición de las operaciones sobre listas
2.5.3.-Implantación de una lista
2.6.-Tablas de dispersión, funciones hash
11
Introducción
Los Tipos de Datos Complejos se integran de varios tipos de Datos Simples
ya sean del mismo tipo o de varios, según las necesidades, pero aquellos se
dividen en Estáticos y Dinámicos. En esta Unidad nos enfocaremos en los
Estáticos, es decir, aquellos cuyo tamaño de memoria requerido se mantiene
fijo a largo de la aplicación de desarrollar.
Arreglos.
Registros
Estáticas
Complejas Colas
Pilas
Dinámicas Árboles
Grafos
Cuadro 2.1 Clasificación de los Tipos de Datos Complejos
Definición
Los datos abstractos son el resultado de empacar un tipo de datos junto con
sus operaciones, de modo que pueda considerarse en términos de su ejemplo,
sin que el programador tenga que preocuparse por una representación en
memoria o la instrucción de sus operaciones.
12
Las estructuras de datos estáticos son aquellas en las que el tamaño ocupado
en memoria se define antes de que el programa se ejecute (no puede
modificarse dicho tamaño durante la ejecución del programa). Esas estructuras
están implementadas en casi todos los lenguajes: arrays (vectores / tablas –
matrices), registros, ficheros. Los conjuntos son específicos del lenguaje
Pascal.
13
COLUMNAS A B …… J
REN A
GLO :
NES :
J X(I, J)
Figura 2.1. Un Elemento en una matriz definida [A..J, 0..5]
Con esta anotación, es posible hablar de los componentes de una matriz y sus
dimensiones, de las nociones de fila y columna, de la relación de vecindad
entre elementos, etc., sin necesidad de establecer estructuras de datos
concretas para manejarlas.
Ejemplo:
Para el TDA diccionario, en la que cada palabra tiene uno o más significados
asociados, el objeto abstracto puede representarse mediante el formalismo
siguiente.
<[palabra1,<si1,...,s1k>],..., [palabraN,<sNi,...,sNk>]>
Incluso podríamos hacer la siguiente representación gráfica.
14
S1…SN
STRING…I
STRING…I+1 S2..SN2
STRING…N SN….N
Figura 2.3. Representación gráfica de un conjunto de cadenas
Lo importante, en todos los casos, es que los componentes del objeto abstracto
sean diferenciables y que su estructura global se haga explícita.
Ejemplo:
Hay objetos abstractos en una representación gráfica natural. Veamos algunos
ejemplos
CONJUNTO DE E1….EN
ELEMENTOS
STRING1…STRINGN C1….CN
C1 = [E1..EN] [C1,C2,C3,C4…CN]
C2 = [E2..EM]
C3 = [E3..ER]
Figura 2.4 Integración de caracteres en cadenas
15
VECTOR 0……N-1
POLINOMIO C0+C1X1+C2X2+….+CNXN
READ
LISTA <X1,X2…….XN>
C
B
POLÍGONO
16
FICHERO TITULO ATRIBUTOS
Materia
Año
Volumen
Paginas
Editorial
Impresión
ISBN
FICHERO BODEGA ATRIBUTOS
Localización
Capacidad
Razón Social
Figura 2.6. TDA LIBRO
Siempre hay que tomar en cuenta que un TDA es un modelo abstracto para
resolver un problema en particular y generar el programa correspondiente. Su
objetivo es poderse aplicar de forma general a los problemas semejantes al
originalmente planteado, por tanto, es independiente del compilador al cual se
le encargará interpretar el programa fuente que creó el programador. El TDA lo
creó el Diseñador o el Programador.
17
Por ejemplo, para el TDA Conjunto y la notación {x1,...xN} asignada para todos
sus ítem (elementos) por fila; el invariante debe exigir que todos los xi
pertenezcan al mismo tipo de dato con iguales propiedades, y que sean
diferentes entre sí en su contenido. De este modo, el objeto abstracto estará
modelando realmente un conjunto. Estructuralmente, el invariante está
compuesto por condiciones que restringen el dominio de los componentes
internos y sus relaciones, los cuales solo son válidos para ese TDA en
particular; pudiéndose generalizar sus propiedades posteriormente.
Ejemplo:
El objeto abstracto del TDA Diccionario debe incluir tres condiciones en el
lenguaje natural y en el formal:
18
<Operaciones>
<Objeto abstracto2>
<Invariante del TDA>
<Operaciones>
Figura 2.7 Estructura de un TDA
19
Es importante colocar una breve descripción de cada operación de manera que
el cliente pueda darse una idea rápida de los servicios que ofrece un TDA, sin
necesidad de hacer una interpretación de su especificación formal, que está
dirigida sobre todo al programador.
Ejemplo:
Para definir el TDA Matriz de los valores internos, se puede utilizar la
especificación siguiente.
TDA BIBLIOTECA
Object_Libro
Titulo <A…Z,a..z,0..9> Numero de Casos
Materia<A..Z,a..z> Numero de Casos
Autor<A..Z,a..z> Numero de Casos
ISBN<0..9> Numero de Casos
Operaciones:
Altas Bajas Prestamos adquisición
Vistas
Figura 2.9. TDA Biblioteca y su Estructura
En el caso del TDA Matriz, el invariante sólo establece una restricción para el
número de filas y de columnas (es decir, coloca una limitante al dominio en el
cual puede tomar valores). También cuenta con cinco operaciones para
administrar un objeto del TDA: una para crearlo, otra para asignar un valor a
una casilla, una más para tomar el valor de una casilla y dos para formar sus
dimensiones. Con este conjunto de operaciones, y sin necesidad de
20
seleccionar estructuras de datos específicos, es posible resolver cualquier
problema que involucre una matriz.
Tipos de operaciones
Las operaciones TDA se clasifican en tres grupos, según su función sobre el
objeto abstracto:
Clase<constructura> (<argumentos>)
{pre:<condiciones de los argumentos>}
{post:<condiciones del objeto inicial, adicionales
al invariante>}
21
En el ejemplo del TDA Matriz la única modificadora es la operación asignarMat,
que altera el contenido de una casilla de la matriz. Otra modificadora posible de
ese TDA sería una que alterara sus dimensiones. Al final de toda modificadora,
se debe continuar cumpliendo el invariante.
<tipo><analizadora>(<objeto abstracto>,
<argumentos>)
{pre:<condiciones del objeto adicionales al
invariante, condiciones de
los argumentos>}
{post:<analizadora>=function (<estado del objeto
abstracto>)}
22
Destrucción: modificadora que se encarga de retornar el espacio de
memoria dinámica ocupado por un objeto abstracto. Después de su
ejecución, el objeto abstracto deja de existir, y cualquier operación que
se aplique en él va a generar error. Sólo se debe llamar esta operación
cuando un objeto temporal del programa ha dejado de ocuparse
Salida a pantalla: analizadora que permite al cliente visualizar el
estado de un elemento del TDA. Esta operación, que parece más
sólida con la interfaz que con el modelo del mundo, puede resultar una
excelente herramienta de depuración en la etapa de pruebas del TDA.
Persistencia: operación que permite salvar/leer el estado de un objeto
abstracto de algún medio de almacenamiento en memoria secundaria.
Esto permite a los elementos de un TDA sobrevivir a la ejecución del
programa que los utiliza.
23
Figura 2.10. TDA Conjunto con sus Elementos
2.2. Arreglos
Definición
Los arreglos son estructuras de datos compuestos en las que se utilizan uno o
más subíndices para identificar los elementos individuales almacenados, a los
que es posible tener acceso en cualquier orden.
Examinaremos varias estructuras de datos, que son parte importante del
lenguaje Pascal, y su utilización. Son del tipo compuesto, y están integradas de
tipos de datos simples que existen en el lenguaje como conjuntos ordenados,
finitos de elementos homogéneos. Es decir, de un número especifico de
elementos grandes o pequeños, pero organizados de tal manera que hay
primero, segundo, tercero, etcétera; además, los elementos deben ser del
mismo tipo.
La forma de estructura de datos no se describe completamente, pero se
debe especificar cómo se puede acceder a ella. Un arreglo tiene dos tipos de
datos asociados, los numéricos y los caracteres. Las dos operaciones básicas
a realizar en un arreglo son la extracción y la alimentación. La primera acepta
un acceso a un elemento con la ayuda de un dato de tipo índice ya sea
ordinario, inicializándolo desde el 0. El elemento más pequeño de un arreglo
del tipo índice es su límite inferior, y el más alto su límite superior.
24
2.2.1 Arreglos Unidimensionales
Una estructura homogénea secuencial comúnmente se le denomina vector por
estar definida por una sola dimensión y sus nodos leídos en una sola dirección.
Se definirán dos Vectores de diferentes formas, la primera como un rango, y la
segunda con la ayuda de una constante.
Const I = 100;
Var Lectura[1..I] of Array;
25
El último subíndice varía rápidamente y no aumenta sino hasta que todas las
combinaciones posibles de los subíndices a su derecha hayan sido
completadas.
Arreglos bidimensionales
El tipo de un arreglo puede ser otro arreglo. Y un elemento de este arreglo
puede ser ingresado mediante la especificación de dos índices —los números
de la fila y de la columna. Por ejemplo, en la fila 2, columna 4 —a{2]{4] o
[2,41—, observamos que a [2J hace referencia a toda la fila 2, pero no hay
forma equivalente para aludir a una columna completa:
26
COLUMNA COLUMNA COLUMNA COLUMNA COLUMNA
1 2 3 4 5
FILA 1 X(1,1)
FILA 2 X(2,3)
FILA 3 X(3,5)
2.3 Pilas
Una pila (stack) es un tipo especial de lista en la que la inserción y borrado de
nuevos elementos se realiza sólo por un extremo que se denomina cima o
tope.
27
2.3.2 Definición de las operaciones sobre pilas
Los dos cambios que pueden hacerse en una pila tienen nombres especiales.
Cuando se agrega un elemento a la pila, éste es “empujando” (pushed) dentro
de la pila. La operación pop retira el elemento superior y lo regresa como el
valor de una función; la empty determina si la pila está o no vacía; y la stacktop
determina el elemento superior de la pila sin retirarlo. (Debe retomarse el valor
del elemento de la parte superior de la pila).
E
D
D
C
C
B
B
A
A
28
Operación para revisar si una pila es vacía o no
Una pila puede utilizarse para registrar los diferentes tipos de signos de
agrupación. En cualquier momento que se encuentre un signo de éstos
abriendo la expresión, es empujado hacia la pila, y cada vez que se detecte el
correspondiente signo terminal, ésta se examina. Si la pila está vacía, quiere
decir que el signo de la agrupación terminal no tiene su correspondiente
apertura, por lo tanto, la hilera es inválida.
Pilas
Las pilas son las estructuras de datos más utilizadas. Se trata de un caso
particular de las estructuras lineales generales (secuencias o listas) que,
29
debido a su amplio ámbito de aplicación, conviene sean estudiadas de forma
independiente.
Fundamentos
La pila es una lista, o colección de elementos, que se distingue porque las
operaciones de inserción y eliminación se realizan solamente en un extremo de
la estructura. El extremo donde se llevan a cabo estas operaciones se
denomina habitualmente cima o parte superior de la pila (top).
30
un elemento, será necesario recorrer todos los elementos hasta encontrarlo y
su índice será asignado al elemento inmediatamente siguiente abajo en caso
de que elimine el elemento buscado.
Crear pila
Crear el Espacio en la Memoria con New()
Comprobar si la pila está vacía (necesario para saber si es posible
eliminar elementos)
Acceder al elemento situado en la cima
Añadir elementos a la cima
Eliminar elementos de la cima.
31
representada por un array tendrá un número limitado de elementos
posibles con un tamaño determinado.
El Indice define la extensión a priori de la Estructura.
Tope o Cima
d c b a
NULL
Nuevo Tope
3
Rigel Galeón, “Estructuras dinámicas de datos”, p. 5, material en línea, disponible en:
[Link]/[Link], recuperado el 18/10/08.
32
PILAS DINÁMICAS
PILAS DINÁMICAS
Tope o Cima
d c b a
NULL
Nuevo Tope
Crear nodo nuevo
e NULL
PILAS DINÁMICAS
Estructura para pilas dinámicas
Struct stack {
Int dato;
Struct stack *siguiente;
33
} ;
2.4 Colas
Son otro tipo de estructura lineal de datos similares a las pilas, pero se
diferencia de éstas en el modo de insertar y eliminar elementos.
34
const maxqueue = 100;
type queue = record
items: array ([Link]) of integer;
front, rear: 0..maxqueue
end;
Por supuesto, al emplear un arreglo para que contenga los elementos de una
cola, es posible que se presente una sobrecarga (overflow) si ésta posee más
elementos de los que fueron reservados en el arreglo. Ignorando la posibilidad
de la subcarga y sobrecarga por el momento, la operación insert (q, x) se
podría implementar mediante las declaraciones siguientes:
[Link]: = [Link] +1;
[Link][[Link]]: = x
x: = [Link] [[Link]];
[Link]: = [Link] + 1
35
q-elementos q-elementos
5
5
4
4
3 C
[Link]ás = 3
3
2 B
2
Frente = 1 1 A
1
[Link]ás = 0 (a)
[Link] = 1
(a)
q-elementos q-elementos
5 E
5 [Link]ás = 5
4 D
4
C 3
[Link] = C
[Link]ás = 3
3
2
[Link]ás = 3
2
1
1 (b)
[Link] = 1
(c) [Link]ás = 0
36
5 5
4 4
3 3 C [Link]ás = 3
2 2 B
1 1 A
[Link]ás = 0
(a) (a)
q-elementos q-elementos
5 5
4 4
3 3 E [Link]ás = 3
2 2 D
1 [Link]ás = 1
1 C
C
(c) (b)
37
Una solución a este problema consiste en modificar la operación remove de tal
manera que cuando se retire un elemento, se desplace toda la cola al principio
del arreglo. La operación x; = remove (q) podría entonces ser modificada
(nuevamente ignorando la posibilidad de bajoflujo) a:
x: = [Link] (1)
for i: 1 to [Link] – 1
do [Link] (i): = [Link] (i+1);
[Link]: = [Link] –1
38
5 E [Link]ás = 5 5 E
4 D 4 D
3 3 [Link] = 3
C [Link] = 3 C
2 2 E
1 1 [Link]ás = 1
(a) (a)
q-elementos q-elementos
5 5
E E
4 4 [Link] = 5
[Link] = 5
3 3
2 2
G [Link]ás = 2
1 [Link]ás = 1
1
(c) F (d) F
q-elementos
3
G
2
F [Link]ás = 2
[Link] = 1
1
(e)
39
está completamente lleno, el último elemento del arreglo está ocupado. Si se
intenta insertar el elemento F en la cola, éste puede colocarse en la posición 1
del arreglo, tal como se muestra en la figura 2.16a. El primer elemento de la
cola está en [Link] (3), que está seguido en la cola por [Link] (4), [Link] (5)
y [Link] (1). Las figuras 2.16b y c se muestran el estado de la cola cuando los
dos primeros elementos, C y D, son retirados, G, insertado y, finalmente, E,
retirado.
40
Procedure insert (var q: queue; x: interger);
begin
q-elementos q-elementos
E 5 E
D [Link]ás = 5 4 D
5
C 3 C
4
[Link] = 2 2 F [Link] = 2
3 [Link]ás = 1
1
2 (a)
1
(a)
5 1 E
4 D
3 C [Link] =
[Link]ás = 2
2 F
1
(b)
Figura 2.17 Inserción en un Arreglo
41
with q
do begin
if rear = maxqueue
then rear: = 1
else rear: = rear + 1;
if rear = front
else items (rear): = x
end { termina with q do begin }
end { termina procedure insert }
42
el índice del primer elemento de la cola, la condición [Link] = [Link] implica
que la cola está vacía.
[Link] = 100
type queue = record
items: array ([Link]) of integer;
front, rear: [Link]
end;
var q: queue
begin
[Link]: = maxqueue
[Link] : = maxqueue
Observemos que [Link] y [Link] son inicializados con el último índice del
arreglo, en lugar de 0 ,1, porque el último elemento del arreglo precede
inmediatamente al primero dentro de la cola bajo esta representación. Puesto
que [Link] = [Link], la cola está inicialmente vacía.
43
La operación remove (q) puede ser codificada como:
Observemos que [Link] debe ser actualizado antes de extraer algún elemento.
Por supuesto, a menudo una condición de bajo flujo tiene significado y puede
utilizarse como una señal dentro de la fase de procesamiento. Si queremos,
podemos utilizar un procedimiento remvandtest que sería declarado por:
44
Operación para obtener el elemento que está al frente de la cola.
Una cola es otra forma de lista de acceso restringido. A diferencia de las pilas,
en las que tanto las inserciones como las eliminaciones tienen lugar en el
mismo extremo, las colas restringen todas las inserciones a un extremo y todas
las eliminaciones al extremo opuesto.
45
pilas; pero dado que debemos efectuar operaciones en ambos extremos de la
estructura, es mejor apartar dos celdas para usarlas como punteros, en lugar
de una sola.
46
(dpq).pqinsert(dpq,x) inserta el elemento x en dpq y es idéntica desde un punto
de vista lógico a pqinsert en el caso de una cola de prioridad ascendente. Por
último, pqmaxdelete (dpq) elimina el elemento máximo de dpq y regresa a su
valor.
47
El orden en que los elementos son procesados y, por tanto, eliminados sigue
estas reglas:
Una cola de prioridad es una estructura de datos que permiten al menos las
siguientes dos operaciones: insertar, que hace la operación obvia; y
eliminar_min, que busca, devuelve y elimina el elemento mínimo del montículo.
La función insertar equivale a encolar; y eliminar_min a desencolar.
48
La siguiente figura es un modelo básico de una cola de prioridad:
eliminar_min(M)
insertar (M)
Por otro lado, debemos mencionar que la propiedad de orden montículo nos
permite efectuar rápidamente las operaciones. Aplicando esta lógica, llegamos
a la propiedad de orden de montículo. (Todo trabajo implica asegurarse de que
se mantenga esta propiedad).
4
Véanse, Mark Allen Weiss, Estructura de datos y algoritmos, México, Addison-Wesley
Iberoamericana, 1995, p.180.; y Luis Joyanes Aguilar, Fundamentos de programación:
algoritmos y estructura de datos, 2ª ed., México, McGraw-Hill, 1996, p.407.
49
(M. elementos “0” es un centinela)
proceder insertar (x: tipo_elemento; var M:
COLA_DEPRIORIDAD);
var y: integer:
begin
(1) [Link]ño := MÀX_ELEMENTO then;
(2) Error (‘El montículo está lleno’)
else
begin
(3) [Link]ño := [Link]ño +1
(4) i :=[Link]ño;
(5)
while [Link] (i div 2)>x do
begin
(6) [Link](i) := [Link]
(i div 2)
(7) i := i div 2
end;
(8) [Link] (i) :=x;
end;
end;
50
montículo –a dicho valor se le llama centinela. Esta idea es semejante a usar
nodos cabecera en las listas enlazadas.
El tiempo para hacer inserción podría llegar a ser O(log n), si el elemento que
se ha de insertar es el nuevo mínimo y es filtrado en todo el camino hacia la
raíz. En término medio, el filtro termina pronto. Se ha demostrado que se
requiere 2.607 comparaciones en promedio para realizar una inserción, en
término medio, para subir un elemento 1.607 niveles.
Int p;
If (avail ==-1)
printf (“overflow\n”);
exit (1);
p=avail;
avail =node (avail).next:
return (p);
[ /*fin de getnode*/
2.4.3 Bicolas
Existe una variante de las colas simples, la doble cola o bicola. Esta es una
cola bidimensional en la que las inserciones y eliminaciones pueden realizarse
en cualquiera de los dos extremos de la lista, pero no por la mitad. El término
bicola hace referencia a que la cola puede verse como una cola bidireccional.
Hay varias formas de representar una bicola en una computadora. A
menos que se indique lo contrario, asumiremos que nuestras bicolas se
mantienen en un array circular BICOLA con punteros IZQ y DER, que apuntará
51
a los dos extremos de la bicola, y que los elementos se encuentran en los
extremos izquierdo y derecho. El término circular hace referencia a que damos
por hecho que la bicola (1) va detrás de bicola (N) en el array.
52
Operación para revisar si una bicola es vacía o no
function observaIzq (b:in bicola) return elemto is
begin
return [Link];
end observaIzq;
function observaDer (b:in bicola) return elemto es
begin
return [Link];
end observaDer;
function esVacia (b:in bicola) return oolean is
begin
return b.n=0;
end esVacia;
function long (b:in bicola) return natural is
begin
return b.n;
end long;
53
end observaDer;
function esVacia (b:in bicola) return oolean is
begin
return b.n=0;
end esVacia;
function long (b:in bicola) return natural is
begin
return b.n;
end long;
54
Operación para insertar un elemento al inicio de la bicola
Procedure agnadeIzq(b:in out bicola; e:in elemto) is
aux: ptUnDato;
begin
aux:=new unDato’ (e,[Link],null);
if [Link]=null then [Link]:=aux; [Link]:=aux;
[Link]: = aux;end if;
b.n:= b.n+1;
end agnadeIzq;
55
Operación para remover un elemento que está al inicio de la bicola
procedure libera(b:in out bicola) is
aux: ptUnDato;
begin
aux: =[Link];
while aux/=null loop
[Link]:=[Link];
disponer (aux);
aux: =[Link];
end loop;
creVacia (b);
end libera;
56
private
type unDato;
type ptDato is access unDato;
type unDato is record dato:elemto; sig,
ant:ptUnDato; end record;
type bicola is record izq,der:ptUnDato;
n:natural; end record;
end bicolas;
#include stdlib.h
int*ptr;/* puntero a enteros */
int* ptr2; /* otro puntero */
/* reserva espacio para 300 enteros */
ptr = (int*) malloc (300*sizeof(int) );
ptr(33) = 15; /* trabaja con el área de memoria
*/
rellena de_ceros (10,ptr); /*otro ejemplo */
ptr2 = ptr + 15; /* asignación a otro puntero */
/* finalmente, libera a la zona de memoria */
free (ptr);
57
Memoria Externa. Los Arreglos y los Registros están definidos por Estructuras
en varios Lenguajes en la Actualidad. Se crean Arreglos y Registros de forma
separada o combinada de acuerdo a las necesidades, dando lugar a diferentes
manejos de los datos, tanto en la memoria interna de la computadora como en
los compiladores actuales, sin mencionar que las imágenes son también
objetos, los cuales no son objeto en la presente asignatura.
Registros
Definición
Los registros son estructuras de datos heterogéneos por medio de las cuales
se tiene acceso, por nombre, a los elementos individuales, llamados campos.
En otras palabras, son un grupo de elementos en ci que cada uno de ellos se
identifica por medio de su propio campo.
58
Podemos visualizar el registro y sus campos como se muestra en la figura
siguiente:
[Link]
El registro
completo se llama:
Artículos
Artículos. Autor
Artículos. Título
Artículos. Pubperiódica
Artículos. Volumen
Artículo. Página
Artículo. Año
Como sucede con los demás tipos de datos compuestos, con excepción de los
arreglos de cadena, aquí no hay constantes de registro. Entonces, no es
posible asignar dar valor constante a una variable de registro; hay que dar
valores individualmente a los diversos campos.
59
Artículo. Autor := ‘Paz, Octavio.
Artículo. Titulo := ‘Aura ‘;
Artículo. PubPeriódica := ‘Letras Vivas’
Artículo. Volumen := 11;
Artículo. Página :=147;
Artículo. Año := 1968;
[Link]
Articulo1 := Articulo2;
Con este proceso, logramos lo mismo que si copiáramos todos los campos
individualmente:
Como ocurre con otros tipos compuestos, excepto las cadenas, no podemos
leer todo un registro a la vez; los procedimientos Read y ReadLn deben leer los
registros uno por uno. Tal como ha sucedido en oraciones anteriores, a
continuación escribiremos nuestro propio procedimiento para leer un registro:
60
Lee del teclado datos bibliográficos de un Articulo publicado; Se leen seis
campos:
BEGiN
WriteLn (‘ Autor o Autores: (40 caracteres como
máximo)’);
ReadLnCadena (Articulo. Autor);
WriteLn (‘Titulo del Articulo: 40 caracteres como
máximo)’);
ReadLnCadena (Artl. Titulo);
WriteLn ( Nombre de la publicación: (40
caracteres como máximo)’);
ReadLnCadena (Artl. PubPeriódica);
WriteLn (‘Nümero de volumen: (entero)’);
ReadLn ([Link]);
WriteLn ( ‘N1’mero de página inicial: (entero)’ )
ReadLn (Articulo. Página);
WriteLn (‘Año: (entero de cuatro digitos)’);
ReadLn ([Link]);
Wri teLn
END; {fin de leer ArticuloPubljcado }
61
Como se indica en la documentación, este desarrollo da por hecho que
contamos con un procedimiento ReadLnCadena para leer valores y colocarlos
en una variable cadena 40.
Podemos construir semejante procedimiento como parte de un TAD
cadena 40.
Write ( ´Autor(es) : ‘ ) ;
EscribirCadena (Artículo. Autor) ;
WriteLn;
Write ( ‘Título: ‘ ) ;
EscribirCadena (Artículo. Título );
WriteLn;
Write(‘ Publicación. ‘ ) ;
EscribirCadena (Artí[Link]ódica );
WriteLn, .
Write (‘Volumen: ‘ );
WriteLn( Artículo. Volumen: 4) ;
62
Write( ´ Página: ‘ );
WriteLn ( Artículo. Página: 4 );
Writen( ´ Año : ‘ );
WritenLn ( Artículo. Año: 4 ) ;
WriteLn
END; {fin de escribirArtículoPublicado}
TYPE
Cadena40 = PACKED ARRAY [1..40] of Char;
ArtículoPublicado =
RECORD
Autor: cadena40;
Titulo :cadena40;
63
PubPeriodica : cadena40;
Volumen: Integer;
Pagina: Integer;
Año : Integer;
END;
Estas declaraciones describen un arreglo Biblia que contiene 100 registros del
tipo ArtículoPublicado. Se trata de una declaración jerárquica porque el arreglo
contiene registros que poseen arreglos y enteros. Los nombres de estos
objetos reflejan la jerarquía:
64
En Pascal, podemos crear toda clase de composiciones: registros que
contienen otros registros, que a la vez tienen otros registros; arreglos de
registros de arreglos de registros; y casi cualquier otra cosa que necesitemos.
La única limitación es que un registro no puede contener registros de un mismo
tipo, pues un tipo recursivo como ése nunca terminaría.
Arreglos de registros
Cuando se trabajó con las estructuras de datos, arreglos y registros, se hizo
manipulando sus elementos individuales; nunca se laboró con el arreglo o con
el registro completos, o con una sola operación. Por ejemplo, si se deseara
buscar en un arreglo, se haría en cada uno de sus elementos individualmente,
comparándolo con la clave deseada. Y si quisiéramos llenar un registro,
ingresamos un valor a cada campo del registro por separado.
Registros anidados
Cuando se ejecuta una estructura cíclica como parte del cuerpo de otra,
decimos que los ciclos están anidados. Hay dos clases de anidamiento: directo
e indirecto.
En el primer caso, la estructura cíclica interior es literalmente uno de los
enunciados incluidos en el cuerpo de la estructura exterior. Y en el segundo
caso, el cuerpo del ciclo exterior llama a un procedimiento o función que
contiene la estructura cíclica interior.
65
Objeto abstracto: conjunto de números naturales es un rango dado, no vacío.
• Nombre: Conjunto (sufijo de las operaciones: Conj)
• Formalismo: inf: {xl, x2,…,xN}: sup
• Invariante: inf < X¡< sup /* todos los. Elementos
están en el rango [inf…sup ]*/
xi ¡= xk, i!= k /*no hay elementos
repetidos */
1 < inf < sup /* el rango es válido */
66
• Analizadoras: la operación básica de consultar un conjunto es verificar si
un elemento pertenece a él; adicionalmente, es necesario permitirle al
cliente revisar los límites del rango de enteros que puede contener. Con
estas tres operaciones, es posible extraer toda la información del
conjunto:
Int estaConj (Conjunto conj, int elem );
Int inferiorConj (Conjunto Conj);
Int supenorConj (Conjunto conj);
67
• Especificación de las operaciones:
{pre: conj = inf: {xl, x2,…, xN}: sup, elem¡=X¡ A¡.inf <
elem < sup}
{post: conj = inf: {xl,…, xN, elem}: sup}
68
Int superiorConj (conjunto conj)
/* retorna el límite superior del rango de valores válidos del conjunto */
2.5 Listas
Una lista lineal es un conjunto de elementos de un tipo dado que se encuentren
ordenados (pueden variar en número). Los elementos de una lista se
almacenan normalmente de manera contigua (un elemento detrás de otro) en
posiciones de la memoria.
69
Cada ejemplo modela un elemento del agrupamiento. Así, e1 es el primero de
la lista; en, el último; y la lista formada por los elementos <e2, e3,.....,en
corresponde al resto de la lista inicial.
Representaciones de listas
Las listas enlazadas tienen una terminología propia que suelen utilizar
normalmente. Los valores se almacenan en un nodo cuyos componentes se
llaman campos. Un nodo tiene al menos un campo de dato o valor y un enlace
(indicador o puntero) con el siguiente campo. El campo enlace apunta
(proporciona la dirección) al siguiente nodo de la lista. El último nodo de la lista
enlazada, por un convenio, suele representarse por un enlace con la palabra
reservada nil (nulo), una barra inclinada (/) y, en ocasiones, el símbolo eléctrico
de la tierra o masa.
5 4 1 7 18 19 9 7 45 ...
(a) array representado por una lista
Lista
5 4 1 1 1 9
70
4. Clasificar los elementos de la lista en orden ascendente o descendente.
5. Unir dos o más listas en una sola.
6. Dividir una lista en varias sublistas.
7. Copiar la lista.
8. Borrar la lista.
Inicio
LeerL (longitud de la lista)
Si L = 0
Entonces, visualizar la lista vacía.
Si no, si 1 < = j < = L
Entonces, B P (j)
Si no, visualizar elemento no existente.
Fin-si
71
Fin-si
Fin
ListainicLista(void)
/ * Crea y retorna una lista vacía * /
post:inicLista =<>
2. Fin_si
72
Segundo Caso. Inserción a continuación de un nodo específico.
Algoritmo correspondiente:
1. NUEVO OBTENERNOMBRE
2. INFO(NUEVO) NOMBRE
3. Q SIG (P)
4. SIG (P) NUEVO
5. SIG (NUEVO) Q
Algoritmo búsqueda:
1. Si la lista está vacía, escribir un mensaje de error.
2. En caso contrario, la lista no está vacía y el algoritmo la seguirá
recorriendo hasta encontrar algún elemento en la búsqueda.
Algoritmo búsqueda
Inicio
Leer PRIMERO
Si PRIMERO = nil
Entonces, escribir “lista vacía”
73
Si no:
Q 1
...
NOTA: una lista enlazada sin ningún elemento se llama lista vacía; es decir, la
representación de una lista vacía si su puntero inicial o de cabecera tiene el
valor nulo (nul).
Algoritmo inserción
Comprobación de sobrecarga
Si DISPO = NULO
Entonces, escribir “sobrecarga”
Si no:
NUEVO DISPO
DISPO P(DISPO)
INFO(NUEVO) ELEMENTO
P(NUEVO) PRIMERO
PRIMERO NUEVO
Fin_si
En una lista circular, el algoritmo para insertar un nodo X al frente de una lista
circular es:
NUEVO NODO
INFO (NUEVO) X
74
SIG(NUEVO) SIG(CABECERA)
SIG(CABECERA) NUEVO
1. Q SIG(P)
2. SIG(P) SIG(Q)
3. LIBERAR NODO (Q)
75
La forma lógica de satisfacer la condición 1 es tener un arreglo global de registros.
Para cualquier celda de arreglo, puede usarse su índice en lugar de una dirección.
Type
Ap_nodo = integer
Nodo = record
Elemento: tipo_elemento
Siguiente: ap_nodo
End;
LISTA = ap_nodo
Posición = ap_nodo
Var ESPACIO_CURSOR: arrary (0...TAMAÑO_ESPACIO) of
nodo
76
función hash perfecta y debe coincidir el mismo número de posiciones
posiciones que el número de elementos que se está transformando.
transformando. Pero el número de elementos no siempre se conoce a
conoce a priori. Con 1000 celdas se pueden almacenar los nombres de
nombres de las variables de un programa.5
5
DROZDEK, A. (2007). Estructura de Datos y Algoritmos en Java (Segunda ed.). México:
Thomson Learning, p. 518.
6
Ibid., p. 528.
77
apuntan a una misma dirección provocando conflicto para el almacenamiento
almacenamiento de la información en la página de memoria interna de la
interna de la computadora.
Por ejemplo, si las claves son números enteros, clave mod N es una función: y
una función: y el valor será un valor entero. Si N fuese 100 y todas las claves
todas las claves terminasen en cero, esta función de dispersión sería una mala
sería una mala opción. Si el resultado de Mod genera un número primo, habría
número primo, habría menos colisiones. Si se generan números enteros de
enteros de forma aleatoria, las claves en la tabla se distribuirán de forma
de forma uniforme. Por lo general, las claves son cadenas de caracteres cuya
caracteres cuya longitud depende de las propiedades y restricciones de la
restricciones de la función mod.
caracteres.
función Dispersión1 (Clave, TamañoClave): ´Indice
valor := ascii(Clave[1]);
para i := 2 hasta TamañoClave hacer
valor := valor + acii(Clave[i])
fin para
devolver valor mod N
fin función
Por ejemplo, si N = 10007 y las claves tienen 8 caracteres, la función sólo toma
función sólo toma valores entre 0 y 1016 = 127 ·8.
7
Facultad de Informática. Universidad de A Coruña. Algoritmos.
[Link]
octubre 18, 2008.
78
Algoritmos Tablass Tablas
Función Hash8
Tabla hash
Una tabla hash o mapa hash es una estructura de datos que asocia
asocia llaves o claves con valores. La operación principal que soporta de
soporta de manera eficiente es la búsqueda: permite el acceso a los
a los elementos (teléfono y dirección, por ejemplo) almacenados a partir
a partir de una clave generada (usando el nombre o número de cuenta,
cuenta, por ejemplo). Funciona transformando la clave con una función
función hash en un hash, un número que la tabla hash utiliza para
para localizar el valor deseado.
8
Esta sección, con todo y las figuras, fue recuperada de Wikipedia en la entrada “Tabla hash”,
actualizada el 27/07/08, disponible en: [Link] fecha de
recuperación: octubre 18 del 2008.
79
Figura No. 2.22 .Ejemplo de tabla hash
80
borrar(llave).
También se pueden ofrecer funciones como iteración en la
en la tabla, crecimiento y vaciado. Algunas tablas hash permiten
almacenar múltiples valores bajo la misma clave.
Para usar una tabla hash se necesita:
Una estructura de acceso directo (normalmente un array).
Una estructura de datos con una clave
Una función resumen (hash) cuyo dominio sea el espacio de claves y su
claves y su imagen (o rango) los números naturales.
Inserción
Para almacenar un elemento en la tabla hash se ha de convertir su clave
clave a un número. Esto se consigue aplicando la función resumen a la
a la clave del elemento.
0. El resultado de la función resumen ha de mapearse al espacio de
de direcciones del array que se emplea como soporte, lo cual se consigue
consigue con la función módulo. Tras este paso se obtiene un índice
índice válido para la tabla.
0. El elemento se almacena en la posición de la tabla, obtenido en el paso
el paso anterior.
0. Si en la posición de la tabla ya había otro elemento, se ha producido una
producido una colisión. Este problema se puede solucionar asociando
asociando una lista a cada posición de la tabla, aplicando otra función o
función o buscando el siguiente elemento libre. Estas posibilidades han
posibilidades han de considerarse a la hora de recuperar los datos.
datos.
Búsqueda
Para recuperar los datos, es necesario únicamente conocer la clave del
del elemento, a la cual se le aplica la función resumen.
0. El valor obtenido se mapea al espacio de direcciones de la tabla.
tabla.
0. Si el elemento existente en la posición indicada en el paso anterior tiene
anterior tiene la misma clave que la empleada en la búsqueda, entonces
entonces es el deseado. Si la clave es distinta, se ha de buscar el
el elemento según la técnica empleada para resolver el problema de las
81
problema de las colisiones al almacenar el elemento.
82
número primo. Esto se hace con el objetivo de evitar la tendencia de que
tendencia de que los hash de enteros grandes tengan divisores comunes
comunes con el tamaño de la tabla hash, lo que provocaría colisiones tras
colisiones tras el cálculo del módulo. Sin embargo, el uso de una tabla de
una tabla de tamaño primo no es un sustituto a una buena función hash.
función hash.
Resolución de colisiones
Si dos llaves generan un hash apuntando al mismo índice, los registros
registros correspondientes no pueden ser almacenados en la misma
misma posición. En estos casos, cuando una casilla ya está ocupada,
ocupada, debemos encontrar otra ubicación donde almacenar el nuevo
nuevo registro, y hacerlo de tal manera que podamos encontrarlo cuando
encontrarlo cuando se requiera.
Para dar una idea de la importancia de una buena estrategia de resolución
resolución de colisiones, considérese el siguiente resultado, derivado de la
derivado de la paradoja de las fechas de nacimiento. Aún cuando
cuando supongamos que el resultado de nuestra función hash genera
genera índices aleatorios distribuidos uniformemente en todo el arreglo, e
83
arreglo, e incluso para arreglos de 1 millón de entradas, hay un 95% de
95% de posibilidades de que al menos una colisión ocurra antes de
de alcanzar los 2500 registros.
Encadenamiento
En la técnica más simple de encadenamiento, cada casilla en el arreglo
arreglo referencia una lista de los registros insertados que colisionan en la
colisionan en la misma casilla. La inserción consiste en encontrar la casilla
la casilla correcta y agregar al final de la lista correspondiente. El borrado
borrado consiste en buscar y quitar de la lista.
84
promedio que la misma tabla a su capacidad recomendada.
Direccionamiento abierto
Las tablas hash de direccionamiento abierto pueden almacenar los
los registros directamente en el arreglo. Las colisiones se resuelven
resuelven mediante un sondeo del arreglo, en el que se buscan diferentes
diferentes localidades del arreglo (secuencia de sondeo) hasta que el
que el registro es encontrado o se llega a una casilla vacía, indicando que
indicando que no existe esa llave en la tabla.
85
Figura 2.24 Ejemplo de direccionamiento abierto
86
capacidad, el número de saltos requeridos por el sondeo puede aumentar
aumentar considerablemente. Una vez que se llena la tabla, los algoritmos
algoritmos de sondeo pueden incluso caer en un círculo sin fin. Incluso
Incluso utilizando buenas funciones hash, el límite aceptable de capacidad
capacidad es normalmente 80%. Con funciones hash pobremente
pobremente diseñadas el rendimiento puede degradarse incluso con poca
con poca información, al provocar aglomeramiento significativo. No se
No se sabe a ciencia cierta qué provoca que las funciones hash generen
generen aglomeramiento, y es muy fácil escribir una función hash que, sin
hash que, sin querer, provoque un nivel muy elevado de aglomeramiento.
aglomeramiento.
Ventajas e inconvenientes de las tablas hash
Una tabla hash tiene como principal ventaja que el acceso a los datos
datos suele ser muy rápido si se cumplen las siguientes condiciones:
condiciones:
Una razón de ocupación no muy elevada (a partir del 75% de ocupación
ocupación se producen demasiadas colisiones y la tabla se vuelve
vuelve ineficiente).
Una función resumen que distribuya uniformemente las claves. Si la
Si la función está mal diseñada, se producirán muchas colisiones.
colisiones.
Los inconvenientes de las tablas hash son:
Necesidad de ampliar el espacio de la tabla si el volumen de datos
datos almacenados crece. Se trata de una operación costosa.
Dificultad para recorrer todos los elementos. Se suelen emplear listas o
listas o colecciones(Collection usadas en .net) para procesar la
totalidad de los elementos .
Desaprovechamiento de la memoria. Si se reserva espacio para todos
todos los posibles elementos, se consume más memoria de la
necesaria; se suele resolver reservando espacio únicamente para
para punteros a los elementos.
87
Implementación en pseudocódigo
El pseudocódigo que sigue es una implementación de una tabla hash de
hash de direccionamiento abierto con sondeo lineal para resolución de
resolución de colisiones y progresión sencilla, una solución común que
que funciona correctamente si la función hash es apropiada.
function buscacasilla(llave) {
i := hash(llave) módulo de numcasillas
loop {
if casilla[i] esta libre or casilla[i].llave =
casilla[i].llave = llave
return i
i := (i + 1) módulo de numcasillas
}
}
function busqueda(llave)
i := buscacasilla(llave)
if casilla[i] está ocupada // llave en la tabla
la tabla
return casilla[i].valor
else // llave es está en la
en la tabla
return no encontrada
88
hacer tabla más grande (nota 1)
i := buscacasilla(llave)
}
casilla[i].llave := llave
casilla[i].valor := valor
}
}
Corolario
Una estructura de datos es una colección de datos que pueden ser
ser caracterizados por su organización y operaciones.
Las Estructuras de Datos deben de cumplir con ciertos requisitos para que no
requisitos para que no se pierda su representatividad ni su lógica de
lógica de manipulación.
Entero (integer)
(integer)
Real (real)
Carácter (caracter)
(caracter)
Datos simples estándar Estándar Lógico (Boolean)
(Boolean)
Float (punto flotante)
89
Definidos por Subrango (subrange)
el programador Enumerativo
Grafos
Compuestos o dinámicos Cadenas (string)
Listas (pilas/colas)
Lista enlaza
Árboles
90
[Link]
91
A.2.3. Investigar en el Lenguaje Pascal y en C++ cuál es la Estructura para
Estructura para definir un Arreglo y un Registro.
A.2.4. Investigar cuáles son las Características de la Programación Orientada a
Programación Orientada a Objetos (POO) y entregar por escrito el
el resultado de tu investigación.
A.2.5. Investigar si el Lenguaje Visual Basic contiene todas las características
características de POO y entregar por escrito el resultado de tu
investigación.
[Link] qué es el Puntero y sus aplicaciones. Presentar por escrito el
por escrito el resultado de tus investigaciones.
A.2.7. Consultar la siguiente página
[Link]
_tablas_de_dispersion.pdf para ver más a detalle los conceptos del
del archivo PDF y los programas ahí mostrados.
A.2.8. Investigar la diferencia entre Invariante (característica de los TDA) y
los TDA) y Herencia (característica de la POO) y entregar el resultado de
resultado de tu comparativo por escrito.
Cuestionario de Autoevaluación
0. Anota el concepto de Registro.
0. Anota el concepto de Arreglo.
0. Anota el concepto de vector.
0. Anota el concepto de TDA y qué significa su sigla.
0. Anota la Estructura de un TDA.
0. Anota el concepto de puntero y sus aplicaciones.
0. Anota las instrucciones para definir un Registro en los Lenguajes Pascal y
Lenguajes Pascal y C++.
0. En el Lenguaje C++, ¿para qué sirve la instrucción Struct?
0. ¿Qué es el Atributo de un Objeto?
0. Explica las características de la POO.
Examen de Autoevaluación
I. Completa las oraciones
92
0. El ________ es un tipo de dato de iguales tipos de sus elementos.
elementos.
0. El ________ sirve para apuntar a la dirección de una localidad de
de memoria.
1.
puede cambiar el Tope o Cima de una Pila
Arreglos solo emplean un índice
tienen cualidades
son un refinamiento de los Registros
Arreglos están en memoria Externa
Registros están en Memoria Externa
Registros Anidados se definen subcampos.
93
TEMA 3. ESTRUCTURAS DE DATOS AVANZADAS
Objetivo Particular.
Al finalizar el alumno conocerá y comprenderá los Tipos de Datos Dinámicos,
Datos Dinámicos, su importancia, las diferencias con respecto a las anteriores
las anteriores estructuras y sus más importantes aplicaciones.
Temario Detallado
3.1. Listas simplemente enlazadas
3.1.1.-Definición del tipo de dato abstracto lista
3.1.2.-Definición de las operaciones sobre listas
3.1.3.-Implantación de una lista
3.3. Árboles
3.3.1.-Definición del tipo de dato abstracto árbol binario
3.3.2.-Definición de las operaciones sobre árboles binarios
3.3.3.-Implantación de un árbol binario
3.4. Grafos
3.4.1.-Definición del tipo de dato abstracto grafo
3.4.2.-Operaciones sobre un grafo
3.4.3.-Implantación de un grafo
94
Introducción
Las Operaciones con Listas son comunes en la comprobación (mapeo) de la
(mapeo) de la memoria Interna de la Computadora y la información se maneja
información se maneja empleando estructuras más simples que las listas para
las listas para poder procesar su formato y estructura en vectores lineales.
vectores lineales.
9
Luis Joyanes Aguilar. op. cit., p. 441.
95
5 Lista 4 1 18 1 9
Cada ejemplo modela un elemento del agrupamiento. Así, e1 es el primero de
el primero de la lista; en, el último; y la lista formada por los elementos <e2,
elementos <e2, e3,.....,en corresponde al resto de la lista inicial.
Representaciones de listas
Las listas enlazadas tienen una terminología propia que suelen utilizar
utilizar normalmente. Los valores se almacenan en un nodo cuyos
cuyos componentes se llaman campos. Un nodo tiene al menos un
un campo de dato o valor y un enlace (indicador o puntero) con el
el siguiente campo. El campo enlace apunta (proporciona la dirección
dirección de) al siguiente nodo de la lista. El último nodo de la lista
lista enlazada, por un convenio, suele representarse por un enlace con
enlace con la palabra reservada nil (nulo), una barra inclinada (/)y, en
(/)y, en ocasiones, el símbolo eléctrico de la tierra o masa.10
5
lista
10
Héctor Fiestas Bancayán, “Estructuras dinámicas de datos”, en línea, disponible en:
[Link]/hwfiestasb/Catedras/EstructurasDatos/SESION_9_A_ESTRUCTURAS_DINA
[Link], recuperado el 18/10/08.
96
0. Insertar, eliminar o localizar un elemento.
0. Determinar el tamaño de la lista.
0. Recorrer la lista para localizar un determinado elemento.
0. Clasificar los elementos de la lista en orden ascendente o
descendente.
0. Unir dos o más listas en una sola.
0. Dividir una lista en varias sublistas.
0. Copiar la lista.
0. Borrar la lista.
97
Inicio
Algoritmo inserción
‘Comprobación de sobrecarga’
Si DISPO=NULO
Entonces, escribir “sobrecarga” ----- Impacto en la Memoria
Si no:
NUEVO DISPO
98
DISPO P(DISPO)
INFO (NUEVO) ELEMENTO
P(NUEVO) PRIMERO
PRIMERO NUEVO
Fin_si
Algoritmo correspondiente:
1. NUEVO OBTENERNOMBRE
2. INFO(NUEVO) NOMBRE
3. Q SIG (P)
4. SIG (P) NUEVO
5. SIG (NUEVO) Q
99
Algoritmo búsqueda:
1. Si la lista está vacía, escribir un mensaje de error.
2. En caso contrario, la lista no está vacía y el algoritmo la seguirá
seguirá recorriendo hasta encontrar algún elemento en la búsqueda.
búsqueda.
Algoritmo búsqueda
Inicio
Leer PRIMERO
Si PRIMERO = nil Entonces, escribir “lista vacía”
vacía”
Si no: Q 1
Fin Si
...
NOTA: una lista enlazada sin ningún elemento se llama lista vacía; es decir, la
vacía; es decir, la representación de una lista vacía, si su puntero inicial o de
puntero inicial o de cabecera tiene el valor nulo (nul).
Algoritmo inserción
100
Comprobación de sobrecarga
Fin_si
A
Figura 3.3 Tope y Sima de una Lista
En una lista circular, el algoritmo para insertar un nodo X al frente de una lista
frente de una lista circular es:
NUEVO NODO
INFO (NUEVO) X
SIG(NUEVO) SIG(CABECERA)
SIG(CABECERA) NUEVO
1. Q SIG(P)
2. SIG(P) SIG(Q)
101
3. LIBERAR NODO (Q) ----- libera espacio de memoria.
memoria.
Para definir las operaciones de una lista, se debe especificar la variable que
variable que contiene al primer nodo (cabecera), que en este caso llamaremos
caso llamaremos PRIMERO.
ENLACE (i) Valor del índice (orden) del array, donde se encuentra el
encuentra el siguiente elemento de la lista.
102
que la componen. Si no tiene ningún elemento, se encuentra vacía y su
vacía y su longitud es 0. Esta estructura se representa mediante la notación
la notación <null>, y se considera simplemente como un caso particular de una
particular de una lista con cero elementos.
1 2i n length <cadena>
<cadena>
lista).
#define INTGR 1
#define FLT 2
#define STRING 3
struct node {
STRING*/
/* elemento correspondiente */
int ival;
float fval;
element;
};
Un algoritmo para invertir los elementos de una lista ligada implica pasar a
implica pasar a través de cada nodo de la lista para cambiar todos los
todos los apuntadores, de tal manera que el último se convierta en el primer
en el primer elemento, y el que era el primero se convierta en el último. El
último. El algoritmo se muestra en la siguiente figura (en lenguaje COBOL):
lenguaje COBOL):
PROCEDURE DIVISION.
………………………………………………………..
-----------------------------------------------------------------
01 APUNTADORES AUXILIARES.
02 NODO-SIGUIENTE PIC9(3)
INVERSIÓN-DE-LA LISTA
IF PRIMER-NODO NOT=0
(ESTE-NODO)
NODO)
ANTERIOR
…………………………………………………………………
…………………………………………………………………
STOP RUN.
…………………………………………………………………
END.
106
Fclose (fp);
DestruirLista (1st);
107
3.1.3 Implantación de una Lista
Type
Ap_nodo = integer
108
Nodo = record
Elemento: tipo_elemento
Siguiente: ap_nodo
End;
LISTA = ap_nodo;
Posición = ap_nodo;
Var ESPACIO_CURSOR: arrary (0...TAMAÑO_ESPACIO) of nodo;
of nodo;
Listas generalizadas
Cuando las estructuras de datos se vuelven más complejas y proporcionan
y proporcionan más posibilidades al usuario, las técnicas de manejo aumentan
de manejo aumentan también su grado de dificultad. Así, la lista generalizada
lista generalizada es un método de implantación de tipo de datos abstractos, en
datos abstractos, en donde varía el tipo de elementos.
109
Definición de las operaciones sobre listas generalizadas
Definiremos ahora una serie de operaciones abstractas cuyas propiedades
propiedades lógicas son las operaciones head y tail, que no están definidas si
están definidas si su argumento no es una lista. Una sublista de una lista R es
una lista R es una lista que resulta de aplicación de cero a más operaciones tail
operaciones tail a 1.
#define INTGR1
#define CH 2
#define LST 3
Struct nodetype {
Int utype; //utype es igual a
igual a INTGR, CH, o LST
Union }
Int intgrinfo; //utype = INTGR
INTGR
Char charinfo; //utype = CH
Struct nodetype *Istinfo //utype = LST
} Info;
Struct nodetype *next;
} ;
typedef struct nodetype *NODEPTR;
110
push para agregar un nodo al frente de la lista.
Q = null;
For (p = list ; p ¡ = null & & x >
Info (p) ; p = next (p) )
q = p;
/* en este momento, un nodo que entrega x deberá insertarse
deberá insertarse if (q = = null) /* insertar x a la cabeza
a la cabeza de la lista.
push (list, x);
else
insafter (q, x)
111
Un nodo de lista comprende un apuntador a un nodo atómico y un indicador de
un indicador de tipo que señala la clase de nodo atómico a la que apunta (así
que apunta (así como un apuntador al siguiente nodo de la lista, por supuesto).
por supuesto). Cuando es necesario colocar un nodo nuevo en una lista, debe
una lista, debe asignarse un nodo atómico del tipo apropiado y de su valor, y
de su valor, y deben establecerse el campo de la información del nodo de lista
del nodo de lista para que apunte al nuevo nodo atómico y el tipo de campo
tipo de campo correcto.
switch (typecode) {
case t1; //hacer algo con node1
node1
case t2; //hacer algo con node2
...
caset10; //hacer algo con node10
} //fin de swiich
struct nodetype {
int utype; /*utype es igual a
igual a INTGR, CH, O LST*/
unión {
int intgrinfo; /*utype = INTGR*/
INTGR*/
char charinfo; /* utype = CH */
112
struct nodetype *lstinfo; /* utype = LIST */
= LIST */ } info;
struct nodetype *next
} ;
typedef struct nodetype *NODEPTR;
Int p;
If (avail ==-1)
printf (“overflow\n”);
exit (1);
p=avail;
avail =node (avail).next:
return (p);
[ /*fin de getnode*/
113
Figura 3.5 Representación Grafica de Lista Doble
través de:
Donde los trazos continuos denotan la situación inicial y los punteados la final.
punteados la final. El ejemplo visto se ajusta a la supresión de un elemento o
un elemento o celda de una lista situada en medio de la misma. Para obviar los
Para obviar los problemas derivados de los elementos extremos (primero y
(primero y último) es práctica común hacer que la cabecera de la lista
la lista doblemente enlazada sea una celda que efectivamente complete el
complete el círculo, es decir, el anterior a la celda de cabecera sea la última
sea la última celda de la lista y la siguiente la primera. De esta manera no
manera no necesitamos verificar para NULL en el anterior procedimiento
procedimiento borrar.
115
Es importante notar que aunque la estructura física de la lista puede hacer
puede hacer pensar que mediante la operación siguiente podemos alcanzar de
podemos alcanzar de nuevo un nodo de la lista, la estructura lógica es la de
lógica es la de una lista y por lo tanto habrá una posición primero y una
primero y una posición fin de forma que al aplicar una operación anterior o
anterior o siguiente respectivamente sobre estas posiciones el resultado será
resultado será un error.
117
Argumentos: Una lista.
Efecto: Devuelve la posición posterior al último elemento de la lista.
lista.
EFICIENCIA
Comparación de la eficiencia para las distintas implementaciones de las listas:
Cuadro 3.1 Comparativo entre las Operaciones para Listas con tres tipos
con tres tipos de Enlaces
tLista Crear()
{
tLista l;
l = (tLista)malloc(sizeof(tipocelda));
if (l == NULL)
Error("Memoria insuficiente.");
l->siguiente = l->anterior = l;
return l;
}
120
tPosicion Primero (tLista l)
{
return l->siguiente;
}
return l;
}
nuevo = (tPosicion)malloc(sizeof(tipocelda));
(tPosicion)malloc(sizeof(tipocelda));
if (nuevo == NULL)
Error("Memoria insuficiente.");
nuevo->elemento = x;
nuevo->siguiente = p;
nuevo->anterior = p->anterior;
p->anterior->siguiente = nuevo;
p->anterior = nuevo;
}
if (*p == l){
Error("Posicion fin(l)");
121
}
q = (*p)->siguiente;
(*p)->anterior->siguiente = q;
q->anterior = (*p)->anterior;
free(*p);
(*p) = q;
}
if (p == l){
Error("Posicion fin(l)");
}
return p->elemento;
}
if (p == l){
Error("Posicion fin(l)");
}
return p->siguiente;
}
if (p == l->siguiente){
Error("Posicion primero(l)");
}
return p->anterior;
}
122
tPosicion posicion (tElemento x, tLista l)
{
tPosicion p;
int encontrado;
p = primero(l);
encontrado = 0;
while ((p != fin(l)) && (!encontrado))
if (p->elemento == x)
encontrado = 1;
else
p = p->siguiente;
return p;
}
3.3. Árboles
Definiciones:
Es un conjunto no vació de vértices (nodos) y aristas (enlaces) que
que cumple una serie de requisitos.11
11
Robert Sedgewick, Algoritmos en c++. México, Pearson Education 2000, p 40.
12
Silvia Guardati, Estructura de datos orientada a objetos, México, Pearson Educación 2007,
p.. 313.
13
Adam Drozdek, Estructura de datos y algoritmos en Java, México Thomson, 2ª dic.2007, p.
214.
123
Anidación de Paréntesis, Grafos y como una Estructura Jerárquica.
Jerárquica.
Elementos del Árbol
Nodo Raíz, Nodos Hijos, Nodos Hermanos, Altura, Recorridos,
Dirección.
Todo Árbol tiene un solo Nodo Raíz.
Los Árboles pueden tener o no Nodos Hijos, si sí los tiene, puede
puede haber Nodos Hermanos.
Si únicamente tiene un Nodo Raíz su Altura = 0 y su Nivel = 1.
Los Recorridos pueden ser en preorden, postorden y en orden.
Un Árbol puede recorrerse en dirección Top-Down De arriba abajo,
abajo, De abajo arriba, (Down-Top), A partir de su rama Izquierda o
Izquierda o a Partir de su rama Derecha.
124
Nodo Raíz Recorrido
Altura
Nodo Hermano
Nodos Internos
125
Figura 3.9 Diagrama de Venn del Árbol de la Figura 3.9
126
Glosario Fundamental de Árboles
Árbol.- Es un conjunto no vacío de vértices y aristas que cumple una serie de
una serie de requisitos. (Sedgewick, p. 40).
Altura del Árbol.- Es el máximo de los niveles considerando todos los nodos.
todos los nodos. (Drozdek, p. 214 y (Sedgewick, p. 41).
Altura de un Árbol.- Es el nivel máximo del Árbol (o la máxima distancia entre
distancia entre la raíz y cualquier nodo). (Guardati, p. 315).
Árbol Multicamino.- Cuando cada nodo debe tener un número específico de
específico de hijos colocados en un orden determinado. (Sedgewick, p. 41).
(Sedgewick, p. 41).
Árbol Binario.- Es un conjunto finito de elementos que está vacío o dividido
vacío o dividido en tres subconjuntos separados. (Yedidyah, ET AL, 249).
AL, 249).
Árbol Binario Lleno.- Es aquel en que los nodos internos llenan todos los
todos los niveles, con la posible excepción del último. (Sedgewick, p. 42).
(Sedgewick, p. 42).
Árbol Binario Completo.- Es un Árbol Binario Lleno en el que los nodos
nodos internos del último nivel aparecen todos a la izquierda de los nodos
los nodos externos de ese mismo nivel. (Drozdek, 216 y Sedgewick, p. 42).
Sedgewick, p. 42).
Árbol Ordenado.- Es aquel en el que se ha especificado el orden de los hijos
orden de los hijos de todos los nodos. (Sedgewick, p. 41).
Arista.- Conexión entre dos nodos.
Arco.- Relación entre nodos
Bosque.- Conjunto de Árboles. (Sedgewick, p. 41).
Camino de un Árbol.- Es una lista de nodos distintos en que dos
consecutivos se enlazan mediante arista, sin embargo, existe un camino
camino entre la raíz y cada uno de los otros nodos del árbol de acuerdo a la
acuerdo a la Dirección y Recorrido previamente establecidos. Si no existe tal
no existe tal camino entonces se estará ante una Estructura de tipo Grafo.
tipo Grafo. (Sedgewick, p. 40).
Grado del Árbol.- Es el máximo de los grados, considerando todos sus
todos sus nodos. (Guardati, p. 315).
127
Grado de un Nodo.- Es el número de hijos que dicho nodo tiene. (Guardati,
(Guardati, p. 315).
Longitud del Camino.- Es la suma de los niveles de todos los nodos del árbol
nodos del árbol (la suma de las longitudes de los caminos desde cada nodo
desde cada nodo a la raíz. (Sedgewick, p. 41).
Longitud del Camino Interno.- Es la suma de los niveles de los nodos
nodos Internos del Árbol. (Drozdek, p. 222 y Sedgewick, p. 41).
Longitud del Camino Externo.- Es la suma de los niveles de los nodos
nodos Externos del Árbol. (Sedgewick, p. 41).
Nivel de un Nodo.- Es el número de nodos del camino que lleva desde este
desde este hasta la raíz (sin incluirse el mismo). (Guardati, p. 314)
314)
Nivel de un Nodo.- Es el número de arcos que deben ser recorridos,
recorridos, partiendo de la raíz, para regresar a dicha raíz. (Sedgewick, p.
(Sedgewick, p. 41).
Nodo.- Cada elemento del Árbol (Yedidyah, ET AL, p. 249).
Nodo Interno.- Aquellos que no son Nodos Terminales ni la Raíz. (Guardati,
Raíz. (Guardati, p. 315).
Nodo Terminal.- Aquel que no tiene nodos Hijos. (Guardati, p. 315).
315).
Recorrido del Árbol.- Es el proceso de visitar cada nodo en el árbol
árbol exactamente una vez. (Drozdek, p. 223).
Recorrido en Amplitud.- Es visitar cada nodo empezando por el nivel inferior
nivel inferior (0 superior) y moverse hacia abajo (o hacia arriba) nivel por
nivel por nivel, visitando nodos en cada nivel de izquierda a derecha, o
derecha, o viceversa.(Drozdek, p. 224)
Recorrido en Profundidad.- Continua lo mas lejos posible a la izquierda (o a
izquierda (o a la derecha), luego regresa hacia arriba hasta el primer cruce,
primer cruce, avanza un paso a la derecha (o a la izquierda) y de nuevo lo
de nuevo lo más lejos posible a la izquierda (o a la derecha). (Drozder, p.
(Drozder, p. 225).
Vértice.- Es un objeto simple (nodo) que puede tener un nombre y puede
nombre y puede llevar otra información asociada: una arista es una conexión
128
una conexión entre dos vértices. (Sedgewick, p. 40).
Binarios Llenos
Equilibrados Completos
Degenerados
Por su Estructura Balanceados
Tipos de
Árboles
Binarios
Binarios de Búsqueda
Por su Recorrido B
B+
Multicamino B*
B+ Prefijos
De Bits.
R
129
2-4
Observe que:
) B es un sucesor izquierdo y C un sucesor derecho del nodo A.
) El subárbol izquierdo de la raíz A consiste en los nodos B, D, E y F,
y F, y el subárbol derecho de A consiste en los nodos C, G, H, J, K y L.
K y L.
14
Confróntese, Adam Drozdek Estructura de Datos y Algoritmos en Java, pp. 214 - 231;
Luis Joyanes Aguilar. Fundamentos de Programación y Robert Sedgewick Algoritmos en C++,
pp. 39-50 y L. Yedidyah, Estructuras de Datos de Datos con C y C++, pp. 249- 319.
130
Figura 3.10 Representación Gráfica de Árbol Binario
15
Leandro Siso, “Área Informática, Estructuras de Datos”. Monografías,
[Link] octubre 18 del 2008
131
Nodo Raíz para ir por las ramas, nodos internos hasta llegar a los nodos
los nodos Terminales.
Ejemplo:
Considere el árbol general T de la figura (2) , suponga que los datos
datos de los nodos de T se guardan en un Array INFO como en la
la figura [3.11] las relaciones estructurales de T se obtienen
asignando valores al puntero RAIZ y a los Arrays HIJO y HERM tal y
tal y como sigue:
) como la raíz A de T se guarda en INFO [ 2 ], se y hace RAIZ:= 2.
2.
) Como el primer hijo de A es el nodo B, guardado en INFO [3 ], se
se hace HIJO [ 2 ]:= 3. como A no tiene hermanos, se hace HERM [
HERM [ 2 ]= NULO.
) Como el primer hijo de B es el nodo E, guardado en INFO [ 15 ], se
se hace HIJO [3]:= 15. como el nodo C es el siguiente hermano de B
de B y C se guarda en INFO [ 4 ], se hace HERM [3]:=4.
132
Y así sucesivamente. La figura [3.11] da los valores finales de HIJO y
HIJO y HERM. Observe que la lista DISP de nodos vacos se mantiene en
mantiene en el primer array, hijo, donde DISP = 1.
3.4. Grafos
Definiciones.-
“Un Grafo Simple G = (V,E) consiste en un conjunto de V de vértices y un
vértices y un conjunto posiblemente vacío E de aristas (edges), siendo cada
siendo cada arista un conjunto de dos vértices de V”.17
16
Leandro Siso, Área Informática, “Estructuras de Datos”, Monografías,
[Link]
17
Adam Drozdek, op. cit., p. 376.
133
predecesores. Están formados por nodos (vértices: representan información) y
información) y por arcos (aristas: relaciones entre la información)”.18
información)”.18
Glosario Fundamental19
Árbol de Expansión de un Grafo.- Es un Subgrafo que contiene todos los
todos los vértices, pero solamente las aristas necesarias para
formar un Árbol.
Bosque.- Un Grupo de Árboles sin conectar.
Camino.- Un camino entre los vértices y y x de un grafo es una lista de
lista de vértices en la que dos elementos sucesivos están
conectados por aristas del grafo.
Camino Cerrado.- Esto ocurre si el vértice origen es igual al vértice
vértice destino.
Camino Simple.- Si todos sus vértices, con excepción del origen y destino,
y destino, son distintos. El Primero y el último pueden ser
18
Silvia Guardati, op. cit., p. 391.
19
Véase, R. Sedgewick, op. cit., pp. 452-4; S. Guardati, op. cit., pp. 395-6.; L. Yedidyah op. cit.
pp. 515- 541 y A. Drozdek, op. cit., pp. 337- 383.
134
iguales.
Camino Simple.- Es un camino en el que no se repite ningún vértice
vértice
Camino del Vértice.- Esta formado por todas las aristas que deben
deben recorrerse para llegar del origen al destino. Si se recorren
n aristas, se dice que el camino es de longitud n.
Ciclo.- Es un camino simple con la característica de que el primero y el
primero y el ultimo vértices son el mismo.
Ciclo.- Es un camino simple cerrado de longitud mayor o igual a tres.
tres.
Grafo Completo.- Es aquel que contiene todas las aristas posibles. Si
posibles. Si cada uno de sus vértices es adyacente a todos los
vértices del grafo.
Grafo Conexo.- Si hay un camino desde cada nodo hacia otro nodo del
nodo del grafo. Existe un camino simple entre cualesquiera de
sus nodos.
Grafo No Conexo.- Esta constituido por componentes conexos.
Grafos Densos.- Son aquellos que les faltan muy pocas aristas de todas
de todas las posibles.
Grafo Dirigido o grafo.- Es aquel cuyas aristas siguen cierta dirección y se
dirección y se representa como v1 => v2.
Grafos Dispersos.- Son los que tienen relativamente pocas aristas (menos
aristas (menos de V log V).
Grafo Etiquetado.- Es aquel si sus aristas tienen asociado un valor. Si este
valor. Si este es un número no negativo, se le conoce con el
nombre de peso, distancia o longitud.
Grado de un Vértice.- Se identifica como grado (v), y es el total de aristas
de aristas que tienen como extremo a v. Cuando su grado = 0,
este recibe el nombre de vértice aislado.
Grafo Sin Ciclos.- Árbol, Árbol libre, Grafo conexo sin ciclos
Lazo.- Es una arista que tiene en ambos extremos el mismo vértice. Se
vértice. Se expresa como a = (v, v).
135
Multigrafo.- Si al menos dos de sus vértices están unidos entre sí por dos
sí por dos aristas paralelas o múltiples.
Subgrafo.- Esta formado por un subconjunto de vértices y de aristas de un
grafo dado. En notación de Conjuntos: G’ de G se define como
G’ = (V’ , A’), donde V’ ≤ V y A’ ≤ A.
Búsqueda en Profundidad
Cláusula Transitiva
Todos los Caminos más cortos
Dirigidos Ordenación Topológica
Recorrido
Recorrido con Profundidad
No Dirigidos Primera Amplitud.
Ponderados Algoritmo de Kruskal.
Todos contra Todos
21
3.4.1 Definición del tipo de dato abstracto grafo
20
Cf., R. Sedgewick, op. cit., pp. 515- 547 y S GUARDATI, op. cit., pp. 393- 446
136
Los datos contienen, en algunos casos, relaciones entre ellos que no son
necesariamente jerárquicas. Por ejemplo, supongamos que unas líneas
aéreas realizan vuelos entre las ciudades conectadas por líneas como se
ve en la figura anterior (más adelante se presentarán grafos con
estructuras de datos); la estructura de datos que refleja esta relación
recibe el nombre de grafo.
Se suelen usar muchos nombres al referirnos a los elementos de una
estructura de datos. Algunos de ellos son “elemento”, “ítem”, “asociación
de ítems”, “registro”, “nodo” y “objeto”. El nombre que se utiliza depende
del tipo de estructura, el contexto en que usamos esa estructura y quien la
utiliza.
En la mayoría de los textos de estructura de datos se utiliza el término
“registro” al hacer referencia a archivos y “nodo” cuando se usan listas
enlazadas, árboles y grafos.
También un grafo es una terna G = (V,A,j ), en donde V y A son conjuntos
finitos, y j es una aplicación que hace corresponder a cada elemento de A
un par de elementos de V. Los elementos de V y de A se llaman,
respectivamente, "vértices" y "aristas" de G, y j asocia entonces a cada
arista con sus dos vértices.
Esta definición da lugar a una representación gráfica, en donde cada
vértice es un punto del plano, y cada arista es una línea que une a sus
dos vértices.
Si el dibujo puede efectuarse sin que haya superposición de líneas, se
dice que G es un grafo plano. Por ejemplo, el siguiente es un grafo plano:
Búsqueda en grafos
Para efectuar una búsqueda de los vértices de un grafo, se pueden
emplear dos estrategias diferentes:
21
Felipe Costales, “Área Informática, Estructuras de Datos”, Monografías,
[Link] octubre 18 del 2008.
22
Ver, Seymour, Lipschutz, Estructura de datos, Madrid, (Schaum) McGraw Hill, Capítulo 8,
página: 337.
137
Búsqueda en profundidad (BEP): Se comienza en cualquier vértice y en
cada paso se avanza a un nuevo vértice adyacente siempre que se
pueda. Cuando todos los adyacentes a X hayan sido visitados, se
retrocede al vértice desde el que se alcanzó X y se prosigue. Así se
consigue etiquetar (visitar) todos los vértices de la componente conexa en
que se encuentre el vértice inicial.
Esta técnica se utiliza cuando necesitamos encontrar respuesta a un
problema sobre un grafo sin condiciones de optimización.
23
Mary Luz Sánchez S., “Grafos”, material electrónico disponible en:
[Link]
seminaro/Grafos%20%20documento%[Link], recuperado el 18/10/08
138
Búsqueda en anchura (BEA)
A diferencia con la BEP ahora se visitan todos los vecinos de un vértice
antes de pasar al siguiente. Por tanto no hay necesidad de retroceder.
Una vez etiquetados todos los vecinos de un vértice X, se continúa con el
primer vértice alcanzado después de X en la búsqueda.
Esta técnica se utiliza para resolver problemas en los que se pide hallar
una solución óptima entre varias.
139
Para lograr el propósito del recorrido mínimo dentro de un grafo G, es
necesaria para nuestro caso en particular (puesto que no es la única
técnica existente), la utilización del algoritmo de WARSHALL -para el
camino mínimo-se expresa de la forma siguiente:
Sea G un grafo con m nodos, 1 , 2 , ..., m suponga que queremos
encontrar la matriz de caminos P para el grafo G. Warshall dio un
algoritmo para este propósito que es mucho más eficiente que calcular las
potencias de la matriz de adyacencia A y aplicar la proposición:
A A A ... A
2 3 m
B m
donde sea A la matriz de adyacencia y P = Pij la matriz de caminos de un grafo G
entonces, Pij = 1 si y solo sí hay un número positivo en la entrada ij de la matriz
Algoritmo de WARSHALL: 26
Un grafo dirigido G con M nodos está en memoria por su matriz adyacente
A, este algoritmo encuentra la matriz de caminos (Booleana) P del grafo
G.
1. [ Inicializar P ] repetir para I, J =1, 2, ... M:
si A[ I, J ]=0, entonces: hacer P[ I, J ]:=0;
si no: hacer P[ I, J ]:=1.
[ fin de bucle ]
2. [ Actualizar P ] repetir paso 3 y 4 para K=1, 2,
..., M:
3. repetir paso 4 para I=1, 2, ..., M:
4. repetir para J=1, 2, ..., M:
hacer P[ I, J ]:= P[ I, J ] V ( P[ I, J] ^ P[ K, J
]).
[ fin de bucle ]
[ fin de bucle paso 3 ]
[ fin de bucle paso 2 ]
5. Salir.
26
ibid., p. 322.
27
ibid., p. 324.
140
Cuando se trata de un grafo con peso G de M nodos está memoria
mediante su matriz de peso W; este algoritmo encuentra la matriz Q tal
que [ I, J ] es la longitud del camino mínimo del nodo VI al nodo VJ .
INFINITO es un número muy grande y MIN es la función del valor mínimo.
Ejemplo de Entrada:
141
2
4 49
53 82 53
2
12
13
8 14728196
81 48 30 64 71 13 91 10 65
3
21
41
81
Ejemplo de Salida:
Grafo 1
1 2 53
2 1 53 4 82
3 4 53
4 2 82 3 53
53 1 2
188 1 2 4 3
Grafo 2
1 4 81
2 6 48 7 30 8 64
3 4 71 6 13
4 1 81 3 71 8 91
5 6 10 7 65
6 2 48 3 13 5 10
7 2 30 5 65
8 2 64 4 91
213 2 6 3 4 1
81 4 1
172 8 4 1
Algoritmo de Dijkstra:
Este algoritmo construye el árbol de caminos de longitud mínima entre un
vértice fijado V y los restantes vértices en un grafo ponderado.
Observaciones:
28
Costales, Felipe, “Programación no numérica: grafos”, material en línea, disponible en:
[Link] recuperado el 18/10/08.
142
Bibliografía del tema 3
Otras fuentes
Fiestas Bancayán, Héctor, “Estructuras dinámicas de datos”, en línea,
disponible en:
[Link]/hwfiestasb/Catedras/EstructurasDatos/SESION_9_A_ESTRU
CTURAS_DINAMICAS.pdf.
143
Algoritmia: Estructuras de datos, 01/07/03, material en línea, disponible en:
[Link]
Actividades de Aprendizaje
144
Cuestionario de autoevaluación
1. Anota los Conceptos de Listas, Listas Enlazadas y Doblemente
Enlazadas.
2. Cuáles son las diferencias entre Pilas y Colas.
3. Cuáles son las diferencias entre Árboles y Grafos.
4. Cuáles son las Partes de los Árboles y los Grafos.
5. Cuáles son las formas de representar a los Árboles.
6. ¿Qué significa el término Implementar en Programación?
7. ¿Cuáles son los tipos de Árboles?
8. ¿Cuáles son los tipos de Grafos?
9. Anota el Procedimiento para crear una Lista Nula.
10. Cuando el tamaño de Archivo crece, ¿Qué Estructuras se aplican para
realizar búsquedas en él?
Examen de Autoevaluación
145
Bibliografía básica
Allen Weiss, Mark, Estructura de datos y algoritmos, México, Addison-Wesley
Iberoamericana, 1995, 180 pp.
146
Sitios Web
Ana Ma. Toledo Salinas, “¿Qué es pilas?”, material en línea, disponible en:
[Link]
[Link]
147
Respuestas a los Exámenes de Autoevaluación
Informática II
148