You are on page 1of 85

MANUAL ESTRUCTURA DE DATOS

1. Representacin de datos e introduccin a el lenguaje C Tipos de datos Estructura de un programa Operadores aritmticos Operadores relacionales y lgicos Estructuras de control 2. Ordenamientos y bsquedas Arreglos Ordenamiento Burbuja y Burbuja Mejorado (Bubble Sort) Ordenamiento por Insercin Directa (InsertSort) Ordenamiento por seleccin (SelectSort) Ordenamiento Shell (ShellSort) Ordenamiento Mezcla (MergeSort) Bsquedas Bsqueda Secuencial Bsqueda Binaria 3. Estructuras de datos lineales, representaciones secuenciales. Conceptos fundamentales: Tipo de Dato, Tipo de Dato Abstracto, Estructura de datos, Registros. T.D.A. Lista Modelo Matemtico Operaciones Implementacin con Arreglos T.D.A Pila Modelo Matemtico Operaciones Implementacin con arreglos Notacin Polaca Recursividad Ordenamiento Rpido (Quick Sort) Torres de Hanoi T.D.A Cola Modelo Matemtico Operaciones Implementacin con arreglos Concepto de Apuntador Listas implementadas con cursores

4. Estructuras de Datos lineales, representaciones ligadas. Teora de lista ligada Listas con encabezado Lista simplemente ligada Lista simplemente ligada circular Lista doblemente ligada Lista doblemente ligada circular Listas sin encabezado Lista simplemente ligada

Pag 1

Pilas Colas 5. Estructuras de datos no lineales, representaciones secuencial y ligada. Teora general de rboles rboles binarios T.D.A. rbol Binario Representaciones secuenciales Representacin ligada Recorridos En-Orden, Pre-Orden y Post-Orden 6. Grafos.

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 unsigned char Char enum unsigned int short int int unsigned long long float double long double

Largo 8 bits 8 bits 16 bits 16 bits 16 bits 16 bits 32 bits 32 bits 32 bits 64 bits 80 bits

Rango 0 to 255 -128 to 127 -32,768 to 32,767 0 to 65,535 -32,768 to 32,767 -32,768 to 32,767 0 to 4,294,967,295 -2,147,483,648 to 2,147,483,647 3.4 * (10**-38) to 3.4 * (10**+38) 1.7 * (10**-308) to 1.7 * (10**+308) 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 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.

Pag 2

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 long double char cadena 0 0L 77 5013 77L 5013L 0.003 1.0|| 0.5013e-2 a b c 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 #define LIMITE PI 100 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 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);

Pag 3

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 n C D E F G S

Cmo se describe el argumento correspondiente Como carcter Como un entero decimal Como nmero de punto flotante en notacin cientfica Como un nmero de punto flotante En el formato-e o el formato-f el que sea ms corto 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 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 n c d f lf s

Los caracteres de la entrada se convierten en Un carcter Un entero decimal Un nmero de punto flotante (float) Un nmero de punto flotante (double) 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 */ ***/

Pag 4

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 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.

Pag 5

int ordena(int &lista) La invocacin de una funcin significa 1. Se evala cada expresin de la lista de argumentos. 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 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

Pag 6

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 const double float int short struct unsigned

break continue else for long signed switch void

case default enum goto register sizeof typedef volatile

char do extern if return static union 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 2 +3.0 2.0 + 3 2.0 + 3.0 =5 = 5.0 = 5.0 = 5.0 Significado Suma Resta Multiplicacin Divisin Mdulo Ejemplo a+b ab a*b a/b a%b Resultado Suma de a y b Diferencia de a y b Producto de a por b Cociente de a por b Resto de a por b

Operador + * / %

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

Pag 7

--

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 -= D -= 4 *= e*=5 /= F /= 3 %= g %= 9

c = c +7 d=d-4 e = e* 5 f=f/3 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?. 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 +

2)

% +

+ Pag 8

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 x2 x1 m = (y2 - y1) / (x2 x1)

