Está en la página 1de 106

Estructura de datos

UNIVERSIDAD DE GUADALAJARA
Departamento de Ciencias Computacionales
Academia de Computacin Bsica
Estructura de datos

1. Representacin de datos e introduccin a el lenguaje C


1.1 Tipos de datos
1.2 Estructura de un programa
1.3 Operadores aritmticos
1.4 Operadores relacionales y lgicos
1.5 Estructuras de control

2. Ordenamientos y bsquedas
2.1 Arreglos
2.2 Ordenamiento
2.2.1 Burbuja y Burbuja Mejorado (Bubble Sort)
2.2.2 Ordenamiento por Insercin Directa (InsertSort)
2.2.3 Ordenamiento por seleccin (SelectSort)
2.2.4 Ordenamiento Shell (ShellSort)
2.2.5 Ordenamiento Mezcla (MergeSort)
2.3 Bsquedas
2.3.1 Bsqueda Secuencial
2.3.2 Bsqueda Binaria

3. Estructuras de datos lineales, representaciones secuenciales.


3.1 Conceptos fundamentales: Tipo de Dato, Tipo de Dato
Abstracto, Estructura de datos, Registros.
3.2 T.D.A. Lista
3.2.1 Modelo Matemtico
3.2.2 Operaciones
3.2.3 Implementacin con Arreglos
3.3 T.D.A Pila
3.3.1 Modelo Matemtico
3.3.2 Operaciones
3.3.3 Implementacin con arreglos
3.3.4 Notacin Polaca
3.3.5 Recursividad
3.3.5.1Ordenamiento Rpido (Quick Sort)
3.3.5.2Torres de Hanoi
3.4 T.D.A Cola
3.4.1 Modelo Matemtico
3.4.2 Operaciones

Pag 1
Estructura de datos

3.4.3 Implementacin con arreglos


3.5 Concepto de Apuntador
3.5.1 Listas implementadas con cursores
UNIVERSIDAD DE GUADALAJARA
Departamento de Ciencias Computacionales
Academia de Computacin Bsica
Estructura de datos

4. Estructuras de Datos lineales, representaciones ligadas.


4.1 Teora de lista ligada
4.2 Listas con encabezado
4.2.1 Lista simplemente ligada
4.2.2 Lista simplemente ligada circular
4.2.3 Lista doblemente ligada
4.2.4 Lista doblemente ligada circular
4.3 Listas sin encabezado
4.3.1 Lista simplemente ligada
4.4 Pilas
4.5 Colas

5. Estructuras de datos no lineales, representaciones secuencial y


ligada.
5.1 Teora general de rboles
5.2 rboles binarios
5.2.1 T.D.A. rbol Binario
5.2.1.1Representaciones secuenciales
5.2.1.2Representacin ligada
5.2.2 Recorridos En-Orden, Pre-Orden y Post-Orden

6. Grafos.

Pag 2
Estructura de datos

Representacin de datos e Introduccin a lenguaje C

Tipos de datos.
Los diferentes objetos de informacin con los que un programa C trabaja se
conocen colectivamente como datos. Todos los datos tienen un tipo asociado
con ellos

La asignacin de tipos a los datos tiene dos objetivos principales:

1. Detectar errores de operaciones en programas.


2. Determinar como ejecutar las operaciones.

El tipo de un dato determina la naturaleza del conjunto de valores que puede


tomar una variable.

Tipos predefinidos
Tipo Largo Rango
unsigned char 8 bits 0 to 255
Char 8 bits -128 to 127
enum 16 bits -32,768 to 32,767
unsigned int 16 bits 0 to 65,535
short int 16 bits -32,768 to 32,767
int 16 bits -32,768 to 32,767
unsigned long 32 bits 0 to 4,294,967,295
long 32 bits -2,147,483,648 to 2,147,483,647
float 32 bits 3.4 * (10**-38) to 3.4 * (10**+38)
double 64 bits 1.7 * (10**-308) to 1.7 * (10**+308)
long double 80 bits 3.4 * (10**-4932) to 1.1 *
(10**+4932)

Tipos de datos definidos por el usuario

Constantes
Las constantes se presentan en expresiones como

c= Y
2* (x + 7) 33
kilmetros = 1.609344 * millas
Adems de tener un valor, una constante tambin tiene un tipo de dato
inherente. Los tipos de datos posibles en las constantes dependen de la

Pag 3
Estructura de datos

mquina. Lo normal es que no existan constantes de los tipos short,


unsigned y float. Aunque esto puede ser distinto en mquinas pequeas, se
supondr que la situacin normal prevalece.

El tipo de dato asociado a una constante depende de la forma en que sta se


escribe. La siguiente lista contiene todos los tipos de datos que admiten
constantes, asi como algunos ejemplos:

int 0 77 5013
long 0L 77L 5013L
double 0.003 1.0|| 0.5013e-2
char a b c
cadena est es una constante de cadena

Una expresin constante slo contiene constantes, y el compilador C las


evaluar en el momento de la compilacin, en lugar de hacerlo en la ejecucin.
Por ejemplo, en la proposicin:

Segundos = 60 * 60 * 24 * das;
Segundos = 86400 * das;

Uso de #define y de #include


El compilador C tiene un preprocesador incorporado. Si las lineas

#define LIMITE 100


#define PI 3.14159

se encuentran en un archivo que se est compilando el preprocesador cambia


primero todos los identificadores LIMITE por 100 y todos los PI por 3.14159,
excepto los que estn en cadenas entre comillas. Una lnea #define puede
estar en cualquier lugar del programa, pero debe empezar en la columna 1 y
solo tendr efecto en las lneas de archivo que le sigue. El preprocesador solo
cambiar los identificadores que se escriben con maysculas.
Si en un programa hay una lnea como:

#include mi_archivo

Se trata tambin de una orden al procesador. Una linea #include puede


presentarse en cualquier parte de un archivo, pero debe empezar en la
columna uno. Las comillas que encierran el nombre de archivo son necesarias.
El efecto de esta lnea es incluir en ese punto de la codificacin una copia del
contenido del archivo mi_archivo

Usos de printf() Y scanf()


La funcin printf() se usa para la salida; en forma similar; la funcin scanf() se
usa para la entrada. Estas funciones no son parte del lenguaje C, sino del
sistema C; residen en una biblioteca estndar y estn disponibles para usarlas

Pag 4
Estructura de datos

donde quiera que haya un sistema C. Las f de printf() y de scanf() significan


con formato. Ambas funciones tienen una lista de parmetros con dos partes:

Cadena_de_control y lista_de_argumentos

La primera es una cadena y puede contener especificaciones de conversin de


formatos. Una especificacin de conversin se inicia con un carcter % y
termina con un carcter de conversin; por ejemplo, en el formato %d la letra
de es el carcter de conversin. Este formato se emplea para imprimir el valor
de una expresin como un entero decimal. Para imprimir las letras ABC en la
pantalla, podra usarse la proposicin:

printf(ABC);

otra manera de hacer esto es mediante la proposicin:

printf(%s,ABC);

El formato %s hace que el argumento ABC se imprima en el formato de una


cadena de caracteres. Esto mismo puede realizarse tambin con la proposicin:

printf(%c%c%c,A,B,C);
Los apstrofos que encierran cada letra se emplean para designar constantes
de caracteres de acuerdo con esto, A es la constante de carcter que
corresponde a la letra A mayscula. El formato %c imprime el valor de una
expresin como un carcter.

printf()
Carcter
de
conversi Cmo se describe el argumento
n correspondiente
C Como carcter
D Como un entero decimal
E Como nmero de punto flotante en notacin
cientfica
F Como un nmero de punto flotante
G En el formato-e o el formato-f el que sea ms
corto
S Como una cadena de caracteres

La funcin scanf() es anloga a la funcin printf(), pero se usa para la


entrada. Su primer argumento es una cadena de control con formatos que
corresponden a las diferentes formas en que pueden interpretarse los
caracteres de la entrada como valores para los diferentes tipos de variables. La
lista de argumentos est formada por direcciones de variables. El smbolo &
representa el operador de direccin; por ejemplo, la proposicin

Pag 5
Estructura de datos

scanf(%d,&x);

Contiene el formato %d, el cual provoca que scanf() interprete los caracteres
de entrada como un entero decimal y que coloque el valor en la direccin x.

Carcter
de
conversi Los caracteres de la entrada se convierten en
n
c Un carcter
d Un entero decimal
f Un nmero de punto flotante (float)
lf Un nmero de punto flotante (double)
s Una cadena

Funciones
La definicin de una funcin consiste en un encabezamiento y un cuerpo. De
manera explicita, podramos decir que es un bloque o una proposicin
compuesta. Si hay declaraciones deben aparecer al inicio del bloque antes de
cualquier proposicin ejecutable. El encabezamiento puede ser tan slo un
identificador y unos parntesis. Un ejemplo sencillo es :

/****Una funcin con un encabezamiento y un cuerpo sencillos ***/

escr_direcc() /* encabezamiento*/
/* el cuerpo es cuanto est entre llaves */
{
printf(\n\n%s\n%s\n%s\n%s\n%s\n\n,
********************,
** SAN NICOLAS *,
** El POLO NORTE *,
** LA TIERRA *,
*******************);
}
Donde quiera que un programa identifique a esta funcin, la expresin har
que se invoque la funcin.

Una definicin de funcin tiene un nombre y unos parntesis que contienen


cero o ms parmetros y un cuerpo. Para cada parmetro debe de haber una
declaracin correspondiente antes del cuerpo, cualquier parmetro que no se
declar se considera un int por omisin, a estos parmetros se les llama
parmetros formales de funcin. El cuerpo de la funcin es un bloque o una
proposicin compuesta, y tambin puede contener declaraciones, todas las
variables declaradas en el cuerpo de una funcin se dice que son variables
locales. Otras variables no declaradas ni como argumentos ni en el cuerpo de

Pag 6
Estructura de datos

la funcin se consideran como globales a la funcin y deben definirse en


forma externa.

La proposicin return
La proposicin return puede incluir u omitir una expresin.

Proposicin _return ::= return; \ return expresin;

Algunos ejemplos son:

return;
return (377);
return (a * b);
return (++x);
return ++x;

Invocacin y llamada por valor


La funciones se declaran como objetos individuales que no pueden anidarse.
Por tanto un programa consiste en una serie de una o ms definiciones de
funciones. Estas funciones estn disponibles para usarse en el cuerpo de las
dems. Pueden emplearse donde quiera que sea apropiado la expresin del
tipo dado para el especificador de tipo de la funcin ( Recurdese que si un
especificador de tipo de una funcin est ausente, entonces por omisin es
int.)

Las funciones se invocan al escribir su nombre y una lista adecuada de


argumentos entre parntesis. Todos los argumentos pasan con una llamada
por valor. Se evala cada argumento y su valor se utiliza de forma local en
lugar del parmetro formal.

Invocacin y llamada por referencia


En la practica moderna de la programacin, el empleo de una variable externa
global como va de comunicacin entre una funcin y el medio que la llama se
considera indeseable. El proceso de declarar un parmetro de funcin como
apuntador y utilizar en forma consistente el parmetro desreferenciado en el
cuerpo de la funcin se le denomina llamada por referencia. Cuando se pasa
una direccin como argumento, puede hacerse que la funcin cambie el valor
de la variable direccionada en el medio que la llama.

Para indicar que un parmetro de funcin es pasado por referencia, slo


coloque ampersand (&) despus del tipo del parmetro en el prototipo de
funcin.

int ordena(int &lista)

La invocacin de una funcin significa


1. Se evala cada expresin de la lista de argumentos.

Pag 7
Estructura de datos

2. Al principio del cuerpo de la funcin se asigna el valor de la expresin a


su parmetro formal correspondiente.
3. Se ejecuta el cuerpo de la funcin.
4. Si se ejecuta una proposicin return, el control regresa al medio que
hizo la llamada.
5. Si la proposicin return incluye una expresin, el valor de la expresin
se convierte (si es necesario) al tipo dado por el especificador de tipo de
la funcin y ese valor tambin regresa al medio que hizo la llamada.
6. Si no hay una proposicin return el control regresa al medio que hizo la
llamada cuando se llega al final del cuerpo de la funcin.
7. Si se ejecuta una proposicin return que no tiene una expresin o si no
hay una proposicin return, entonces no regresa ningn valor til al
medio que hizo la llamada

Especificador de tipo void


El especificador de tipo void se usa para declarar funciones que no se pretende
que devuelvan valores. Emplear una funcin void en una expresin que
requiere un valor hace que el compilador produzca un mensaje de error.

Estructura de un programa
La base de la programacin en C es la funcin, pues constituye una parte
fundamental de la codificacin en el proceso de solucin de problemas. Todas
las funciones estn en el mismo nivel externo; no se pueden anidar unas en
otras. Un programa contiene una o ms funciones en uno o ms archivos. Una
de las funciones es main() donde se inicia la ejecucin del programa. El resto
de las funciones se llaman desde main() y desde el interior de otras

main()
Todo programa tiene una funcin main donde inicia la ejecucin; los
parntesis que van despus de main indican al compilador que se trata
de una funcin

{
Las llaves encierran al cuerpo de la funcin; tambin se usan para
agrupar varias proposiciones.

printf()

El sistema contiene una biblioteca estndar con funciones que pueden


usarse en programas; est es una funcin de la biblioteca estndar que
imprime en pantalla.

de mar en mar C\n

En C una constante de cadena es una serie de caracteres entre comillas.


Esta cadena es un argumento de la funcin printf() y controla lo que se
escribir los dos caracteres \n al final de esta cadena representan un

Pag 8
Estructura de datos

carcter sencillo llamado nuevalnea; ste es un carcter que no se


imprime, su efecto es hacer que el cursor avance hacia una lnea nueva.

}
La llave derecha hace pareja con la llave de una funcin y da por
terminada la funcin main

Identificadores.
Los identificadores representan objetos de un programa ( constantes,
variables, tipos de datos procedimientos, funciones, unidades, programas y
campos de registros). Un identificador es una secuencia de caracteres que
pueden ser de cualquier longitud, pero slo los primeros 63 caracteres son
significativos.

Un identificador se caracteriza por estas reglas:


1. Debe comenzar con una letra (A a Z maysculas o minsculas) y no
puede contener blancos.
2. Letras dgitos y caracteres subrayados estn permitidos despus del
primer carcter.
3. No se puede utilizar una palabra reservada como identificador, sin
embargo, los identificadores estndar se pueden redefinir

Palabras reservadas.
Las palabras reservadas en C tienen un significado especial y no se pueden
utilizar para otros propositos

auto break case char


const continue default do
double else enum extern
float for goto if
int long register return
short signed sizeof static
struct switch typedef union
unsigned void volatile while

Operadores aritmticos
Los operadores aritmticos (+,-,*) pueden ser utilizados con tipos enteros o
reales, si ambos son enteros el resultado es entero si uno es real el resultado
es real.

2+3 =5
2 +3.0 = 5.0
2.0 + 3 = 5.0
2.0 + 3.0 = 5.0

Pag 9
Estructura de datos

Operador Significado Ejemplo Resultado


+ Suma a+b Suma de a y b
- Resta ab Diferencia de a y b
* Multiplicacin a*b Producto de a por b
/ Divisin a/b Cociente de a por b
% Mdulo a%b Resto de a por b

Operadores Incrementales y decrementales


++ ++a Se incrementa a en 1 y a continuacin se
utiliza el nuevo valor de a en la expresin en
la cual resida a.
++ a++ Utiliza el valor actual de a en la expresin en
la cual reside a, y despus se incrementa a en
1
-- --b Se decrementa b en 1 y a continuacin se
utiliza el nuevo valor de b en la expresin en
la cual reside b.
-- b-- Se utiliza el valor actual de b en la expresin
en la cual reside b, y despus se decrementa
a b en 1

Operadores de asignacin
+= C += 7 c = c +7
-= D -= 4 d=d-4
*= e*=5 e = e* 5
/= F /= 3 f=f/3
%= g %= 9 g=g%9

Prioridad de operadores
Cuando una expresin aritmtica se evala, el resultado es siempre un
nmero. Cuando en una expresin aparecen dos o ms operadores, qu
operacin se realiza primero?.

Pag 10
Estructura de datos

Reglas de evaluacin de expresiones.

1. Todas las subexpresiones entre parntesis se evalan primero. Las subexpresiones con
parntesis anidados se evalan de dentro a afuera; el parntesis ms interno se evala
primero.
2. Prioridad de operaciones. Dentro de una misma expresin o subexpresin, los
operadores se evalan en el siguiente orden:

*,/,% primero
+,- ltimo
3. Regla asociativa izquierda. Los operadores en una misma expresin o subexpresin con
igual nivel de prioridad (tal como * y /) se evalan de izquierda a derecha.

El orden de las operaciones es:


x -(A + B % 2) + y * z

%
*

Pag 11
Estructura de datos

Escritura de formulas matemticas en C.


En C las formulas matemticas se deben escribir en formato lineal. Esto obliga
al uso frecuente de parntesis que indiquen el orden de evaluacin correcto de
los operadores.

M = y2 y1 m = (y2 - y1) / (x2 x1)


x2 x1

Operadores relacionales y lgicos.

Operadores de relacin
Se utilizan para expresar condiciones y describen una relacin entre dos
valores.

Operador Significado Equivalente matemtico


< Mayor que >
> Menor que <
== Igual que =
>= Mayor o igual que
<= Menor o igual que
!= Distinto a

Los operadores de relacin se utilizan en condiciones cuyo formato tiene una


de las siguientes formas:

1. variable operador relacional variable


2. constante operador relacional constante

El resultado de la expresin lgica es un valor tipo lgico: verdadero o falso.

Expresin valor
6.7315 < 6.7342 0
-124.2 < 0.003 1
8 == 8.0 1
A < B 1
Z < H 1

Operadores lgicos
Las expresiones lgicas pueden combinarse para formar expresiones ms
complejas utilizando los operadores lgicos: &&, || y !. Estos operadores se
utilizan con constantes lgicas de forma similar al modo en que los operadores
aritmticos se utilizan con las constantes numricas, estos operadores trabajan
con operandos que son expresiones lgicas.

