You are on page 1of 85

MANUAL ESTRUCTURA DE DATOS

1. Representación de datos e introducción a el lenguaje C Tipos de datos Estructura de un programa Operadores aritméticos Operadores relacionales y lógicos Estructuras de control 2. Ordenamientos y búsquedas Arreglos Ordenamiento Burbuja y Burbuja Mejorado (Bubble Sort) Ordenamiento por Inserción Directa (InsertSort) Ordenamiento por selección (SelectSort) Ordenamiento Shell (ShellSort) Ordenamiento Mezcla (MergeSort) Búsquedas Búsqueda Secuencial Búsqueda 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 Matemático Operaciones Implementación con Arreglos T.D.A Pila Modelo Matemático Operaciones Implementación con arreglos Notación Polaca Recursividad Ordenamiento Rápido (Quick Sort) Torres de Hanoi T.D.A Cola Modelo Matemático Operaciones Implementación con arreglos Concepto de Apuntador Listas implementadas con cursores

4. Estructuras de Datos lineales, representaciones ligadas. Teoría 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. Teoría general de Árboles Árboles binarios T.D.A. Árbol Binario Representaciones secuenciales Representación ligada Recorridos En-Orden, Pre-Orden y Post-Orden 6. Grafos.

Representación de datos e Introducción a lenguaje C Tipos de datos. Los diferentes objetos de información con los que un programa C trabaja se conocen colectivamente como datos. Todos los datos tienen un tipo asociado con ellos La asignación 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 kilómetros = 1.609344 * millas Además de tener un valor, una constante también tiene un tipo de dato inherente. Los tipos de datos posibles en las constantes dependen de la máquina. Lo normal es que no existan constantes de los tipos short, unsigned y float. Aunque esto puede ser distinto en máquinas pequeñas, se supondrá que la situación 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 expresión constante sólo contiene constantes, y el compilador C las evaluará en el momento de la compilación, en lugar de hacerlo en la ejecución. Por ejemplo, en la proposición: Segundos = 60 * 60 * 24 * días; Segundos = 86400 * días; 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 estén en cadenas entre comillas. Una línea #define puede estar en cualquier lugar del programa, pero debe empezar en la columna 1 y solo tendrá efecto en las líneas de archivo que le sigue. El preprocesador solo cambiará los identificadores que se escriben con mayúsculas. Si en un programa hay una línea como: #include “mi_archivo” Se trata también 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 línea es incluir en ese punto de la codificación una copia del contenido del archivo mi_archivo Usos de printf() Y scanf() La función printf() se usa para la salida; en forma similar; la función scanf() se usa para la entrada. Estas funciones no son parte del lenguaje C, sino del sistema C; residen en una biblioteca estándar y están 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 parámetros con dos partes: Cadena_de_control y lista_de_argumentos

La primera es una cadena y puede contener especificaciones de conversión de formatos. Una especificación de conversión se inicia con un carácter % y termina con un carácter de conversión; por ejemplo, en el formato %d la letra de es el carácter de conversión. Este formato se emplea para imprimir el valor de una expresión como un entero decimal. Para imprimir las letras ABC en la pantalla, podría usarse la proposición: printf(“ABC”); otra manera de hacer esto es mediante la proposición: 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 también con la proposición: printf(“%c%c%c”,’A’,’B’,’C’); Los apóstrofos que encierran cada letra se emplean para designar constantes de caracteres de acuerdo con esto, ‘A’ es la constante de carácter que corresponde a la letra A mayúscula. El formato %c imprime el valor de una expresión como un carácter. printf() Carácter de conversió n C D E F G S

Cómo se describe el argumento correspondiente Como carácter Como un entero decimal Como número de punto flotante en notación científica Como un número de punto flotante En el formato-e o el formato-f el que sea más corto Como una cadena de caracteres

La función scanf() es análoga a la función 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 símbolo & representa el operador de dirección; por ejemplo, la proposición 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 dirección x. Carácter de conversió n c d f lf s

Los caracteres de la entrada se convierten en Un carácter Un entero decimal Un número de punto flotante (float) Un número de punto flotante (double) Una cadena

Funciones La definición de una función consiste en un encabezamiento y un cuerpo. De manera explicita, podríamos decir que es un bloque o una proposición compuesta. Si hay declaraciones deben aparecer al inicio del bloque antes de cualquier proposición ejecutable. El encabezamiento puede ser tan sólo un identificador y unos paréntesis. Un ejemplo sencillo es : /****Una función con un encabezamiento y un cuerpo sencillos escr_direcc() { /* encabezamiento*/ /* el cuerpo es cuanto está entre llaves */ ***/

Pag 4

) Las funciones se invocan al escribir su nombre y una lista adecuada de argumentos entre paréntesis. a estos parámetros se les llama parámetros formales de función. \ return expresión. } Donde quiera que un programa identifique a esta función. return ++x. El proceso de declarar un parámetro de función como apuntador y utilizar en forma consistente el parámetro desreferenciado en el cuerpo de la función se le denomina “llamada por referencia”. cualquier parámetro que no se declaré se considera un int por omisión. Una definición de función tiene un nombre y unos paréntesis que contienen cero o más parámetros y un cuerpo. Estas funciones están disponibles para usarse en el cuerpo de las demás. Por tanto un programa consiste en una serie de una o más definiciones de funciones.printf(“\n\n%s\n%s\n%s\n%s\n%s\n\n”. “ ** El POLO NORTE *”. la expresión hará que se invoque la función. Se evalúa cada argumento y su valor se utiliza de forma local en lugar del parámetro formal. sólo coloque ampersand (&) después del tipo del parámetro en el prototipo de función. “ *******************”). El cuerpo de la función es un bloque o una proposición compuesta. Todos los argumentos pasan con una “llamada por valor”. entonces por omisión es int. Para cada parámetro debe de haber una declaración correspondiente antes del cuerpo. Algunos ejemplos son: return. “ ** SAN NICOLAS *”. puede hacerse que la función cambie el valor de la variable direccionada en el medio que la llama. y también puede contener declaraciones. “ ********************”. Otras variables no declaradas ni como argumentos ni en el cuerpo de la función se consideran como “globales” a la función y deben definirse en forma externa. return (++x). todas las variables declaradas en el cuerpo de una función se dice que son variables locales. Invocación y llamada por valor La funciones se declaran como objetos individuales que no pueden anidarse. “ ** LA TIERRA *”. Pueden emplearse donde quiera que sea apropiado la expresión del tipo dado para el especificador de tipo de la función ( Recuérdese que si un especificador de tipo de una función está ausente. el empleo de una variable externa global como vía de comunicación entre una función y el medio que la llama se considera indeseable. La proposición return La proposición return puede incluir u omitir una expresión. Proposición _return ::= return. Pag 5 . return (377). Cuando se pasa una dirección como argumento. return (a * b). Para indicar que un parámetro de función es pasado por referencia. Invocación y llamada por referencia En la practica moderna de la programación.

3. 4. los paréntesis que van después de main indican al compilador que se trata de una función { Las llaves encierran al cuerpo de la función. Una de las funciones es main() donde se inicia la ejecución del programa. Si se ejecuta una proposición return. 2. el control regresa al medio que hizo la llamada. 6. Todas las funciones están en el mismo nivel externo. Si se ejecuta una proposición return que no tiene una expresión o si no hay una proposición return. Si no hay una proposición return el control regresa al medio que hizo la llamada cuando se llega al final del cuerpo de la función.int ordena(int &lista) La invocación de una función significa 1. “ de mar en mar C\n” En C una constante de cadena es una serie de caracteres entre comillas. el valor de la expresión se convierte (si es necesario) al tipo dado por el especificador de tipo de la función y ese valor también regresa al medio que hizo la llamada. también se usan para agrupar varias proposiciones. éste es un carácter que no se imprime. 7. está es una función de la biblioteca estándar que imprime en pantalla. Se ejecuta el cuerpo de la función. } La llave derecha hace pareja con la llave de una función y da por terminada la función main Pag 6 . entonces no regresa ningún 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. Esta cadena es un argumento de la función printf() y controla lo que se escribirá los dos caracteres \n al final de esta cadena representan un carácter sencillo llamado nuevalínea. pues constituye una parte fundamental de la codificación en el proceso de solución de problemas. Al principio del cuerpo de la función se asigna el valor de la expresión a su parámetro formal correspondiente. Emplear una función void en una expresión que requiere un valor hace que el compilador produzca un mensaje de error. printf() El sistema contiene una biblioteca estándar con funciones que pueden usarse en programas. Si la proposición return incluye una expresión. no se pueden anidar unas en otras. Un programa contiene una o más funciones en uno o más archivos. Estructura de un programa La base de la programación en C es la función. su efecto es hacer que el cursor avance hacia una línea nueva. Se evalúa cada expresión de la lista de argumentos. 5. El resto de las funciones se llaman desde main() y desde el interior de otras main() Todo programa tiene una función main donde inicia la ejecución.

2+3 2 +3.0 2. los identificadores estándar se pueden redefinir Palabras reservadas. Los identificadores representan objetos de un programa ( constantes. unidades.0 =5 = 5.0 + 3. ++ a++ Utiliza el valor actual de a en la expresión en la cual reside a. programas y campos de registros). Un identificador se caracteriza por estas reglas: 1. 2. y después se incrementa a en 1 ---b Se decrementa b en 1 y a continuación se utiliza el nuevo valor de b en la expresión en la cual reside Pag 7 .0 Significado Suma Resta Multiplicación División Módulo Ejemplo a+b a–b 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 continuación se utiliza el nuevo valor de a en la expresión en la cual resida a.0 + 3 2. 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 aritméticos Los operadores aritméticos (+. Letras dígitos y caracteres subrayados están permitidos después del primer carácter. tipos de datos procedimientos. Debe comenzar con una letra (A a Z mayúsculas o minúsculas) y no puede contener blancos. pero sólo los primeros 63 caracteres son significativos. 3.-.Identificadores. funciones.0 = 5. si ambos son enteros el resultado es entero si uno es real el resultado es real. variables.0 = 5. sin embargo. No se puede utilizar una palabra reservada como identificador. Un identificador es una secuencia de caracteres que pueden ser de cualquier longitud.*) pueden ser utilizados con tipos enteros o reales.

Las subexpresiones con paréntesis anidados se evalúan de dentro a afuera. y después se decrementa a b en 1 Operadores de asignación += 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 expresión aritmética se evalúa. ¿qué operación se realiza primero?. 2./. 1. El orden de las operaciones es: x -(A + B % 2) + y * z % + * - + Pag 8 . Todas las subexpresiones entre paréntesis se evalúan primero. Reglas de evaluación de expresiones. el paréntesis más interno se evalúa primero. los operadores se evalúan en el siguiente orden: *.% primero +. Se utiliza el valor actual de b en la expresión en la cual reside b. Dentro de una misma expresión o subexpresión. Prioridad de operaciones. Cuando en una expresión aparecen dos o más operadores. el resultado es siempre un número.último 3.-- b-- b. Los operadores en una misma expresión o subexpresión con igual nivel de prioridad (tal como * y /) se evalúan de izquierda a derecha. Regla asociativa izquierda.

7315 < 6. 3. || y !.0 ‘A’ < ‘B’ ‘Z’ < ‘H’ valor 0 1 1 1 1 Operadores lógicos Las expresiones lógicas pueden combinarse para formar expresiones más complejas utilizando los operadores lógicos: &&. Operador < > == >= <= != Significado Mayor que Menor que Igual que Mayor o igual que Menor o igual que Distinto a Equivalente matemático > < = ≥ ≤ ≠ Los operadores de relación se utilizan en condiciones cuyo formato tiene una de las siguientes formas: 1. 2. M = y2 – y1 x2 –x1 m = (y2 . ! && || Pag 9 . Expresión 6. Operadores de relación Se utilizan para expresar condiciones y describen una relación entre dos valores. [operando1] operador operando2 Según el tipo de operador puede no existir Orden 1. Estos operadores se utilizan con constantes lógicas de forma similar al modo en que los operadores aritméticos se utilizan con las constantes numéricas. En C las formulas matemáticas se deben escribir en formato lineal. de evaluación de operadores lógicos.7342 -124.Escritura de formulas matemáticas en C. Esto obliga al uso frecuente de paréntesis que indiquen el orden de evaluación correcto de los operadores.2 < 0.y1) / (x2 – x1) Operadores relacionales y lógicos. variable operador relacional 2. estos operadores trabajan con operandos que son expresiones lógicas. constante operador relacional variable constante El resultado de la expresión lógica es un valor tipo lógico: verdadero o falso.003 8 == 8.

> Si existen paréntesis las expresiones de su ((x * 2 > y 3) || (x Prioridad Mas alta (se evalúa primero) Mas baja se evalúa al ultimo Interior se evalúan primero > y - 1)) && (y < 5) * - - > > < || && Pag 10 .&& +./.Operador ! *.%.|| < . == . >= . <= .-. <> .

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 .

Sentencia de control que dirige a la computadora para ejecutar una sentencia si la expresión es verdadera. else if (grado >=70) Printf(“C\n”). else if (grado >=60) Printf(“D\n”). 1. y otra en caso de ser falsa. 2. else if (grado >=50) Printf(“F\n”).h> main() { if (grado >=90) Printf(“A\n”). n. La sentencia if. if (condición) sentencia Sentencia compuesta. 3. { sentencia sentencia sentencia sentencia } Ejemplo: #include <stdio. else if (grado >=80) Printf(“B\n”). Formato if (expresión lógica) proposicion1 else proposición2 proposición siguiente En muchos casos se desea que una determinada acción sólo ejecute si una cierta condición es verdadera y no realizar ninguna acción si la condición es falsa.Estructuras de control Selectivas. Pag 12 . } La sentencia switch La sentencia switch se utiliza para elegir entre diferentes alternativas.

/*Contando calificaciones*/ #include <stdio.} break. break. ccontador).break. fcontador = 0. acontador). contador = 0. dcontador =0. case ‘F’: case ‘f’: fcontador. printf(“C: %d\n”. bcontador). printf(“Mete la Letra de su calificación.h> main() { int Letra. printf(“Mete el carácer EOF para finalizar las entradas. default {printf(“ Letra de entrada incorrecta”). break. case n : sentencian. case ‘D’: case ‘d’: dcontador. case ‘B’: case ‘b’: bcontador. Pag 13 . } } printf(“\nTotales de cada calificacion\n”). break. printf(“ Meta una nueva calificación.switch (expresion_entera){ case 1 : sentencia1. int acontador = 0. break. bcontador =0.break. case 2 : sentencia2. case ‘C’: case ‘c’: ccontador.break. [default :sentencia x] } {case} El siguiente ejemplo utiliza switch para contar el número de cada distinta letra de calificación que los estudiantes alcanzarón en un examen.\n”). break. printf(“B: %d\n”. case 3 : sentencia3. while ((Letra = getchar() ) != EOF) { switch (Letra) { case ‘A’: case ‘a’: acontador.\n”). printf(“A: %d\n”.\n”).break.

Si la condición se evalúa falso cuando se ejecuta el bucle por primera vez el cuerpo del bucle no se ejecutará nunca. dcontador). 2. Pag 14 . printf(“F: %d\n”. esto significa que el bucle se ejecutará indefinidamente a menos que algo en el interior del bucle modifique la condición haciendo que su valor pase a falso. fcontador). definida en el archivo de cabecera <stdio. while (expresión lógica) { Sentencia 1 Sentencia n } 1. La condición (expresión lógica) se evalúa antes y después de cada ejecución del bucle. Si la condición es verdadera se ejecuta el bucle y si es falsa el control pasa a la sentencia siguiente al bucle. Mientras la condición sea verdadera el bucle se ejecutará. La estructura repetitiva while (mientras) es aquella en la que el número de iteraciones no se conoce por anticipado y el cuerpo del bucle se repite mientras se cumple una determinada condición. } EOF es una constante simbólica entera. 3.h> Mete la letra de su c Mete el carácer EOF para finalizar las entradas A B C C A D F C E Letra de entrada incorrecta meta una nueva calificación D A B Totales de cada calificación A: 3 B: 2 C: 3 D: 2 F: 1 La sentencia while.printf(“D: %d\n”. return 0.

contador. promdio). Si la expresión lógica es verdadera. El siguiente ejemplo utiliza una estructura do while para imprimir los números del 1 al 10. Una variante de la sentencia while es la sentencia do while. contador). do Sentencia 1 sentencia n while (expresión lógica). ejecutar el cálculo de promedio e imprimir el resultado. } Pag 15 .&grado). } while (++contador <= 10). La condición se evalúa al final del bucle. grado. /*procesamiento*/ while (counter <=10) { printf (“Mete grado:”).h> main() { int contador = 1. Si la expresión lógica es verdadera. /*inicializacion*/ counter = 1. 1. promedio. 3. La sintaxis no requiere {}. counter++. se vuelve a repetir el bucle y se ejecutan todas las sentencias. se sale del bucle y se ejecuta la siguiente sentencia a while.Ejemplo: El promedio de la clase es igual a la suma de calificaciones dividida por el número de alumnos. } La sentencia do while. return 0. printf(“El promedio de la clase es %d\n”. total. return 0.h> main() { int. El algoritmo para resolver este problema en una computadora. #include <stdio. #include <stdio. } promedio = total / 10. scanf(“%d”. después de ejecutarse todas las sentencias 2. total = total +grado. esta sentencia especifica un bucle condicional que se repite hasta que la condición se hace falsa. total = 0. do { printf (“%d “. debe introducir cada una de las calificaciones. 4.

for (j=99. i <=77. i++) b) Varíe la varible de control de 100 a 1 en incrementos de –1 (decrementos de 1).sum). for ( i = 7. return 0.h> main() { int contador. expresion3 ) { Sentencia 1. for (j= 2.contador). j <=20. 88. 5. return 0. Esta sentencia requiere que sepamos por anticipado el número de veces que se ejecutan las sentencias del interior del bucle. contador <=10. number +=2) sum += number. contador++) printf(“%d\n”. 55. 11. 66. expresion2. Sentencia n } #include <stdio. } ejemplo utillizando la estructura for a) Varíe la variable de control de 1 a 100 en incrementos de 1.h> main() { int sum = 0. number <=100. 20.i+=7) c) Variar la variable de control a lo largo de la siguiente secuencia de valore: 2.j-=11) Los siguientes ejemplos proporcionan aplicaciones simples de la estructura for. for (i=1. for ( expresion1. j+=3) d) Variar la variable de control de acuerdo a la siguiente secuencia de valores: 99. for (number = 2. number. /*inicialización.La sentencia for. condición de repetición.j>=0. 14. 44. El programa siguiente utiliza la estructura for para sumar todos los enteros pares desde 2 hasta 100 /*Sumación con for*/ #include <stdio. printf(“Sum es %d\n”. Sentencia 2.i<=100. 8. 22. 0. 33. e incremento*/ for (contador=1. 17. 11. 77. } Sum es 2550 Pag 16 .

Por ejemplo. 2.45. x[2]. Almacenar los elementos del arreglo en memoria continua. Sus 8 elementos se conocen como x[0]. Tener un único nombre de variable que representa todos los elementos y estos a sus vez se diferencian por un índice o subíndice.0 1 4 6 4 x[7] El número de posición que aparece dentro de los corchetes se conoce más formalmente como subíndice. Cualquiera de estos elementos puede ser referenciado dándole el nombre del arreglo seguido del número de posición de dicho elementos en particular en paréntesis cuadrados o corchetes ([]). Note que un nombre de arreglo con subíndice es un Ivalue que puede ser utilizado al lado izquierdo de una asignación. Este arreglo contiene 8 elementos.. x[1]. el valor de x[6] es de 2. Entonces el primer elemento de un arreglo x se conoce como x[0]. entonces el enunciado C[a + b] +=2.x[7]. escribiríamos Printf (“%f”.. 2. Acceso directo o aleatorio a los elementos individuales del arreglo. el elemento de orden i del arreglo x se conoce como x[i-1]. Examinaremos el arreglo x.65 y el valor de x[7] es 13. es una lista de un número finito n de elementos del mismo tipo que se caracteriza por: 1.Ordenamientos y búsquedas Arreglos Un arreglo es una estructura de datos en la que se almacena una colección de datos del mismo tipo.65 13. el valor de c[2] es 3. Los nombres de los arreglos siguen las mismas reglas convencionales que los demás nombres de la variables. Para imprimir la suma de los valores contenidos en los primeros tres elementos del arreglo x. El primer elemento de cualquier arreglo es el elemento cero. El valor de x[0] es 45. Ejemplo: float x[8]. Para referirse a una posición en particular o elemento dentro del arreglo. si a = 3 y b = 4. Si un programa utiliza una expresión como subíndice.0 3.2 12.x[0] +x[1] + x[2]).45 4.21. Los arreglos se clasifican en: Unidimensionales Multidimensionales (vectores o listas) (tablas o matrices) En la figura se muestra un arreglo de números reales llamado x . el séptimo como x[6] y en general. Para dividir el valor del séptimo elemento del arreglo x entre 2 y asignar el resultado a la variable c escribiríamos C= x[6] / 2.04. entonces la expresión se evalúa para determinar el subíndice. Añade 2 al elemento del arreglo c[11]. el valor de c[1] es 12... Un subíndice debe de ser un entero o una expresión entera.31 513.32 0. Pag 17 .0.. especificamos el nombre del arreglo y el número de posición del elemento particular dentro del mismo. 3. x[0] x[1] x[2] x[3] x[4] x[5] x[6] 45. el segundo como x[1].

Ejemplos utilizando arreglos El programa siguiente utiliza la estructura de repetición for para inicializar los elementos de un arreglo entero de diez elementos n a ceros. } Elemento 0 1 2 3 4 5 6 7 8 9 Value 0 0 0 0 0 0 0 0 0 0 Pag 18 . i <= 9. return 0. “Elemento”. El enunciado printf muestra los encabezados de columnas de las dos columnas impresas en la estructura for. es utilizada. for(i= 0. i++) /*imprime arreglo*/ printf(“%7d%13d\n”. Para reservar 100 elementos para el arreglo entero b y 27 elementos para el arreglo entero x. Para indicarle a la computadora que reserve 12 elementos para el arreglo entero c. “Value”). i++) /*inicializa el arreglo*/ n[i] = 0. Por ejemplo un arreglo de tipo char puede ser utilizado para almacenar una cadena de caracteres. x[27]. /* inicializa el arreglo*/ #include <stdio. n[i]). El programador especifíca el tipo de cada elemento y el número de elementos requerido por cada arreglo. printf(“%s%13s\n”. i <= 9. de tal forma que la computadora pueda reservar la cantidad apropiada de memoría. Los arreglos pueden ser declarados para que contengan otro tipo de datos.Como declarar arreglos Los arreglos ocupan espacio en memoría. la declaración int c[12]. La memoria puede ser reservada para varios arreglos dentro de una sola declaración. for (i = 0.h> main() { int n[10]. e imprime el arreglo en formato tabular.i. i. se puede utilizar la siguiente declaración int b[100].