Operadores relacionales y lgicos. Operadores de relacin Se utilizan para expresar condiciones y describen una relacin entre dos valores. Operador < > == >= <= != Significado Mayor que Menor que Igual que Mayor o igual que Menor o igual que Distinto a Equivalente matemtico > < =

Los operadores de relacin se utilizan en condiciones cuyo formato tiene una de las siguientes formas: 1. variable operador relacional 2. constante operador relacional variable constante

El resultado de la expresin lgica es un valor tipo lgico: verdadero o falso. Expresin 6.7315 < 6.7342 -124.2 < 0.003 8 == 8.0 A < B Z < H valor 0 1 1 1 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

Segn el tipo de operador puede no existir

Orden 1. 2. 3.

de evaluacin de operadores lgicos. ! && ||

Pag 9

Operador ! *,/,%,&& +,-,|| < , <= , == , <> , >= , > Si existen parntesis las expresiones de su ((x * 2 > y 3) || (x

Prioridad Mas alta (se evala primero) Mas baja se evala al ultimo Interior se evalan primero

>

1))

&&

(y <

5)

>

>

<

||

&&

Pag 10

Operador && Operando1 1 1 0 0 Operador || Operando1 1 1 0 0 Operador ! Operando1 1 Operando2 1 0 1 0 Operando1 && Operando2 1 0 0 0

Operando2 1 0 1 0

Operando1 || Operando2 1 1 1 0

! Operando1 0

Pag 11

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 proposicin2 proposicin siguiente


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 sentencia sentencia sentencia } 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. 1; 2; 3;

n;

Pag 12

switch (expresion_entera){ case 1 : sentencia1;break; case 2 : sentencia2;break; case 3 : sentencia3;break; case n : sentencian;break; [default :sentencia x] } {case}
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);

Pag 13

printf(D: %d\n, dcontador); printf(F: %d\n, fcontador); return 0; } 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. 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.

Pag 14

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. 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; }

Pag 15

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++) 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

Pag 16

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 Multidimensionales

(vectores o listas) (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] 45.2 12.0 3.45 4.32 0.31 513. 2.65 13.0 1 4 6 4 x[7]

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;

Pag 17

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 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 0 1 2 3 4 5 6 7 8 9

Value 0 0 0 0 0 0 0 0 0 0

Pag 18

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 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; }

Pag 19

Rating 1 2 3 4 5 6 7 8 9 10

Frecuencia 2 2 2 2 5 11 5 7 1 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 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];

Pag 20

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 Fila 2 Fila 3 B[0] [0] B[1] [0] B[2] [0] B[0] [1] B[1] [1] B[2] [1] B[0] [2] B[1] [2] B[2} [2] B[0] [3] B[1] [3] B[2] [3] B[0] [4] B[1] [4] 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 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] a[2][1] a[2][2] a[2][3] = = = = 0; 0; 0; 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 */

Pag 21

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 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: ); scanf(%s,string1); printf(string1 es: %s\nstring2 es %s\n string1 con espacios entre caracteres es: \n, string1, string2);

Pag 22

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}; 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};

Pag 23

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]); return 0 } 1 2 3 4 5 6 7 8 9 10 11 12 January February March Apr May June July Aug Sep Oct Nov 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.

Pag 24

int *contadorAptr, contador;

contador
7

Contador se refiere directamente a la variable cuyo valor es 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
Representacin grfica de un apuntador apuntando a una variable entera en memoria.

Pag 25

500000

yPtr 600000

y 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. 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

Pag 26

*(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 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 caarcter de cualquier longitud.

Suit[0] Suit[1]

C D

o i r s

r a e p

a m b a

z a o d

o n l a

n t e s

e e s \0

s s \0

\0 \0

Suit[2]

T
Suit[3]

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 memoria por lo general se lleva a cabo con las funciones estandar de biblioteca malloc y free. Considere la declaracin TypeName *ptr;

Pag 27

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 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.

Pag 28

Los mtodos (algoritmos) de ordenacin son numerosos, por ello se debe prestar especial atencin en su eleccin Cmo se sabe cual es el mejor 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 indirectos(avanzados) burbuja, seleccin, insercin 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] A[2] A[3] A[4] A[5]