[operando1] operador operando2

Pag 12
Estructura de datos

Segn el tipo de operador puede no existir

Operador &&

Operando1 Operando2 Operando1 && Operando2

1 1 1
1 0 0
0 1 0
0 0 0

Operador ||
Operando1 Operando2 Operando1 || Operando2

1 1 1
1 0 1
0 1 1
0 0 0

Operador !
Operando1 ! Operando1

1 0

Orden de evaluacin de operadores lgicos.


1. !
2. &&
3. ||

Operador Prioridad
! Mas alta (se evala primero)
*,/,%,&& -
+,-,|| -
< , <= , == , <> , >= , > Mas baja se evala al ultimo
Si existen parntesis las Interior se evalan primero
expresiones de su

Pag 13
Estructura de datos

((x * 2 > y - 3) || (x > y - 1)) && (y <

* - -

<

5)

> >

||

&&

Estructuras de control
Selectivas.
La sentencia if.
Sentencia de control que dirige a la computadora para ejecutar una sentencia
si la expresin es verdadera, y otra en caso de ser falsa.

Formato

if (expresin lgica)
proposicion1
else Pag 14
proposicin2
proposicin siguiente
Estructura de datos

En muchos casos se desea que una determinada accin slo ejecute si una
cierta condicin es verdadera y no realizar ninguna accin si la condicin es
falsa.

if (condicin)
sentencia

Sentencia compuesta.

{
sentencia 1;
sentencia 2;
sentencia 3;
-
-
sentencia n;
}

Ejemplo:

#include <stdio.h>

main()
{
if (grado >=90)
Printf(A\n);
else if (grado >=80)
Printf(B\n);
else if (grado >=70)
Printf(C\n);
else if (grado >=60)
Printf(D\n);
else if (grado >=50)
Printf(F\n);
}

La sentencia switch
La sentencia switch se utiliza para elegir entre diferentes alternativas.

switch (expresion_entera){
case 1 : sentencia1;break;
case 2 : sentencia2;break;
case 3 : sentencia3;break;
-
-
case n : sentencian;break;
[default :sentencia x]
} {case}
Pag 15
Estructura de datos

El siguiente ejemplo utiliza switch para contar el nmero de cada distinta


letra de calificacin que los estudiantes alcanzarn en un examen.

/*Contando calificaciones*/
#include <stdio.h>

main()
{
int Letra;
int acontador = 0, bcontador =0, contador = 0,
dcontador =0, fcontador = 0;

printf(Mete la Letra de su calificacin.\n);


printf(Mete el carcer EOF para finalizar las entradas.\n);

while ((Letra = getchar() ) != EOF) {


switch (Letra) {
case A: case a:
acontador;
break;
case B: case b:
bcontador;
break;
case C: case c:
ccontador;
break;
case D: case d:
dcontador;
break;
case F: case f:
fcontador;
break;
default
{printf( Letra de entrada incorrecta);
printf( Meta una nueva calificacin.\n);}
break;
}
}
printf(\nTotales de cada calificacion\n);
printf(A: %d\n, acontador);
printf(B: %d\n, bcontador);
printf(C: %d\n, ccontador);
printf(D: %d\n, dcontador);
printf(F: %d\n, fcontador);

return 0;

Pag 16
Estructura de datos

EOF es una constante simblica entera, definida en el archivo de cabecera


<stdio.h>

Mete la letra de su c
Mete el carcer EOF para finalizar las entradas
A
B
C
C
A
D
F
C
E
Letra de entrada incorrecta meta una nueva calificacin
D
A
B
Totales de cada calificacin
A: 3
B: 2
C: 3
D: 2
F: 1

La sentencia while.

La estructura repetitiva while (mientras) es aquella en la que el nmero de


iteraciones no se conoce por anticipado y el cuerpo del bucle se repite mientras
se cumple una determinada condicin.

while (expresin lgica)


{
Sentencia 1
-
-
Sentencia n
}

1. La condicin (expresin lgica) se evala antes y despus de cada


ejecucin del bucle. Si la condicin es verdadera se ejecuta el bucle y si
es falsa el control pasa a la sentencia siguiente al bucle.
2. Si la condicin se evala falso cuando se ejecuta el bucle por primera
vez el cuerpo del bucle no se ejecutar nunca.

Pag 17
Estructura de datos

3. Mientras la condicin sea verdadera el bucle se ejecutar, esto significa


que el bucle se ejecutar indefinidamente a menos que algo en el
interior del bucle modifique la condicin haciendo que su valor pase a
falso.

Ejemplo: El promedio de la clase es igual a la suma de calificaciones dividida


por el nmero de alumnos. El algoritmo para resolver este problema en una
computadora, debe introducir cada una de las calificaciones, ejecutar el clculo
de promedio e imprimir el resultado.
#include <stdio.h>
main()
{
int, contador, grado, total, promedio;
total = 0; /*inicializacion*/
counter = 1;
/*procesamiento*/
while (counter <=10) {
printf (Mete grado:);
scanf(%d,&grado);
total = total +grado;
counter++;
}
promedio = total / 10;
printf(El promedio de la clase es %d\n, promdio);
return 0;
}

La sentencia do while.
Una variante de la sentencia while es la sentencia do while, esta sentencia
especifica un bucle condicional que se repite hasta que la condicin se hace
falsa.

do
Sentencia 1
-
-
-
sentencia n
while (expresin lgica);

1. La condicin se evala al final del bucle, despus de ejecutarse todas las


sentencias
2. Si la expresin lgica es verdadera, se vuelve a repetir el bucle y se
ejecutan todas las sentencias.

Pag 18
Estructura de datos

3. Si la expresin lgica es verdadera, se sale del bucle y se ejecuta la


siguiente sentencia a while.
4. La sintaxis no requiere {}.

El siguiente ejemplo utiliza una estructura do while para imprimir los


nmeros del 1 al 10.

#include <stdio.h>
main()
{
int contador = 1;
do {
printf (%d , contador);
}
while (++contador <= 10);
return 0;
}

La sentencia for.
Esta sentencia requiere que sepamos por anticipado el nmero de veces que se
ejecutan las sentencias del interior del bucle.

for ( expresion1; expresion2; expresion3 )


{
Sentencia 1;
Sentencia 2;
-
-
-
Sentencia n
}

#include <stdio.h>
main()
{
int contador;
/*inicializacin, condicin de repeticin, e incremento*/
for (contador=1; contador <=10; contador++)
printf(%d\n,contador);
return 0;
}

ejemplo utillizando la estructura for

a) Vare la variable de control de 1 a 100 en incrementos de 1.


for (i=1;i<=100; i++)

Pag 19
Estructura de datos

b) Vare la varible de control de 100 a 1 en incrementos de 1


(decrementos de 1).
for ( i = 7; i <=77;i+=7)
c) Variar la variable de control a lo largo de la siguiente secuencia de
valore: 2, 5, 8, 11, 14, 17, 20.
for (j= 2; j <=20; j+=3)
d) Variar la variable de control de acuerdo a la siguiente secuencia de
valores: 99, 88, 77, 66, 55, 44, 33, 22, 11, 0.
for (j=99;j>=0;j-=11)
Los siguientes ejemplos proporcionan aplicaciones simples de la estructura for.
El programa siguiente utiliza la estructura for para sumar todos los enteros
pares desde 2 hasta 100

/*Sumacin con for*/


#include <stdio.h>

main()
{
int sum = 0, number;
for (number = 2; number <=100; number +=2)
sum += number;
printf(Sum es %d\n,sum);
return 0;
}

Sum es 2550

Ordenamientos y bsquedas

Arreglos
Un arreglo es una estructura de datos en la que se almacena una coleccin de
datos del mismo tipo, es una lista de un nmero finito n de elementos del
mismo tipo que se caracteriza por:
1. Almacenar los elementos del arreglo en memoria continua.
2. Tener un nico nombre de variable que representa todos los elementos y
estos a sus vez se diferencian por un ndice o subndice.
3. Acceso directo o aleatorio a los elementos individuales del arreglo.

Para referirse a una posicin en particular o elemento dentro del arreglo,


especificamos el nombre del arreglo y el nmero de posicin del elemento
particular dentro del mismo.

Los arreglos se clasifican en:


Unidimensionales (vectores o listas)

Pag 20
Estructura de datos

Multidimensionales (tablas o matrices)

En la figura se muestra un arreglo de nmeros reales llamado x . Este arreglo


contiene 8 elementos. Cualquiera de estos elementos puede ser referenciado
dndole el nombre del arreglo seguido del nmero de posicin de dicho
elementos en particular en parntesis cuadrados o corchetes ([]). El primer
elemento de cualquier arreglo es el elemento cero. Entonces el primer
elemento de un arreglo x se conoce como x[0], el segundo como x[1], el
sptimo como x[6] y en general, el elemento de orden i del arreglo x se
conoce como x[i-1]. Los nombres de los arreglos siguen las mismas reglas
convencionales que los dems nombres de la variables.
Ejemplo:

float x[8];

x[0] x[1] x[2] x[3] x[4] x[5] x[6] x[7]


45.2 12.0 3.45 4.32 0.31 513. 2.65 13.0
1 4 6 4

El nmero de posicin que aparece dentro de los corchetes se conoce ms


formalmente como subndice. Un subndice debe de ser un entero o una
expresin entera. Si un programa utiliza una expresin como subndice,
entonces la expresin se evala para determinar el subndice. Por ejemplo, si a
= 3 y b = 4, entonces el enunciado

C[a + b] +=2;

Aade 2 al elemento del arreglo c[11]. Note que un nombre de arreglo con
subndice es un Ivalue que puede ser utilizado al lado izquierdo de una
asignacin.

Examinaremos el arreglo x. Sus 8 elementos se conocen como x[0], x[1],


x[2],....,x[7]. El valor de x[0] es 45.21, el valor de c[1] es 12.0, el valor de
c[2] es 3.45, el valor de x[6] es de 2.65 y el valor de x[7] es 13.04.

Para imprimir la suma de los valores contenidos en los primeros tres elementos
del arreglo x, escribiramos

Printf (%f,x[0] +x[1] + x[2]);


Para dividir el valor del sptimo elemento del arreglo x entre 2 y asignar el
resultado a la variable c escribiramos

C= x[6] / 2;

Como declarar arreglos


Los arreglos ocupan espacio en memora. El programador especifca el tipo de
cada elemento y el nmero de elementos requerido por cada arreglo, de tal

Pag 21
Estructura de datos

forma que la computadora pueda reservar la cantidad apropiada de memora.


Para indicarle a la computadora que reserve 12 elementos para el arreglo
entero c, la declaracin

int c[12];
es utilizada. La memoria puede ser reservada para varios arreglos dentro de
una sola declaracin, Para reservar 100 elementos para el arreglo entero b y
27 elementos para el arreglo entero x, se puede utilizar la siguiente
declaracin

int b[100], x[27];


Los arreglos pueden ser declarados para que contengan otro tipo de datos. Por
ejemplo un arreglo de tipo char puede ser utilizado para almacenar una cadena
de caracteres.

Ejemplos utilizando arreglos


El programa siguiente utiliza la estructura de repeticin for para inicializar los
elementos de un arreglo entero de diez elementos n a ceros, e imprime el
arreglo en formato tabular.
El enunciado printf muestra los encabezados de columnas de las dos columnas
impresas en la estructura for.

/* inicializa el arreglo*/
#include <stdio.h>
main()
{
int n[10], i;
for (i = 0; i <= 9; i++) /*inicializa el arreglo*/
n[i] = 0;
printf(%s%13s\n, Elemento, Value);
for(i= 0; i <= 9; i++) /*imprime arreglo*/
printf(%7d%13d\n,i, n[i]);
return 0;
}

Elemento Value
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0

Pag 22
Estructura de datos

El programa siguiente suma los valores contenidos en un arreglo entero a de


doce elementos. El enunciado en el cuerpo del ciclo for se ocupa de la
totalizacin .

/* Calcula la suma de los elementos del arreglo*/


#include <stdio.h>
#define SIZE 12

main()
{
int a [SIZE] = {1, 3, 5, 4, 7, 2, 99, 16, 45, 67, 89, 45}
i, total =0;
for (i=0; i <=SIZE 1;i++)
total += a[i]
printf(El total del valor de los elementos del arreglo es %d\n,total);
return 0;
}

El total del valor de los elementos del arreglo es 383

El siguiente ejemplo utiliza arreglos para resumir los resultados de datos


recopilados en una investigacin . Deseamos resumir el nmero de respuestas
de cada tipo (es decir del 1 hasta el 10). El arreglo respuestas es un arreglo de
40 elementos correspondiente a las respuestas de los alumnos. Utilizamos un
arreglo de 11 elementos, frecuencia para contar el nmero de ocurrencias de
cada respuesta. Ignoramos el primer elemento frecuencia[0], por que es ms
lgico tener un incremento de respuesta 1 frecuencia[1] que frecuencia[0].
Esto nos permite utilizar cada respuesta directa como subndice en el arreglo
frecuencia.