h> #define SIZE 12 main() { int a [SIZE] = {1. 6. 6. 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. 8. 7. 7. 10). 6. 8. rating <= FRECUENCIA_SIZE –1. 3. answer++) ++frecuencia[respuestas[pregunta]]. 5. Ignoramos el primer elemento frecuencia[0]. 5. 3. for (i=0. El arreglo respuestas es un arreglo de 40 elementos correspondiente a las respuestas de los alumnos. 8. pregunta <= RESPUESTA_SIZE – 1. El enunciado clave es ++frecuencia[respuestas[pregunta] Este enunciado incrementa el contador frecuencia apropiado. return 0. } Pag 19 . 2. 6. printf (“%s%17s\n”. for (rating = 1. 7. rating++) printf(“%6d%17d\n”. i <=SIZE – 1. 99.El programa siguiente suma los valores contenidos en un arreglo entero a de doce elementos. 10. 6. 5. 4. ++frecuencia [respuesta[pregunta]]. frecuencia[rating]). 7. int frecuencia[FRECUENCIA_SIZE] = {0}. rating.”Rating”. 5. return 0. 4. 4. frecuencia para contar el número de ocurrencias de cada respuesta. Utilizamos un arreglo de 11 elementos. por que es más lógico tener un incremento de respuesta 1 frecuencia[1] que frecuencia[0]. 16. Por ejemplo cuando la variable del contador pregunta es 0. 6. for (pregunta = 0. 45. /* Calcula la suma de los elementos del arreglo*/ #include <stdio. Esto nos permite utilizar cada respuesta directa como subíndice en el arreglo frecuencia. se interpreta en realidad como ++frecuencia[1]. 45} i. 6. 2. 6. respuestas[pregunta] es 1 y por lo tanto.i++) total += a[i] printf(“El total del valor de los elementos del arreglo es %d\n”. 2. dependiendo del valor de respuestas[pregunta]. 7. 6. 8. 8. 3. 6. 5. 89. 67. 10. 8. El enunciado en el cuerpo del ciclo for se ocupa de la totalización. rating. } 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 investigación. 6. #include <stdio. 7. “Frecuencia”).total). 8. int respuestas[RESPUESTAS_SIZE] = {1.h> #define RESPUESTAS_SIZE 40 #define FRECUENCIA_SIZE 11 main() { int answer. 5. 1. 9. Deseamos resumir el número de respuestas de cada tipo (es decir del 1 hasta el 10). total =0.

Por ejemplo. El nombre del arreglo de hecho es la dirección del primer elemento de dicho arreglo. Por lo tanto. de tal forma que pueda procesar el número especifico de elementos incluidos en dicho arreglo. &array[0]). especifique el nombre del arreglo. si el arreglo Temperaturas ha sido declarado como int Temperaturas[24]. en sus localizaciones de memoria originales. Por ejemplo si se declara: int b[3] [5]. Pag 20 . El enunciado de llamada a la función ModificaArreglo(Temperaturas. return 0. 24). cuando en su cuerpo de función. sin corchete alguno. C pasa de forma automática los arreglos a las funciones utilizando simulación de llamadas por referencia –las funciones llamadas pueden modificar los valores de los elementos en los arreglos originales de los llamadores. Dado que ha sido pasada la dirección inicial del arreglo. con frecuencia resulta útil imaginar que un arreglo bidimensional es un conjunto rectangular de elementos con filas y columnas. printf (“ array = %p\n&array[0] = %p\n”. al pasar un arreglo esl tamaño del arreglo se pasa a una funcion. Pasa el arreglo Temperaturas y su tamaño. #include <stdio. array. la función llamada sabe precisamente dónde está el arreglo almacenado.h> main() { char array[5].Rating 1 2 3 4 5 6 7 8 9 10 Frecuencia 2 2 2 2 5 11 5 7 1 3 Cómo pasar arreglos a funciones Para pasar cualquier argumento de arreglo a una función. está modificando los elementos reales del arreglo. } array = FFF0 &array = FFF0 Arreglos multidimensionales Aunque los elementos de los arreglos se almacenan en forma contigua. a la función ModificaArreglo. la función llamada modifica los elementos del arreglo.

column 3.2}. la siguiente estructura define todos los elementos en el tercer renglón del arreglo a int a[4][4].{3. Muchas manipulaciones comunes con arreglos utilizan estructuras de repetición for.Puede imaginarse que los elementos del arreglo están 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 subíndice se referencian con los subíndice dentro de un mismo par de corchetes separando por comas los subíndices. renglon++) for (column = 0. “ nada se ejecuta “” /* la cadena nula */ Pag 21 . 0. El ciclo for varía sólo en el segundo subíndice (es decir. “una cadena de texto” “ /*una cadena de caracteres blancos */ “z” “.. for (column = 0. Cadenas Una cadena es una secuencia de caracteres encerrada entre comillas “. y no t[x] [y]. for (renglon =0. column++) A[2] [column] =0 Especificamos el tercer renglón.column <=3. x =sin(y). 0.basura” “una cadena con \” comillas incorporadas” “a = b + c. Si el carácter “ ha de aparecer en una cadena. A continuación se presentan varios ejemplos de cadenas. Por ejemplo. debe ir precedido del carácter \. por lo tanto. 0. no dos. Total = 0.y]. column++) total +=[renglon] [column].4}}. La estructura for anterior es equivalente a los enunciados de asignación siguientes: a[2][0] a[2][1] a[2][2] a[2][3] = = = = 0. el subíndice de columnas). la siguiente estructura for anidada determina el total de todos los elementos. En el arreglo a. Los arreglos de doble subíndice pueden ser declarados e inicializados de la siguiente manera int b[2][2] = {{1. Obsérvese que el símbolo “es un solo carácter.1kM87tt . t[x. renglon <=3. sabemos que el primer subíndice será siempre 2 (o es el primer renglón y 1 el segúndo).

Por ejemplo. string2). printf(“Mete un string: “).string1). a fin de que se pueda almacenar un valor ahí. int i. Pag 22 . Inicializa los elementos de la cadena string1 a los carácteres individuales de la literal de cadena “first”. Por lo tanto. Un arreglo de caracteres puede ser inicializado utilizando una literal de cadena. char string2[20]. El nombre de un arreglo es la dirección del inicio del arreglo y. la declaración char string1[] = “first”.‘\0’}. La declaración anterior es equivalente a char string1[] = {‘f’. string1. por tanto. utilizando la notación de subíndices de arreglos.Las cadenas son arreglos unidimensionales de tipo char que tinen varías características únicas. #include <stdio. scanf(“%s”. Un arreglo de caracteres que represente una cadena puede ser sacado utilizando printf y el especificador de convesión %s. basado en la longitud de la cadena. es el carácter ‘f’ y string[3] es el carácter ‘s’. scanf podría escribir más allá del final del arreglo. También podemos introducir desde el teclado directamente una cadena en un arreglo de caracteres.’r’. string1[0]. El tamaño del arreglo string1 en la declaración anterior queda determinada por el compilador. Es importante hacer notar que “first” contiene 5 caracteres. La función scanf lee caracteres del teclado hasta que se encuentra con el primer carácter de espacio en blanco sin impotarle que tan grande es el arreglo.. string2[] = “string literal”. conocido como carácter nulo. Por ejemplo. Por ejemplo. En C todas las cadenas terminan con este carácter. Crea un arreglo de caracteres capaz de almacenar una cadena de 19 caracteres y un carácter nulo de terminación. Un arreglo de caracteres representando una cadena debería declararse siempre lo suficientemente grande.’i’. la declaración . más un carácter especial de terminación de cadena. string2). & no es necesario. Note que el nombre del arreglo se pasa a scanf sin colocar el & precedente. que en otras variables se utiliza. El enunciado scanf (“%s”. El arreglo string2 se imprime utilizando el enunciado printf (“%s\n”. utilizando scanf y la especificación de conversión %s.’s’. la representación de la constante de caracteres del carácter nulo es ‘\0’. Dado que la cadena es un arreglo de caracteres podemos tener acceso directo a los caracteres individuales de una cadena.h> main() { char string1[20].’t’. para contener el número de caracteres de la caden incluyendo el carácter nulo de terminación. Lee una cadena del teclado y la coloca en string2. printf(“string1 es: %s\nstring2 es %s\n “string1 con espacios entre caracteres es: \n”. Los arreglos de caracteres también pueden ser inicializados con constantes individuales de caracteres en una lista de inicialización. El & es utilizado por lo regular para darle a scanf una localización de variable en memoria. string2). Entonces el arreglo string1 de hecho contiene 6 elementos.

AUG. SEP. únicamente un tipo de datos. En la mayor parte de casos las uniones contienen dos o más tipos de datos.cuyos miembros comparten el mismo espacio de almacenamiento. Las operaciones que pueden ser ejecutadas en una unión son: asignar una unión a otra unión del mismo tipo. NOV. printf(“\n”). a menos de que se defina de otra manera. por lo que esta puede ser utilizada para declarar variables en todo el programa. MAY. en vez de desperdiciar almacenamiento en variables que no esten siendo utilizadas. y se incrementan en 1. return 0. en efecto. Por ejemplo. por lo tanto. Por ejemplo enum months {JAN. tomar la dirección (&) de una unión. Pag 23 . Para distintas situaciones en un programa. DEC}. Estas constantes de enumeración son. FEB. porque la union esta inicializada con un int. algunas variables pudieran no ser de importancia. MAR. } 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.for (i = 0. definido por el usuario. es un conjunto de constantes enteras representadas por identificadores. en la union anterior. JUL. OCT. deben ser por lo menos suficientes para contener al miembro mas grande. En una declaración. y tener acceso a los miembros de una union utilizando el operador de miembro de estructura y el operador de apuntador de estructura. Una enumeración. En un programa normalmente la definición de unión antecede a main. una union puede ser inicializada únicamente con un valor del mismo tipo que el primer miembro de la union. la declaración union number value ={10}. pero la siguiente declaración no sería valida: union number value = [1. JUN. conocido como una enumeración. APR. Es una inicialización valida de la variable de union value. string1[i] != ‘\0’. Los miembros de la union pueden ser de cualquier tipo el número de bytes utilizados para almacenar una unión. constantes simbólicas.43] Constantes de enumeración C proporciona un tipo final. float y. introducida por la palabra reservada enum. Unicamente un miembro y. cuyos valores pueden ser definidos automáticamente. I++) printf(%c ”. pero otras variables lo son –por lo que una union comparte el espcacio. puede ser referenciado en un momento dado union number{ int x. } indica que number es un tipo union con miembros int x y float. string1[i]). Los valores de un enum se inician con 0.