23 19 45 31 15 Lista sin ordenar

15 19 23 31 44 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.

Pag 29

Pasada 1: i=1

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

23 19 45 31 15
j=1

19 23 45 31 15
j=2

19 23 45 31 15
j=3

19 23 31 45 15
j=4

19 23 31 15 45 elemento ordenado

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] A[2] A[3] A[4] A[5]

19 23 31 15 45
J=1

19 23 31 15 45
j=2

19 23 31 15 45
j=3

19 23 15 31 45
j=4

19 23 15 31 45 elemento ordenado

Pasada 3: i = 3

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

19 23 15 31 45
j=1

19 23 15 31 45
j=2

19 15 23 31 45
j=3

19 15 23 31 45
j=4

19 15 23 31 45 elemento ordenado

Pag 30

Pasada 4: i =4

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

19 15 23 31 45
j=1

15 19 23 31 45
j=2

15 19 23 31 45
j=3

15 19 23 31 45
j=4

15 19 23 31 45
j=5

elemento ordenado

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){ 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.

Pag 31

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; } 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 Cuatro cartas Cinco cartas 2 2 6 6 10 //////// 9 //////// //////// 7 //////// 10

10

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
A[N]

10

15

Pag 32

1
Algoritmo

10

15

{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 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; while (k > 1 && !encontrado) if (Lista [k 1] > aux) { Lista[k] = Lista[k 1];

Pag 33

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 desordenada

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

5 14 -2 10 2

5 2 -2 10 14

5 2 -2 10 14

-2 2 5 10 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++) 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++) {

Pag 34

/*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) 504 88 513 62 908 171 898 277 654 427 150 510 612 675 750 704

Pag 35

Segundo paso (divisin/ordenacin por 4) 504 88 150 62 612 171 760 277 654 427 513 510 908 675 898 704

Tercer paso (divisin/ordenacin por 2) 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

Pag 36

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 ; 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; }

Pag 37

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). 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 -15 4 0 78 13 97 15 lista A lista B

78

90

96

2 i < j

78

97

Lista A

j se ha incrementado junto con k

-15 0 -15 0

13

15

78

90

94

96

Lista B

Lista C

Lista C

Pag 38

2 i < j

78

97

Lista A

-15 0 -15

13

15

78

90

94

96

Lista B

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

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 39

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. Nmeros Seguridad Social

A[1} A [2] A [3] A[4] A[98] A[99] A[100]

451871 120467 401321 25761 339412 81467 924116

Elemento a buscar: t 453714

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 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

Pag 40

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. 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

Elemento buscado

1331 1373 1555 1850 1892 1898 1989 2002 2400 2670 3200

Elemento central

Pag 41

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 2670 3200

Elemento central

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

1989 2002

Elemento considerado central

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. 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

Pag 42

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. 2. 3. 4. Lectura del vector Ordenacin del vector Bsqueda binaria 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) 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

Pag 43

(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 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 */ { 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.

Pag 44

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]; 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);

Pag 45

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; 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: 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*/

Pag 46

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); }

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,...n1 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.

Pag 47

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.

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 48

1 2

primer elemento 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); 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);

Pag 49

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 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 ;

Pag 50

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 { 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; }

Pag 51

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); } 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.

Pag 52

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. 2. 3. 4. 5. 6. ANULA(P). Que convierte la pila en una Pila Vaca. TOPE(P). Devuelve el elemento que est en el tope de la Pila. POP(P). Suprime el elemento que est en el tope de la pila P. PUSH(x, P). Inserta el elemento x en la parte superior de la Pila. VACIA(P). Devuelve verdadero si la Pila est vaca y falso en caso contrario. 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 53

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. Los operadores tienen su precedencia

Pag 54

Operador x,/ +,-