El primer ciclo for toma las respuestas del arreglo respuestas una por una e
incrementa uno de los diez contadores (frecuencia[1] hasta frecuencia [10] )
en el arreglo frecuencia. El enunciado clave es
++frecuencia[respuestas[pregunta]
Este enunciado incrementa el contador frecuencia apropiado, dependiendo del
valor de respuestas[pregunta]. Por ejemplo cuando la variable del contador
pregunta es 0, respuestas[pregunta] es 1 y por lo tanto, ++frecuencia
[respuesta[pregunta]]; se interpreta en realidad como

++frecuencia[1];

#include <stdio.h>
#define RESPUESTAS_SIZE 40
#define FRECUENCIA_SIZE 11

Pag 23
Estructura de datos

main()
{
int answer, rating;
int respuestas[RESPUESTAS_SIZE] = {1, 2, 6, 4, 8, 5, 9, 7, 8, 10, 1, 6,
3, 8, 6, 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7, 5, 6, 6, 5, 6, 7, 5, 6, 4, 8, 6,
8, 10);
int frecuencia[FRECUENCIA_SIZE] = {0};
for (pregunta = 0; pregunta <= RESPUESTA_SIZE 1; answer++)
++frecuencia[respuestas[pregunta]];
printf (%s%17s\n,Rating, Frecuencia);

for (rating = 1; rating <= FRECUENCIA_SIZE 1; rating++)


printf(%6d%17d\n, rating, frecuencia[rating]);
return 0;
}

Rating Frecuencia
1 2
2 2
3 2
4 2
5 5
6 11
7 5
8 7
9 1
10 3

Cmo pasar arreglos a funciones


Para pasar cualquier argumento de arreglo a una funcin, especifique el
nombre del arreglo, sin corchete alguno. Por ejemplo, si el arreglo
Temperaturas ha sido declarado como

int Temperaturas[24];

El enunciado de llamada a la funcin


ModificaArreglo(Temperaturas, 24);

Pasa el arreglo Temperaturas y su tamao, a la funcin ModificaArreglo, al


pasar un arreglo esl tamao del arreglo se pasa a una funcion, de tal forma
que pueda procesar el nmero especifico de elementos incluidos en dicho
arreglo.

C pasa de forma automtica los arreglos a las funciones utilizando simulacin


de llamadas por referencia las funciones llamadas pueden modificar los
valores de los elementos en los arreglos originales de los llamadores. El
nombre del arreglo de hecho es la direccin del primer elemento de dicho

Pag 24
Estructura de datos

arreglo. Dado que ha sido pasada la direccin inicial del arreglo, la funcin
llamada sabe precisamente dnde est el arreglo almacenado. Por lo tanto,
cuando en su cuerpo de funcin, la funcin llamada modifica los elementos del
arreglo, est modificando los elementos reales del arreglo, en sus
localizaciones de memoria originales.

#include <stdio.h>
main()
{
char array[5];
printf ( array = %p\n&array[0] = %p\n, array, &array[0]);
return 0;
}

array = FFF0
&array = FFF0

Arreglos multidimensionales
Aunque los elementos de los arreglos se almacenan en forma contigua, con
frecuencia resulta til imaginar que un arreglo bidimensional es un conjunto
rectangular de elementos con filas y columnas. Por ejemplo si se declara:

int b[3] [5];

Puede imaginarse que los elementos del arreglo estn ordenados de la manera
siguiente:
Col 1 Col 2 Col 3 Col 4 Col 5

Fila 1 B[0] [0] B[0] [1] B[0] [2] B[0] [3] B[0] [4]
Fila 2 B[1] [0] B[1] [1] B[1] [2] B[1] [3] B[1] [4]
Fila 3 B[2] [0] B[2] [1] B[2} [2] B[2] [3] B[2] [4]

Los arreglos de doble subndice se referencian con los subndice dentro de un


mismo par de corchetes separando por comas los subndices.

t[x,y];
y no
t[x] [y];

Los arreglos de doble subndice pueden ser declarados e inicializados de la


siguiente manera
int b[2][2] = {{1,2},{3,4}};

Muchas manipulaciones comunes con arreglos utilizan estructuras de repeticin


for. Por ejemplo, la siguiente estructura define todos los elementos en el tercer
rengln del arreglo a

Pag 25
Estructura de datos

int a[4][4];
for (column = 0;column 3; column++)
A[2] [column] =0
Especificamos el tercer rengln, por lo tanto, sabemos que el primer subndice
ser siempre 2 (o es el primer rengln y 1 el segndo). El ciclo for vara slo
en el segundo subndice (es decir, el subndice de columnas). La estructura for
anterior es equivalente a los enunciados de asignacin siguientes:

a[2][0] = 0;
a[2][1] = 0;
a[2][2] = 0;
a[2][3] = 0;

En el arreglo a, la siguiente estructura for anidada determina el total de todos


los elementos.

Total = 0;
for (renglon =0; renglon <=3; renglon++)
for (column = 0;column <=3; column++)
total +=[renglon] [column];

Cadenas
Una cadena es una secuencia de caracteres encerrada entre comillas .
Obsrvese que el smbolo es un solo carcter, no dos. Si el carcter ha de
aparecer en una cadena, debe ir precedido del carcter \. A continuacin se
presentan varios ejemplos de cadenas.

una cadena de texto


/*una cadena de caracteres blancos */
z
,.1kM87tt - basura
una cadena con \ comillas incorporadas
a = b + c; x =sin(y); nada se ejecuta
/* la cadena nula */

Las cadenas son arreglos unidimensionales de tipo char que tinen varas
caractersticas nicas. Un arreglo de caracteres puede ser inicializado utilizando
una literal de cadena. Por ejemplo, la declaracin

char string1[] = first;


Inicializa los elementos de la cadena string1 a los carcteres individuales de la
literal de cadena first. El tamao del arreglo string1 en la declaracin anterior
queda determinada por el compilador, basado en la longitud de la cadena. Es
importante hacer notar que first contiene 5 caracteres, ms un carcter
especial de terminacin de cadena, conocido como carcter nulo. Entonces el
arreglo string1 de hecho contiene 6 elementos, la representacin de la

Pag 26
Estructura de datos

constante de caracteres del carcter nulo es \0. En C todas las cadenas


terminan con este carcter. Un arreglo de caracteres representando una
cadena debera declararse siempre lo suficientemente grande, para contener el
nmero de caracteres de la caden incluyendo el carcter nulo de terminacin..
Los arreglos de caracteres tambin pueden ser inicializados con constantes
individuales de caracteres en una lista de inicializacin. La declaracin anterior
es equivalente a

char string1[] = {f,i,r,s,t,\0};

Dado que la cadena es un arreglo de caracteres podemos tener acceso directo


a los caracteres individuales de una cadena, utilizando la notacin de
subndices de arreglos. Por ejemplo, string1[0], es el carcter f y string[3] es
el carcter s.

Tambin podemos introducir desde el teclado directamente una cadena en un


arreglo de caracteres, utilizando scanf y la especificacin de conversin %s.
Por ejemplo, la declaracin .

char string2[20];
Crea un arreglo de caracteres capaz de almacenar una cadena de 19
caracteres y un carcter nulo de terminacin. El enunciado

scanf (%s, string2);


Lee una cadena del teclado y la coloca en string2. Note que el nombre del
arreglo se pasa a scanf sin colocar el & precedente, que en otras variables se
utiliza. El & es utilizado por lo regular para darle a scanf una localizacin de
variable en memoria, a fin de que se pueda almacenar un valor ah.
El nombre de un arreglo es la direccin del inicio del arreglo y, por tanto, & no
es necesario.

La funcin scanf lee caracteres del teclado hasta que se encuentra con el
primer carcter de espacio en blanco sin impotarle que tan grande es el
arreglo. Por lo tanto, scanf podra escribir ms all del final del arreglo.

Un arreglo de caracteres que represente una cadena puede ser sacado


utilizando printf y el especificador de convesin %s. El arreglo string2 se
imprime utilizando el enunciado

printf (%s\n, string2);

#include <stdio.h>
main()
{
char string1[20], string2[] = string literal;
int i;
printf(Mete un string: );

Pag 27
Estructura de datos

scanf(%s,string1);
printf(string1 es: %s\nstring2 es %s\n
string1 con espacios entre caracteres es: \n,
string1, string2);
for (i = 0; string1[i] != \0; I++)
printf(%c , string1[i]);
printf(\n);
return 0;
}

Mete un string: Hello there


string1 es: Hello
string2 es : string literal
string con espacios entre caracteres es :
He l l o

Uniones
Una union es un tipo de datos derivado como lo es una estructura- cuyos
miembros comparten el mismo espacio de almacenamiento. Para distintas
situaciones en un programa, algunas variables pudieran no ser de importancia,
pero otras variables lo son por lo que una union comparte el espcacio, en vez
de desperdiciar almacenamiento en variables que no esten siendo utilizadas.
Los miembros de la union pueden ser de cualquier tipo el nmero de bytes
utilizados para almacenar una unin, deben ser por lo menos suficientes para
contener al miembro mas grande. En la mayor parte de casos las uniones
contienen dos o ms tipos de datos. Unicamente un miembro y, por lo tanto,
nicamente un tipo de datos, puede ser referenciado en un momento dado

union number{
int x;
float y;
}
indica que number es un tipo union con miembros int x y float. En un
programa normalmente la definicin de unin antecede a main, por lo que esta
puede ser utilizada para declarar variables en todo el programa.

Las operaciones que pueden ser ejecutadas en una unin son: asignar una
unin a otra unin del mismo tipo, tomar la direccin (&) de una unin, y tener
acceso a los miembros de una union utilizando el operador de miembro de
estructura y el operador de apuntador de estructura.

En una declaracin, una union puede ser inicializada nicamente con un valor
del mismo tipo que el primer miembro de la union. Por ejemplo, en la union
anterior, la declaracin

union number value ={10};

Pag 28
Estructura de datos

Es una inicializacin valida de la variable de union value, porque la union esta


inicializada con un int, pero la siguiente declaracin no sera valida:

union number value = [1.43]

Constantes de enumeracin
C proporciona un tipo final, definido por el usuario, conocido como una
enumeracin. Una enumeracin, introducida por la palabra reservada enum,
es un conjunto de constantes enteras representadas por identificadores. Estas
constantes de enumeracin son, en efecto, constantes simblicas, cuyos
valores pueden ser definidos automticamente. Los valores de un enum se
inician con 0, a menos de que se defina de otra manera, y se incrementan en
1. Por ejemplo

enum months {JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT,
NOV, DEC};
crea un nuevo tipo en enum months, en el cual los identificadores son
definidos automticamente a los enteros 0 a 11. Para numerar los meses 1 a
12, utilice la enumeracin siguiente

enum monts {JAN =1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT,
NOV,
DEC};
Dado que el primer valor de la enumeracin se define explcitamente en 1, los
valores subsiguientes se incrementan en 1dando como resultado los valores
1hasta 12
Los identificadores en una enumeracin deben de ser unicos. En una
enumeracin el valor de cada constante en la enumeracin puede ser
establecido explcitamente en la definicin, mediante la asignacin de un valor
al identificador. Varios miembros pueden tener el mismo valor entero.

#include <stdio.h>

enum months {JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV,
DEC};

main ()
{
enum months month;
char *monthName[] = {, January,February,March,April,May,
June,July,August, September, October,November,
December};

for (month = JAN; month <=DEC; month++)


printf(%2d%11s\n,month, monthName[month]);

Pag 29
Estructura de datos

return 0
}
1 January
2 February
3 March
4 Apr
5 May
6 June
7 July
8 Aug
9 Sep
10 Oct
11 Nov
12 Dec

Apuntadores
Los apuntadores le permiten a los programas simular llamadas por referencia,
crear y manipular estructuras de datos es decir, estructuras de datos que
pueden crecer o encongerse como son listas enlazadas, colas de espera, pilas y
rboles.
Los apuntadores son variables que contienen direcciones de memoria como sus
valores. Por lo regular una variable contiene directamente un valor especifico,
un apuntador por otra parte, contiene la direccin de una variable que contiene
un valor especifico. En este sentido un nombre de variable se refiere
directamente a un valor y un apuntador se refiere indirectamente a un valor, el
referirse a un valor a travs de un apuntador se conoce como indireccin.
Los apuntadores como cualquier otra variable deben ser declarados antes de
que puedan ser utilizados.

int *contadorAptr, contador;


Contador se refiere
directamente a la
variable cuyo valor es
7.

Pag 30
Estructura de datos

contador
7

contadorAptr contador
7 ContadorAptr se
refiere en forma
indirecta a la
variable cuyo valor
es 7

La declaracin declar la variable contadorAptr siendo del tipo int*, se lee,


contadorAptr es un apuntador a int, o bien contadorAptr apunta a un objeto
de tipo entero. Tambin, la variable contador se declara como un entero, no
un apuntador a un entero. El *solo se aplica a contadorAptr en la declaracin.

Los apuntadores deben ser inicializados cuando son declarados o en un


enunciado de asignacin. Un apuntador puede ser inicializado a 0, NULL, o una
direccin. Un apuntador con el valor NULL apunta a nada. NULL es una
constante simblica, definida en el archivo de cabecera<stdio.h>. Cuando se
inicializa 0 es equivalente a inicializar un apuntador a NULL. El valor 0 es el
nico valor entero que puede ser directamente asignado a una variable de
apuntador

Operadores de apuntador
El &, u operador de direccin, es un operador unario que regresa la direccin
de su operando. Por ejemplo, suponiendo las declaraciones

int y= 5;
int *yPtr;
el enunciado
yPtr = &y;
asigna la direccin de la variable y a la variable de apuntador yPtr. La variable
yPtr se dice que apunta a

yPtr

Pag 31
Estructura de datos

Representacin grfica de un apuntador apuntando a una variable entera en


memoria.

yPtr y
500000 600000 600000 5

Representacin en memoria de y y yPtr.

La direccin de a y el valor de aPtr son idnticos en la salida, confirmando asi


que de hecho la direccin de a ha sido asignada a la variable de apuntador
aPtr. Los operadores & * son complementos el uno del otro.

En C se utilizan los apuntadores y el operador de indireccin para simular


llamadas por referencia. Cuando se pasan direcciones de los argumentos que
deban ser modificados, se pasan las direcciones de los argumentos. Esto se
lleva a cabo aplicando el operador de direccin (&), a la variable cuyo valor
deber ser modificado. Cuando se pasa a una funcin la direccin de una
variable, el operador de indireccin (*), puede ser utilizado en la funcin para
modificar el valor de esa posicin en la memoria de ese llamador

#include <stdio.h>

CuboPorReferencia(int *);

main()
{
int numero = 5;

printf(El valor original del nmero es %d\n,number)


cuboPorReferencia(&number);
printf(El nuevo valor de el nmero es %d\n,number);
return 0;
}
void cuboPorReferencia(int *nPtr)
{
*nPtr = *nPtr * *nPtr* *nPtr;
}

Relacin entre apuntadores y arreglos


Los arreglos y los apuntadores en C estn relacionados en forma intima y
pueden ser utilizados casi en forma indistinta . Un nombre de arreglo puede
ser considerado como un apuntador constante. Los apuntadores pueden ser
utilizados para hacer cualquier operacin que involucre subndices de arreglos.

Pag 32
Estructura de datos

Suponga que han sido declarados el arreglo entero b[5] y la variable de


apuntador entera bPtr. Dado que el nombre del arreglo (sin subndice) es un
apuntador al primer elemento del arreglo, podemos definir bPtr igual a la
direccin del primer elemento en el arreglo b mediante el enunciado.

bPtr = b;
Este enunciado es equivalente a tomar la direccin del primer elemento del
arreglo, como sigue

bPtr = &b[0]
Alternativamente el elemento del arreglo b[3] puede ser referenciado con la
expresin de apuntador

*(bPtr + 3)
El 3 en la expresin arriba citada es el desplazamiento del apuntador. Cuando
el apuntador apunta al principio del arreglo, el desplazamiento indica que el
elemento del arreglo debe ser referenciado, y el valor del desplazamiento es
idntico al subndice del arreglo, la notacin anterior se conoce como notacin
apuntador/desplazamiento.Al igual que el elemento del arreglo puede ser
referenciado con una expresin de apuntador, la direccin

&[3]
puede ser escrita con la expresin de apuntador

bPtr + 3
El arreglo mismo puede ser tratado como un apuntador, y utilizado en
aritmtica de un apuntador.
Por ejemplo la expresin

*(b + 3)
tambin se refiere al elemento del arreglo b[3]. En general, todas las
expresiones de arreglos son subndice pueden ser escritas mediante un
apuntador y un desplazamiento.

Arreglos de apuntadores
Los arreglos pueden contener apuntadores. Un uso comn para una estructura
de datos como sta, es formar un arreglo de cadenas. Cada entrada del arreglo
es una cadena pero en C una cadena es esencial un apuntador a su primer
carcter. Por lo que en un arreglo de cadenas cada entrada es de hecho un
apuntador al primer carcter.

char *suit[4] = {Corazones,Diamantes, Espadas, Treboles);


La porcin suit[4] indica un arreglo de 4 elementos. La porcin char * de la
declaracin ndica que cada elemento del arreglo suit es del tipo apuntador a
char. Los cuatro valores a colocarse en el arreglo son: Corazones, Diamantes,
Treboles y Espadas. Cada una de estas est almacenada en memoria como una
cadena de caracteres terminada por NULL, de una longitud de un carcter ms

Pag 33
Estructura de datos

largo que el nmero de caracteres entre las comillas. En el arreglo suit parece
que estan colocadas estas cadenas pero en el arreglo solo estn almacenados
los apuntadores.

Cada apuntador seala al primer carcter de su cadena correspondiente. Por lo


tanto, aunque el arreglo suit es de tamao fijo, permite el acceso a cadenas de

Suit[0] C o r a z o n e s \0

Suit[1]
D i a m a n t e s \0
Suit[2]

Suit[3]

caarcter de cualquier longitud.


Un ejemplo grfico de un arreglo suit
Asignacin dinmica de memoria mediante new y delete
Los operadores new y delete de C++ le permiten a los programas llevar a cabo
la asignacin dinmica de memoria. En ANSI C, la asignacin dinmica de

T r e b o l e s \0

E s p a d a s \0
memoria por lo general se lleva a cabo con las funciones estandar de biblioteca
malloc y free. Considere la declaracin

TypeName *ptr;
Donde typeName es cualquier tipo (como int, float, char, etctera). En ANSI C,
el enunciado siguiente asigna en forma dinmica un objeto typeName, regresa
un apuntador void al objeto, y asigna dicho apuntador a ptr

Ptr = malloc (sizeof(typeName))


En C la asignacin dinmica de memoria requiere una llamada de funcin a
malloc y una referencia explicita al operador sizeof (o una mensin explicita al
nmero necesario de bytes). La memoria se asigna sobre el montn (memoria
adicional disponible para el programa en tiempo de ejecucin). Tambin, en
puestas en prctica anteriores a ANSI C, el apuntador regresado por malloc
debe ser explcitamente convertido (cast) al tipo apropiado de apuntador con el
convertidor explicito (cast) (typeName*).

En C++, el enunciado

Ptr = new typeName


Asigna memoria para un objeto del tipo typeName partiendo de la tienda libre
del programa. El operador new crea automticamente un objeto del tamao

Pag 34
Estructura de datos

apropiado y regresa un apuntador del tipo apropiado. Si mediante new no se


puede asignar memoria, se regresa un apuntador nulo.

Para liberar en C++ el espacio para este objeto se utiliza el siguiente


enunciado:

delete ptr
En C, se invoca la funcin free con el argumento ptr, a fin de desasignar
memoria. El operador delete slo puede ser utilizado para desasignar memoria
ya asignada por new. Aplicar delete a un apuntador previamente desasignado
puede llevar a errores, inesperados durante la ejecucin del programa. Aplicar
delete a un apuntador nulo no tiene efecto en la ejecucin del programa.

C++ permite un inicializador para un objeto recin asignado. Por ejemplo,

float* cosaPtr = new float ( 3.14159);


Tambin mediante new los arreglos pueden ser creados dinmicamente. El
siguiente enunciado asigna dinmicamente un arreglo de un subndice de 100
enteros y asigna el apuntador regresado por new al apuntador entero arrayPtr

int *arrayPtr;
ArrayPtr = new int[100]; //creando arreglo dinmico

Para desasignar la memoria asignada dinmicamente para arrayPtr por new,


utilice el enunciado

delete [] arrayPtr;

Ordenamiento
La ordenacin o clasificacin de datos (sort) es una operacin consistente en
disponer un conjunto estructura- de datos, en algn determinado orden con
respecto a uno de los campos elementos del conjunto. Los elementos
numricos se pueden ordenar en orden creciente o decreciente de acuerdo al
valor numrico del elemento. En terminologa de ordenacin, el elemento por
el cual esta ordenado un conjunto de datos se denomina clave.

Una coleccin de datos puede ser almacenada en un archivo, un arreglo


(vector o tabla), un arreglo de registros, una lista enlazada o un rbol. Cuando
los datos estn almacenados en un arreglo, una lista enlazada o un rbol, se
denomina ordenacin interna. Si los datos estn almacenados en un archivo el
proceso de ordenacin se llama ordenacin externa.

Los mtodos (algoritmos) de ordenacin son numerosos, por ello se debe


prestar especial atencin en su eleccin Cmo se sabe cual es el mejor

Pag 35
Estructura de datos

algoritmo? La eficiencia es el factor que mide la calidad y rendimiento de un


algoritmo.
1. tiempo menor de ejecucin en computadora
2. menor nmero de instrucciones

Los mtodos de ordenacin se suelen dividir en dos grandes grupos:

directos burbuja, seleccin, insercin


indirectos(avanzados) Shell, ordenacin rpida, ordenacin por
mezcla

Ordenacin por burbuja

Este mtodo es clsico y muy sencillo, aunque por desgracia poco eficiente. La
ordenacin por burbuja se basa en la comparacin de elementos adyacentes
de la lista (vector) e intercambiar sus valores si estn desordenados. De este
modo se dice que los valores ms pequeos burbujean hacia la parte superior
de la lista, mientras que los valores ms grandes se hunden haca el fondo de
la lista.

Supongamos un vector A[1], A[2],...,A[n]. Se comienza el seguimiento del


vector de izquierda a derecha, comparando A[1] con A[2]; si estn
desordenados se intercambian entre s. A continuacin se compara A[2] con
A[3], intercambindolos si estn desordenados, este proceso de
comparaciones e intercambios continua a lo largo de toda la lista. Estas
operaciones constituyen una pasada a travs de la lista. Al terminar esta
pasada el elemento mayor est en la parte inferior y algunos de los elementos
han burbujeado hacia arriba de la lista. Se vuelve a explorar de nuevo la lista
comparando elementos consecutivos e intercambindolos cuando estn
desordenados, pero esta vez el elemento mayor no se compara, ya que se
encuentra en su posicin correcta, al terminar esta pasada se habr situado
en su sitio el segundo elemento ms grande se siguen las comparaciones hasta
que toda la lista este ordenada cosa que suceder cuando se hayan realizado
(n-1) pasadas. Para su mejor comprensin, veamos grficamente el proceso
anterior con un vector (lista) con cinco elementos; A[1], A[2], A[3], A[4],
A[5].

A[1] 23 15
A[2] 19 19
A[3] 45 23
A[4] 31 31

Pag 36
Estructura de datos

A[5] 15 44
Lista sin ordenar Lista ordenada

En la lista A, i ser el nmero de la pasada y j indica el orden del elemento de


la lista.

Se compara el elemento j-esimo y el (j+1)-simo.

Pasada 1: i=1

A[1] 23 19 19 19 19
A[2] 19 23 23 23 23
A[3] 45 45 45 31 31
A[4] 31 31 31 45 15 elemento
ordenado
A[5] 15 15 15 15 45
j=1 j=2 j=3 j=4

Se han realizado cuatro comparaciones (5-1 o bien n-1 en el caso de n


elementos) y tres intercambios.

Pasada 2: i = 2

A[1] 19 19 19 19 19
A[2] 23 23 23 23 23
A[3] 31 31 31 15 15
elemento
A[4] 15 15 15 31 31 ordenado

A[5] 45 45 45 45 45
J=1 j=2 j=3 j=4

Pasada 3: i = 3

Pag 37
Estructura de datos
A[1] 19 19 19 19 19
A[2] 23 23 15 15 15
A[3] 15 15 23 23 23
elemento
A[4] 31 31 31 31 31 ordenado

A[5] 45 45 45 45 45
j=1 j=2 j=3 j=4

Pasada 4: i =4
A[1] 19 15 15 15 15
A[2] 15 19 19 19 19
A[3] 23 23 23 23 23
elemento
A[4] 31 31 31 31 31 ordenado

A[5] 45 45 45 45 45
j=1 j=2 j=3 j=4 j=5

Se observa que se necesitan cuatro pasadas para ordenar una lista de nmeros
de cinco elementos, por lo que una lista de n elementos necesitar n-1
pasadas.
El nmero de pasadas se puede controlar con un bucle for y cada secuencia de
comparaciones se puede controlar con un bucle for anidado al bucle de
pasadas, en el que j vara desde 1 hasta 5 menos el valor especifico de i.

Algoritmo (Pseudocdigo)

Desde i = 1hasta n-1 hacer


Desde j= 1 hasta n-i hacer
Si A[j] > A[j+1]
Entonces Intercambio (A[j],A[j+1])
Fin_si
Fin _desde {bucle j}
Fin_desde {bucle i}

void burbuja(int Lista[],int N){

Pag 38
Estructura de datos

int i, j,aux;

for (j=1; j<N; j++)


for (i=0; i < N-j;i++)
if (Lista[i]> Lista[I+1]){
aux = Lista[I];
Lista[i] =Lista[i+1];
Lista[i+1]= aux;
}
}

Burbuja mejorado
El algoritmo burbuja se puede mejorar si disponemos de algn tipo de
indicador que registre si se han producido intercambios en la pasada. Cuando
se explor la lista y el indicador no refleje intercambios, la lista estar ya
ocupada y se terminarn las comparaciones.

El intercambio ser una variable lgica NoIntercambio (o bien ordenado) que


se inicializa a 1 (significa que la lista a priori est desordenada). Si dos
elementos se intercambian en una pasada. No Intercambio se pone en 0. Al
principio de cada pasada NoIntercambio se fija a 1 y a 0 si se produce a
intercambios. El bucle externo for se sustituye por un bucle do while o bien
while y un contenido i se necesitara para contar el nmero de pasadas.

i =1
Repetir
NoIntercambio = true
Desde j = i hasta n i hacer
Si A[j] > A[J+1]
Entonces Intercambio (A[j], A[j+1])
NoIntercambio = false
Fin si
Fin_desde
i = i+1
Hasta que NoIntercambio = true

void burbuja_mejorada(int Lista[],int N){


int i,j=1,aux,bandera=1;
while (j<N && bandera==1){
Bandera=0;
for(I=0;I<N-j;I++)
if (Lista[I]>Lista[I+1]){
Bandera=1;
Aux=Lista[I];
Lista[i]=Lista[i];
Lista[i+1]=aux;
}

Pag 39
Estructura de datos

j++;
}

Ordenacin por insercin


Este mtodo est basado en la tcnica utilizada por los jugadores de cartas
para clasificar sus cartas. El jugador va colocando (insertando) cada carta en
su posicin correcta.

Tres cartas 2 6 10

Cuatro 2 6 //////// 10
cartas 9 ////////

Cinco 2 6 //////// 9 10
cartas 7 ////////

El mtodo se basa en considerar una parte de la lista ya ordenada y situar


cada uno de los elementos restantes insertndolo en el lugar que le
corresponde por su valor.

A[1] A[2] A[3] A[4] A[5]


1 4 10 15
A[N]

1 4 6 10 15

Algoritmo

{para cada elemento de la lista despus del primero}


desde k = 2 hasta n hacer
Guardar el valor de este elemento A[k] en una variable Aux.
Hacer espacio para Aux desplazando todos los valores mayores que
dicho valor
A[k] una posicin.
Insertar el valor de Aux en el lugar del ultimo valor desplazado.

Fin_desde

Pag 40
Estructura de datos

La operacin de desplazamiento se realiza con un procedimiento Desplazar,


que mueve todos los elementos de la lista mayores que Aux, comenzando con
el elemento de la lista de posicin Aux-1. Si Aux es el valor ms pequeo hasta
aqu, la operacin de desplazamiento termina despus que todos los elementos
de la lista se han desplazado. Si Aux no es el valor ms pequeo, la operacin
de desplazamiento termina cuando un valor menor o igual a Aux se alcanza.
Aux se inserta en la posicin que ocupaba el ltimo valor que se desplaz.

Algoritmo de desplazamiento
Mientras el primer elemento no se desplaza y el valor del elemento >
Aux hacer
o Desplazar elemento una posicin.
o Comprobar valor del siguiente elemento.
o Definir NuevaPos como posicin original del ltimo elemento
desplazado.
Fin_mientras

Codificacin del procedimiento OrdenarInsercin

void OrdenacionInsercion(int Lista[];int n){


/*Lista (entrada/salida) , n (entrada) */
/*Lista = array de N elementos enteros */
int k, /*subindice del siguiente elemento al que se
inserta*/
nueva_pos, /*subindice de este elemento despus de la
insercin*/
aux;

for(k=2;k<=n;k++){
aux = lista[k]; /*obtener siguiente elemento a insertar*/
Lista[nueva_pos(Lista,k,aux)]=aux; /*insertar aux en posicin
nueva*/
}
return 0;
}

La funcin de nueva posicin que desplaza todos los elementos de la lista

int nueva_pos (int Lista[],int k,int aux ){


int encontrado ; {indicador}
/*desplazar valores > Aux . Comenzar con
el elemento K 1 */
encontrado= 0;

Pag 41
Estructura de datos

while (k > 1 && !encontrado)


if (Lista [k 1] > aux) {
Lista[k] = Lista[k 1];
k--;
}
else
encontrado = 1;
return k;
} {Desplazar}

Ordenacin por seleccin


El algoritmo de ordenacin por seleccin de una lista (vector) de n elementos
tiene los siguientes pasos:

1. Encontrar el elemento mayor de la lista.


2. Intercambiar el elemento mayor con el elemento de subndice n (o bien
el elemento menor en el subndice 1).
3. A continuacin se busca el elemento mayor en la sublista de subndices
1...n 1, y se intercambiaba con el elemento de subndice n 1: por
consiguiente, se sita el segundo elemento mayor en la posicin n-1.
4. A continuacin se busca el elemento mayor en la sublista 1...n 2 y as
sucesivamente.

Lista A[1] 5 5 5 -2
desordenada
A[2] 14 2 2 2
A[3] -2 -2 -2 5
A[4] 10 10 10 10
A[5] 2 14 14 14

El algoritmo de PosMayor debe guardar j como la posicin del elemento mayor


y luego poder intercambiar.

int PosMayor (int Ultimo,int Lista[] ){


/*encuentra el indice del elemento mayor en
la tabla [1.. Ultimo]*/
int Indice_Max, Indice;
Indice_Max = 1;
for(Indice= 2; Indice<=Ultimo;Indice++)

Pag 42
Estructura de datos

if (Lista [Indice] > Lista [Indice_Max] )


indice_Max = Indice;
return Indice_Max;
}

void Seleccion (int Limi,int Lista[]){


int Aux, J, Mayor;

for(J=Limi; >=2 ;J++)


{
/*encontrar el elemento mayor de 1..J*/
Mayor = PosMayor (J, Lista);
/*Intercambio con el elemento Tabla [J]*/
Aux = Lista[Mayor];
Lista[Mayor] = Lista[J];
Lista [J] = Aux;
}
}

Ordenamiento Shell
La ordenacin Shell debe el nombre a su inventor, D. L. Shell. Se suele
denominar tambin ordenacin por disminucin de incremento (gap). La idea
general del mtodo (algoritmo) es la siguiente:

Lista original

504 88 513 62 908 171 898 277 654 427 150 510 612
675 750 704

1. Se divide la lista original (16 elementos, en este ejemplo) en ocho


grupos de dos (consideramos un incremento o intervalo de 16/2 = 8).
2. Se clasifica cada grupo por separado (se comparan las parejas de
elementos y si no estan ordenados se intercambian entre si de
posiciones).
3. Se divide ahora la lista en cuatro grupos de cuatro (intervalo o salto de
8/2 = 4) y nuevamente se clasifica cada grupo por separado.
4. Un tercer paso clasifica dos grupos de ocho registros y luego un cuarto
paso completa el trabajo clasificando todos los 16 registros.

Primer paso (divisin/ordenacin por 8)

Pag 43
Estructura de datos

504 88 513 62 908 171 898 277 654 427 150 510

612 675 750 704

Segundo paso (divisin/ordenacin por 4)

Pag 44
Estructura de datos

504 88 150 62 612 171 760 277 654 427 513 510

908 675 898 704

Tercer paso (divisin/ordenacin por 2)

Pag 45
Estructura de datos

504 88 150 62 612 171 513 277 654 427 760 510

908 675 898 704

Cuarto paso (divisin/ordenacin por 1)

154 62 504 88 513 171 612 277 654 427 760 510

898 675 908 704

62 88 154 171 277 427 504 510 513 612 654 675
704 760 898 908

El algoritmo de Shell tiene diferentes modelos ms populares y citados en


numerosas obras de programacin

Algoritmo

Intervalo = n div 2
Mientras (intervalo > 0 ) hacer
Desde i = (intervalo + 1) hasta n hacer
J = i intervalo
Mientras (j >0 ) hacer
K = j + intervalo
Si ( a [j] <= a[k])
Entonces
J=0
Si no
Intercambio (a[j], a[k]
Fin si
j = j intervalo
Fin mientras
Fin desde
Intervalo = intervalo div 2
Fin mientras

Cdigo

void Intercambio (int X,int Y){


int Aux ;

Pag 46
Estructura de datos

Aux = X;
X = Y;
Y =Aux;
}

void Shell (int Lista[],int N){


int Intervalo =N/2, I, J, K ;

while (Intervalo > 0){


for( I = Intervalo + 1; I <=N;I++) {
J = I Intervalo;
while (J > 0) {
K = J + Intervalo;
if (Lista[J] <= Lista[K])
J = 0;
else
Intercambio (*(Lista +J),*(Lista +K));
J = J Intervalo;
} {while}
}
Intervalo /= 2;
}
return 0;
}

Ordenamiento Mezcla (MergeSort)

El proceso de mezcla, fusin o intercalacin (merge) consiste en tomar dos


vectores ordenados (a, b) y obtener un nuevo vector tambin ordenado.

El algoritmo ms sencillo para resolver el problema es:

1. Situar todos los elementos del vector a en el nuevo vector c.


2. Situar todos los elementos del vector b en el nuevo vector c.
3. Ordenar todo el vector c.

Esta solucin no tiene en cuenta que los valores de de a y b ya estn


ordenados.

El algoritmo que tiene en cuenta la ordenacin es el siguiente:

1. Seleccionar el elemento de valor o clave mas pequeo con cualquiera de


dos vectores y situarlo en el nuevo vector c.
2. Comparar a(i) y b(i) y poner el elemento de vector ms pequeo en
c(k).

Pag 47
Estructura de datos

3. Seguir esta secuencia de comparaciones hasta que los elementos de un


vector se hayan agotado en cuyo momento se copia el resto del otro
vector en c.

Ejemplo:
Mezclar las dos listas de nmeros a y b.

2 4 78 97 lista A
-15 0 13 15 78 90 96 lista B

2 4 78 97 Lista A
j se ha incrementado
i junto con k
<

Lista C
Lista B
k -15 0 13 15 78 90 94 96

-15 0 Lista C

Comparar A[i]
Y B[j]. Poner el
Ms pequeo en
C[k]. Incrementar
Los indices apropiados

Pag 48
EstructuraLista
de datos
A
2 4 78 97
i
<

Lista B
k -15 0 13 15 78 90 94 96

-15
B < A[j], de modo que C[k] se obtiene de B[j]
Procedimiento mezcla de los vectores A y B
void Mezcla (int A[],int B[],int C[],int M,int N ){
int I, J, K ; /*A Y B : entrada. Vectores ya ordenados*/
I=J=K=1; /*M Y N: nmero de elementos de A y B
respectivamente*/
while ((I <= M) &&(J <= N)) { /*C : salida. Vector mezcla ordenado*/
if (A[I] <= B[J]){ /*El tipo Lista, tendr una longitud minima
de M + N elementos*/
C [K] = A[I];
I++;
}
else
{
C [K] = B[J];
J ++;
}
K++;
}
{copiar el resto del vector no agotado}
if (I > M)
for( P=J;P<=N;P++){
C [K] = B [P];
K++;
}
else
for( P=I;P< M;P++){
C [K] = A [P];
K++;
}
return 0;
}

Pag 49
Estructura de datos

Bsquedas

Bsqueda secuencial
Un problema importante en el proceso de datos, como ya se ha comentado, es
la bsqueda en un conjunto de datos de un elemento especifico y la
recuperacin de alguna informacin asociada al mismo.

La bsqueda lineal o secuencial es la tcnica ms simple para buscar un


elemento en un arreglo (vector). Consiste el mtodo en el recorrido de todo el
vector, desde el primer elemento hasta el ltimo, y de uno en uno. Si el vector
contiene el elemento, el proceso devolver la posicin del elemento buscado y
en caso contrario un mensaje que indique la falta de xito en la bsqueda.

Supongamos una lista de nmeros de la seguridad Social incluidos en un


arreglo a y se desea buscar a ver si existe el nmero 453714.

A[1} 451871
A [2] 120467
A [3] 401321
A[4] 25761
- - Elemento a buscar: t
- - 453714
- -
A[98] 339412
A[99] 81467
A[100] 924116
Nmeros Seguridad Social

Mediante un bucle desde ir comparando el elemento t buscando con a[i],


donde i vara, en el ejemplo anterior, de 1 a 100. En caso de encontrarlo,
almacenar la posicin (el indice arreglo) del mismo dentro de la lista y
finalmente se devolver al programa principal.

Pseudocdigo 1

Posicin = 0
{lista = vector a[i] de n elementos}
desde i = 1 hasta n hacer
si a[i] = t
entonces Posicin = i
fin si
fin desde

Pag 50
Estructura de datos

Este algoritmo tiene un inconveniente, sea cual sea el resultado se recorre el


vector completo. El algoritmo se puede mejorar con un bucle while o do_while,
y utilizando unas banderas que detecten cuando se encuentre el elemento. El
bucle se terminar por dos causas:

La bandera o indicador toma el valor esperado y la bsqueda ha tenido


xito.
El valor del ndice i es mayor que el nmero de trminos de la lista, lo
que significa que se ha terminado de recorrer la misma y el elemento
buscado no ha aparecido.

Pseudocdigo 2

Encontrado = falso
Posicin = 0
i=1
mientras (i <= n) y (No Encontrado=verdadero) hacer
si a[i] = t
entonces Posicin = i
Encontrado = verdadero
Fin si
i=i+1
fin_mientras

Funcin bsqueda lineal

int BusquedaLineal (int A[]; /*entrada, vector bsqueda*/


int N ; /* entrada, nmero de elementos */
int T /*elemento a buscar*/){

int Encontrado=0,I=1,Posicion;

Posicion= 0; /*posicin del elemento caso de no xitir*/


while (I <= N) && ! Encontrado){
if (A[I] == T ){
Posicion= I;
Encontrado = 1;
} {fin del if y del while}
I ++;
return 0;
} {posicin del elemento en la lista

Bsqueda Binaria
La bsqueda lineal, por sus simplicidad es buena para listas de datos pequeas
para listas grandes es ineficiente, la bsqueda binaria es el mtodo idneo. Se
basa en el conocido mtodo divide y vencers.

Pag 51
Estructura de datos

Este mtodo tiene una clara expresin en la bsqueda de una palabra en un


diccionario. Cuando se busca una palabra no se comienza la bsqueda por la
pgina 1 y se sigue secuencialmente sino que se abre el diccionario por una
pagina donde aproximadamente se piensa puede estar la palabra, es decir se
divide el diccionario en dos partes, al abrir la pgina se ve si se ha acertado o
en que parte se encuentra la palabra buscada. Se repite este proceso hasta
que por divisiones o aproximaciones sucesivas se encuentra la palabra.
Supongamos que la lista donde se busca es

1331
1373
1555
1850
1892
1898 Elemento central
1989
Elemento buscado
2002
2400
2670
3200
Y que se busca el nmero 1989
Se examina en primer lugar el elemento central de la lista (las divisiones se
toman iguales) 1898. Dado que 1989 es mayor que 1898, el elemento a buscar
estar en la segunda mitad. Por consiguiente, se sigue la bsqueda en esta
mitad:

Elemento a buscar 1989


2002
2400 Elemento central
2670
3200

El elemento central en esta sublista es 2400, y como1989 es menor, la nueva


sublista donde buscar es

1989 Elemento considerado central


2002

como ya no hay elemento central se toma el nmero inmediatamente anterior


de la posicin central, que en este caso es 1989. En este caso se ha
encontrado el elemento deseado en tres comparaciones, mientras que en la
bsqueda lineal hubiese necesitado al menos seis comparaciones. Este mtodo
es muy eficiente con el nico inconveniente, que requiere la lista ordenada.

Pag 52
Estructura de datos

Algoritmo
1. Establecer Primero = 1 y Ultimo = n (n, nmero de elementos). Estas
variables representan la primera y ltima posicin de la lista o sublista
donde se est buscando y permite el calculo de la posicin del elemento
central.
2. Encontrado = falso .
3. mientras Primero <= Ultimo y Encontrado = falso hacer
{Encontrar posicin central}
Central = (Primero + Ultimo) div 2
{Comparar elemento buscado t con A[Central]}
si t = a [Central]
entonces Encontrado = verdadero
sino si t > A [Central]
entonces Primero = Central + 1
sino Ultimo = Central 1
fin mientras
4. si Encontrado = verdadero
entonces Posicin = Central {existe elemento}
si no Posicin = 0 {no se ha encontrado}
fin_si

La bsqueda binaria requiere una ordenacin previa del vector o lista en el que
se va ha efectuar la bsqueda. Por consiguiente, las acciones tpicas (mdulos)
en un algoritmo de bsqueda binaria son:

1. Lectura del vector


2. Ordenacin del vector
3. Bsqueda binaria
4. Visualizar resultado

int Binaria (int T, int L[] ,int N ){


int Primero, Ultimo, Central,
Encontrado;
Primero = 1;
Ultimo = N;
Encontrado = 0 ;
while ((Primero <= Ultimo) && !Encontrado){
Central = ( Primero + Ultimo) / 2;
if (T == L [Central])
Encontrado = 1;
else
if ( T > L [Central])
Primero = Central + 1;
else
Ultimo = Central 1;
}
if (!Encontrado)

Pag 53
Estructura de datos

return 0;
else
return Central;
}

Estructuras de datos lineales, representaciones


secuenciales.

Conceptos fundamentales: Tipo de Dato, Tipo de Dato Abstracto,


Estructura de Datos, Registros.

Concepto de estructuras de datos


Aunque los terminos tipo de datos (o simplemente <<tipo>>), <<estructura
de datos>> y <<tipo de dato abstracto>> parecen semejantes, su significado
es diferente.

Una estructura de datos es una coleccin de datos organizados de un modo


particular, las estructuras de datos pueden ser de dos tipos: estructuras
de datos estticas y estructuras de datos dinmicas.

Las estructuras de datos estticas son aquellas en las que se asigna una
cantidad fija de memoria cuando se declara la variable, en numerosas
ocasiones se necesitan, colecciones de datos que crezcan y reduzcan su
tamao en memoria a medida que el programa progresa, a estas estructuras
de datos cuya ocupacin en memoria puede aumentar o disminuir en tiempo
de ejecucin se denominan estructuras dinmicas de datos.

En un lenguaje de programacin, el tipo de datos de una variable es el


conjunto de valores que este puede tomar. Por ejemplo una variable de tipo
booleano puede tomar los valores verdadero o falso, pero ningn otro, los tipos
de datos bsicos varian de un lenguaje a otro; en C son enteros (int), real
(float), y carcter (char). Las reglas para construir tipos de datos compuestos
a partir de los bsicos tambin varan de un lenguaje a otro

Un tipo de datos abstracto (TDA) es un modelo matemtico, junto con varias


operaciones definidas sobre ese modelo. Para representar el modelo
matemtico bsico de un TDA se emplean estructuras de datos, que son

Pag 54
Estructura de datos

conjuntos de variables quiza de tipos distintos, conectadas entre si de diversas


formas.

El componente bsico de una estructura de datos es la celda. Se puede


representar una celda como una caja capaz de almacenar un valor tomado de
algn tipo de datos bsico o compuesto, las estructuras de datos se crean
dando nombres a agregados de celdas

El mecanismo de agregacin ms sencillo en C y en la mayor parte de los


lenguajes de programacin es el arreglo (unidimensional), que es una sucesin
de celdas de un tipo dado al cual se llamar casi siempre <<tipo_celda>> .

Estructuras
Las estructuras son colecciones de variables relacionadas a veces
denominados agregados bajo un nombre. Las estructuras pueden contener
variables de muchos tipos diferentes de datos a diferencia de los arreglos, que
contienen unicamente elemento de un mismo tipo de datos. Generalmente las
estructuras se utilizan para definir registros a almacenar en archivos.

Creacin de estructuras
Crear una estructura es definir un nuevo tipo de datos, denominado tipo
estructura y declarar una variable de este tipo. En la definicin del tipo
estructura, y declarar una variable de este tipo. En la definicin del tipo
estructura, se especifican los elementos que la componen as como sus tipos.
Cada elemento de la estructura recibe el nombre de miembro (campo del
registro). La sntaxis es la siguiente:

struct tipo_estructura
{
declaraciones de los miembros
};

tipo_estructura es un identificador que nombra el nuevo tipo definido.

Despus de definir un tipo estructura, podemos declarar una variable de ese


tipo, de la forma:

struct tipo_estructura [variable[, variable]];


Para referirse a un determinado miembro de la estructura, se utiliza la
notacin:

variable.miembro

Ejemplo:

struct ficha /* definicin del tipo estructura ficha */


{

Pag 55
Estructura de datos

char nombre[40];
char direccin[40];
long telefono;
};

La anterior definicin no reserva ningn espacio en memoria, ms bien genera


un nuevo tipo de datos, que se utiliza para declarar variables.

struct ficha var1, var2;

Este ejemplo define las variables var1 y var2, de tipo ficha, por lo que cada
una de las variables consta de los miembros: nombre, direccin y telfono.

Una variable que es un miembro de una estructura, puede utilizarse


exactamente igual que cualquier otra variable.

Ejemplo:

var1. Telefono = 232323;


gets(var2.nombre);
La declaracin de las variables var1 y var2, puede realizarse tambin
directamente de la siguiente forma:

struct ficha
{
char nombre[40];
char direccion[40];
long telefono;
} var1, var2;

La declaracin de un miembro de una estructura no puede contener


calificadores de clase de almacenamiento extern, static, auto o register y no
puede ser inicializado. Su tipo puede ser: fundamental, array, puntero, unin,
estructura o funcin.
Los miembros de la estructura pueden ser variables de los tipos de datos
bsicos, o agregados, como son los arreglos y otras estructuras. Una
estructura no puede tener una instancia de si misma, sin embargo pudiera ser
incluido un apuntador a la estructura ( estructura autoreferenciada).

struct card {
char false[10];
char suit[10];
}a, deck[52];

Pag 56
Estructura de datos

El nombre del rtulo es opcional. Si la definicin de una estructura no contiene


un nombre de rtulo e estructura, las variables de ese tipo de estructura
pueden nicamente ser declaradas dentro de la definicin de la estructura.

La nicas operaciones vlidas que pueden ejecutarse sobre estructuras son :


asignar variables de estructura a variables de estructura del mismo tipo,
tomando la direccin (&) de una variable de estructura obteniendo acceso a los
miembros de una varible de estructura, y utilizando el operador sizeof, a fin
de determinar el tamao de la variable de estructura.

Como inicializar estructuras


Las estructuras pueden ser inicializadas mediante listas de inicializacin como
los arreglos.

struct card a = (Three, Hearts);

Como tener acceso a los miembros de estructuras

Para tener acceso a miembros de estructuras se utilia el operador de miembro


de estructura (.).

printf (%s, a.suit);

Cmo utilizar estructuras con funciones


Las estructuras pueden ser pasadas a funciones pasando miembros de
estructuras individuales, pasando toda la estructura o pasando un apuntador a
una estructura. Cuando se pasan estructuras o miembros individuales de
estructura a una funcin se pasan en llamada por valor.
Para pasar una estructura en llamada por referencia, pase la direccin de la
variable de estructura. Los arreglos de estructura como todos los dems
arreglos son automticamente pasados en llamada por referencia.

typedef
La palabra reservada typedef proporciona un mecanismo para la creacin de
sinnimos (o alias)para tipos de datos anteriormente definidos

typedef struct card Card;


define el nuevo nombre de tipo Card como un sinnimo para el tipo struct card.
Lo anterior se puede expresar de la siguiente forma

typedef struct{
char false[10];
char suit[10];
}Card;

Pag 57
Estructura de datos

Card puede ser utilizado para declarar variables de tipo struct card. La
declaracin

Card deck[52];
Al crear un nuevo nombre utilizando typedef no se crea un nuevo tipo; typedef
simplemente crea un nuevo nombre de tipo que puede ser utilizado como un
seudnimo para un nombre de tipo existente.

Ejemplo:

Pag 58
Estructura de datos

El siguiente programa lee una lista de alumnos y sus correspondientes notas


de final de curso, dando como resultado el tanto porciento de alumnos
aprobados y suspendidos

#include <stdio.h>
#include <stdlib.h>
#define NA 10

main()
{
struct ficha
{
char nombre[60];
float nota;
};

struct ficha alumnos[NA]; /*arreglo de estructuras o registros */


int n =0,i;
char *fin; /* Puntero al nombre leido*/
float aprobados = 0, suspensos = 0;

/*Entrada de datos*/
printf(Finalizar la entrada con cont/Z\n\n);
printf(Nombre);
fin = gets(alumnos[n].nombre);

while (n <NA && fin =NULL)


{
printf(Nota );
scanf(%f,&alumnos[n++].nota);
printf(Nombre );
fin =gets(alumnos[n].nombre);
}

for (i =0;i<n>i++)
if (alumnos[i].nota >=5)
aprobados ++;
else
suspensos ++;
printf(Aprobados %.2g %%\n,aprobados /n*100);
printf(Suspensos %.2g %% \n,suspensos/n*100);
}

Pag 59
Estructura de datos

T.D.A. Lista

Modelo Matemtico
Las listas constituyen una estructura flexible en particular, por que pueden
crecer y acortarse segn se requiera, los elementos son accesibles y se pueden
insertar y suprimir en cualquier posicin de la lista, las listas tambin pueden
concatenarse entre si o dividirse en sublistas; se representan de manera
rutinara en aplicaciones como manera de aplicacin.

Matemticamente, una lista es una secuencia de cero o ms elementos de un


tipo determinado. A menudo se representa una lista como una sucesin de
elementos separados por comas

A1, a2, a3,.....an

Donde n 0 y cada a1 es de tipo tipo_elemento. Al nmero n de elementos se


le llama longitud de la lista. Al suponer que n 1, se dice que a1 es el primer
elemento y an el ltimo elemento. Si n = 0, se tiene una lista vaca, es decir,
que no tiene elementos.

Una propiedad importante de una lista es que sus elementos pueden estar
ordenados en forma lineal de acuerdo con sus posiciones en la misma. Se dice
que ai precede a ai+1 para i = 1, 2,...n-1 y que ai sucede a ai-1 para i = 2,
3,...n. Se dice que el elemento ai est en la posicin i. Es conveniente postular
tambin la existencia de una posicin que sucede al ltimo elemento de la
lista. La funcin FIN(L) devolver la posicin que sigue a la posicin que sigue
a la posicin n en una lista L de n elementos. Observese que la posicin FIN(L),
con respecto al principio de la lista, est en una distancia que vara conforme
la lista crece o se reduce, mientras que las demas posiciones guardan una
distancia fija con respecto al principio de la lista.

Para formar un tipo de dato abstracto a partir de la nocin matemtica de la


lista, se debe de definir un conjunto de operaciones con objetos de tipo lista.

Operaciones
Se representar ahora un conjunto representativo de operaciones con listas.
Ah, L es una lista de objetos de tipo tipo_elemento, x es un objeto de ese tipo
y p es de tipo posicin. Observese que <<posicin>> es otro tipo de datos
cuya implantacin cambiar con aquella que se haya elegido para las listas.
Aunque de manera informal se piensa en las posiciones como enteros, en la
practica pueden tener otra representacin.

Pag 60
Estructura de datos

1. INSERTA(x, p, L). Esta funcin inserta x en la posicin p de la lista L,


pasando los elementos de la posicin p y siguientes a la posicin
inmediata posterior. Esto quiere decir que si L es a1,a2,...,an, se
convierte en a1, a2,..., ap-1, x, ap,..., an. Si p es FIN(L) entonces L se
convierte en a1, a2,...., an, x. Si la lista L no tiene posicin p, el
resultado es indefinido.
2. LOCALIZA(x, L). Esta funcin devuelve la posicin de x en la lista L. Si x
figura ms de una vez en L, la posicin de la primera aparicin de x es la
que se devuelve. Si x no figura en la lista entonces se devuelve FIN(L).
3. RECUPERA(p, L). Esta funcin devuelve el elemento que esta en la
posicin p de la lista L. El resultado no est definido si p = FIN(L) o si L
no tiene la posicin p. Obsrvese que si se utiliza RECUPERA, los
elementos deben ser de un tipo que pueda ser devuelto por una funcin.
No obstante en la practica siempre es posible modificar RECUPERA para
devolver un apuntador a un objeto de tipo elemento.
4. SUPRIME(p, L). Esta funcin elimina el elemento en la posicin p de la
lista L. Si L es a1, a2,...,an, L se convierte en a1, a2,...ap-1, ap+1,...,
an. El resultado no est definido si L no tiene posicin p o si p = FIN(L).
5. SIGUIENTE(p, L) y ANTERIOR (p,L) devuelven las posiciones siguiente y
anterior, respectivamente, a p en la lista L. Si p es la ltima posicin de
L, SIGUIENTE(p, L) =FIN(L). SIGUIENTE no esta definida si p es FIN(L).
ANTERIOR no esta definida si p es 1, ambas funciones no estan
definidas cuando L no tiene posicin P.
6. ANULA(L). Esta funcin ocasiona que L se convierta en la lista vaca y
devuelve la posicin FIN(L).
7. PRIMERO(L). Esta funcin devuelve la primera posicin de la lista L. Si L
est vaca, la posicin que se devuelve es FIN(L).
8. IMPRIME_LISTA(L). Imprime los elementos de L en su orden de
aparicin en la lista.

Implementacin de listas con arreglos

En la realizacin de una lista mediante arreglos, los elementos de esta se


almacenan en celdas contiguas de un arreglo. Esta representacin permite
recorrer con facilidad una lista y agregarle elementos nuevos al final, pero
insertar un elemento en la mitad de la lista obliga a desplazarse una posicin
dentro del arreglo a todos los elementos que siguen al nuevo elemento para
concederle espacio. De la misma forma la eliminacin de un elemento, excepto
el ltimo, requiere desplazamientos de elementos para llenar de nuevo el vacio
formado.

Pag 61
Estructura de datos

1 primer elemento
2 segundo elemento

lista

lt ltimo elemento

vaco

Long_mx

En la realizacin con arreglos se define el tipo LISTA como un registro con dos
campos, el primero es un arreglo de elementos que tiene la longitud adecuada
para contener la lista de mayor tamao que se puede representar. El segundo
campo es un entero lt que indica la posicin del ltimo elemento de la lista en
el arreglo. El i-simo elemento de la lista est en la i-sima posicin, mediante
el entero i. La funcin FIN(L) slo tiene que devolver lt +1. Las declaraciones
importantes son:

#include <stdio.h>
#include <conio.h>
# define TAM 10
# define TRUE 1
# define FALSE 0

typedef int dato;


typedef int posicion;
typedef char logico;

struct LISTA
{
dato elem[TAM];
posicion ult;
};

typedef struct LISTA Lista;


posicion Fin(Lista &L);
void Anula(Lista &L);
void Imprime(Lista L);

Pag 62
Estructura de datos

posicion Ultimo(Lista &L);


logico Vacia(Lista &L);
logico Llena(Lista &L);
posicion Primero(Lista &L);
posicion Siguiente(posicion p, Lista &L);
posicion Anterior(posicion p, Lista &L);
void Insertar(dato x, posicion p, Lista &L);
void Suprimir(posicion p, Lista &L);
posicion Localiza(dato x, Lista &L);
dato Recupera(posicion p, Lista &L);

posicion Fin(Lista &L)


{
return (L.ult+1);
}
void Anula(Lista &L)
{
L.ult=-1;
}
posicion Ultimo(Lista &L)
{
if (L.ult >=0)
return L.ult;
else
return -1;
}

posicion Primero(Lista &L)


{
if (L.ult >=0)
return (0);
else
return -1;
}
posicion Siguiente(posicion p, Lista &L)
{
if (p>=L.ult || p< 0 )
return(-1);
else
return (++p);
}

posicion Anterior(posicion p, Lista &L)


{
if (p>L.ult || p== 0 )
return(-1);
else

Pag 63
Estructura de datos

return (--p);
}

logico Vacia(Lista &L)


{
if (L.ult==-1)
return 1;
else
return 0;
}
logico Llena(Lista &L)
{
if (L.ult==TAM-1)
return 1 ;
else
return 0;
}
void Insertar(dato x, posicion p, Lista &L)
{
posicion q;
if ( Llena(L) )
printf("Lista Llena");
else
{
if (p>Fin(L) || p< 0)
printf( "Posicion invalida");
else
{
for ( q= L.ult; q>=p; q--)
L.elem[q+1]=L.elem[q];
L.elem[p]=x;
L.ult++;
}
}
}

void Suprimir(posicion p, Lista &L)


{
posicion q;
if (Vacia(L))
printf("Lista Vacia");
else
if (p>L.ult || p< 0)
printf( "Posicion invalida");
else
{

Pag 64
Estructura de datos

for ( q= p; q<L.ult; q++)


L.elem[q]=L.elem[q+1];
L.ult--;
}
}
posicion Localiza(dato x, Lista &L)
{
posicion q=0;
while (q<Fin(L) && L.elem[q]!=x)
q++;
if (q==Fin(L))
return (-1);
else
return(q);
}
dato Recupera(posicion p, Lista &L)
{
if (p>L.ult || p< 0)
{
printf( "Posicion invalida");
return -1;
}
else
return(L.elem[p]);
}
void Imprime(Lista L)
{
posicion q;
for ( q=0; q<Fin(L); q++)
printf("%d\n",L.elem[q]);
}
void main( void )
{
Lista L;
dato x;
posicion p;
int i;
clrscr();
Anula(L);
Insertar(1,0,L);

for(i=0;i<9;i++)
{
printf("Dame el numero");
scanf("%d",&x);

Insertar(x,Primero(L),L);

Pag 65
Estructura de datos

}
Imprime(L);
printf("La posicion el numero");
scanf("%d",&p);
Suprimir(p,L);
clrscr();
Imprime(L);
x=Recupera(1,L);
printf("%d",x);
x=Anterior(3,L);
printf("%d",x);
x=Siguiente(5,L);
printf("%d",x);

getch();
}

Ventajas y Desventajas de la implementacin de Listas con arreglos.


La implementacin con arreglos requiere especificar el tamao mximo de una
lista en tiempo de compilacin, esto quiere decir que el tamao es fijo y no
puede establecerse mientras se est ejecutando el programa, lo cual implica
conocer con exactitud la longitud de la Lista para evitar desperdicio de
memoria o bien si se estableci de un tamao mximo y al utilizarla se rebasa,
esto implicara falla del programa, en otras palabras, un arreglo es una variable
esttica.

Las operaciones como INSERTA y SUPRIME requieren un tiempo proporcional al


nmero de elementos que haya que desplazar, y por lo general ests son las
operaciones ms importantes.

Una ventaja es que como es bien sabido, los arreglos son estructuras de
acceso directo, por lo que las dems operaciones son triviales, as que una
representacin con arreglos en un momento dado pudiera ser de utilidad.

El tipo de Dato Abstracto PILA


Una pila es un tipo especial de Lista en la que todas las inserciones y
supresiones tienen lugar en un extremo denominado tope. Son estructuras
LIFO (Last In First Out) o ltimo en entrar, primero en salir en las que solo es
posible quitar el elemento que se encuentra en la parte superior de la pila
(tope), son muy tiles en muchos aspectos de la programacin, tales como
evaluacin de expresiones, anidacin de parentisis, asi como en la
implementacin de rutinas recursivas, entre muchas otras aplicaciones. Una
estructura de este tipo generalmente incluye las Operaciones siguientes:

1. ANULA(P). Que convierte la pila en una Pila Vaca.


2. TOPE(P). Devuelve el elemento que est en el tope de la Pila.
3. POP(P). Suprime el elemento que est en el tope de la pila P.

Pag 66
Estructura de datos

4. PUSH(x, P). Inserta el elemento x en la parte superior de la Pila.


5. VACIA(P). Devuelve verdadero si la Pila est vaca y falso en caso
contrario.
6. LLENA(P). Regresa verdadero si la Pila est llena y falso si no lo est.

Implementacin de Pilas basadas en Arreglos

Para representar una Pila con arreglos es posible hacerlo de manera similar
como se hizo con las Listas con arreglos, establecemos la Pila como un registro
don dos campos, el primero un arreglo para contener los datos de la Pila y un
campo para almacenar la posicin del elemento superior de la pila (que en lo
sucesivo llamaremos tope).

#define TAM 38
typedef char tipo_elem;
typedef int logico;

struct Pila{
Tipo_elem elemento[TAM];
Int tope;
};

1
2
3
4

.
tope .
.

TAM

Pag 67
Estructura de datos

void ANULA(Pila &P){


P.tope =TAM +1; {Creamos una Pila Vaca}
}

tipo_elem TOPE(Pila &P){


if (VACIA(P))
return TAM +1;
else
return L.elemento[P.tope];
}

void POP(Pila &P){


if (VACIA(P))
printf (Error, Pila Vaca\n);
else
P.tope++; /*Eliminamos el elemento del tope*/
}

void PUSH(tipo_elemento x,Pila &P){


if (LLENA(P))
printf(Error, Pila Llena\n)
else {
P.tope--;
P.elemento[P.tope]=x;
}
}

int VACIA(Pila &P){


return P.tope==TAM+1;
}

int LLENA(Pila &P){


return P.tope==1;
}

Notacin Polaca
Las expresiones se componen de operandos, operadores y delimitadores. Los
operandos son valores numricos que se utilizan para calcular la expresin. Los
operadores indican las operaciones matemticas que van hacerse sobre los
operandos respectivos. Tambin determinan la cantidad de operandos
necesarios para cada tipo de operacin (binarios y unarios).

Es evidente que el orden en que se calculan las operaciones puede ser muy
importante, como en la expresin 6 + 4/2. Si la resolvemos como (6+4)/2, la
respuesta es 5, si lo hacemos como 6 + (4/2), el resultado es 8.

Pag 68
Estructura de datos

Los operadores tienen su precedencia

Operador Valor
3
x,/ 2
+,- 1

Para cambiar el orden de clculo de una expresin, pueden utilizarse parntisis


pero en su ausencia las operaciones de mayor precedencia se resuelven
primero. Cuando una expresin incluye operaciones de igual precedencia, se
calculan de izquierda a derecha.

La forma ms usual de representar una expresin es la forma infija, es decir,


colocamos los operadores entre sus operandos.

A + B (infija)
A B + (posfija o polaca)
La segunda forma con el operador despus del operando se conoce como
notacin posfija (polaca).

Al utilizar las reglas de precedencia de operadores, podemos convertir


expresiones infijas a la notacin polaca correspondiente, los pasos que
debemos seguir son:

1. Encerrar entre parentisis toda la expresin infija.


2. Volver a colocar (mover) los operadores, uno por uno y en orden de
precedencia, a su posicin final en notacin postfija (a la derecha de sus
operandos).
3. Quitar los parentisis.

Por ejemplo, vamos a convertir la expresin a + b X c a notacin polaca. El


primer paso es agregar los parntesis:

a + (bXc)

Despus se colocan los operadores en orden de precedencia, as el primero que


debemos mover es el signo de multiplicacin, X, para que la expresin
resultante sea de este modo:

a + (bcX)
Los dos operandos del operador X son b y c, por lo que es fcil determinar la
posicin posfija de ese signo, pero cuales son los dos operandos del operador
+?, la respuesta es a y el resultado de la subexpresin (bXc). Y ponemos el
operador + despus del parentisis de cierre:

Pag 69
Estructura de datos

a(bcX)+
El paso final es quitar el parntesis

abcX+
Ahora, usando parntesis, vamos a cambiar el orden del clculo de los
operandos y a convertir la expresin (a + b) X c en notacin polaca:

(a + b) X c expresin infija
(a + b) X c se aaden parntesis sin cambio
(ab +)X c se convirti el +
(ab +) c X se convirti el X
ab + c X se elimin el parntesis

Recursividad
Un subprograma (procedimiento o funcin) recursivo es aquel que se llama as
mismo. La recursividad es una alternativa a la iteracin o repeticin, y aunque
en tiempo de computadora y en ocupacin de memoria es la solucin recursiva
menos eficiente que la solucin iterativa, existen numerosas situaciones en las
que la recursividad es una solucin simple y natural a un problema que en caso
contrario sera difcil de resolver.

Una aplicacin importante de las pilas se da en la aplicacin de procedimientos


recursivos, en los lenguajes de programacin. La organizacin a tiempo de
ejecucin de uno de tales lenguajes es el conjunto de estructuras de datos
usadas para representar los valores de las variables de un programa durante
su ejecucin. Todo lenguaje que como Pascal, permita procedimientos
recursivos, utiliza una pila de registros de activacin, para representar los
valores de todas las variables que pertenecen a cada procedimiento activo de
un programa. Cuando se llama a un procedimiento P, se coloca en la pila un
nuevo registro de activacin para P, con independencia de si ya existe en ella
otro registro de activacin para ese mismo procedimiento. Cuando P vuelve, su
registro de activacin debe estar en el tope de la pila, pueso que P no puede
volver si no lo han hecho todos los procedimientos a los que P ha llamado. As,
se puede sacar de la pila el registro de activacin correspondiente a la llamada
actual de P y hacer que el control regrese al punto en el que P fue llamado
(este punto, conocido como direccin de retorno, se coloc en el registro de
activacin de P al llamar a este procedimiento).

Ordenamiento Rpido (Quick Sort)

Pag 70
Estructura de datos

Es uno de los mtodos ms rpidos y frecuentemente utilizados en ordenacin


(Quick Sort) Fue inventado por C.H. Hoare, y la cantidad de cdigo necesario
es sorprendentemente pequeo comparando con la excelente velocidad que
proporciona.

La idea bsica de la ordenacin rpida es:


Elegir un elemento de la lista denominado pivote.
Dividir o partir la lista original en dos sublistas o mitades, de modo que
en una de ellas estn todos los elementos menores que el pivote;
Las sublistas deben ser ordenadas, independientemente, del mismo
modo, lo que conduce a un algoritmo recursivo.

La eleccin del pivote es arbitraria aunque por comodidad es usual utilizar el


termino central de la lista original, o bien el primero o el ltimo elemento de la
misma.

9 23 31 17 21 19 13 15 26

1. Elijamos el elemento pivote; supongamos el trmino central, 21.

9 23 31 17 // 19 13 15 26
21//

pivote
2. A continuacin se establecen dos punteros en la lista I o J. El primer
puntero apunta al primer elemento. Por consiguiente, I =1. El segundo
puntero apunta al ltimo elemento y, por lo tanto, J=9 (noveno
elemento).

9 23 31 17 // 19 13 15 26
21//

I= 1 J=9

3. Mientras I apunte a un elemento que sea menor que 20, se incrementa


el valor de I en 1, hasta que se encuentre un elemento mayor que el
pivote. A continuacin se realiza la misma tarea con el puntero J,
buscando un elemento menor que 21, y mientras no lo encuentra se
decrementa J en 1.

Pag 71
Estructura de datos

9 23 31 17 // 19 13 15 26
21//

I J
4. Se intercambian los elementos apuntados por I y J y a
continucacin se incrementan en uno los contadores I, J.

9 15 31 17 // 19 13 23 26
21//

I J
5. El proceso se repite

9 15 13 17 // 19 31 23 26
21//

I J

9 15 13 17 // 19 31 23 26
21//

I J

9 15 13 17 // 21 31 23 26
19//

J I

6. En el momento en que J < I, se ha terminado la particin. Se han


generado dos sublistas que tienen las propiedades citadas: la primera

Pag 72
Estructura de datos

sublista, todos los elementos menores o iguales a 20, y en segunda


todos los elementos mayores que 20.

Sublista Izquierda Sublista Derecha

9 15 13 17 19 21 31 23 26

I J I J

9 15 13 17 19 21 31 23 26

I J I J

9 15 13 17 19 21 23 31 26

I J J I

9 13 15 17 19 31 26

J I I J

9 13 15 17 19 31 26

J I J I

Sublista Izquierda II Sublista izquierda 12

9 13 15 17 19

Sublista izquierda 1 ordenada

9 13 15 17 19

Lista Ordenada

Pag 73
Estructura de datos

9 13 15 17 // 21 23 26 31
19//

Algoritmo de Ordenacin Rpida:

1. Inicializar I a Primero (primer indice de la lista)


2. Inicializar J a Ultimo (ltimo indice en la lista)
3. Seleccionar el elemento pivote (termino Central)
Central = [(Primero + Ultimo) div 2]
4. Repetir
Mientras A[I] < Central hacer
I=I + 1
Mientras A[J] > Central hacer
J=J+1
Si I <= J entonces Intercambiar A[I],A[J] hasta que
J<I
Hasta _que I>J
5. Si J > Primero, llamar al procedimiento Partir, para dividir la sublista
izquierda [Primero..J]
6. Si I < Ultimo, llamar al procedimiento Partir, para dividir la sublista
derecha [J..Ultimo]

Cdigo para Quick Sort

typedef
int enteros;
enteros Lista[]

void Intercambiar(int &m,int &n ){


int Aux ;
aux =m; /*intercambiar*/
m = n;
n = aux;
}

Pag 74
Estructura de datos

void Partir(int primero,int ultimo,enteros Lista[] ){


int I,J, Central;
{partir}
I =primero;
J =ultimo;
//encontrar elemento pivote central
Central = lista [(primero + ultimo)div 2];
do{
while (lista[I]< central)
I++;
while (lista[J]> central)
J--;
if (I <= J){
Intercambiar(lista[I], lista[J]);
I++;
J--;
} // if
}
while (I<=J);
if (primero < J)
Partir (primero, J,Lista);
if (I<ultimo)
Partir(I, ultimo,Lista)
} { partir }

void rapido(enteros a[],int N){


Partir (l, n,a)
}

Torres de Hanoi

Un caso tpico de resolucin de un problema con un mtodo recursivo es el


juego de nios conocidos como torres de hanoi.

Se dispone de tres postes (1,2,3) con soportes de madera (varillas de alambre


o similar) y un juego de discos de diferentes tamaos (el nmero de ellos se
leer en el programa principal) que se situan en el primer poste, el disco de
mayor tamao (dimetro) se sita en el fondo y el ms pequeo en la parte
superior. El juego consiste en mover los discos del poste 1 al poste 3 de
acuerdo a las siguientes reglas:

1. Slo un disco se puede mover a la vez.


2. Un disco nunca puede estar encima de otro disco con un dimetro ms
pequeo.

Pag 75
Estructura de datos

3. Un disco siempre debe estar en uno de los postes (Excepto cuando se


este moviendo.

Anlisis
El problema a primera vista parece sencillo, pero su solucin es francamente
difcil y slo la solucin recursiva facilita la resolucin. Tres, cuatro discos son
imaginables, 64 (las leyendas citan esta cifra como la propuesta de un rey
tibetano a sus subditos, al estilo del tambin famoso problema del tiempo
necesario para llenar un tablero de ajedrez en progresin geomtrica) es
prcticamente inimaginable y casi imposible, sin solucin recursiva.

Algoritmo (3 discos)

Mover dos (3-1) discos desde el poste 1 hasta el poste 2.


Mover el disco ms grande desde el poste 1 hasta el poste 3.
Mover los dos discos desde el poste 2 hasta el poste 3 utilizando el poste
1.

Algoritmo (n discos)
Mover n-1 discos desde 1 hasta el 2 utilizando el poste 3.
Mover el disco restante desde 1 hasta el 3.
Mover la torre de n-1 discos desde el poste 3 utilizando el poste 1.

Situacin
inicial

Poste 1 Poste 2 Poste 3

Pag 76
Estructura de datos

Poste 1 Poste 2 Poste 3

Este modelo de solucin es recursivo.


#include

typedef int Poste;

typedef int Pos ;

Pos NumDiscos;

void Mover Disco (Poste Desde,Poste Hasta ){


printf (mover un disco desde el poste %d, desde);
printf(hasta el poste %d\n, hasta);
}

void MoverTorre(Pos N,Poste Uno,Poste Dos,Poste Tres){


if (N==1)
MoverDisco (Uno, Tres);
else{
MoverTorre (N 1, Uno, Tres, Dos);
MoverDisco (Uno, Tres);
Mover Torre ( N 1, Dos, Uno, Tres);
}
}

main(){
printf (Introduzca nmero de discos en juego\n);
scanf(%d,&NumDiscos);
printf(Para %d discos, NumDiscos);
printf (Los movimientos sucesivos son :\n);
MoverTorre(NumDiscos, 1, 2, 3);
}

El nmero de movimientos H(n) para una torre de n discos se calcula as:

H(1) =1
H(n) = 1 + 2 H(n-1) (n>1)

Con lo que se puede deducir

H(n) = 2 E(n) 1 para n > = 1

Pag 77
Estructura de datos

Para una torre de 4 discos los movimientos son 15 y para una torre de 64
discos los movimientos son inimaginables 2 E(64) 1.

Colas
Una cola es otro tipo especial de lista en el cual los elementos se insertan en
un extremo (el posterior ) y se suprimen en el otro (el anterior o frente). Las
colas se conocen tambin como listas <<FIFO>> (first-in, first out) o listas
<<primero en entrar, primero en salir>>. Las operaciones para una cola son
anlogas a las de las pilas, las diferencias sustanciales consisten en que las
inserciones se hacen al final de la lista, y no al principio, y en que la
terminologa tradicional para colas y listas no es la misma. Se usarn las
siguientes operaciones con colas.

Operaciones

1. ANULA( C) convierte la cola C en una lista vaca.


2. FRENTE( C) es una funcin que devuelve el valor del primer elemento de
la cola C. FRENTE( C) se puede escribir en funcin de operaciones con
listas, como RECUPERA (PRIMERO( C), C).
3. PONE_EN_COLA(x, C) inserta el elemento x al final de la cola C. En
funcin de operaciones con listas, PONE_EN_COLA(x, C) es INSERTA(x,
FIN( C), C).
4. QUITA_DE_COLA( C) suprime el primer elemento de C; es decir,
QUITA_DE_COLA( C) es SUPRIME(PRIMERO( C), C).
5. VACIA( C) devuelve verdadero si, y slo si, C es una cola vaca.

Implementacin de Colas basadas en Arreglos

La representacin de colas por medio de arreglos, se le conoce como Colas


Circulares. La estructura de datos para representarlas se muestra a
continuacin:

#define TAM 20
#define TRUE 1
#define FALSO 0
typedef int tipo_elem,logico;

typedef struct{
Tipo_elem elemento [TAM];
Int final, frente;
}Cola;

Pag 78
Estructura de datos

Final Frente

En la figura se representa una cola vaca, esto sucede si anterior est en la


posicin que sigue a la posicin del posterior.

void ANULA(COLA &C){


C.Final =TAM-1;
C.Frente =1;
}

logico VACIA(COLA &C){


if ((C.final+1==C.frente) || (C.final ==TAM-1 && C.frente==0))
return (TRUE);
else
return (FALSE);
}

tipo_elemento FRENTE (COLA &C){


if (VACIA ( C))
return -1;
else
return (C.elementos[C.Frente]);
}

void PONE_EN_COLA(tipo_elem x;COLA &C){


if (LLENA( C)))
printf(la cola est llena)
else{
if (C.final==TAM-1)
C.final =0;

Pag 79
Estructura de datos

else
C.final++;
c.elem[C.final]=x;
}
}

void QUITA_DE_COLA(COLA &C){


if (VACIA ( C))
printf(la cola est vaca)
else{
if(C.frente==TAM-1)
C.frente =0;
else
C-frente++;
}
}

Concepto de apuntador

En una computadora cada posicin de memora tiene una direccin y un valor


especifico almacenado en esa posicin. Se han utilizado nombres de variables
en lugar de direcciones. Para almacenar un nuevo valor a la memoria se asigna
a una variable, y la computadora enva una direccin a la memoria seguida por
el valor a almacenar en esa posicin. Con los apuntadores se puede hacer
referencia a variables por sus direcciones.

Considerese un programa que procese registros de empleados [es comn que


los registros de empleados sean muy largos; para nuestro ejemplo,
supondremos que su tamao es de 2048 bytes. Supongamos que ya hemos
escrito una funcin para la nmina que procesa estos registros e imprime
recibos. Una forma de suministrarle datos a nuestra funcin es pasarle cada
uno de los registros de empleados como argumento.

Estructuras autoreferenciadas
Una estructura autoreferenciada contiene un miembro de apuntador que
apunta a una estructura del mismo tipo de estructura. Por ejemplo, la
definicin

struct nodo {
int dato;
struct nodo *proxPtr;
};

Define un tipo struct nodo. Una estructura del tipo struct nodo tiene dos
miembros el miembro entero dato y el miembro de apuntador proxPtr. El
miembro nextPtr apunga a una estructura de tipo struct nodo Una estructura

Pag 80
Estructura de datos

del mismo tipo que la que se est declarando aqu, de ah el termino


estructura autorreferenciada. El miembro proxPtr se conoce como un enlace o
vnculo es decir proxPtr puede ser utilizada para vincular una estructura del
tipo struct nodo con otra estructura del mismo tipo. Las estructuras
autorreferenciadas pueden ser enlazadas juntas para formar tiles estructuras
de datos como son las listas, las colas de esperas, las pilas y los rboles.

15 10

Dos estructuras autoreferenciadas enlazadas juntas

Listas implementadas con cursores


Algunos lenguajes, como FORTRAN y ALGOL, no tienen apuntadores. Si se
trabaja con un lenguaje tal, se pueden simular los apuntadores mediante
cursores; esto es, con enteros que indican posiciones en arreglos. Se crea un
arreglo de registros para almacenar todas las listas de elementos cuyo tipo es
tipo_elemento; cada registro contiene un elemento y un entero que se usa
como curso Es decir, se define

#define TAM 10
typedef int Tipo_elem;

struct ESPACIO{
Tipo_elem Elemento;
int sig;
}Lista[TAM];
int dis,Prim;

Para la realizacin de este tipo de listas nos podemos auxiliar de una variable
llamada Disponible, que nos da la posicin del arreglo del primer disponible o
vaca y en el caso de saber donde inicia la lista hacemos uso de una variable
entera que llamaremos Primero.

Pag 81
Estructura de datos

ESPACIO
1 D 8
2 4
Prim 3 C 1
4 6
5 A 9
6 7
7 0
8 E 5
9 B 0
Disponible 10 10
11 2
Elemento sig

Estructuras de Datos lineales, representaciones ligadas

Teora de lista ligadas


La segunda forma de realizacin de listas, celdas enlazadas sencillas, utiliza
apuntadores para enlazar elementos consecutivos. Esta implantacin permite
eludir el empleo de memoria contigua para almacenar una lista y, por tanto,
tambin elude los desplazamientos de elementos para hacer inserciones o
rellenar vacos creados por la eliminacin de elementos. No obstante, por esto
hay que pagar el precio de un espacio adicional para los apuntadores.

Listas con encabezado

Lista simplemente ligada

Pag 82
Estructura de datos

En esta representacin, una lista est formada por celdas; cada celda contiene
un elemento de la lista y un apuntador a la siguiente celda. Si la lista a1, a2,
a3,...,an, la celda que contiene ai tiene un apuntador a la celda que contiene a
ai+1, para i=1,2,...,n 1. La celda que contiene an posee un apuntador a
NULL. Existe tambin una celda de encabezamiento que apunta a la celda que
contiene a1; esta celda de encabezamiento no tiene ningn elemento. En este
caso hablamos de una lista simplemente ligada con nodo de encabezamiento
vaco, en la que el empleo de una celda completa para el encabezado simplifica
la implementacin de las operaciones para manipular la lista, aunque tambin
se puede utilizar el encabezado para almacenar el primer elemento y a este
tipo de representacin se le conoce como lista simplemente ligada con nodo de
encabezamiento no vaco, en la que las inserciones y supresiones al principio
de la lista se manejan de manera especial.

En el caso de una lista con encabezado vacio, el apuntador al siguiente nodo es


NULL ya que no se tienen ms celdas. La estructura de datos que emplearemos
para representar una lista con apuntadores ser un registro con dos campos,
uno para guardar el elemento de la lista y otro para mantener la direccin del
siguiente nodo.

A continuacin se muestra la figura de una lista simplemente ligada lineal con


nodo de encabezamiento vaco y longitud n.

A1 A2 An

encabezado Lista

Las operaciones ms comunes a las listas se muestran a continuacin:

typedef tipo_elemento:

struct nodo{
tipo_elemento elemento;
nodo *sig;
}

typedef nodo *Nodo;

void INICIALIZA ( Nodo Encabezado){


Encabezado =new(nodo); //Se crea el nodo de encabezado
Encabezado->sig =NULL; //Se le pone nil al campo siguiente, ya que la lista
est vaca

Pag 83
Estructura de datos

{La siguiente figura muestra una lista vaca}

encabezado

void INSERTA (tipo_elemento x, Nodo p ){


//Coloca el elemento x delante de la celda que apuntada por p
Nodo aux;
aux= new(nodo); //Se reserva memoria para el nuevo nodo
aux-> elemento=x; //Se almacena el elemento
aux->sig =p->sig; //Se enlaza con el siguiente nodo de p
p->sig=aux; //p se enlaza con el nuevo nodo
}

A1 A2 An

encabezado p x

aux

void SUPRIME(Nodo p){ //Suprime el nodo siguiente al apuntado por p


Nodo aux;
Aux=p->sig; //Se almacena la direccin del nodo que se desea eliminar
p->sig=aux->sig //Se enlaza con el nodo siguiente al que se quiere
suprimir
delete(aux); //Se libera el espacio de memoria ocupada por
el nodo
}

A1 x A2 An

encabezado p aux

Pag 84
Estructura de datos

Nodo LOCALIZA(tipo_elemento x,Nodo p){


//Si encuentra el dato en la lista devuelve el apuntador al nodo anterior a
donde lo encontr, //en caso contrario regresa nil
while ((p->sig!=NULL)&&(p->sig->elemento!=x){
p=p->sig;
if (p->sig=NULL)
return NULL;
else
return p;
}

tipo_elemento RECUPERA (Nodo p){


//regresa el elemento del nodo siguiente al apuntado por p
if (!VACIA(p))
return p->sig->elemento;
else
printf(Error, no hay elemento);
}

int VACIA (Nodo p){


VACIA=p->sig==NULL;
}

void IMPRIME (Nodo p){


if (VACIA(p))
printf(Error, Lista Vaca);
else
while (!VACIA(p)) {
printf(%d,RECUPERA(p));
p=p->sig;
}
}

Lista simplemente ligada circular


La implementacin de una lista tambin puede hacerse mediante una lista
simplemente ligada circular, que es muy similar a la anterior, con la diferencia
de que el ltimo nodo contiene en su campo siguiente un apuntador al
encabezado en lugar de NULL.

La estructura de datos que se emplea para representar este tipo de lista es la


misma que para una lista ligada lineal, y al igual que estas tambin el nodo de
encabezado puede contener informacin.

A1 A2 An
Pag 85

encabezado Lista
Estructura de datos

Una desventaja de las listas lineales es que dado un apuntador p a un nodo de


la lista, no se puede tener acceso a cualquier otro nodo anterior a p. Si se
recorre una lista, el apuntador al encabezado no debe ser modificado a fin de
poder hacer referencia a esta lista. Si hacemos un pequeo cambio en la
estructura de tal manera que el ltimo nodo en lugar de tener en el campo
siguiente un apuntador nulo (nil) tenga la direccin al inicio de la lista,
entonces desde cualquier otro punto de la lista es posible llegar a cualquier
otro punto. En este caso la lista se llama lista circular.

typedef int tipo_elemento


struct nodo{
Tipo_elemento Elemento;
nodo *sig;
}
typedef nodo *Nodo;

void INICIALIZA(Nodo Encabezado){ //Se crea el nodo de encabezado


encabezado = new(nodo); //Se hace que el campo siguiente del
encabezado->sig = encabezado;
} {encabezado se apunte asi mismo}

encabezado

void INSERTA (tipo_elemento x, Nodo p){


/*Coloca el elemento x delante de la celda apuntada por p es
exactamente igual que una lista lineal */
Nodo aux;
aux =new(nodo); /*Se reserva memoria para el nuevo nodo*/
aux->elemento =x; /*Se almacena el elemento*/
aux->sig =p^->sig; /*Se enlaza con el siguiente nodo de p*/
p->sig = aux; /*p se enlaza con el nuevo nodo*/
}

void SUPRIME(Nodo p){


//Suprime el nodo siguiente al apuntado por p
Nodo aux;
Aux =p->sig; //Se almacena la direccin del nodo que se desea
eliminar
p->sig =aux->sig; //Se enlaza con el nodo siguiente al que se quiere
suprimir
delete(aux); //Se libera el espacio de memoria ocupado por el nod

Pag 86
Estructura de datos

Nodo LOCALIZA(tipo_elemento x,Nodo p)


/*Si se encuentra el dato en la lista devuelve el apuntador al nodo anterior a
donde lo encontr, en caso contrario regresa nil, en este caso hay que
almacenar la direccin del nodo de encabezado para saber cuando recorrimos
toda la lista*/
Nodo q;
q=p;
while (p->sig!=q) &&(p->sig->elemento!=x)
p =p->.sig;
if (p->sig==q)
return NULL;
else
return p;
}

tipo_elem RECUPERA(Nodo p){


//Regresa el elemento del nodo siguiente al apuntado por p
if (p->sig!=p)
return p->sig->elemento;
else
return -1;
}

int VACIA(Nodo p){


VACIA=p->sig==p //Si el que sigue del nodo de encabezado, es l
mismo
} entonces no hay datos

void IMPRIME(Nodo p){


Nodo q;
if (VACIA(p ))
printf(Error, Lista Vaca\n);
else{
q = p;
while (p!=q->sig){
printf(%d,RECUPERA(p));
p = p->sig;
}
}
}

Ventajas de la representacin con apuntadores


Operaciones como INSERTAR Y SUPRIMIR tienen un nmero constante de
pasos para este tipo de listas, sin importar cuntos elementos contenga, a

Pag 87
Estructura de datos

diferencia de la representacin con arreglos que requiere un tiempo


proporcional al nmero de elementos que siguen.

En la implementacin con arreglos se desperdicia espacio, independientemente


de el nmero de elementos que en realidad tiene la lista en un momento dado,
mientras que la representacin con apuntadores utiliza slo el espacio
necesario para los elementos que contenga la lista en cada momento, pero
requiere espacio para almacenar el apuntador a la siguiente celda. Asi que
cualquiera de los dos mtodos podra usar ms espacio que el otro
dependiendo de las circunstancias.

Lista doblemente ligada lineal


En algunas aplicaciones puede ser deseable poder recorrer eficientemente una
lista, tanto haca adelante como hacia atrs, o dado un elemento podra
determinarse con rapidez el siguiente y el anterior. Para representar estas
situaciones se emplean las listas Doblemente ligadas, en las que cada nodo o
celda de la lista adems de contener el elemento, contiene dos apuntadores,
uno a la celda siguiente y otro a la celda anterior. De la misma manera que las
listas simplemente ligadas, ests cuentan con un nodo de encabezamiento, que
contiene la direccin del nodo que contiene al primer elemento de la lista o
bien este nodo de encabezamiento puede contener al primer dato.

Aun cuando una Lista Circular tiene ventajas sobre una Lista Lineal, sta
todava tiene algunas deficiencias, uno no puede recorrer esta lista en direccin
contraria, ni tampoco se puede eliminar un nodo de una lista simplemente
ligada circular simplemente un apuntador a ese nodo. En el caso de que se
requieran tener estas flexibilidades la estructura de datos para representar una
lista con apuntadores es la lista doblemente ligada.

Como se mencion anteriormente, cada nodo en esta lista contiene dos


apuntadores, uno es predecesor y el otro a su sucesor. Este tipo de lista puede
ser tanto lineal como circular y puede contener o no un nodo de encabezado.

La estructura de datos para representar este tipo de Lista sera un nodo com
un registro con tres campos, uno para almacenar al elemento de la lista, y dos
para almacenar las direcciones de los nodos siguiente y anterior a uno dado.

Para insertar un dato hay que saber si se quiere insertar a la derecha o a la


izquierda, adems de que la insercin al principio y al final son diferentes, y
sucede lo mismo para suprimir.

Pag 88
Estructura de datos

typedef int tipo_elem


struct nodo{
Tipo_elem elemento;
nodo *sig, *ant ;
};
typedef nodo *Nodo;
Una lista Vaca se representara de la siguiente manera:

Encabezado

nil

void INICIALIZA (Nodo Encabezado){


Encabezado =new(nodo); //Se crea el nodo de encabezado
Encabezado->sig =NULL;
Encabezado->ant =NULL;
}

{La siguiente figura muestra una lista vaca}

void INSERTA (tipo_elemento x, Nodo p ){


//Coloca el elemento x delante de la celda que apuntada por p
Nodo aux;
aux = new(nodo); //Se reserva memoria para el nuevo nodo
aux-> elemento =x; //Se almacena el elemento
aux->sig =p->sig; //Se enlaza con el siguiente nodo de p
aux->ant=p;
if (p-<sig!=NULL)
p->sig->ant =aux; {REVISARp se enlaza con el nuevo nodo}
p->sig =aux;
}

Pag 89
Estructura de datos

void SUPRIME(Nodo p){ //Suprime el nodo siguiente al apuntado por p


Nodo aux;
aux =p->sig->sig;
delete(p->sig);
p->sig=aux;
if (p->sig!=NULL)
aux->ant=p;
}

Nodo LOCALIZA(tipo_elemento x,Nodo p){


/*Si encuentra el dato en la lista devuelve el apuntador al nodo anterior a
donde lo encontr, en caso contrario regresa nil*/
Nodo aux;
aux=p;
while (aux->sig!=NULL)&& (aux->sig->elemento!=x) {
aux =aux->sig;
if (aux->sig==NULL)
return NULL;
else
return aux;
}

tipo_elemento RECUPERA(Nodo p){


//regresa el elemento del nodo siguiente al apuntado por p
if (! VACIA(p))
return p->sig->elemento;
else
return -1;
}

int VACIA (Nodo p){


VACIA=p->sig==NULL;
}

Nodo ANTERIOR(Nodo p){


if (p->ant->ant!=NULL)
return p->ant;
else
return NULL;
}

void IMPRIME (Nodo p){

Pag 90
Estructura de datos

Nodo Aux;
Aux =p;
if (VACIA(p))
printf (Error, Lista Vaca\n);
else
while (!VACIA(p)) {
printf(%d,RECUPERA(p));
p=p->sig;
}
}

Lista doblemente ligada circular


Como se puede apreciar, este tipo de lista es similar a una lista Doblemente
Ligada Lineal, pero en esta, el primer nodo (el encabezado) en su campo
anterior contiene un apuntador al ltimo nodo de la lista, mientras que el
ltimo nodo en su campo siguiente contiene la direccin del primer nodo es
decir del encabezado.

typedef int tipo_elem


struct nodo{
Tipo_elem elemento;
Nodo *sig, *ant;
}

typedef nodo *Nodo;

Encabezado

void INICIALIZA(Nodo Encabezado){


Encabezado = new(nodo); //Se crea el nodo de encabezado
Encabezado->ant =Encabezado;
Encabezado->sig =Encabezado; //Se hace que el campo siguiente del
} //encabezado se apunte asi mismo

Pag 91
Estructura de datos

void INSERTA (tipo_elemento x, Nodo p){


/*Coloca el elemento x delante de la celda apuntada por p es exactamente
igual que una lista lineal */
Nodo aux;
aux =new(nodo); //Se reserva memoria para el nuevo nodo
aux->elemento=x; //Se almacena el elemento
aux->ant =p;
aux->sig=p->sig; //Se enlaza con el siguiente nodo de p
if (p->sig==p)
p->ant = aux //p se enlaza con el nuevo nodo
else
p->sig->ant =aux;
p->sig =aux;
}

void SUPRIME(Nodo p){


//Suprime el nodo siguiente al apuntado por p
Nodo aux;
aux=p->sig->sig;
delete (p->sig);
p->sig = aux;
aux->ant=p;
}

Nodo LOCALIZA(tipo_elemento x,Nodo p){


/*Si se encuentra el dato en la lista devuelve el apuntador al nodo anterior a
donde lo encontr, en caso contrario regresa nil, en este caso hay que
almacenar la direccin del nodo de encabezado para saber cuando recorrimos
toda la lista*/
Nodo aux;
aux=p;
while ((p->sig!=aux) && (p->sig->elemento!=x)) {
p=p->sig;
if (p->sig==aux) then
return NULL;
else
return p;
}

tipo_elem RECUPERA(Nodo p){


//Regresa el elemento del nodo siguiente al apuntado por p
if (p->sig!=p)
return =p->sig->elemento;

Pag 92
Estructura de datos

else
return -1;
}

int VACIA(Nodo p){


return p->sig==p; //Si el que sigue del nodo de encabezado, es l
mismo
}
// entonces no hay datos

void IMPRIME(Nodo p){


Nodo q;
if (VACIA(p ))
printf (Error, Lista Vaca);
else{
q=p;
while(p!=q->sig)
printf(%d,RECUPERA(p));
p=p->sig;
}
}

Listas sin encabezado


Lista simplemente ligada sin encabezado

typedef int tipo_elemento;


struct nodo{
tipo_elemento elemento;
nodo *sig;
}
typedef nodo *Nodo;

void INICIALIZA (Nodo L){


L =NULL;
}

void INSERTA (tipo_elemento x,Nodo p,Nodo L){


//Coloca el elemento x delante de la celda que apuntada por p
Nodo aux,nuevo;
if (p==NULL){
nuevo = new(nodo);
nuevo->elemento=x; //Se almacena el elemento
nuevo->sig =L; //Se enlaza con el siguiente nodo de p
L=nuevo;} //p se enlaza con el nuevo nodo
else{
aux =L->sig;

Pag 93
Estructura de datos

nuevo = new(nodo);
nuevo->tipo_elemento:=x;
nuevo->sig = aux;
L->sig =Nuevo;
}
}

void SUPRIME(Nodo p,Nodo &L){ {Suprime el nodo siguiente al apuntado por


p}
Nodo aux;
if (p==NULL) {
aux:=L->sig;
delete(L);
L=aux;
}
else
{
aux=p->sig->sig;
delete (p->sig);
p->sig =aux;
}
}

Nodo LOCALIZA(tipo_elemento x,Nodo L){


/*Si encuentra el dato en la lista devuelve el apuntador al nodo anterior a
donde lo encontr, en caso contrario regresa nil*/
if (L->info==x) then
LOCALIZA:=nil
else
{
while (L->sig!NULL && L->sig->elemento!=x){
L =L->sig;
if (L->sig==NULL)
return NULL;
else
return L;
}

tipo_elemento RECUPERA (Nodo p){


//regresa el elemento del nodo siguiente al apuntado por p
if (!VACIA(p))
return p->sig->elemento;
else
printf(Error, no hay elemento);
}

int VACIA (Nodo p){

Pag 94
Estructura de datos

return L==NULL;
}

void IMPRIME (Nodo p){


while (L!=NULL){
printf(%d,L->elemento);
L=L->sig;
}
}

void ANULA (Nodo L){


Nodo aux;
while (L!= NULL){
aux=L->sig;
delete (L);
L=aux;
}
}

Implementacion de Pilas basadas en apuntadores


La representacin de pilas basadas en apuntadores es similar a la de una lista
simplemente ligada lineal, en donde el encabezado es el topo de la pila, y las
inserciones y supresiones siempre se hacen en el tope (encabezado).

typedef char tipo_elem;


struct Nodo{
Tipo_elem elemento;
Nodo *sig;
};
typedef Nodo *posicion;
typedef int logico;

void INICIALIZA (posicin P){


P = new Nodo;
P ->sig=NULL
}

logico PILA_VACIA(posicion P){


if (P=nil)
return 1;
else
return 0;
}

void METER(tipo elem x; posicion p){


posicion aux;

Pag 95
Estructura de datos

aux = new(Nodo);
aux->elemento=x;
aux->sig:=P->sig;
P ->sig = aux;
}

void SACAR(posicin P){


posicion Aux:;
if (!PILA_VACIA(P)) {
Aux=P->sig;
P->sig =Aux->sig;
delete(Aux);
}
else
printf(Pila vaca\n);
}

elemento TOPE(posicion P){


if (PILA_VACIA (P))
return -1;
else
return P->sig->elemento;
}

Implementacion de Colas basadas en apuntadores

typedef char tipo_elem;


struct nodo{
Tipo_elem elemento;
nodo *sig;
};
typedef nodo *Nodo;
typedef int logico;

struct Cola{
Nodo Fondo,Frente;
}

void INICIALIZA (Cola C){


C.Frente=NULL;
C.Fondo=NULL;
}

int VACIA(Cola C){


if (C.Frente=NULL)
return 1;

Pag 96
Estructura de datos

else
return 0;
}

void METER (elemento x;Cola C);


Nodo Aux;
Aux =new(Nodo);
Aux->elemento =x;
Aux->sig =NULL;
if (C.Fondo!=NULL) {
C.Fondo->sig =Aux;
C.Fondo =Aux;
}
else{
C.Frente =Aux;
C.Fondo =Aux;
}
}

void SACAR (Cola C){


Nodo Aux:;
if (! VACIA( C)) {
Aux:=C.Frente;
C.Frente =Aux->.sig;
delete(aux);
}
else
printf(lista vaca);
}

elemento PRIMERO (Cola C){


if (!VACIA( C))
return C.Frente->elemento;
else
return -1;
}

elemento ULTIMO (Cola C){


if (!VACIA( C))
return C.Fondo->elemento;
else
return -1;
}

Estructura de datos no lineales, representaciones secuencial y ligada

Pag 97
Estructura de datos

Teora general de rboles


Un rbol impone una estructura jerrquica sobre una coleccin de objetos. Los
rboles genealgicos y los organigramas son ejemplos comunes de rboles.
Entre otras aplicaciones, los rboles se emplean para analizar circuitos
elctricos y para representar la estructura de frmulas matemticas, as como
para organizar la informacin de bases de datos y para representar la
estructura sintctica de un programa fuente en compiladores. A continuacin
se representan las definiciones bsicas y algunas de las operaciones ms
comunes con rboles y veremos como esta estructura de datos puede ser
representada en Pascal.

rboles binarios
rbol Binario
Un rbol binario es un conjunto finito de elementos que puede estar vaco o
contener un elemento denominado la raiz del rbol, esta raz contiene cuando
mucho un subrbol izquierdo y ubrbol derecho; los cuales a su vez tambin
son rboles binarios. A cada elemento de un rbol binario se le denomina Nodo
del rbol.

Formalmente, un rbol se puede definir de manera recursiva como sigue:

1. Un nodo es, por si mismo, un rbol. Ese nodo es tambin la raz de dicho
rbol.
2. Supongamos que n es un nodo y que A1, A2,...Ak son rboles con races
n1,n2,...nk, respectivamente. Se puede construir un nuevo rbol
haciendo que n se convierta en el padre de los nodos n1,n2,..., nk. En
dicho rbol, n es la ras y A1,A2,..,Ak son los subrboles de la raz. Los
nodos n1,n2,....nk reciben el nombre de hijos del nodo n.

raz

Subrbol izquierdo

Subrbol
derecho
Pag 98
Estructura de datos

Una forma de visualizar un rbol binario es considerar cada nodo conformado


por tres campos fundamentales, uno para almacenar la informacin que
contiene el nodo, y dos para almacenar la direccin del subrbol izquierdo y
derecho. Si el subrbol izquierdo o derecho est vaco, contiene un apuntador
nulo o nil.

Arboles binarios de bsqueda


Los rboles binarios pueden ser implementados como un arreglo o mediante el
uso de apuntadores. Como caso particular representaremos la implementacin
con apuntadores de un rbol Binario de bsqueda. Este tipo especial de rbol
tiene como caracterstica la siguiente: Para cada nodo, todos los elementos que
estn a su izquierda son menores que l y todos los elementos que estn a su
derecha son mayores, y esta definicin es recursiva.

Representacin ligada
typedef int tipo_elemento;

struct nodo{
Tipo_elemento Info;
Nodo *Izq, *Der
}

typedef nodo *Nodo;

void INICIALIZA( Nodo R){


R = NULL;
}

void INSERTA(tipo_elemento x,Nodo R){


If (R==NULL){
R= new(nodo);
R->info =x;
R->Izq =NULL;
R->Der =NULL;
}
else
if (x>R->info) {Si el dato a insertar es mayor}
INSERTA(x, R->Der) {Se revisa el lado derecho}
else
if (x<R->Info)
INSERTA(x,R->Izq);
}

Nodo BUSCAR (Info x,Nodo R){


Nodo P;

Pag 99
Estructura de datos

P=Raiz;
if ((P==NULL) || (x=P->Info))
return P;
else
if (x<P->Info)
return BUSCAR(x,P->Izq);
else
return BUSCAR(x,P->Der);
}

Info MXIMO (Nodo P){


while (P->Der !=NULL)
P =P->Der;
return P-> Info;
printf(%d,P->info);
}

Info MINIMO (Nodo P){


while (P->Izq !=NULL)
P=P->Izq;
return P-> Info;
printf(P->Info);
}

Representaciones secuenciales
Una forma conveniente de estructurar datos para representar un rbol binario
consiste en dar a sus nodos los nombres 1, 2, ...,n y utilizar un arreglo de
registros declarado como:

Struct Nodo{
int Hijo_izq;
int Hijo_der;
}Espacio_celdas[TAM_NODOS];

La idea es que espacio_celdas[i]. Hijo_izq sea el hijo izquierdo del nodo i, y


que suceda lo mismo con hijo_der. Un valor 0 en cualquiera de esos campos
indicar la ausencia de un hijo

Recorridos En-Orden, Pre-Orden y Post-Orden


Existen tres mtodos de recorrer todos los nodos de un rbol, todos ellos sern
definidos de manera recursiva, de tal manera que el recorrer un rbol binario
comprende la visita a la raz y el recorrido de los subrboles izquierdo y
derecho. La nica diferencia entre estos mtodos es el orden en que se
realizan estas operaciones.

Recorrido en Preorden u Orden Previo


1. Visitar la raz

Pag 100
Estructura de datos

2. Recorrer el subrbol izquierdo en Preorden


3. Recorrer el subrbol derecho en Preorden

Recorrido en Entreorden u Orden Simtrico


1. Recorrer el subrbol izquierdo en Entreorden
2. Visitar la raz
3. Recorrer el subrbol derecho en Entreorden

Recorrido en Postorden u Orden Posterior


1. Recorrer el subrbol izquierdo en Postorden
2. Recorrer el subrbol derecho en Postorden
3. Visitar la raz

typedef int tipo_elemento;


struct nodo{
Tipo_elemento Info;
Nodo *Izq, *Der;
}

typedef nodo *Nodo;

void PREORDEN(Nodo R)
if (R!=NULL){
printf(%d,R->info); //Visitamos la raz
PREORDEN(R->izq); //Recorremos el subrbol izquierdo
PREORDEN(R->der); //Recorremos el subrbol derecho
}
}

void ENTREORDEN (Nodo R){


if (R!=NULL) {
ENTREORDEN(R->Izq); //Recorremos el subrbol izquierdo
printf(%d,R->Info); //Visitamos la raz
ENTREORDEN(R->Der); //Recorremos el subrbol derecho
}
}

void POSTORDEN(Nodo R){


if (R =! NULL){
POSTORDEN(R->Izq);
POSTORDEN(R->Der);
pirntf(%d,P->info);
}
}

Pag 101
Estructura de datos

Grafos

En los problemas originados en la computacin, matemticas, ingeniera y


muchas otras disciplinas, a menudo es necesario representar relaciones
arbitrarias entre objetos de datos. Los grafos los grafos dirigos y los no dirigos
son modelos naturales de tales relaciones

Grafo dirigido
Un grafo dirigido G consiste en un conjunto de vrtices V y un conjunto de
arcos A. Los vrtices se denominan tambin nodos o puntos; los arcos pueden
llamarse arcos dirigidos o lineas dirigidas. Un arco es un par ordenado de
vrtces (v, w); y es la cola y w la cabeza del arco. El arco (v, w) se expresa a
menudo como v w va de v a w, y que w es adyacente a v.

Los vrtices de un grafo dirido pueden usarse para representar objetos, y los
arcos relacionados ente los objetos. Por ejemplo, los vrtices pueden
representar ciudades y los arcos, vuelos areos de de una ciudad a otra. Un
grafo dirigo puede emplearse para representar el flujo de control en un
programa de computador. Los vrtces representan bloques bsicos, y los arcos
posibles tranasferencias del flujo de control.

Un camino en un grafo dirigo es una secuencia de vrtices v1, v2, ...vn, tal que
v1v2, v2v3,.....vn-1vn.son arcos. Este camino va del vrtices v1 al vrtice
vn, pasa por los vrtices v2, v3,...vn-1 y termina en el vrtice vn. La longitud
de un camino es el nmero de arcos en ese camino, en este caso n-1. Como
caso especial, un vrtice sencillo, v, por si mismo denota un camino de
longitud cero de v a v. En la figura la secuencia 1, 2, 4, es un camino de
longitud 2 que va del vrtice 1 al vrtice 4.

1 2

3 4

Grafo dirigido

Un camino es simple si todos los vrtices, excepto tal vez el primero y el


ltimo, son distintos. Un ciclo simple es un camino simple de longitud por lo

Pag 102
Estructura de datos

menos uno, que empieza y termina en el mismo vrtice. En la figura, el camino


3, 2, 4, 3 es un ciclo de longitud 3.

En muchas aplicaciones es til asociar informacin a los vrtices y arcos de un


grafo dirigido. Para este propsito es posible usar un grafo dirigido etiquetado,
en el cual cada arco, cada vrtice o ambos pueden tener una etiqueta
asociada. Una etiqueta puede ser un nombre, un costo o un valor de cualquier
tipo de datos dado.

La siguiente figura muestra un grafo dirigido etiquetado en el que cada arco


esta etiquetado con una letra que causa una transicin de un vrtice a otro.
Este grafo dirigido etiquetado tiene la interesante propiedad de que las
etiquetas de los arcos de cualquier ciclo que sale del vrtice 1 y vuelve e l
producen una cadena de caminos a y b en el cual los nmeros de a y de b son
pares.

Un grafo dirido etiquetado, un vrtice puede tener a la vez un nombre y una


etiqueta. A menudo se emplear la etiqueta del vrtice como si fuera el
nombre. As, los nmeros de la figura pueden interpretarse como nombres o
como etiquetas de vrtices.

a
1 2
a
b b b b

a
3 4
a

Representacin de grafos dirigidos


Para representar un grafo dirigido se pueden emplear varias estructuras de
datos; la seleccin apropiada depende de las operaciones que se aplicarn a
los vrtices y a los arcos del grafo. Una representacin comn para un grafo
dirido es G= (V,A) es la matriz de adyacencia. Supngase que V=
{1,2,...,n}.La matriz de adyacencia para G es una matriz una matriz A de
dimensin n X n, de elementos bolanos, donde A[i, j] es verdadero si, y slo
si, existe un arco que vaya del vrtice i al j. Con frecuencia se exhibirn
matrices de adyacencias con 1 para verdadero y 0 para falso.; las matrices de
adyacencia pueden incluso obtenerse de esa forma. En la representacin con
una matriz de adyacencia, el tiempo de acceso requerido a un elemento es
independiente del tamao de V y A. As la representacin con matriz de
adyacencia es til en algoritmos para grafos, en los cuales suele ser necesario
saber si un arco dado est presente.

Pag 103
Estructura de datos

Algo muy relacionado con esto es la representacin con matriz de adyacencia


etiquetada de un grafo dirido, donde a[i,j] es la etiqueta del arco que va del
vrtice i al vrtice j. Si no existe un arco de i a j, debe emplearse como
entrada para A[i,j] un valor que no pueda ser una etiqueta vlida.

1 2 3 4
1 a B
2 a b
3 b A
4 b a

Matriz de adyacencia etiquetada para el grafo dirigido de la anterior figura.

La principal desventaja de usar una matriz de adyacencia para representar un


grafo dirido es que requiere un espacio (n) aun si el grafo dirido tiene menos
de n arcos. Slo leer o examinar la matriz puede llevar un tiempo O(n), lo
cual invalidara los algoritmos O(n) para la manipulacin de grafos dirigidos con
O(n) arcos.

Para evitar esta desventaja se puede utilizar otra representacin comn para
un grafo dirigido G=(V,A) llamada representacin con lista de adyacencia. La
lista de adyacencia para un vrtice i es una lista, en algn orden, de todos los
vrtices adyacentes a i. Se puede representar G por medio de un arreglo
CABEZA, donde CABEZA[i] es un apuntador a la lista de adyacencia del vrtice
i. La representacin con lista de adyacencia de un grafo dirigido requiere un
espacio proporcional a la suma del nmero de vrtices ms el nmero de
arcos; se usa bastante cuando el nmero de arcos es mucho menor que n .
Sin embargo, una desventaja potencial de la representacin con lista de
adyacencia es que puede llaeva un tiempo O(n) determinar si existe un arco
del vrtice i al vrtice j, ya que puede haber O(n) vrtices en la lista de
adyacencia para el vrtice i.

La figura muestra una representacin con lista de adyacencia para el grafo


dirigido de la primera figura, donde se usan listas enlazadas sencillas. Si los
arcos tienen etiquetas, stas podran incluirse en las celdas e la lista ligada.

Pag 104
Estructura de datos

1 2 3

2 4

3 2

4
3

Si hubo inserciones y supresiones en las listas de adyacencias, sera preferible


tener el arreglo CABEZA apuntando a celdas de encabezamiento que no
contienen vrtices adyacentes. Por otra parte, si se espera que el grafo
permanezca fijo, sin cambios (o con muy pocos) en las listas de adyacencia,
sera preferible que CABEZA[i] fuera un cursor a un arreglo ADY, donde
ADY[CABEZAA[i]], ADY[CABEZA[i]+1], ..., y as sucesivamente, contuvieran
los vrtices adyacentes al vrtice i, hasta el punto en ADY donde se encuentra
por primera vez un cero, el cual marca el fin de la lista de vrtices adyacentes
a i.

Grafos no dirigidos
Un grafo no dirigido G =(V,A) consta de un conjunto finito de vrtices V y de
un conjunto de aristas A. Se diferencia de un grafo dirigido en que cada arista
en A es un par no ordenado de vrtices. Si (v,w) es una arista no dirigida,
entonces (v,w) =(w,v).

Los grafos se emplean en distintas disciplinas para modelar relaciones


simtricas entre objetos. Los objetos se representan por los vrtices del grafo,
y dos objetos estn conectados por una arista si estn relacionados entre s.

Pag 105
Estructura de datos

El siguiente programa pone de manifiesto el uso de operadores de miembro de


estructura y de apuntador de estructura.

#include <stdio.h>

struct card {
char *face;
char *suit;
};

main()
{
struct card a;
struct card *aPtr;

a.face = Ace;
a.suit = Spades;
aPtr = &a;

printf(%s%s%s\n%s%s%s\n%s%s%s\n,a.face, of , a.suit, aPtr-


>face,
of, aPtr->suit, (*aPtr).face, of, (*aPtr).suit);
return 0;
}

Ace of Spades
Ace of Spades
Ace of Spades

Pag 106

También podría gustarte