month++) printf(“%2d%11s\n”.”August”. “January”.February”. JUL. un apuntador por otra parte. MAR. DEC}. JUL.h> enum months {JAN = 1. for (month = JAN. FEB. MAY.month. “June”. AUG. crear y manipular estructuras de datos es decir. char *monthName[] = {“”. AUG. NOV. En este sentido un nombre de variable se refiere directamente a un valor y un apuntador se refiere indirectamente a un valor. APR. Varios miembros pueden tener el mismo valor entero. monthName[month]). NOV.”July”. JUN. 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. pilas y árboles. month <=DEC. Los apuntadores como cualquier otra variable deben ser declarados antes de que puedan ser utilizados. MAY. Pag 24 . #include <stdio. OCT. Dado que el primer valor de la enumeración se define explícitamente en 1. MAR.”April”. “December”}. FEB. Los apuntadores son variables que contienen direcciones de memoria como sus valores.”March”. APR. contiene la dirección de una variable que contiene un valor especifico. SEP. en el cual los identificadores son definidos automáticamente a los enteros 0 a 11.”May”. main () { enum months month. En una enumeración el valor de cada constante en la enumeración puede ser establecido explícitamente en la definición. el referirse a un valor a través de un apuntador se conoce como indirección. Por lo regular una variable contiene directamente un valor especifico. los valores subsiguientes se incrementan en 1dando como resultado los valores 1hasta 12 Los identificadores en una enumeración deben de ser unicos.”November”. estructuras de datos que pueden crecer o encongerse como son listas enlazadas. SEP. colas de espera. mediante la asignación de un valor al identificador.Crea un nuevo tipo en enum months. JUN. utilice la enumeración siguiente enum monts {JAN =1. OCT. “October”. DEC}. Para numerar los meses 1 a 12. “September”.

o bien contadorAptr apunta a un objeto de tipo entero”. Cuando se inicializa 0 es equivalente a inicializar un apuntador a NULL. La variable yPtr se dice que apunta a y yPtr Representación gráfica de un apuntador apuntando a una variable entera en memoria. asigna la dirección de la variable y a la variable de apuntador yPtr. También. definida en el archivo de cabecera<stdio. int *yPtr. NULL es una constante simbólica. NULL. o una dirección. Los apuntadores deben ser inicializados cuando son declarados o en un enunciado de asignación. no un apuntador a un entero. El *solo se aplica a contadorAptr en la declaración. Pag 25 . El valor 0 es el único valor entero que puede ser directamente asignado a una variable de apuntador Operadores de apuntador El &. se lee. contador. Por ejemplo. u operador de dirección. es un operador unario que regresa la dirección de su operando.int *contadorAptr. contadorAptr contador 7 ContadorAptr se refiere en forma indirecta a la variable cuyo valor es 7 La declaración declará la variable contadorAptr siendo del tipo int*. Un apuntador puede ser inicializado a 0. la variable contador se declara como un entero. suponiendo las declaraciones int y= 5. el enunciado yPtr = &y. contador 7 Contador se refiere directamente a la variable cuyo valor es 7.h>. Un apuntador con el valor NULL apunta a nada. “contadorAptr es un apuntador a int”.

Cuando se pasa a una función la dirección de una variable. bPtr = b. como sigue bPtr = &b[0] Alternativamente el elemento del arreglo b[3] puede ser referenciado con la expresión de apuntador Pag 26 . Suponga que han sido declarados el arreglo entero b[5] y la variable de apuntador entera bPtr.number) cuboPorReferencia(&number). Un nombre de arreglo puede ser considerado como un apuntador constante. } void cuboPorReferencia(int *nPtr) { *nPtr = *nPtr * *nPtr* *nPtr.500000 yPtr 600000 y 600000 5 Representación en memoria de y y yPtr. Esto se lleva a cabo aplicando el operador de dirección (&). } Relación entre apuntadores y arreglos Los arreglos y los apuntadores en C están relacionados en forma intima y pueden ser utilizados casi en forma indistinta . Los operadores & * son complementos el uno del otro. Cuando se pasan direcciones de los argumentos que deban ser modificados. printf(“El nuevo valor de el número es %d\n”. printf(“El valor original del número es %d\n”. Dado que el nombre del arreglo (sin subíndice) es un apuntador al primer elemento del arreglo. se pasan las direcciones de los argumentos. La dirección de a y el valor de aPtr son idénticos en la salida. el operador de indirección (*). Los apuntadores pueden ser utilizados para hacer cualquier operación que involucre subíndices de arreglos. En C se utilizan los apuntadores y el operador de indirección para simular llamadas por referencia. confirmando asi que de hecho la dirección de a ha sido asignada a la variable de apuntador aPtr. puede ser utilizado en la función para modificar el valor de esa posición en la memoria de ese llamador #include <stdio. podemos definir bPtr igual a la dirección del primer elemento en el arreglo b mediante el enunciado. return 0.number). main() { int numero = 5. a la variable cuyo valor deberá ser modificado. Este enunciado es equivalente a tomar la dirección del primer elemento del arreglo.h> CuboPorReferencia(int *).

todas las expresiones de arreglos son subíndice pueden ser escritas mediante un apuntador y un desplazamiento. la notación anterior se conoce como notación apuntador/desplazamiento. Pag 27 . char *suit[4] = {“Corazones”. Por lo que en un arreglo de cadenas cada entrada es de hecho un apuntador al primer carácter.”Diamantes”. y utilizado en aritmética de un apuntador. el desplazamiento indica que el elemento del arreglo debe ser referenciado. La porción suit[4] indica un arreglo de 4 elementos. En ANSI C.Al igual que el elemento del arreglo puede ser referenciado con una expresión de apuntador. En el arreglo suit parece que estan colocadas estas cadenas pero en el arreglo solo están almacenados los apuntadores. Los cuatro valores a colocarse en el arreglo son: Corazones. 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] E Un ejemplo gráfico de un arreglo suit Asignación dinámica de memoria mediante new y delete Los operadores new y delete de C++ le permiten a los programas llevar a cabo la asignación dinámica de memoria. Diamantes. Cuando el apuntador apunta al principio del arreglo. de una longitud de un carácter más largo que el número de caracteres entre las comillas. permite el acceso a cadenas de caarácter de cualquier longitud.*(bPtr + 3) El 3 en la expresión arriba citada es el desplazamiento del apuntador. Un uso común para una estructura de datos como ésta. “Espadas”. Por lo tanto. En general.” Treboles”). la dirección &[3] puede ser escrita con la expresión de apuntador bPtr + 3 El arreglo mismo puede ser tratado como un apuntador. Considere la declaración TypeName *ptr. Cada entrada del arreglo es una cadena pero en C una cadena es esencial un apuntador a su primer carácter. Cada apuntador señala al primer carácter de su cadena correspondiente. la asignación dinámica de memoria por lo general se lleva a cabo con las funciones estandar de biblioteca malloc y free. aunque el arreglo suit es de tamaño fijo. y el valor del desplazamiento es idéntico al subíndice del arreglo. Por ejemplo la expresión *(b + 3) también se refiere al elemento del arreglo b[3]. Arreglos de apuntadores Los arreglos pueden contener apuntadores. Cada una de estas está almacenada en memoria como una cadena de caracteres terminada por NULL. es formar un arreglo de cadenas. La porción char * de la declaración índica que cada elemento del arreglo suit es del tipo apuntador a char. Treboles y Espadas.

C++ permite un inicializador para un objeto recién asignado. Cuando los datos están almacenados en un arreglo. en puestas en práctica anteriores a ANSI C. La memoria se asigna sobre el montón (memoria adicional disponible para el programa en tiempo de ejecución). el enunciado Ptr = new typeName Asigna memoria para un objeto del tipo typeName partiendo de la tienda libre del programa. Si mediante new no se puede asignar memoria. utilice el enunciado delete [] arrayPtr. El operador new crea automáticamente un objeto del tamaño apropiado y regresa un apuntador del tipo apropiado. Aplicar delete a un apuntador nulo no tiene efecto en la ejecución del programa. un arreglo (vector o tabla). También mediante new los arreglos pueden ser creados dinámicamente. Aplicar delete a un apuntador previamente desasignado puede llevar a errores. se regresa un apuntador nulo.14159). //creando arreglo dinámico Para desasignar la memoria asignada dinámicamente para arrayPtr por new.Donde typeName es cualquier tipo (como int. regresa un apuntador void al objeto. Para liberar en C++ el espacio para este objeto se utiliza el siguiente enunciado: delete ptr En C.de datos. Por ejemplo. El siguiente enunciado asigna dinámicamente un arreglo de un subíndice de 100 enteros y asigna el apuntador regresado por new al apuntador entero arrayPtr int *arrayPtr. en algún determinado orden con respecto a uno de los campos elementos del conjunto. En ANSI C. inesperados durante la ejecución del programa. float* cosaPtr = new float ( 3. a fin de desasignar memoria. Pag 28 . y asigna dicho apuntador a ptr Ptr = malloc (sizeof(typeName)) En C la asignación dinámica de memoria requiere una llamada de función a malloc y una referencia explicita al operador sizeof (o una mensión explicita al número necesario de bytes). etcétera). char. Ordenamiento La ordenación o clasificación de datos (sort) es una operación consistente en disponer un conjunto –estructura. un arreglo de registros. el apuntador regresado por malloc debe ser explícitamente convertido (cast) al tipo apropiado de apuntador con el convertidor explicito (cast) (typeName*). float. se denomina ordenación interna. el enunciado siguiente asigna en forma dinámica un objeto typeName. una lista enlazada o un árbol. Si los datos están almacenados en un archivo el proceso de ordenación se llama ordenación externa. El operador delete sólo puede ser utilizado para desasignar memoria ya asignada por new. Una colección de datos puede ser almacenada en un archivo. se invoca la función free con el argumento ptr. una lista enlazada o un árbol. En terminología de ordenación. el elemento por el cual esta ordenado un conjunto de datos se denomina clave. ArrayPtr = new int[100]. Los elementos numéricos se pueden ordenar en orden creciente o decreciente de acuerdo al valor numérico del elemento. En C++. También.

1. A[2]. este proceso de comparaciones e intercambios continua a lo largo de toda 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. por ello se debe prestar especial atención en su elección ¿Cómo se sabe cual es el mejor algoritmo? La eficiencia es el factor que mide la calidad y rendimiento de un algoritmo. tiempo menor de ejecución en computadora 2. comparando A[1] con A[2]. A[2]. Se compara el elemento j-esimo y el (j+1)-ésimo. De este modo se dice que los valores más pequeños burbujean hacia la parte superior de la lista. Estas operaciones constituyen una pasada a través de la lista. A[4]. Para su mejor comprensión. si están desordenados se intercambian entre sí. aunque por desgracia poco eficiente. ya que se encuentra en su posición correcta. intercambiándolos si están desordenados. ordenación por mezcla Ordenación por burbuja Este método es clásico y muy sencillo. Supongamos un vector A[1]. A continuación se compara A[2] con A[3]. A[1]. A[3]. ordenación rápida.. selección.A[n].. Se vuelve a explorar de nuevo la lista comparando elementos consecutivos e intercambiándolos cuando estén desordenados. inserción Shell. 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 número de la pasada y j indica el orden del elemento de la lista. Pag 29 . menor número de instrucciones Los métodos de ordenación se suelen dividir en dos grandes grupos:   directos indirectos(avanzados) burbuja. pero esta vez el elemento mayor no se compara. veamos gráficamente el proceso anterior con un vector (lista) con cinco elementos. Se comienza el seguimiento del vector de izquierda a derecha.Los métodos (algoritmos) de ordenación son numerosos.. mientras que los valores más grandes se hunden hacía el fondo de la lista. La ordenación por burbuja se basa en la comparación de elementos adyacentes de la lista (vector) e intercambiar sus valores si están desordenados. A[5]. al terminar esta pasada se habrá situado en su sitio el segundo elemento más grande se siguen las comparaciones hasta que toda la lista este ordenada cosa que sucederá cuando se hayan realizado (n-1) pasadas.

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 .