Valor 3 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: 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 (a + b) X c (ab +)X c (ab +) c X ab + c X

expresin infija se aaden parntesis sin cambio se convirti el + se convirti el X se elimin el parntesis

Pag 55

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) 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 // 21// 19 13 15 26

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 // 21// 19 13 15 26

I= 1

J=9

Pag 56

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.

23

31

17

// 21//

19

13

15

26

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

15

31

17

// 21//

19

13

23

26

I 5. El proceso se repite

15

13

17

// 21//

19

31

23

26

I 9 15 13 17 // 21//

J 19 31 23 26

I 9 15 13 17 // 19//

J 21 31 23 26

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 sublista, todos los elementos menores o iguales a 20, y en segunda todos los elementos mayores que 20.

Pag 57

Sublista Izquierda 9 I 15 13 17 19 J

Sublista Derecha 21 I 31 23 26 J

15

13

17

19

21

31

23

26

I 9 15 13

J 17 19 21

I 23

J 31 26

I 9 13

J 15 17 19

J 31

I 26

J 9 13

I 15 17 19

I 31

J 26

I Sublista izquierda 12 15 17 19

Sublista Izquierda II 9 13

Sublista izquierda 1 ordenada 9 13 15 17 19

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

Pag 58

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; } 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) }

Pag 59

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. 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

Pag 60

Poste 1

Poste 2

Poste 3

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:

Pag 61

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 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 62

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; else C.final++; c.elem[C.final]=x; } }

Pag 63

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 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

Pag 64

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.

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

Prim

E B

Disponible

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.

Pag 65

Listas con encabezado Lista simplemente ligada 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 encabezado

A2 Lista

An

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 } {La siguiente figura muestra una lista vaca}

encabezado Pag 66

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

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

A2

An

encabezado

aux

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

Pag 67

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

encabezado

Lista

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){ encabezado = new(nodo); //Se crea el nodo de encabezado //Se hace que el campo siguiente del

Pag 68

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 }

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; }

Pag 69

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 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.

Pag 70

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.

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; }

void SUPRIME(Nodo p){ Nodo aux; aux =p->sig->sig;

//Suprime el nodo siguiente al apuntado por p

Pag 71

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

Pag 72

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); Encabezado->ant =Encabezado; Encabezado->sig =Encabezado; }

//Se crea el nodo de encabezado //Se hace que el campo siguiente del //encabezado se apunte asi mismo

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){

Pag 73

//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; 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;

Pag 74

} 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; nuevo = new(nodo); nuevo->tipo_elemento:=x; nuevo->sig = aux; L->sig =Nuevo; } } void SUPRIME(Nodo p,Nodo &L){ Nodo aux; if (p==NULL) { aux:=L->sig; delete(L); L=aux; } else { aux=p->sig->sig; delete (p->sig); p->sig =aux; } } {Suprime el nodo siguiente al apuntado por p}

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){

Pag 75

//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){ 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 76

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; else return 0; } void METER (elemento x;Cola C); Nodo Aux; Aux =new(Nodo); Aux->elemento =x;

Pag 77

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 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.

Pag 78

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

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.

Pag 79

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; 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); }

Pag 80

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 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); PREORDEN(R->izq); PREORDEN(R->der); } } void ENTREORDEN (Nodo R){ if (R!=NULL) { ENTREORDEN(R->Izq); printf(%d,R->Info); ENTREORDEN(R->Der);

//Visitamos la raz //Recorremos el subrbol izquierdo //Recorremos el subrbol derecho

//Recorremos el subrbol izquierdo //Visitamos la raz //Recorremos el subrbol derecho

Pag 81

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

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 v1 v2, v2 v3,.....vn-1 vn.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.

3
Grafo dirigido

Pag 82

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 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 a b b a 3 a 4 b b 2

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. 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.

Pag 83

1 1 2 3 4 b a

2 a

4 B

b b a 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.

1 2 3 4

2 4 2 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.

Pag 84

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.

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 85

You might also like