El número 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.i++) if (Lista[i]> Lista[I+1]){ aux = Lista[I]. Cuando se exploré la lista y el indicador no refleje intercambios.A[j+1]) Fin_si Fin _desde {bucle j} Fin_desde {bucle i} void burbuja(int Lista[]. j++) for (i=0. No Intercambio se pone en 0. i < N-j. Lista[i+1]= aux.int N){ int i. por lo que una lista de n elementos necesitará n-1 pasadas. for (j=1. Si dos elementos se intercambian en una pasada.aux. } } Burbuja mejorado El algoritmo burbuja se puede mejorar si disponemos de algún tipo de indicador que registre si se han producido intercambios en la pasada. El bucle externo for se sustituye por un bucle do while o bien while y un contenido i se necesitara para contar el número de pasadas.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 números de cinco elementos. Pag 31 . Algoritmo (Pseudocódigo) Desde i = 1hasta n-1 hacer Desde j= 1 hasta n-i hacer Si A[j] > A[j+1] Entonces Intercambio (A[j]. j<N. en el que j varía desde 1 hasta 5 menos el valor especifico de i. j. Al principio de cada pasada NoIntercambio se fija a 1 y a 0 si se produce a intercambios. Lista[i] =Lista[i+1]. la lista estará ya ocupada y se terminarán las comparaciones. El intercambio será una variable lógica NoIntercambio (o bien ordenado) que se inicializa a 1 (significa que la lista a priori está desordenada).

I++) if (Lista[I]>Lista[I+1]){ Bandera=1. Lista[i]=Lista[i]. A[1] A[2] A[3] A[4] A[5] 1 A[N] 4 10 15 6 Pag 32 . A[j+1]) NoIntercambio = false Fin si Fin_desde i = i+1 Hasta que NoIntercambio = true void burbuja_mejorada(int Lista[]. Tres cartas Cuatro cartas Cinco cartas 2 2 6 6 10 //////// 9 //////// //////// 7 //////// 10 2 6 9 10 El método se basa en considerar una parte de la lista ya ordenada y situar cada uno de los elementos restantes insertándolo en el lugar que le corresponde por su valor.i =1 Repetir NoIntercambio = true Desde j = i hasta n – i hacer Si A[j] > A[J+1] Entonces Intercambio (A[j].aux. Lista[i+1]=aux. } j++. } Ordenación por inserción Este método está basado en la técnica utilizada por los jugadores de cartas para clasificar sus cartas.j=1.int N){ int i. for(I=0.I<N-j. El jugador va colocando (insertando) cada carta en su posición correcta.bandera=1. Aux=Lista[I]. while (j<N && bandera==1){ Bandera=0.

la operación de desplazamiento termina después que todos los elementos de la lista se han desplazado. /*subindice de este elemento después de la inserción*/ aux.k.  Hacer espacio para Aux desplazando todos los valores mayores que dicho valor A[k] una posición. /*obtener siguiente elemento a insertar*/ Lista[nueva_pos(Lista.int n){ /*Lista (entrada/salida) .k++){ aux = lista[k]. la operación de desplazamiento termina cuando un valor menor o igual a Aux se alcanza. comenzando con el elemento de la lista de posición Aux-1.k<=n. Comenzar con el elemento K – 1 */ encontrado= 0.int aux ){ int encontrado . for(k=2. Si Aux no es el valor más pequeño. /*insertar aux en posición nueva*/ } return 0. Fin_mientras Codificación del procedimiento OrdenarInserción void OrdenacionInsercion(int Lista[]. que mueve todos los elementos de la lista mayores que Aux. /*subindice del siguiente elemento al que se inserta*/ nueva_pos. Aux se inserta en la posición que ocupaba el último valor que se desplazó. Fin_desde La operación de desplazamiento se realiza con un procedimiento Desplazar. Pag 33 . while (k > 1 && !encontrado) if (Lista [k– 1] > aux) { Lista[k] = Lista[k – 1].aux)]=aux. } La función de nueva posición que desplaza todos los elementos de la lista int nueva_pos (int Lista[]. n (entrada) */ /*Lista = array de N elementos enteros */ int k. Algoritmo de desplazamiento Mientras el primer elemento no se desplaza y el valor del elemento > Aux hacer o Desplazar elemento una posición. Si Aux es el valor más pequeño hasta aquí.int k. o Definir NuevaPos como posición original del último elemento desplazado. {indicador} /*desplazar valores > Aux .  Insertar el valor de Aux en el lugar del ultimo valor desplazado.1 Algoritmo 4 6 10 15 {para cada elemento de la lista después del primero} desde k = 2 hasta n hacer  Guardar el valor de este elemento A[k] en una variable Aux. o Comprobar valor del siguiente elemento.

k--. y se intercambiaba con el elemento de subíndice n – 1: por consiguiente. int PosMayor (int Ultimo.. } void Seleccion (int Limi. 2.n – 1.. for(J=Limi.J++) { Pag 34 . for(Indice= 2. Indice.. 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 posición del elemento mayor y luego poder intercambiar. 4.int Lista[] ){ /*encuentra el indice del elemento mayor en la tabla [1. Mayor. se sitúa el segundo elemento mayor en la posición n-1. 3.n – 2 y así sucesivamente. J. } else encontrado = 1. return k. Ultimo]*/ int Indice_Max. A continuación se busca el elemento mayor en la sublista 1. >=2 . return Indice_Max.int Lista[]){ int Aux. Intercambiar el elemento mayor con el elemento de subíndice n (o bien el elemento menor en el subíndice 1). Indice<=Ultimo. } {Desplazar} Ordenación por selección El algoritmo de ordenación por selección de una lista (vector) de n elementos tiene los siguientes pasos: 1. Indice_Max = 1. A continuación se busca el elemento mayor en la sublista de subíndices 1. Encontrar el elemento mayor de la lista.Indice++) if (Lista [Indice] > Lista [Indice_Max] ) indice_Max = Indice...

La idea general del método (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. L. Se suele denominar también ordenación por disminución de incremento (gap). Se clasifica cada grupo por separado (se comparan las parejas de elementos y si no estan ordenados se intercambian entre si de posiciones).. Lista[Mayor] = Lista[J]. 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. 2. en este ejemplo) en ocho grupos de dos (consideramos un incremento o intervalo de 16/2 = 8). D. Shell. Lista [J] = Aux. 3.J*/ Mayor = PosMayor (J. Primer paso (división/ordenación por 8) 504 88 513 62 908 171 898 277 654 427 150 510 612 675 750 704 Pag 35 . Lista). 4. Un tercer paso clasifica dos grupos de ocho registros y luego un cuarto paso completa el trabajo clasificando todos los 16 registros. } } Ordenamiento Shell La ordenación Shell debe el nombre a su inventor. /*Intercambio con el elemento Tabla [J]*/ Aux = Lista[Mayor]./*encontrar el elemento mayor de 1.

Segundo paso (división/ordenación por 4) 504 88 150 62 612 171 760 277 654 427 513 510 908 675 898 704 Tercer paso (división/ordenación por 2) 504 88 150 62 612 171 513 277 654 427 760 510 908 675 898 704 Cuarto paso (división/ordenación 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 más populares y citados en numerosas obras de programación 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

Código 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, fusión o intercalación (merge) consiste en tomar dos vectores ordenados (a, b) y obtener un nuevo vector también ordenado. El algoritmo más 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 solución no tiene en cuenta que los valores de de a y b ya están ordenados. El algoritmo que tiene en cuenta la ordenación es el siguiente: 1. Seleccionar el elemento de valor o clave mas pequeño 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 más pequeño 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 números a y b. 2 -15 4 0 78 13 97 15 lista A lista B

78

90

96

2 i < j

4

78

97

Lista A

j se ha incrementado junto con k

k

-15 0 -15 0

13

15

78

90

94

96

Lista B

Lista C

Lista C

Pag 38

2 i < j

4

78

97

Lista A

k

-15 0 -15

13

15

78

90

94

96

Lista B

Comparar A[i] Y B[j]. Poner el Más pequeño 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: número 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

Búsquedas Búsqueda secuencial Un problema importante en el proceso de datos, como ya se ha comentado, es la búsqueda en un conjunto de datos de un elemento especifico y la recuperación de alguna información asociada al mismo. La búsqueda lineal o secuencial es la técnica más simple para buscar un elemento en un arreglo (vector). Consiste el método 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 posición del elemento buscado y en caso contrario un mensaje que indique la falta de éxito en la búsqueda. Supongamos una lista de números de la seguridad Social incluidos en un arreglo a y se desea buscar a ver si existe el número 453714. Números 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 varía, en el ejemplo anterior, de 1 a 100. En caso de encontrarlo, almacenar la posición (el indice arreglo) del mismo dentro de la lista y finalmente se devolverá al programa principal. Pseudocódigo 1 Posición = 0 {lista = vector a[i] de n elementos} desde i = 1 hasta n hacer si a[i] = t entonces Posición = 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 búsqueda ha tenido éxito. El valor del índice i es mayor que el número de términos de la lista, lo que significa que se ha terminado de recorrer la misma y el elemento buscado no ha aparecido.

Pseudocódigo 2

Pag 40

} {posición del elemento en la lista Búsqueda Binaria La búsqueda lineal.I=1. Encontrado = 1. por sus simplicidad es buena para listas de datos pequeñas para listas grandes es ineficiente. 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 . /*entrada. Se repite este proceso hasta que por divisiones o aproximaciones sucesivas se encuentra la palabra. return 0. Cuando se busca una palabra no se comienza la búsqueda por la página 1 y se sigue secuencialmente sino que se abre el diccionario por una pagina donde aproximadamente se piensa puede estar la palabra. vector búsqueda*/ int N . /*posición del elemento caso de no éxitir*/ while (I <= N) && ! Encontrado){ if (A[I] == T ){ Posicion= I. al abrir la página se ve si se ha acertado o en que parte se encuentra la palabra buscada. /* entrada. Posicion= 0. número de elementos */ int T /*elemento a buscar*/){ int Encontrado=0. Este método tiene una clara expresión en la búsqueda de una palabra en un diccionario. } {fin del if y del while} I ++. Se basa en el conocido método divide y vencerás. es decir se divide el diccionario en dos partes. la búsqueda binaria es el método idóneo.Posicion.Encontrado = falso Posición = 0 i=1 mientras (i <= n) y (No Encontrado=verdadero) hacer si a[i] = t entonces Posición = i Encontrado = verdadero Fin si i=i+1 fin_mientras Función búsqueda lineal int BusquedaLineal (int A[].

En este caso se ha encontrado el elemento deseado en tres comparaciones. Este método es muy eficiente con el único inconveniente. mientras Primero <= Ultimo y Encontrado = falso hacer {Encontrar posición 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. número de elementos). 2. la nueva sublista donde buscar es 1989 2002 Elemento considerado central como ya no hay elemento central se toma el número inmediatamente anterior de la posición central. Establecer Primero = 1 y Ultimo = n (n. Estas variables representan la primera y última posición de la lista o sublista donde se está buscando y permite el calculo de la posición del elemento central. 3. el elemento a buscar estará en la segunda mitad. Por consiguiente. que en este caso es 1989. y como1989 es menor. Encontrado = falso . Dado que 1989 es mayor que 1898. que requiere la lista ordenada. se sigue la búsqueda en esta mitad: Elemento a buscar 1989 2002 2400 2670 3200 Elemento central El elemento central en esta sublista es 2400. si Encontrado = verdadero entonces Posición = Central {existe elemento} si no Posición = 0 {no se ha encontrado} fin_si Pag 42 . Algoritmo 1. mientras que en la búsqueda lineal hubiese necesitado al menos seis comparaciones.Y que se busca el número 1989 Se examina en primer lugar el elemento central de la lista (las divisiones se toman iguales) 1898.

else Ultimo = Central –1. Central. Primero = 1. Estructura de Datos. Las estructuras de datos estáticas son aquellas en las que se asigna una cantidad fija de memoria cuando se declara la variable. 2. pero ningún otro. Tipo de Dato Abstracto. Ultimo. 3. else if ( T > L [Central]) Primero = Central + 1. en C son enteros Pag 43 . Por consiguiente. 4. Una estructura de datos es una colección de datos organizados de un modo particular. int L[] . su significado es diferente. las acciones típicas (módulos) en un algoritmo de búsqueda binaria son: 1. el tipo de datos de una variable es el conjunto de valores que este puede tomar. Ultimo = N. Lectura del vector Ordenación del vector Búsqueda binaria Visualizar resultado int Binaria (int T. representaciones secuenciales. } Estructuras de datos lineales. Encontrado. Encontrado = 0 .int N ){ int Primero. if (T == L [Central]) Encontrado = 1. } if (!Encontrado) return 0. Registros. colecciones de datos que crezcan y reduzcan su tamaño en memoria a medida que el programa progresa. while ((Primero <= Ultimo) && !Encontrado){ Central = ( Primero + Ultimo) / 2. en numerosas ocasiones se necesitan. a estas estructuras de datos cuya ocupación en memoria puede aumentar o disminuir en tiempo de ejecución se denominan estructuras dinámicas de datos. Por ejemplo una variable de tipo booleano puede tomar los valores verdadero o falso. Concepto de estructuras de datos Aunque los terminos tipo de datos (o simplemente <<tipo>>). las estructuras de datos pueden ser de dos tipos: estructuras de datos estáticas y estructuras de datos dinámicas. Conceptos fundamentales: Tipo de Dato. else return Central. <<estructura de datos>> y <<tipo de dato abstracto>> parecen semejantes.La búsqueda binaria requiere una ordenación previa del vector o lista en el que se va ha efectuar la búsqueda. los tipos de datos básicos varian de un lenguaje a otro. En un lenguaje de programación.

char dirección[40]. conectadas entre si de diversas formas. que son conjuntos de variables quiza de tipos distintos. tipo_estructura es un identificador que nombra el nuevo tipo definido. La anterior definición no reserva ningún espacio en memoria. Para representar el modelo matemático básico de un TDA se emplean estructuras de datos. que es una sucesión de celdas de un tipo dado al cual se llamará casi siempre <<tipo_celda>> .miembro Ejemplo: struct ficha /* definición del tipo estructura ficha */ { char nombre[40]. Pag 44 . variable]…]. En la definición del tipo estructura. que contienen unicamente elemento de un mismo tipo de datos. denominado tipo estructura y declarar una variable de este tipo. las estructuras de datos se crean dando nombres a agregados de celdas El mecanismo de agregación más sencillo en C y en la mayor parte de los lenguajes de programación es el arreglo (unidimensional). Las estructuras pueden contener variables de muchos tipos diferentes de datos a diferencia de los arreglos. y declarar una variable de este tipo. }. real (float). Creación de estructuras Crear una estructura es definir un nuevo tipo de datos. que se utiliza para declarar variables.(int). La síntaxis es la siguiente: struct tipo_estructura { declaraciones de los miembros }. más bien genera un nuevo tipo de datos. Generalmente las estructuras se utilizan para definir registros a almacenar en archivos. Se puede representar una celda como una caja capaz de almacenar un valor tomado de algún tipo de datos básico o compuesto. se especifican los elementos que la componen así como sus tipos. junto con varias operaciones definidas sobre ese modelo. de la forma: struct tipo_estructura [variable[. Para referirse a un determinado miembro de la estructura. Las reglas para construir tipos de datos compuestos a partir de los básicos también varían de un lenguaje a otro Un tipo de datos abstracto (TDA) es un modelo matemático. podemos declarar una variable de ese tipo. Cada elemento de la estructura recibe el nombre de miembro (campo del registro). Después de definir un tipo estructura. y carácter (char). se utiliza la notación: variable. Estructuras Las estructuras son colecciones de variables relacionadas –a veces denominados agregados bajo un nombre. En la definición del tipo estructura. long telefono. El componente básico de una estructura de datos es la celda.

de tipo ficha. o agregados. unión. auto o register y no puede ser inicializado. las variables de ese tipo de estructura pueden únicamente ser declaradas dentro de la definición de la estructura. Telefono = 232323. struct card a = (“Three”. Como inicializar estructuras Las estructuras pueden ser inicializadas mediante listas de inicialización como los arreglos. dirección y teléfono. puede utilizarse exactamente igual que cualquier otra variable. La únicas operaciones válidas que pueden ejecutarse sobre estructuras son : asignar variables de estructura a variables de estructura del mismo tipo. array. deck[52]. Si la definición de una estructura no contiene un nombre de rótulo e estructura. Su tipo puede ser: fundamental. var2. La declaración de las variables var1 y var2. El nombre del rótulo es opcional. struct card { char false[10]. y utilizando el operador sizeof. puntero. sin embargo pudiera ser incluido un apuntador a la estructura ( estructura autoreferenciada). long telefono. a fin de determinar el tamaño de la variable de estructura. La declaración de un miembro de una estructura no puede contener calificadores de clase de almacenamiento extern. estructura o función.nombre). por lo que cada una de las variables consta de los miembros: nombre. Los miembros de la estructura pueden ser variables de los tipos de datos básicos. var2. Pag 45 . char suit[10]. como son los arreglos y otras estructuras. }a. char direccion[40]. “Hearts”). static. puede realizarse también directamente de la siguiente forma: struct ficha { char nombre[40]. Este ejemplo define las variables var1 y var2. Ejemplo: var1.struct ficha var1. Una variable que es un miembro de una estructura. tomando la dirección (&) de una variable de estructura obteniendo acceso a los miembros de una varible de estructura. } var1. Una estructura no puede tener una instancia de si misma. gets(var2.

/* Puntero al nombre leido*/ Pag 46 . pasando toda la estructura o pasando un apuntador a una estructura. }Card. Cómo utilizar estructuras con funciones Las estructuras pueden ser pasadas a funciones pasando miembros de estructuras individuales. Lo anterior se puede expresar de la siguiente forma typedef struct{ char false[10].Como tener acceso a los miembros de estructuras Para tener acceso a miembros de estructuras se utilia el operador de miembro de estructura (. Al crear un nuevo nombre utilizando typedef no se crea un nuevo tipo. Cuando se pasan estructuras o miembros individuales de estructura a una función se pasan en llamada por valor. float nota. char suit[10]. Para pasar una estructura en llamada por referencia. La declaración Card deck[52].h> #include <stdlib. Ejemplo: El siguiente programa lee una lista de alumnos y sus correspondientes notas de final de curso.). Card puede ser utilizado para declarar variables de tipo struct card. a. struct ficha alumnos[NA]. typedef simplemente crea un nuevo nombre de tipo que puede ser utilizado como un seudónimo para un nombre de tipo existente. define el nuevo nombre de tipo Card como un sinónimo para el tipo struct card. /*arreglo de estructuras o registros */ int n =0. char *fin. pase la dirección de la variable de estructura.suit). Los arreglos de estructura como todos los demás arreglos son automáticamente pasados en llamada por referencia. }. dando como resultado el tanto porciento de alumnos aprobados y suspendidos #include <stdio. typedef La palabra reservada typedef proporciona un mecanismo para la creación de sinónimos (o alias)para tipos de datos anteriormente definidos typedef struct card Card.h> #define NA 10 main() { struct ficha { char nombre[60]. printf (”%s”.i.

Si n = 0. 3. los elementos son accesibles y se pueden insertar y suprimir en cualquier posición de la lista.. printf(“Aprobados %. fin = gets(alumnos[n].aprobados /n*100). Pag 47 . Matemáticamente.. las listas también pueden concatenarse entre si o dividirse en sublistas. que no tiene elementos. por que pueden crecer y acortarse según se requiera. Lista Modelo Matemático Las listas constituyen una estructura flexible en particular. Observese que la posición FIN(L).2g %%\n”.nota). Una propiedad importante de una lista es que sus elementos pueden estar ordenados en forma lineal de acuerdo con sus posiciones en la misma.A.2g %% \n”. Se dice que ai precede a ai+1 para i = 1.. scanf(“%f”.nota >=5) aprobados ++. A menudo se representa una lista como una sucesión de elementos separados por comas A1. La función FIN(L) devolverá la posición que sigue a la posición que sigue a la posición n en una lista L de n elementos. Para formar un tipo de dato abstracto a partir de la noción matemática de la lista. mientras que las demas posiciones guardan una distancia fija con respecto al principio de la lista.suspensos/n*100).nombre).. a3.i<n>i++) if (alumnos[i].n1 y que ai sucede a ai-1 para i = 2. con respecto al principio de la lista. se representan de manera rutinaría en aplicaciones como manera de aplicación. 2. printf(“Nombre”).n...&alumnos[n++]. Es conveniente postular también la existencia de una posición que sucede al último elemento de la lista. } for (i =0.. printf(“Nombre “).D. se debe de definir un conjunto de operaciones con objetos de tipo lista. else suspensos ++.an Donde n ≥ 0 y cada a1 es de tipo tipo_elemento.float aprobados = 0. suspensos = 0. Se dice que el elemento ai está en la posición i. a2.. printf(“Suspensos %. se dice que a1 es el primer elemento y an el último elemento. /*Entrada de datos*/ printf(“Finalizar la entrada con cont/Z\n\n”). while (n <NA && fin ¡=NULL) { printf(“Nota “). se tiene una lista vacía. } T.nombre).. Al suponer que n ≥ 1. Al número n de elementos se le llama longitud de la lista. fin =gets(alumnos[n].. una lista es una secuencia de cero o más elementos de un tipo determinado. es decir.. está en una distancia que varía conforme la lista crece o se reduce.

... ambas funciones no estan definidas cuando L no tiene posición P. SIGUIENTE(p. Esta función inserta x en la posición p de la lista L. L). L). Observese que <<posición>> es otro tipo de datos cuya implantación cambiará con aquella que se haya elegido para las listas.. ANTERIOR no esta definida si p es 1. Esta función devuelve el elemento que esta en la posición p de la lista L..a2.. De la misma forma la eliminación de un elemento.. L) y ANTERIOR (p.. a2. Si p es FIN(L) entonces L se convierte en a1. SIGUIENTE(p.. IMPRIME_LISTA(L). SUPRIME(p....L) devuelven las posiciones siguiente y anterior. El resultado no está definido si p = FIN(L) o si L no tiene la posición p. 6. a2. 5. L). INSERTA(x...ap-1. an. ap+1. ap. x es un objeto de ese tipo y p es de tipo posición. Esta función elimina el elemento en la posición p de la lista L. en la practica pueden tener otra representación. L se convierte en a1. a p en la lista L. la posición de la primera aparición de x es la que se devuelve. No obstante en la practica siempre es posible modificar RECUPERA para devolver un apuntador a un objeto de tipo elemento. 7. Implementación de listas con arreglos En la realización de una lista mediante arreglos. a2.. Si p es la última posición de L. an. PRIMERO(L). Si x figura más de una vez en L. los elementos deben ser de un tipo que pueda ser devuelto por una función... RECUPERA(p.Operaciones Se representará ahora un conjunto representativo de operaciones con listas. L es una lista de objetos de tipo tipo_elemento... Esta representación permite recorrer con facilidad una lista y agregarle elementos nuevos al final.. LOCALIZA(x.an. Obsérvese que si se utiliza RECUPERA. Si la lista L no tiene posición p. Si L es a1. Ahí.. p. respectivamente. Esto quiere decir que si L es a1.. 1.. el resultado es indefinido. x. a2. Esta función ocasiona que L se convierta en la lista vacía y devuelve la posición FIN(L).an.. 3. an. pero insertar un elemento en la mitad de la lista obliga a desplazarse una posición dentro del arreglo a todos los elementos que siguen al nuevo elemento para concederle espacio. SIGUIENTE no esta definida si p es FIN(L). ap-1.. la posición que se devuelve es FIN(L). El resultado no está definido si L no tiene posición p o si p = FIN(L). pasando los elementos de la posición p y siguientes a la posición inmediata posterior.. Si L está vacía.. Imprime los elementos de L en su orden de aparición en la lista. L). L) =FIN(L). excepto el último. se convierte en a1. x. requiere desplazamientos de elementos para llenar de nuevo el vacio formado. 2. ANULA(L). Esta función devuelve la posición de x en la lista L. 8. Esta función devuelve la primera posición de la lista L. Pag 48 .. 4. Aunque de manera informal se piensa en las posiciones como enteros. los elementos de esta se almacenan en celdas contiguas de un arreglo. Si x no figura en la lista entonces se devuelve FIN(L).

Las declaraciones importantes son: #include <stdio. mediante el entero i. Lista &L). posicion Fin(Lista &L). typedef struct LISTA Lista. void Anula(Lista &L). void Imprime(Lista L). posicion Primero(Lista &L). typedef char logico. el primero es un arreglo de elementos que tiene la longitud adecuada para contener la lista de mayor tamaño que se puede representar. El segundo campo es un entero últ que indica la posición del último elemento de la lista en el arreglo. Lista &L). posicion Siguiente(posicion p. logico Llena(Lista &L).h> # define TAM 10 # define TRUE 1 # define FALSE 0 typedef int dato. posicion ult. posicion Anterior(posicion p. El i-ésimo elemento de la lista está en la i-ésima posición. typedef int posicion.1 2 primer elemento segundo elemento lista últ último elemento vacío Long_máx En la realización con arreglos se define el tipo LISTA como un registro con dos campos. Pag 49 . struct LISTA { dato elem[TAM].h> #include <conio. posicion Ultimo(Lista &L). La función FIN(L) sólo tiene que devolver últ +1. logico Vacia(Lista &L). }.

Lista &L). } logico Vacia(Lista &L) { if (L. else return -1.ult+1). } void Anula(Lista &L) { L. else return -1. posicion Fin(Lista &L) { return (L. } posicion Ultimo(Lista &L) { if (L.ult || p== 0 ) return(-1).ult || p< 0 ) return(-1). Pag 50 . posicion Localiza(dato x.ult. else return 0. Lista &L).ult >=0) return (0). Lista &L). } posicion Siguiente(posicion p.void Insertar(dato x. dato Recupera(posicion p. void Suprimir(posicion p. } logico Llena(Lista &L) { if (L. else return (--p). Lista &L) { if (p>L.ult >=0) return L.ult==TAM-1) return 1 . Lista &L). } posicion Anterior(posicion p. Lista &L) { if (p>=L.ult==-1) return 1. posicion p. else return (++p).ult=-1. } posicion Primero(Lista &L) { if (L.

Lista &L) { if (p>L. posicion p.else return 0. } void Insertar(dato x. else return(q). Lista &L) { posicion q=0. if (Vacia(L)) printf("Lista Vacia").ult--. if (q==Fin(L)) return (-1). } } } void Suprimir(posicion p.elem[p]=x.elem[q]!=x) q++. else { if (p>Fin(L) || p< 0) printf( "Posicion invalida"). Lista &L) { posicion q. q--) L.ult || p< 0) printf( "Posicion invalida"). L.elem[q]=L.ult.ult || p< 0) { printf( "Posicion invalida"). else if (p>L. while (q<Fin(L) && L. } Pag 51 .ult.ult++. if ( Llena(L) ) printf("Lista Llena"). L. } dato Recupera(posicion p. q++) L. L. q>=p.elem[q+1]. Lista &L) { posicion q. } } posicion Localiza(dato x. else { for ( q= L.elem[q+1]=L.elem[q]. return -1. q<L. else { for ( q= p.

Una ventaja es que como es bien sabido. } Ventajas y Desventajas de la implementación de Listas con arreglos. scanf("%d". los arreglos son estructuras de acceso directo. } Imprime(L). Pag 52 . x=Anterior(3.L). Insertar(1. clrscr(). printf("La posicion el numero"). } void main( void ) { Lista L. printf("%d". esto quiere decir que el tamaño es fijo y no puede establecerse mientras se está ejecutando el programa.&p).i<9.L).L.L). for ( q=0. La implementación con arreglos requiere especificar el tamaño máximo de una lista en tiempo de compilación. for(i=0.i++) { printf("Dame el numero").else return(L. esto implicaría falla del programa.&x). int i.Primero(L).L). } void Imprime(Lista L) { posicion q. lo cual implica conocer con exactitud la longitud de la Lista para evitar desperdicio de memoria o bien si se estableció de un tamaño máximo y al utilizarla se rebasa.L). así que una representación con arreglos en un momento dado pudiera ser de utilidad. Las operaciones como INSERTA y SUPRIME requieren un tiempo proporcional al número de elementos que haya que desplazar. Suprimir(p. getch(). Anula(L). scanf("%d". por lo que las demás operaciones son triviales. dato x. y por lo general estás son las operaciones más importantes.L).x). printf("%d". Insertar(x. posicion p. en otras palabras.x).elem[q]). q<Fin(L). x=Siguiente(5. x=Recupera(1. clrscr(). un arreglo es una variable estática. q++) printf("%d\n".x).elem[p]). Imprime(L). printf("%d".0.

establecemos la Pila como un registro don dos campos.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. ANULA(P). Regresa verdadero si la Pila está llena y falso si no lo está. tales como evaluación de expresiones. P). el primero un arreglo para contener los datos de la Pila y un campo para almacenar la posición del elemento superior de la pila (que en lo sucesivo llamaremos tope). Implementación 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. 2. TAM Pag 53 . TOPE(P). asi como en la implementación de rutinas recursivas. #define TAM 38 typedef char tipo_elem. }. Devuelve verdadero si la Pila está vacía y falso en caso contrario. 3. 4. entre muchas otras aplicaciones. anidación de parentisis. Son estructuras LIFO (Last In First Out) o último en entrar. . Suprime el elemento que está en el tope de la pila P. primero en salir en las que solo es posible quitar el elemento que se encuentra en la parte superior de la pila (tope). Devuelve el elemento que está en el tope de la Pila. 1 2 3 4 tope . Int tope. PUSH(x. typedef int logico. Que convierte la pila en una Pila Vacía. son muy útiles en muchos aspectos de la programación. VACIA(P). Una estructura de este tipo generalmente incluye las Operaciones siguientes: 1. 6. struct Pila{ Tipo_elem elemento[TAM]. POP(P). . 5. Inserta el elemento x en la parte superior de la Pila. LLENA(P).

elemento[P.elemento[P.tope]=x. operadores y delimitadores.Pila &P){ if (LLENA(P)) printf(“Error. si lo hacemos como 6 + (4/2). /*Eliminamos el elemento del tope*/ } void PUSH(tipo_elemento x. } void POP(Pila &P){ if (VACIA(P)) printf (“Error. else P. Pila Llena\n”) else { P.tope =TAM +1. Si la resolvemos como (6+4)/2. } Notación Polaca Las expresiones se componen de operandos.tope++. } } int VACIA(Pila &P){ return P. Los operadores tienen su precedencia Pag 54 . Los operandos son valores numéricos que se utilizan para calcular la expresión. Los operadores indican las operaciones matemáticas que van hacerse sobre los operandos respectivos. else return L.tope--. como en la expresión 6 + 4/2.void ANULA(Pila &P){ P. el resultado es 8. P. } int LLENA(Pila &P){ return P. Pila Vacía\n”).tope].tope==TAM+1. También determinan la cantidad de operandos necesarios para cada tipo de operación (binarios y unarios). Es evidente que el orden en que se calculan las operaciones puede ser muy importante.tope==1. la respuesta es 5. {Creamos una Pila Vacía} } tipo_elem TOPE(Pila &P){ if (VACIA(P)) return TAM +1.

colocamos los operadores entre sus operandos. Encerrar entre parentisis toda la expresión infija.Operador x. usando paréntesis. A + B (infija) A B + (posfija o polaca) La segunda forma con el operador después del operando se conoce como notación posfija (polaca). Cuando una expresión incluye operaciones de igual precedencia. los pasos que debemos seguir son: 1. se calculan de izquierda a derecha. pueden utilizarse paréntisis pero en su ausencia las operaciones de mayor precedencia se resuelven primero.- Valor 3 2 1 Para cambiar el orden de cálculo de una expresión. vamos a cambiar el orden del cálculo de los operandos y a convertir la expresión (a + b) X c en notación polaca: (a + b) X c (a + b) X c (ab +)X c (ab +) c X ab + c X expresión infija se añaden paréntesis sin cambio se convirtió el + se convirtió el X se eliminó el paréntesis Pag 55 . pero cuales son los dos operandos del operador +?. Volver a colocar (mover) los operadores. podemos convertir expresiones infijas a la notación polaca correspondiente. para que la expresión resultante sea de este modo: a + (bcX) Los dos operandos del operador X son b y c. la respuesta es a y el resultado de la subexpresión (bXc)./ +. 2. por lo que es fácil determinar la posición posfija de ese signo. Quitar los parentisis. Y ponemos el operador + después del parentisis de cierre: a(bcX)+ El paso final es quitar el paréntesis abcX+ Ahora. así el primero que debemos mover es el signo de multiplicación. vamos a convertir la expresión a + b X c a notación polaca. X. uno por uno y en orden de precedencia. a su posición final en notación postfija (a la derecha de sus operandos). es decir. Al utilizar las reglas de precedencia de operadores. La forma más usual de representar una expresión es la forma infija. El primer paso es agregar los paréntesis: a + (bXc) Después se colocan los operadores en orden de precedencia. Por ejemplo. 3.

o bien el primero o el último elemento de la misma. por lo tanto. 9 23 31 17 21 19 13 15 26 1. 9 23 31 17 // 21// 19 13 15 26 pivote 2. y la cantidad de código necesario es sorprendentemente pequeño comparando con la excelente velocidad que proporciona. La recursividad es una alternativa a la iteración o repetición. A continuación se establecen dos punteros en la lista I o J. para representar los valores de todas las variables que pertenecen a cada procedimiento activo de un programa. Así. Cuando se llama a un procedimiento P. utiliza una pila de registros de activación. de modo que en una de ellas estén todos los elementos menores que el pivote. en los lenguajes de programación. Todo lenguaje que como Pascal. su registro de activación debe estar en el tope de la pila. existen numerosas situaciones en las que la recursividad es una solución simple y natural a un problema que en caso contrario sería difícil de resolver. y aunque en tiempo de computadora y en ocupación de memoria es la solución recursiva menos eficiente que la solución iterativa. La elección del pivote es arbitraria aunque por comodidad es usual utilizar el termino central de la lista original. El primer puntero apunta al primer elemento. independientemente. Elijamos el elemento pivote. La organización a tiempo de ejecución 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 ejecución. 21. supongamos el término central.Recursividad Un subprograma (procedimiento o función) recursivo es aquel que se llama así mismo. se colocó en el registro de activación de P al llamar a este procedimiento). Hoare. Una aplicación importante de las pilas se da en la aplicación de procedimientos recursivos.  Dividir o partir la lista original en dos sublistas o mitades. pueso que P no puede volver si no lo han hecho todos los procedimientos a los que P ha llamado.  Las sublistas deben ser ordenadas. del mismo modo. se coloca en la pila un nuevo registro de activación para P. Por consiguiente.H. conocido como dirección de retorno. Cuando P vuelve. El segundo puntero apunta al último elemento y. se puede sacar de la pila el registro de activación correspondiente a la llamada actual de P y hacer que el control regrese al punto en el que P fue llamado (este punto. La idea básica de la ordenación rápida es:  Elegir un elemento de la lista denominado pivote. I =1. permita procedimientos recursivos. J=9 (noveno elemento). lo que conduce a un algoritmo recursivo. Ordenamiento Rápido (Quick Sort) Es uno de los métodos más rápidos y frecuentemente utilizados en ordenación (Quick Sort) Fue inventado por C. con independencia de si ya existe en ella otro registro de activación para ese mismo procedimiento. 9 23 31 17 // 21// 19 13 15 26 I= 1 J=9 Pag 56 .

El proceso se repite J 9 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 J I 6. 9 23 31 17 // 21// 19 13 15 26 I J 4. se incrementa el valor de I en 1. buscando un elemento menor que 21. Pag 57 . se ha terminado la partición. Se intercambian los elementos apuntados por I y J y a continucación se incrementan en uno los contadores I. J. todos los elementos menores o iguales a 20. y en segunda todos los elementos mayores que 20. A continuación se realiza la misma tarea con el puntero J.3. En el momento en que J < I. Mientras I apunte a un elemento que sea menor que 20. y mientras no lo encuentra se decrementa J en 1. Se han generado dos sublistas que tienen las propiedades citadas: la primera sublista. 9 15 31 17 // 21// 19 13 23 26 I 5. hasta que se encuentre un elemento mayor que el pivote.

Sublista Izquierda 9 I 15 13 17 19 J Sublista Derecha 21 I 31 23 26 J 9 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 J I Sublista izquierda 12 15 17 19 J I 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 .

para dividir la sublista izquierda [Primero. } void Partir(int primero. ultimo.Lista) } { partir } void rapido(enteros a[]. while (lista[J]> central) J--.. do{ while (lista[I]< central) I++. lista[J]). } // if } while (I<=J). Repetir Mientras A[I] < Central hacer I=I + 1 Mientras A[J] > Central hacer J=J+1 Si I <= J entonces Intercambiar A[I]. if (I <= J){ Intercambiar(lista[I].int N){ Partir (l.enteros Lista[] ){ int I.Ultimo] Código para Quick Sort typedef int enteros.int &n ){ int Aux .int ultimo. Seleccionar el elemento pivote (termino Central) Central = [(Primero + Ultimo) div 2] 4. n = aux.J] 6. J--. Si J > Primero. llamará al procedimiento Partir. {partir} I =primero. aux =m. n.. /*intercambiar*/ m = n.A[J] hasta que J<I Hasta _que I>J 5. Si I < Ultimo. Central. para dividir la sublista derecha [J.Lista). llamar al procedimiento Partir. J.Algoritmo de Ordenación Rápida: 1. if (I<ultimo) Partir(I.a) } Pag 59 . //encontrar elemento pivote central Central = lista [(primero + ultimo)div 2]. if (primero < J) Partir (primero. Inicializar J a Ultimo (último indice en la lista) 3. enteros Lista[] void Intercambiar(int &m. Inicializar I a Primero (primer indice de la lista) 2. I++.J. J =ultimo.

3) con soportes de madera (varillas de alambre o similar) y un juego de discos de diferentes tamaños (el número de ellos se leerá en el programa principal) que se situan en el primer poste. Mover el disco más grande desde el poste 1 hasta el poste 3. cuatro discos son imaginables. 64 (las leyendas citan esta cifra como la propuesta de un rey tibetano a sus subditos. 2. sin solución recursiva. 3. Tres. El juego consiste en mover los discos del poste 1 al poste 3 de acuerdo a las siguientes reglas: 1. Un disco nunca puede estar encima de otro disco con un diámetro más pequeño.2. Se dispone de tres postes (1. pero su solución es francamente difícil y sólo la solución recursiva facilita la resolución. Situación inicial Pag 60 . Algoritmo (n discos)  Mover n-1 discos desde 1 hasta el 2 utilizando el poste 3. Sólo un disco se puede mover a la vez. Mover los dos discos desde el poste 2 hasta el poste 3 utilizando el poste 1. Algoritmo (3 discos)    Mover dos (3-1) discos desde el poste 1 hasta el poste 2. Un disco siempre debe estar en uno de los postes (Excepto cuando se este moviendo.  Mover el disco restante desde 1 hasta el 3. el disco de mayor tamaño (diámetro) se sitúa en el fondo y el más pequeño en la parte superior.Torres de Hanoi Un caso típico de resolución de un problema con un método recursivo es el juego de niños conocidos como torres de hanoi.  Mover la torre de n-1 discos desde el poste 3 utilizando el poste 1. Análisis El problema a primera vista parece sencillo. al estilo del también famoso problema del tiempo necesario para llenar un tablero de ajedrez en progresión geométrica) es prácticamente inimaginable y casi imposible.

scanf(“%d”. Tres).Poste Uno. Tres). else{ MoverTorre (N – 1. void Mover Disco (Poste Desde. MoverTorre(NumDiscos. printf(“Para %d discos”. Mover Torre ( N – 1. Tres. printf (“Los movimientos sucesivos son :\n”). #include typedef int Poste. desde).Poste Hasta ){ printf (“mover un disco desde el poste %d“. typedef int Pos . } } main(){ printf (“Introduzca número de discos en juego\n”). Tres). NumDiscos).&NumDiscos). 1. Dos. } El número de movimientos H(n) para una torre de n discos se calcula así: Pag 61 . Uno. } void MoverTorre(Pos N. printf(“hasta el poste %d\n”. hasta). Uno.Poste 1 Poste 2 Poste 3 Poste 1 Poste 2 Poste 3 Este modelo de solución es recursivo. MoverDisco (Uno.Poste Dos. 3). 2. Pos NumDiscos.Poste Tres){ if (N==1) MoverDisco (Uno. Dos).

La estructura de datos para representarlas se muestra a continuación: #define TAM 20 #define TRUE 1 #define FALSO 0 typedef int tipo_elem.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. primero en salir>>. se le conoce como Colas Circulares. 2. es decir. Se usarán las siguientes operaciones con colas. 4. como RECUPERA (PRIMERO( C). FRENTE( C) es una función que devuelve el valor del primer elemento de la cola C. 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). C). C) es INSERTA(x.logico. QUITA_DE_COLA( C) suprime el primer elemento de C. typedef struct{ Tipo_elem elemento [TAM]. Las colas se conocen también como listas <<FIFO>> (first-in. PONE_EN_COLA(x. }Cola. En función de operaciones con listas. FIN( C). VACIA( C) devuelve verdadero si. y sólo si. 5. las diferencias sustanciales consisten en que las inserciones se hacen al final de la lista. y no al principio. frente. C). C). y en que la terminología tradicional para colas y listas no es la misma. 3. FRENTE( C) se puede escribir en función de operaciones con listas. C es una cola vacía. Implementación de Colas basadas en Arreglos La representación de colas por medio de arreglos. C) inserta el elemento x al final de la cola C. Las operaciones para una cola son análogas a las de las pilas. Pag 62 . QUITA_DE_COLA( C) es SUPRIME(PRIMERO( C). Int final. PONE_EN_COLA(x. first out) o listas <<primero en entrar. Operaciones 1. ANULA( C) convierte la cola C en una lista vacía.

} void PONE_EN_COLA(tipo_elem x.frente==0)) return (TRUE). } tipo_elemento FRENTE (COLA &C){ if (VACIA ( C)) return -1. } } Pag 63 .final+1==C. c. else return (C. else C.final =0.final++.final ==TAM-1 && C.final==TAM-1) C.frente) || (C.Final =TAM-1. esto sucede si anterior está en la posición que sigue a la posición del posterior. } logico VACIA(COLA &C){ if ((C.elementos[C. void ANULA(COLA &C){ C. C.Frente =1.elem[C.Final Frente En la figura se representa una cola vacía.Frente]).final]=x. else return (FALSE).COLA &C){ if (LLENA( C))) printf(“la cola está llena”) else{ if (C.

Las estructuras autorreferenciadas pueden ser enlazadas juntas para formar útiles estructuras de datos como son las listas. con enteros que indican posiciones en arreglos. las pilas y los árboles. la definición struct nodo { int dato. Supongamos que ya hemos escrito una función para la nómina que procesa estos registros e imprime recibos. struct nodo *proxPtr. El miembro proxPtr se conoce como un enlace o vínculo es decir proxPtr puede ser utilizada para vincular una estructura del tipo struct nodo con otra estructura del mismo tipo. Se crea un arreglo de registros para almacenar todas las listas de Pag 64 . Se han utilizado nombres de variables en lugar de direcciones. }. Con los apuntadores se puede hacer referencia a variables por sus direcciones. Una estructura del tipo struct nodo tiene dos miembros el miembro entero dato y el miembro de apuntador proxPtr. se pueden simular los apuntadores mediante cursores. 15 10 Dos estructuras autoreferenciadas enlazadas juntas Listas implementadas con cursores Algunos lenguajes.frente =0. no tienen apuntadores. Estructuras autoreferenciadas Una estructura autoreferenciada contiene un miembro de apuntador que apunta a una estructura del mismo tipo de estructura. Para almacenar un nuevo valor a la memoria se asigna a una variable. como FORTRAN y ALGOL. las colas de esperas. y la computadora envía una dirección a la memoria seguida por el valor a almacenar en esa posición. 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”. Una forma de suministrarle datos a nuestra función es pasarle cada uno de los registros de empleados como argumento. else C-frente++.void QUITA_DE_COLA(COLA &C){ if (VACIA ( C)) printf(“la cola está vacía”) else{ if(C. Por ejemplo. Define un tipo struct nodo.frente==TAM-1) C. para nuestro ejemplo. esto es. supondremos que su tamaño es de 2048 bytes. Si se trabaja con un lenguaje tal. Considerese un programa que procese registros de empleados [es común que los registros de empleados sean muy largos. } } Concepto de apuntador En una computadora cada posición de memoría tiene una dirección y un valor especifico almacenado en esa posición.

cada registro contiene un elemento y un entero que se usa como curso Es decir. se define #define TAM 10 typedef int Tipo_elem. Para la realización de este tipo de listas nos podemos auxiliar de una variable llamada Disponible. que nos da la posición del arreglo del primer disponible o vacía y en el caso de saber donde inicia la lista hacemos uso de una variable entera que llamaremos Primero. Pag 65 . int sig.Prim. por esto hay que pagar el precio de un espacio adicional para los apuntadores. struct ESPACIO{ Tipo_elem Elemento. utiliza apuntadores para enlazar elementos consecutivos. }Lista[TAM]. int dis. también elude los desplazamientos de elementos para hacer inserciones o rellenar vacíos creados por la eliminación de elementos.elementos cuyo tipo es tipo_elemento. Esta implantación permite eludir el empleo de memoria contigua para almacenar una lista y. celdas enlazadas sencillas. representaciones ligadas Teoría de lista ligadas La segunda forma de realización de listas. 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. No obstante. por tanto.

an. en la que las inserciones y supresiones al principio de la lista se manejan de manera especial. void INICIALIZA ( Nodo Encabezado){ Encabezado =new(nodo). para i=1.. En este caso hablamos de una lista simplemente ligada con nodo de encabezamiento vacío.... a3.Listas con encabezado Lista simplemente ligada En esta representación. nodo *sig.. //Se le pone nil al campo siguiente.n – 1. A continuación se muestra la figura de una lista simplemente ligada lineal con nodo de encabezamiento vacío y longitud n.. } typedef nodo *Nodo. uno para guardar el elemento de la lista y otro para mantener la dirección del siguiente nodo. en la que el empleo de una celda completa para el encabezado simplifica la implementación de las operaciones para manipular la lista. cada celda contiene un elemento de la lista y un apuntador a la siguiente celda. una lista está formada por celdas. aunque también se puede utilizar el encabezado para almacenar el primer elemento y a este tipo de representación se le conoce como lista simplemente ligada con nodo de encabezamiento no vacío. a2. Existe también una celda de encabezamiento que apunta a la celda que contiene a1..2. el apuntador al siguiente nodo es NULL ya que no se tienen más celdas. la celda que contiene ai tiene un apuntador a la celda que contiene a ai+1. ya que la lista está vacía } {La siguiente figura muestra una lista vacía} encabezado Pag 66 . A1 encabezado A2 Lista • • • An Las operaciones más comunes a las listas se muestran a continuación: typedef tipo_elemento: struct nodo{ tipo_elemento elemento. La estructura de datos que emplearemos para representar una lista con apuntadores será un registro con dos campos.. La celda que contiene an posee un apuntador a NULL. //Se crea el nodo de encabezado Encabezado->sig =NULL. Si la lista a1. En el caso de una lista con encabezado vacio. esta celda de encabezamiento no tiene ningún elemento.

//Se reserva memoria para el nuevo nodo aux-> elemento=x. //Se libera el espacio de memoria ocupada por el nodo } A1 x A2 • • • An encabezado p aux Nodo LOCALIZA(tipo_elemento x. Aux=p->sig. } tipo_elemento RECUPERA (Nodo p){ //regresa el elemento del nodo siguiente al apuntado por p if (!VACIA(p)) return p->sig->elemento. if (p->sig=NULL) return NULL. else Pag 67 . else return p.Nodo p){ //Si encuentra el dato en la lista devuelve el apuntador al nodo anterior a donde lo encontró. //Se almacena el elemento aux->sig =p->sig. //Se enlaza con el siguiente nodo de p p->sig=aux. //en caso contrario regresa nil while ((p->sig!=NULL)&&(p->sig->elemento!=x){ p=p->sig. //p se enlaza con el nuevo nodo } A1 A2 • • • An encabezado p x aux void SUPRIME(Nodo p){ //Suprime el nodo siguiente al apuntado por p Nodo aux. Nodo p ){ //Coloca el elemento x delante de la celda que apuntada por p Nodo aux. //Se almacena la dirección del nodo que se desea eliminar p->sig=aux->sig //Se enlaza con el nodo siguiente al que se quiere suprimir delete(aux). aux= new(nodo).void INSERTA (tipo_elemento x.

} typedef nodo *Nodo. En este caso la lista se llama lista circular. no se puede tener acceso a cualquier otro nodo anterior a p. p=p->sig. y al igual que estas también el nodo de encabezado puede contener información. La estructura de datos que se emplea para representar este tipo de lista es la misma que para una lista ligada lineal. entonces desde cualquier otro punto de la lista es posible llegar a cualquier otro punto. el apuntador al encabezado no debe ser modificado a fin de poder hacer referencia a esta lista. //Se crea el nodo de encabezado //Se hace que el campo siguiente del Pag 68 . que es muy similar a la anterior.RECUPERA(p)). void INICIALIZA(Nodo Encabezado){ encabezado = new(nodo). no hay elemento”).printf(“Error. nodo *sig. typedef int tipo_elemento struct nodo{ Tipo_elemento Elemento. Lista Vacía”). else while (!VACIA(p)) { printf(“%d”. } } Lista simplemente ligada circular La implementación de una lista también puede hacerse mediante una lista simplemente ligada circular. Si hacemos un pequeño 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 dirección al inicio de la lista. } void IMPRIME (Nodo p){ if (VACIA(p)) printf(“Error. } int VACIA (Nodo p){ VACIA=p->sig==NULL. con la diferencia de que el último nodo contiene en su campo siguiente un apuntador al encabezado en lugar de NULL. Si se recorre una lista. A1 A2 • • • An encabezado Lista Una desventaja de las listas lineales es que dado un apuntador p a un nodo de la lista.

//Se libera el espacio de memoria ocupado por el nod } Nodo LOCALIZA(tipo_elemento x. } Pag 69 . //Se enlaza con el nodo siguiente al que se quiere suprimir delete(aux). while (p->sig!=q) &&(p->sig->elemento!=x) p =p->. /*Se almacena el elemento*/ aux->sig =p^->sig.sig. } tipo_elem RECUPERA(Nodo p){ //Regresa el elemento del nodo siguiente al apuntado por p if (p->sig!=p) return p->sig->elemento. /*Se enlaza con el siguiente nodo de p*/ p->sig = aux. aux =new(nodo). en este caso hay que almacenar la dirección del nodo de encabezado para saber cuando recorrimos toda la lista*/ Nodo q. Aux =p->sig. } {encabezado se apunte asi mismo} encabezado void INSERTA (tipo_elemento x. /*Se reserva memoria para el nuevo nodo*/ aux->elemento =x.encabezado->sig = encabezado.Nodo p) /*Si se encuentra el dato en la lista devuelve el apuntador al nodo anterior a donde lo encontró. /*p se enlaza con el nuevo nodo*/ } void SUPRIME(Nodo p){ //Suprime el nodo siguiente al apuntado por p Nodo aux. if (p->sig==q) return NULL. else return -1. //Se almacena la dirección del nodo que se desea eliminar p->sig =aux->sig. q=p. else return p. Nodo p){ /*Coloca el elemento x delante de la celda apuntada por p es exactamente igual que una lista lineal */ Nodo aux. en caso contrario regresa nil.

RECUPERA(p)). } } } Ventajas de la representación con apuntadores Operaciones como INSERTAR Y SUPRIMIR tienen un número constante de pasos para este tipo de listas. que contiene la dirección del nodo que contiene al primer elemento de la lista o bien este nodo de encabezamiento puede contener al primer dato. independientemente de el número de elementos que en realidad tiene la lista en un momento dado. estás cuentan con un nodo de encabezamiento. ni tampoco se puede eliminar un nodo de una lista simplemente ligada circular simplemente un apuntador a ese nodo. Aun cuando una Lista Circular tiene ventajas sobre una Lista Lineal. ésta todavía tiene algunas deficiencias. o dado un elemento podría determinarse con rapidez el siguiente y el anterior. ••• Pag 70 . p = p->sig. uno a la celda siguiente y otro a la celda anterior. a diferencia de la representación con arreglos que requiere un tiempo proporcional al número de elementos que siguen. Asi que cualquiera de los dos métodos podría usar más espacio que el otro dependiendo de las circunstancias. cada nodo en esta lista contiene dos apuntadores. Este tipo de lista puede ser tanto lineal como circular y puede contener o no un nodo de encabezado. Lista Vacía\n”). De la misma manera que las listas simplemente ligadas.int VACIA(Nodo p){ VACIA=p->sig==p } //Si el que sigue del nodo de encabezado. tanto hacía adelante como hacia atrás. uno es predecesor y el otro a su sucesor. uno no puede recorrer esta lista en dirección contraria. Para representar estas situaciones se emplean las listas Doblemente ligadas. Como se mencionó anteriormente. sin importar cuántos elementos contenga. pero requiere espacio para almacenar el apuntador a la siguiente celda. En la implementación con arreglos se desperdicia espacio. Lista doblemente ligada lineal En algunas aplicaciones puede ser deseable poder recorrer eficientemente una lista. mientras que la representación con apuntadores utiliza sólo el espacio necesario para los elementos que contenga la lista en cada momento. else{ q = p. en las que cada nodo o celda de la lista además de contener el elemento. while (p!=q->sig){ printf(“%d”. if (VACIA(p )) printf(“Error. es él mismo entonces no hay datos void IMPRIME(Nodo p){ Nodo q. contiene dos apuntadores. 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.

y dos para almacenar las direcciones de los nodos siguiente y anterior a uno dado. //Se almacena el elemento aux->sig =p->sig. y sucede lo mismo para suprimir. }. //Suprime el nodo siguiente al apuntado por p Pag 71 .La estructura de datos para representar este tipo de Lista sería un nodo com un registro con tres campos. Para insertar un dato hay que saber si se quiere insertar a la derecha o a la izquierda. aux =p->sig->sig. Encabezado->ant =NULL. Una lista Vacía se representaría de la siguiente manera: Encabezado nil void INICIALIZA (Nodo Encabezado){ Encabezado =new(nodo). además de que la inserción al principio y al final son diferentes. } {La siguiente figura muestra una lista vacía} void INSERTA (tipo_elemento x. nodo *sig. {REVISARp se enlaza con el nuevo nodo} p->sig =aux. Nodo p ){ //Coloca el elemento x delante de la celda que apuntada por p Nodo aux. uno para almacenar al elemento de la lista. } void SUPRIME(Nodo p){ Nodo aux. if (p-<sig!=NULL) p->sig->ant =aux. //Se reserva memoria para el nuevo nodo aux-> elemento =x. aux = new(nodo). //Se crea el nodo de encabezado Encabezado->sig =NULL. typedef nodo *Nodo. *ant . typedef int tipo_elem struct nodo{ Tipo_elem elemento. //Se enlaza con el siguiente nodo de p aux->ant=p.

else return aux. aux=p. else return NULL.Nodo p){ /*Si encuentra el dato en la lista devuelve el apuntador al nodo anterior a donde lo encontró. if (VACIA(p)) printf (“Error. } int VACIA (Nodo p){ VACIA=p->sig==NULL. } Nodo ANTERIOR(Nodo p){ if (p->ant->ant!=NULL) return p->ant.RECUPERA(p)). p->sig=aux. if (aux->sig==NULL) return NULL. } tipo_elemento RECUPERA(Nodo p){ //regresa el elemento del nodo siguiente al apuntado por p if (! VACIA(p)) return p->sig->elemento. Aux =p. else return -1. p=p->sig. while (aux->sig!=NULL)&& (aux->sig->elemento!=x) { aux =aux->sig. Lista Vacía\n”). } } Pag 72 . else while (!VACIA(p)) { printf(“%d”. if (p->sig!=NULL) aux->ant=p. en caso contrario regresa nil*/ Nodo aux.delete(p->sig). } void IMPRIME (Nodo p){ Nodo Aux. } Nodo LOCALIZA(tipo_elemento x.

} typedef nodo *Nodo. Encabezado void INICIALIZA(Nodo Encabezado){ Encabezado = new(nodo). *ant. pero en esta. ••• typedef int tipo_elem struct nodo{ Tipo_elem elemento. //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. este tipo de lista es similar a una lista Doblemente Ligada Lineal. } //Se crea el nodo de encabezado //Se hace que el campo siguiente del //encabezado se apunte asi mismo void INSERTA (tipo_elemento x. } void SUPRIME(Nodo p){ Pag 73 . p->sig =aux. Nodo p){ /*Coloca el elemento x delante de la celda apuntada por p es exactamente igual que una lista lineal */ Nodo aux. aux->sig=p->sig.Lista doblemente ligada circular Como se puede apreciar. Nodo *sig. aux =new(nodo). Encabezado->ant =Encabezado. el primer nodo (el encabezado) en su campo anterior contiene un apuntador al último nodo de la lista. Encabezado->sig =Encabezado. //Se almacena el elemento aux->ant =p. mientras que el último nodo en su campo siguiente contiene la dirección del primer nodo es decir del encabezado. //Se reserva memoria para el nuevo nodo aux->elemento=x.

Nodo p){ /*Si se encuentra el dato en la lista devuelve el apuntador al nodo anterior a donde lo encontró. } Nodo LOCALIZA(tipo_elemento x.//Suprime el nodo siguiente al apuntado por p Nodo aux. en caso contrario regresa nil. p=p->sig. else return -1. Pag 74 . es él mismo // entonces no hay datos void IMPRIME(Nodo p){ Nodo q. } //Si el que sigue del nodo de encabezado. aux->ant=p. en este caso hay que almacenar la dirección del nodo de encabezado para saber cuando recorrimos toda la lista*/ Nodo aux. } } Listas sin encabezado Lista simplemente ligada sin encabezado typedef int tipo_elemento. } tipo_elem RECUPERA(Nodo p){ //Regresa el elemento del nodo siguiente al apuntado por p if (p->sig!=p) return =p->sig->elemento. delete (p->sig). while ((p->sig!=aux) && (p->sig->elemento!=x)) { p=p->sig. p->sig = aux. } int VACIA(Nodo p){ return p->sig==p. else{ q=p. aux=p. else return p. struct nodo{ tipo_elemento elemento. while(p!=q->sig) printf(“%d”. Lista Vacía”). aux=p->sig->sig.RECUPERA(p)). if (VACIA(p )) printf (“Error. if (p->sig==aux) then return NULL. nodo *sig.

//Se almacena el elemento nuevo->sig =L. } } {Suprime el nodo siguiente al apuntado por p} Nodo LOCALIZA(tipo_elemento x. p->sig =aux. en caso contrario regresa nil*/ if (L->info==x) then LOCALIZA:=nil else { while (L->sig!NULL && L->sig->elemento!=x){ L =L->sig.Nodo L){ //Coloca el elemento x delante de la celda que apuntada por p Nodo aux.Nodo L){ /*Si encuentra el dato en la lista devuelve el apuntador al nodo anterior a donde lo encontró. nuevo->tipo_elemento:=x. void INICIALIZA (Nodo L){ L =NULL. } void INSERTA (tipo_elemento x. L->sig =Nuevo. //Se enlaza con el siguiente nodo de p L=nuevo.} //p se enlaza con el nuevo nodo else{ aux =L->sig. nuevo->sig = aux.nuevo. else return L.Nodo &L){ Nodo aux. if (p==NULL){ nuevo = new(nodo). nuevo = new(nodo).} typedef nodo *Nodo. if (L->sig==NULL) return NULL. delete (p->sig). nuevo->elemento=x. } else { aux=p->sig->sig. } tipo_elemento RECUPERA (Nodo p){ Pag 75 . if (p==NULL) { aux:=L->sig. } } void SUPRIME(Nodo p.Nodo p. delete(L). L=aux.

} void IMPRIME (Nodo p){ while (L!=NULL){ printf(“%d”. } } void ANULA (Nodo L){ Nodo aux. else return 0. Pag 76 . struct Nodo{ Tipo_elem elemento. } } Implementacion de Pilas basadas en apuntadores La representación de pilas basadas en apuntadores es similar a la de una lista simplemente ligada lineal. posicion p){ posicion aux. typedef Nodo *posicion. P ->sig=NULL } logico PILA_VACIA(posicion P){ if (P=nil) return 1. typedef char tipo_elem. void INICIALIZA (posición P){ P = new Nodo. else printf(“Error. }. y las inserciones y supresiones siempre se hacen en el tope (encabezado). en donde el encabezado es el topo de la pila. typedef int logico. delete (L). } void METER(tipo elem x. } int VACIA (Nodo p){ return L==NULL. L=L->sig.L->elemento).//regresa el elemento del nodo siguiente al apuntado por p if (!VACIA(p)) return p->sig->elemento. while (L!= NULL){ aux=L->sig. no hay elemento”). Nodo *sig. L=aux.

Aux =new(Nodo).Frente=NULL.Cola C). else return 0. nodo *sig. C. }. if (!PILA_VACIA(P)) { Aux=P->sig. typedef int logico. aux->elemento=x. Nodo Aux. } else printf(“Pila vacía\n”). } elemento TOPE(posicion P){ if (PILA_VACIA (P)) return -1. Aux->elemento =x. Pag 77 . P ->sig = aux. aux->sig:=P->sig. typedef nodo *Nodo. P->sig =Aux->sig.Frente=NULL) return 1.Fondo=NULL.Frente.aux = new(Nodo). } int VACIA(Cola C){ if (C. delete(Aux). struct nodo{ Tipo_elem elemento. struct Cola{ Nodo Fondo. } void METER (elemento x. else return P->sig->elemento. } void SACAR(posición P){ posicion Aux:. } void INICIALIZA (Cola C){ C. } Implementacion de Colas basadas en apuntadores typedef char tipo_elem.

} } void SACAR (Cola C){ Nodo Aux:. A cada elemento de un árbol binario se le denomina Nodo del árbol.Fondo!=NULL) { C. } elemento ULTIMO (Cola C){ if (!VACIA( C)) return C. if (C.Frente->elemento.Frente. los árboles se emplean para analizar circuitos eléctricos y para representar la estructura de fórmulas matemáticas. A continuación se representan las definiciones básicas y algunas de las operaciones más comunes con árboles y veremos como esta estructura de datos puede ser representada en Pascal. C. Árboles binarios Árbol Binario Un árbol binario es un conjunto finito de elementos que puede estar vacío o contener un elemento denominado la raiz del árbol. } elemento PRIMERO (Cola C){ if (!VACIA( C)) return C. } Estructura de datos no lineales.Fondo->elemento.Aux->sig =NULL. así como para organizar la información de bases de datos y para representar la estructura sintáctica de un programa fuente en compiladores. else return -1. esta raíz contiene cuando mucho un subárbol izquierdo y ubárbol derecho. representaciones secuencial y ligada Teoría general de Árboles Un árbol impone una estructura jerárquica sobre una colección de objetos.Frente =Aux->.Fondo->sig =Aux. Entre otras aplicaciones. Los árboles genealógicos y los organigramas son ejemplos comunes de árboles.sig.Frente =Aux.Fondo =Aux. } else{ C. los cuales a su vez también son árboles binarios. Pag 78 .Fondo =Aux. C. } else printf(“lista vacía”). delete(aux). if (! VACIA( C)) { Aux:=C. else return -1. C.

Un nodo es..Ak son los subárboles de la raíz.. Arboles binarios de búsqueda Los árboles binarios pueden ser implementados como un arreglo o mediante el uso de apuntadores. un árbol. A2....n2.nk reciben el nombre de hijos del nodo n. por si mismo.. Ese nodo es también la raíz de dicho árbol. contiene un apuntador nulo o nil... un árbol se puede definir de manera recursiva como sigue: 1.n2. y esta definición es recursiva. Supongamos que n es un nodo y que A1.A2...n2.Ak son árboles con raíces n1. En dicho árbol. Como caso particular representaremos la implementación con apuntadores de un Árbol Binario de búsqueda..Formalmente. Los nodos n1. Si el subárbol izquierdo o derecho está vacío.. y dos para almacenar la dirección del subárbol izquierdo y derecho.. raíz Subárbol izquierdo Subárbol derecho Una forma de visualizar un árbol binario es considerar cada nodo conformado por tres campos fundamentales. Se puede construir un nuevo árbol haciendo que n se convierta en el padre de los nodos n1. Este tipo especial de árbol tiene como característica la siguiente: Para cada nodo. n es la raís y A1. nk.nk... todos los elementos que están a su izquierda son menores que él y todos los elementos que están a su derecha son mayores. 2.. Pag 79 . respectivamente.. uno para almacenar la información que contiene el nodo.

P->info). void INICIALIZA( Nodo R){ R = NULL. R->info =x. return P-> Info. Nodo *Izq. } Info MINIMO (Nodo P){ while (P->Izq !=NULL) P=P->Izq. P=Raiz. struct nodo{ Tipo_elemento Info. R->Der =NULL.R->Izq). } else if (x>R->info) {Si el dato a insertar es mayor} INSERTA(x. if ((P==NULL) || (x=P->Info)) return P. return P-> Info.Nodo R){ If (R==NULL){ R= new(nodo). printf(“%d”. } Pag 80 .P->Der).Nodo R){ Nodo P. } Info MÁXIMO (Nodo P){ while (P->Der !=NULL) P =P->Der. } Nodo BUSCAR (Info x. *Der } typedef nodo *Nodo. R->Izq =NULL.P->Izq). printf(P->Info). else if (x<P->Info) return BUSCAR(x. else return BUSCAR(x. } void INSERTA(tipo_elemento x. R->Der) {Se revisa el lado derecho} else if (x<R->Info) INSERTA(x.Representación ligada typedef int tipo_elemento.

R->Info). } } void ENTREORDEN (Nodo R){ if (R!=NULL) { ENTREORDEN(R->Izq). Recorrer el subárbol izquierdo en Postorden 2. 2. } typedef nodo *Nodo. }Espacio_celdas[TAM_NODOS]. Visitar la raíz 2..R->info).Representaciones secuenciales Una forma conveniente de estructurar datos para representar un árbol binario consiste en dar a sus nodos los nombres 1. ENTREORDEN(R->Der). La idea es que espacio_celdas[i]. de tal manera que el recorrer un árbol binario comprende la visita a la raíz y el recorrido de los subárboles izquierdo y derecho. PREORDEN(R->izq). todos ellos serán definidos de manera recursiva. struct nodo{ Tipo_elemento Info. //Visitamos la raíz //Recorremos el subárbol izquierdo //Recorremos el subárbol derecho //Recorremos el subárbol izquierdo //Visitamos la raíz //Recorremos el subárbol derecho Pag 81 . Hijo_izq sea el hijo izquierdo del nodo i. y que suceda lo mismo con hijo_der. Recorrer el subárbol derecho en Entreorden Recorrido en Postorden u Orden Posterior 1.. Un valor 0 en cualquiera de esos campos indicará la ausencia de un hijo Recorridos En-Orden. Nodo *Izq. PREORDEN(R->der). Recorrer el subárbol derecho en Postorden 3. int Hijo_der. Recorrer el subárbol izquierdo en Preorden 3. La única diferencia entre estos métodos es el orden en que se realizan estas operaciones.n y utilizar un arreglo de registros declarado como: Struct Nodo{ int Hijo_izq. void PREORDEN(Nodo R) if (R!=NULL){ printf(“%d”.. printf(“%d”. Visitar la raíz 3. Pre-Orden y Post-Orden Existen tres métodos de recorrer todos los nodos de un árbol. Recorrer el subárbol derecho en Preorden Recorrido en Entreorden u Orden Simétrico 1. Visitar la raíz typedef int tipo_elemento. . *Der. Recorrido en Preorden u Orden Previo 1. Recorrer el subárbol izquierdo en Entreorden 2.

En la figura la secuencia 1. en este caso n-1.. Un grafo dirigo puede emplearse para representar el flujo de control en un programa de computador. POSTORDEN(R->Der). y es la cola y w la cabeza del arco. pasa por los vértices v2.vn-1 vn. v2. v3.. Los vértices se denominan también nodos o puntos. los vértices pueden representar ciudades y los arcos. pirntf(“%d”. ingeniería y muchas otras disciplinas.. y que w es adyacente a v. 4. El arco (v. 2. a menudo es necesario representar relaciones arbitrarias entre objetos de datos. y los arcos posibles tranasferencias del flujo de control. vuelos aéreos de de una ciudad a otra.. Este camino va del vértices v1 al vértice vn. 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 vértices V y un conjunto de arcos A.vn. por si mismo denota un camino de longitud cero de v a v. Por ejemplo.. Los vértces representan bloques básicos.. Los vértices de un grafo dirido pueden usarse para representar objetos. v2 v3.. Un arco es un par ordenado de vértces (v. w).} } void POSTORDEN(Nodo R){ if (R =! NULL){ POSTORDEN(R->Izq). v. matemáticas.. un vértice sencillo.vn-1 y termina en el vértice vn. Un camino en un grafo dirigo es una secuencia de vértices v1. . y los arcos relacionados ente los objetos. es un camino de longitud 2 que va del vértice 1 al vértice 4. 1 2 3 Grafo dirigido 4 Pag 82 . los arcos pueden llamarse arcos dirigidos o lineas dirigidas.son arcos. La longitud de un camino es el número de arcos en ese camino. tal que v1 v2. w) se expresa a menudo como v w va de v a w.. Como caso especial.P->info). } } Grafos En los problemas originados en la computación..

de elementos boléanos. cada vértice o ambos pueden tener una etiqueta asociada. que empieza y termina en el mismo vértice.j] un valor que no pueda ser una etiqueta válida. Una representación común para un grafo dirido es G= (V. Para este propósito es posible usar un grafo dirigido etiquetado. el tiempo de acceso requerido a un elemento es independiente del tamaño de V y A. Una etiqueta puede ser un nombre.. donde a[i. excepto tal vez el primero y el último. En la representación con una matriz de adyacencia. en los cuales suele ser necesario saber si un arco dado está presente. debe emplearse como entrada para A[i. existe un arco que vaya del vértice i al j. las matrices de adyacencia pueden incluso obtenerse de esa forma. La siguiente figura muestra un grafo dirigido etiquetado en el que cada arco esta etiquetado con una letra que causa una transición de un vértice a otro. Un grafo dirido etiquetado. A menudo se empleará la etiqueta del vértice como si fuera el nombre. Así. Este grafo dirigido etiquetado tiene la interesante propiedad de que las etiquetas de los arcos de cualquier ciclo que sale del vértice 1 y vuelve e él producen una cadena de caminos a y b en el cual los números de a y de b son pares.2. a 1 a b b a 3 a 4 b b 2 Representación de grafos dirigidos Para representar un grafo dirigido se pueden emplear varias estructuras de datos.Un camino es simple si todos los vértices. Pag 83 . 2. un costo o un valor de cualquier tipo de datos dado.. 4.. j] es verdadero si. Si no existe un arco de i a j. la selección apropiada depende de las operaciones que se aplicarán a los vértices y a los arcos del grafo. Con frecuencia se exhibirán matrices de adyacencias con 1 para verdadero y 0 para falso. Algo muy relacionado con esto es la representación con matriz de adyacencia etiquetada de un grafo dirido.. En muchas aplicaciones es útil asociar información a los vértices y arcos de un grafo dirigido. los números de la figura pueden interpretarse como nombres o como etiquetas de vértices. en el cual cada arco.A) es la matriz de adyacencia. 3 es un ciclo de longitud 3. y sólo si. Así la representación con matriz de adyacencia es útil en algoritmos para grafos. el camino 3.j] es la etiqueta del arco que va del vértice i al vértice j. donde A[i. En la figura. Un ciclo simple es un camino simple de longitud por lo menos uno. Supóngase que V= {1. un vértice puede tener a la vez un nombre y una etiqueta. son distintos.n}..La matriz de adyacencia para G es una matriz una matriz A de dimensión n X n.

1 2 3 4 2 4 2 3 3 Si hubo inserciones y supresiones en las listas de adyacencias. donde CABEZA[i] es un apuntador a la lista de adyacencia del vértice i. ya que puede haber O(n) vértices en la lista de adyacencia para el vértice i. sería preferible que CABEZA[i] fuera un cursor a un arreglo ADY.A) llamada representación con lista de adyacencia. ADY[CABEZA[i]+1]. La figura muestra una representación con lista de adyacencia para el grafo dirigido de la primera figura. Por otra parte. de todos los vértices adyacentes a i. si se espera que el grafo permanezca fijo. el cual marca el fin de la lista de vértices adyacentes a i. lo cual invalidaría los algoritmos O(n) para la manipulación de grafos dirigidos con O(n) arcos.1 1 2 3 4 b a 2 a 3 4 B b b a A Matriz de adyacencia etiquetada para el grafo dirigido de la anterior figura. sin cambios (o con muy pocos) en las listas de adyacencia.. Sin embargo. éstas podrían incluirse en las celdas e la lista ligada. y así sucesivamente. donde se usan listas enlazadas sencillas. contuvieran los vértices adyacentes al vértice i. Pag 84 . La lista de adyacencia para un vértice i es una lista. . donde ADY[CABEZAA[i]]. Sólo leer o examinar la matriz puede llevar un tiempo O(n²).. La representación con lista de adyacencia de un grafo dirigido requiere un espacio proporcional a la suma del número de vértices más el número de arcos. se usa bastante cuando el número de arcos es mucho menor que n² . en algún orden. 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. una desventaja potencial de la representación con lista de adyacencia es que puede llaeva un tiempo O(n) determinar si existe un arco del vértice i al vértice j. Si los arcos tienen etiquetas. Se puede representar G por medio de un arreglo CABEZA. Para evitar esta desventaja se puede utilizar otra representación común para un grafo dirigido G=(V.. hasta el punto en ADY donde se encuentra por primera vez un cero. sería preferible tener el arreglo CABEZA apuntando a celdas de encabezamiento que no contienen vértices adyacentes.

“of”. a. char *suit. a. entonces (v. #include <stdio.w) =(w. main() { struct card a.face.w) es una arista no dirigida.A) consta de un conjunto finito de vértices V y de un conjunto de aristas A.Grafos no dirigidos Un grafo no dirigido G =(V.face = “Ace”.v). struct card *aPtr.suit. aPtr = &a.face. (*aPtr). “ of “.suit). return 0. Si (v. El siguiente programa pone de manifiesto el uso de operadores de miembro de estructura y de apuntador de estructura. “of”. }. a.a. (*aPtr). aPtr->suit. Los objetos se representan por los vértices del grafo. aPtr->face.h> struct card { char *face. Los grafos se emplean en distintas disciplinas para modelar relaciones simétricas entre objetos.suit = “Spades”. y dos objetos están conectados por una arista si están relacionados entre sí. } Ace of Spades Ace of Spades Ace of Spades Pag 85 . Se diferencia de un grafo dirigido en que cada arista en A es un par no ordenado de vértices. printf(“%s%s%s\n%s%s%s\n%s%s%s\n”.