Capítulo I Introducción a las Estructuras De...

94
Programación II 1 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia Capítulo I Introducción a las Estructuras De Datos 1.1. Introducción Para procesar información en un computador es necesario hacer una abstracción de los datos que tomamos del mundo real, abstracción en el sentido de que se ignoran algunas propiedades de los objetos reales, es decir, se simplifican. Se hace una selección de los datos más representativos de la realidad a partir de los cuales pueda trabajar el computador para obtener unos resultados. Cualquier lenguaje suministra una serie de tipos de datos simples, como son los números enteros, caracteres, números reales. En realidad suministra un subconjunto de éstos, pues la memoria del ordenador es finita. Los punteros (si los tiene) son también un tipo de datos. El tamaño de todos los tipos de datos depende de la máquina y del compilador sobre los que se trabaja. 1.2. Abstracción Una abstracción es un proceso mental donde se extraen rasgos esenciales de algo para representarlos por medio de un lenguaje gráfico o escrito. El pato y la Serpiente A orillas de un estanque, diciendo estaba un pato: -¿A qué animal dio el cielo los dones que me ha dado? Soy de agua, tierra y aire; cuando de andar me canso, si se me antoja, vuelo; si se me antoja, nado. Una serpiente astuta, que le estaba escuchando, le llamó con un silbido y le dijo: -No hay que ser tan arrogante señor pato, pues ni anda como el ciervo, ni vuela como el halcón, ni nada como el pez; y así tenga sabido que lo importante y raro no es entender de todo, sino ser diestro en algo. de Tomás de Iriarte

Transcript of Capítulo I Introducción a las Estructuras De...

Programación II 1 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Capítulo I

Introducción a las Estructuras De Datos

1.1. Introducción

Para procesar información en un computador es necesario hacer una abstracción de los datos que tomamos del mundo real, abstracción en el sentido de que se ignoran algunas propiedades de los objetos reales, es decir, se simplifican. Se hace una selección de los datos más representativos de la realidad a partir de los cuales pueda trabajar el computador para obtener unos resultados.

Cualquier lenguaje suministra una serie de tipos de datos simples, como son los números enteros, caracteres, números reales. En realidad suministra un subconjunto de éstos, pues la memoria del ordenador es finita. Los punteros (si los tiene) son también un tipo de datos. El tamaño de todos los tipos de datos depende de la máquina y del compilador sobre los que se trabaja.

1.2. Abstracción Una abstracción es un proceso mental donde se extraen rasgos esenciales de algo para representarlos por medio de un lenguaje gráfico o escrito.

El pato y la Serpiente

A orillas de un estanque, diciendo estaba un pato: -¿A qué animal dio el cielo los dones que me ha dado? Soy de agua, tierra y aire; cuando de andar me canso, si se me antoja, vuelo; si se me antoja, nado. Una serpiente astuta, que le estaba escuchando, le llamó con un silbido y le dijo: -No hay que ser tan arrogante señor pato, pues ni anda como el ciervo, ni vuela como el halcón, ni nada como el pez; y así tenga sabido que lo importante y raro no es entender de todo, sino ser diestro en algo. de Tomás de Iriarte

Programación II 2 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1.3. Abstracción de datos Técnica o metodología que permite diseñar estructuras de datos, permite representar bajo ciertos lineamientos las características esenciales de las estructuras de datos.

1.4. Definición de Estructuras de Datos Una estructura de datos es cualquier colección de datos organizados de tal forma que tengan asociados un conjunto de operaciones para poder manipularlos. Ejemplo: Estructura de Datos: Sus operaciones: Otros ejemplos de estructuras de datos: OPERACIONES

Lista de elementos Archivos (conjunto de registros) Vector (conjunto de elementos) Registros

1.5. T.D.A. (Tipo de Dato Abstracto) Al diseñar una estructura de datos con la técnica de abstracción pasa a ser un TDA, que:

Puede implementarse en cualquier lenguaje Puede aplicarse en cualquier concepto

1.6. Especificación lógica de un T.D.A. (Tipo de Dato Abstracto)

1. Elementos que conforman la estructura 2. Tipo de organización en que se guardarán los elementos :

Lineal Jerárquica Red sin relación

Arreglo

Comparación Asignación Escritura Lectura

Ordenar Buscar Eliminar Modificar Insertar desplegar

Programación II 3 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

3. Dominio de la estructura 4. Descripción de las operaciones de la estructura:

Nombre de la operación Descripción breve, Datos de entrada Datos de salida, Precondición Poscondición

Ejemplo: TDA: Matriz, int x Ejemplo: TDA: VECTOR: <x1, x2, x3,…, xn>

int vector[10] ; Declaración de un vector

Vector[0] Vector[9] A continuación vemos en la siguiente figura el TDA vector con sus diferentes operaciones

int

suma Multiplicación

resta

modulo

división

VECTOR

quitar

insertar cambiar

asignar

sumar

buscar

Programación II 4 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1.7. Niveles de datos

1. Nivel lógico o abstracto: Se define abstractamente la estructura de datos y las operaciones relacionadas con ella. Independientemente del lenguaje en el que se implementará.

2. Nivel físico o de implementación: En que lenguaje, qué tipos de datos ya definidos servirán y se implementa como un módulo cada una de las operaciones del TDA.

3. Nivel de aplicación o de uso: Llamar a las operaciones de la estructura, siguiendo las reglas especificadas.

1.8. Encapsulamiento de datos El usuario no tiene que usar información solamente trata con los datos de su descripción lógica.

1.9. Estructuras Fundamentales Los datos a procesar por una computadora se clasifican en:

Simples Estructurados

Los datos simples ocupan sólo una casilla de memoria, por tanto una variable simple hace referencia a un único valor a la vez. Los datos Estructurados se caracterizan por el hecho de que con un nombre (identificador de variable estructurada) se hace referencia a un grupo de casillas de memoria. Tiene varios componentes.

Cabe hacer notar que en el presente texto, se tomará como herramienta para los programas y representar las diferentes estructuras de datos el Lenguaje C++.

Programación II 5 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Ejemplos: Dato Simple Declaramos una variable A de tipo entero y asignamos el valor 25.

A Identificador int A;

A = 25;

Dato Estructurado

Declaramos un dato compuesto o estructurado A que tendrá 5 elementos de tipo entero.

A Identificador Int A[5] ; A = {20,30,40,50,60};

1.10. Arreglos Con frecuencia se presentan problemas cuya solución no resulta fácil de implementar (a veces es imposible) si se utilizan datos simples. A continuación se presentará un problema y dos posibles soluciones del mismo utilizando tipos simples de datos. El objetivo de este ejemplo es ilustrar lo complejo que resulta un algoritmo de solución para ciertos problemas, sin usar tipos estructurados de datos. Problema. Se tienen calificaciones de un grupo de 50 alumnos. Se necesita saber cuántos alumnos tienen una calificación superior al promedio del grupo. ¿Cómo resolver este problema? Primera Solución: DOBLE LECTURA Vea el programa N°1 Segunda Solución: MUCHAS VARIABLES Esta solución, resuelve el problema planteado utilizando múltiples variables. Vea el programa N°2

25

20 30 40 50 60

Programación II 6 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Se observa que ninguna de las dos soluciones resulta práctica ni eficiente. Es necesario un nuevo tipo de datos que permita tratar estos problemas de una manera más adecuada. Los tipos de datos estructurados que ayudan a resolver problemas como éste son los arreglos. Tercera Solución: UTILIZAR UN ARREGLOu Esta solución, resuelve el problema planteado utilizando un arreglo. Vea el programa N°3

//PROGRAMA N°1 // Este programa resuelve el problema planteado por medio de una doble lectura #include <iostream.h> #include <conio.h> void main (void) { int I, CONT; float AC, PROM, C; clrscr(); AC = 0; I = 1; while ( I < = 50) { cout << ” Introduzca la calificación del alumno ” << I; cin >> C; AC = AC + C; I ++; } PROM = AC / 50; //Como se necesita decir cuántos alumnos obtuvieron una calificación superior al promedio, se deberá //volver a leer las 50 calificaciones para poder comparar cada una de ellas con el promedio CONT = 0; I = 1; while ( I < = 50 ) {

cout << ” Introduzca la calificación del alumno ” << I; cin >> C; if ( C > PROM ) CONT = CONT + 1; I + +; } cout<< “el numero de alumnos cuya calificación es mayor al promedio son “ << CONT; getch (); } }

Programación II 7 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1.10.1. Definición De Arreglo Finita: Todo arreglo tiene un límite; es decir, debe determinarse cuál será el número máximo de elementos que podrán formar parte del arreglo. Homogénea: Todos los elementos del arreglo son del mismo tipo (todos enteros, todos boléanos, etc., pero nunca una combinación de distintos tipos) Gráficamente podemos representar un arreglo como sigue: Ejemplo: Sea un arreglo C que almacena las 50 calificaciones

//PROGRAMA N°2 SEGUNDA SOLUCION #include <iostream.h> #include <conio.h> void main (void) { int CONT, C1,C2, C3, …, C50; float PROM, AC, C; clrscr(); cout << “ introduzca la calificación del alumno 1 “; cin >> C1; cout << “ introduzca la calificación del alumno 2 “; cin >> C2; . . cout << “ introduzca la calificación del alumno 50 “; cin >> C50; // las calificaciones correspondientes a los 50 alumnos AC = C1 + C2 + … + C50; PROM = AC / 50; CONT = 0; If ( C1 > PROM ) CONT = CONT + 1; If ( C2 > PROM ) CONT = CONT + 1; . . If ( C50 > PROM ) CONT = CONT + 1;

cout<< “el numero de alumnos cuya calificación es mayor al promedio son “ << CONT; getch();

} }

Un arreglo se define como una colección finita, homogénea y ordenada de elementos.

Programación II 8 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

componente

30 50 70 60 0 1 2 … 49 Identificador Indice

1.10.2. Declaración de vectores Para declarar un vector en el lenguaje C, tenemos la siguiente sintaxis: Ejemplo: Un vector a de 10 enteros de tipo int se declara:

int a[10]; El vector a comprende los elementos a[0], a[1], a[2], . . . , a[9], todos de tipo int . En una misma línea se puede declarar más de un vector, siempre que todos compartan el mismo tipo de datos para sus componentes. Ejemplo. En esta línea se declaran dos vectores de double, uno con 20 componentes y otro con 100:

double a[20], b[100];

Preferiblemente se usa constantes para declarar el tamaño o dimensión de un vector:

#define TAM 80 ...

int vector[TAM];

1.10.3. Inicialización de los vectores

Los arrays pueden ser inicializados en la declaración.

Ejemplos:

float R[10] = {2, 32, 4.6, 2, 1, 0.5, 3, 8, 0, 12};

float S[] = {2, 32, 4.6, 2, 1, 0.5, 3, 8, 0, 12};

int N[] = {1, 2, 3, 6};

int M[][3] = { 213, 32, 32, 32, 43, 32, 3, 43, 21};

char Mensaje[] = "Error de lectura";

C =

Tipo_dato nombre_vector[tamaño];

Programación II 9 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

En estos casos no es obligatorio especificar el tamaño para la primera dimensión, como ocurre en los ejemplos de las líneas 2, 3, 4 y 5. En estos casos la dimensión que queda indefinida se calcula a partir del número de elementos en la lista de valores iniciales.

En el caso 2, el número de elementos es 10, ya que hay diez valores en la lista.

En el caso 3, será 4.

En el caso 4, será 3, ya que hay 9 valores, y la segunda dimensión es 3: 9/3=3.

Y en el caso 5, el número de elementos es 17, 16 caracteres más el cero de fin de cadena

Una vez creado un vector, sus elementos presentan valores arbitrarios. Es un error suponer que los valores del vector son nulos tras su creación. Podemos inicializar todos los valores de un vector a cero con un bucle for:

#include <stdio.h> #define TAM 10 void main(void ) { int i, a[TAM]; for (i = 0; i < TAM; i++) a[i] = 0; }

Los vectores pueden ser inicializados también, al momento de su declaración:

int b[TAM] = {1, 2, 3, 4, 5};

Programación II 10 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Ejercicio: Queremos efectuar estadísticas con una serie de valores (las edades de 15 personas). En una primera versión, solicitaremos las edades de todas las personas y, a continuación, calcularemos y mostraremos por pantalla la edad media, la desviación estándar, la moda y la mediana. Las fórmulas para la media y la desviación estándar son:

15

15

1∑ == i ixx

15)(15

12∑ =

−= i i xx

δ

donde Xi es la edad del individuo número i. La moda es la edad que más veces aparece (si dos o más edades aparecen muchas veces con la máxima frecuencia, asumiremos que una cualquiera de ellas es la moda). La mediana es la edad tal que el 50% de las edades son inferiores o iguales a ella y el restante 50% son mayores o iguales. Empezamos por la declaración del vector que albergará las 15 edades y por leer los datos:

//PROGRAMA N°3 SOLUCION AL PROBLEMA DE LAS CALIFICACIONES DE 5O //ALUMNOS #include <iostream.h> #include <conio.h> void main (void) { int C[51]; //declara un vector C para 50 elementos int I; float AC, PROM; for ( I =1 ; I < = 50 ; I++) { cout<< “ Introduzca la calificación del alumno “ << I ; cin >> C [ I ]; AC = AC + C [I]; } PROM = AC / 50; for ( I =1 ; I < = 50 ; I++) if ( C [ I ] > PROM ) CONT ++;

cout<< “el numero de alumnos cuya calificación es mayor al promedio son “ << CONT; getch();

}

Programación II 11 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

#include <conio.h> #include <iosteam.h> #include <math.h> #define PERSONAS 6 void IngresarDatos(int edad[PERSONAS]) { //Lectura de edades for (i=0; i<PERSONAS; i++) { cout<<"Por favor, introduce la edad de la persona nïumero"<< i; cin>>edad[i]; } //fin del for } // fin de IngresarDatos void main(void ) { int edad[PERSONAS], i; IngresarDatos(edad); getch(); }

Pasamos ahora a calcular la edad media y la desviación estándar: #include <conio.h> #include <iostream.h> #include <math.h> #define PERSONAS 6 void IngresarDatos(int edad[PERSONAS]) { //Lectura de edades for (int i=0; i<PERSONAS; i++) { cout<<"Por favor, introduce la edad de la persona numero"<< i; cin>>edad[i]; } } // Calculo de la media double Media(int edad[PERSONAS]) { double sumaedad = 0, media; for (int i=1; i<=PERSONAS; i++) sumaedad = sumaedad +edad[i]; media =(double) sumaedad / PERSONAS; return media; } // Calculo de la desviación estándar double Desviacion(int edad[PERSONAS],double media) { double desviacion, sumadesviacion = 0.0; for (int i=0; i<PERSONAS; i++) sumadesviacion = sumadesviacion+(edad[i]-media)*(edad[i] - media); desviacion = sqrt(sumadesviacion / PERSONAS ); return desviacion; }

Programación II 12 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

void main(void ) { int edad[PERSONAS], i; double med, des; IngresarDatos(edad); med=Media(edad); des=Desviacion(edad,med); cout<<"La media es "<<med; cout<<"La desviación estándar es "<<des; getch(); }

El cálculo de la moda (la edad más frecuente) resulta más problemática. La solución propuesta consiste en ordenar el vector de edades y contar la longitud de cada secuencia de edades iguales. La edad cuya secuencia sea más larga es la moda, la ordenación del vector se deja como ejercicio al lector, el siguiente segmento de programa se realizó bajo el supuesto que el vector está ordenado: frecuencia = 0; frecuenciamoda = 0; moda = -1; for (i=0; i<PERSONAS-1; i++) // Búsqueda de la serie de valores idénticos más larga. if (edad[i] == edad[i+1]) { frecuencia++; if (frecuencia > frecuenciamoda) { frecuencia_moda = frecuencia; moda = edad[i]; } } else frecuencia = 0;

1.10.4. Cadenas de Caracteres Los arreglos de caracteres tienen varias características únicas. Un arreglo de caracteres puede inicializarse de la siguiente forma:

• El tamaño de CADENA1 está determinado por el compilador, y generalmente es de

256 caracteres máximo.

char CADENA1[ ] = "rosa";

Programación II 13 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

• La cadena "rosa" contiene cuatro caracteres, más un caracter especial de terminación denominado caracter nulo (' \0 ').

• Los arreglos de caracteres pueden inicializarse también con constantes individuales

dentro de una lista. • También podemos colocar una cadena de caracteres directamente dentro de un

arreglo de caracteres desde el teclado. Por ejemplo: char CAD2[20];

cin >> CAD2;

EJERCICIO 1. El programa puede parecer a primera vista muy sencillo. En primer lugar vamos a leer y escribir una cadena. La primera solución intuitiva:

EJERCICIO 1. Programa para leer caracteres desde teclado hasta que el caracter sea nulo

char CADENA1[ ]= {' r ', ' o ', ' s ', ' a ', ' \0 ' };

#include <iostream> // USAMOS: cin, cout

void main() { char s[20]; // Cadena de hasta 19 caracteres cin >> s; // Leer la primera palabra cout << endl << s // Escribir en nueva línea la cadena << endl; // Y pasar a la línea siguiente }

Programación II 14 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

EJERCICIO 3. Bien, ahora veamos un ejemplo tradicional, se trata de leer caracteres desde el teclado y contar cuántos hay de cada tipo. Los tipos que deberemos contar serán: consonantes, vocales, dígitos, signos de puntuación, mayúsculas, minúsculas y espacios. Cada carácter puede pertenecer a uno o varios grupos. Los espacios son utilizados frecuentemente para contar palabras.

// Cuenta letras #include <iostream.h> #include <ctype.h> int main() { int consonantes = 0; int vocales = 0; int digitos = 0; int mayusculas = 0; int minusculas = 0; int espacios = 0; int puntuacion = 0; char c; // caracteres leídos desde el teclado cout << "Contaremos caracteres hasta que se pulse ^Z" << endl; while((c = getchar()) != EOF) { if(isdigit(c)) digitos++; else if(isspace(c)) espacios++; else if(ispunct(c)) puntuacion++; else if(isalpha(c)) {

#include <iostream.h> // Usamos: ios, cin, cout #include <iomanip.h> // Usamos: resetiosflags void Leer_Cadena(char * s) { cin >> resetiosflags(ios::skipws); // Para que no pase los caracteres blancos. for (int i= 0; cin >> s[i]; i++) // Leer caracteres hasta el caracter nulo. if (s[i] == '\n') break; s[i]= '\0'; // Pone el caracter de fin de cadena. } void main() { char s[100]; // Buffer de capacidad de hasta 99 caracteres y '\0'. Si // metemos mas caracteres hay posibilidad de 'cuelgue' cout << endl << "Introduce una cadena (termina con CTRL-Z o INTRO)" << endl; Leer_Cadena(s); cout << endl << "La cadena es: " << s << endl; }

Programación II 15 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1.10.5. Arreglos Multidimensionales Existe en la mayoría de los lenguajes una estructura de arreglos multidimensionales. El número de dimensiones (índices) permitido depende del lenguaje elegido.

1.10.5.1. Matrices Una matriz es un arreglo de dos dimensiones, y para especificar cualquier elemento, debemos hacer referencia a dos índices (que representan la posición como renglón y columna). Aunque no se justificará aquí, es conveniente mencionar que la representación matricial es puramente conceptual y con el único fin de facilitar al programador el manejo de los elementos, ya que la computadora almacena los datos en una forma totalmente diferente. Se analizarán primero los arreglos bidimensionales (caso especial de los multidimensionales) por ser los más utilizados.

if(isupper(c)) mayusculas++; else minusculas++; switch(tolower(c)) { case 'a': case 'e': case 'i': case 'o': case 'u': vocales++; break; default: consonantes++; } } } cout << "Resultados:" << endl; cout << "Dígitos: " << digitos << endl; cout << "Espacios: " << espacios << endl; cout << "Puntuación: " << puntuacion << endl; cout << "Alfabéticos: " << mayusculas + minusculas<< endl; cout << "Mayúsculas: " << mayusculas << endl; cout << "Minúsculas: " << minusculas << endl; cout << "Vocales: " << vocales << endl; cout << "Consonantes: " << consonantes << endl; cout << "Total: " << digitos + espacios + vocales + consonantes + puntuacion << endl; return 0; }

Programación II 16 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

• C++ soporta hasta arreglos con 12 dimensiones. En arreglos de dos dimensiones, el

primer elemento representa el renglón y el segundo la columna. • Cada elemento de un arreglo bidimensional puede referenciarse de la siguiente

manera: arreglo [ i ] [ j ].

• Un arreglo multidimensional puede inicializarse desde su declaración. Por ejemplo, un arreglo bidimensional b[2][2] puede declararse e inicializarse así:

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

• Los valores son agrupados en renglones.

Declaración de una Matríz La sintaxis en el lenguaje C++ es el siguiente: donde:

• tipo puede ser cualquier tipo de dato (int, float, char, etc.). • nombre_de_variable es el nombre del arreglo. • rango 1 corresponde al número de renglones que conforman el arreglo. • rango 2 corresponde al número de columnas. Podemos trabajar con cada uno de los elementos de la matriz:

X[3][5] = 20;

Operaciones con arreglos bidimensionales Las operaciones que pueden realizarse con arreglos bidimensionales son las siguientes:

• Lectura/escritura • Asignación • Actualización: Inserción

Eliminación Modificación

• Ordenación

tipo nombre_de_variable [rango1][rango2];

Programación II 17 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

• Búsqueda En general los arreglos bidimensionales son una generalización de los unidimensionales, por lo que se realizará un ejemplo con algunas de estas operaciones a continuación. Ejemplo: La Tabla contiene gastos que registra una ama de casa correspondientes a los 12 meses del año anterior. Meses/Gastos Agua Luz Telefono Mercado Enero 23 57 840 250 Febrero 28 60 560 280 Marzo 34 55 400 275 Abril 24 87 700 340 Mayo 29 80 450 310 Junio 34 65 670 320 Julio 45 67 560 325 Agosto 48 78 570 323 Septiembre 32 54 540 290 Octubre 33 50 250 300 Noviembre 35 70 330 350 Diciembre 38 62 300 430 Es posible interpretar esta tabla de la siguiente manera: dado un mes, se conocen los gasto realizados por la ama de casa; y dado un gasto se conocen los gastos mensuales. El programa será el siguiente:

#iclude<iostream.h> #include<conio.h> typedef int MATRIZ[13][5]; //definimos un tipo de dato MATRIZ de números enteros //PROCEDIMIENTO PARA REGISTRAR LOS GASTOS EN LA MATRIZ G //DE DIMENSIONES m filas y g columnas void Lee_gastos(MATRIZ G, int m, int g) { int i; //contador de meses int j; //contador de columnas de gasto for ( i= 1; i<=m ; i++) for ( j=1; j<=g; j++) { cout<<”\n Ingrese el gasto del mes “<< i << “gasto ”<< j<<; cin>>G[i][j]; } }

Programación II 18 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1.11. Registros (Estructuras) Cuando se habló de los arreglos se mencionó que se trataba de una colección de datos, todos del mismo tipo, que era un tipo estructurado de datos, y que son ellos se podía solucionar un gran número de problemas. Sin embargo, en la práctica a veces se necesitan estructuras que permitan almacenar distintos tipos de datos (característica con la cual no cuentan los arreglos). Ejemplo Una compañía tiene por cada empleado los siguientes datos:

// PROCEDIMIENTO PARA IMPRIMIR TODOS LOS GASTOS DE LA // MATRIZ G void Imprime_Gastos(MATRIZ G, int m, int g) { for (int i=1; i<=m; i++) { for(int j=1; j<=n; j++) cout<<G[i][j]<<” “; cout<<”\n”; } } //PROCEDIMIENTO PARA LISTAR SUMAR TODOS LOS GASTOS DE TODO EL AÑO void Suma_Gastos (MATRIZ G, int m, int g) { int TotalGastos=0;

for (int i=1; i<=m; i++) for(int j=1; j<=n; j++) TotalGastos = TotalGastos + G[i][j]; Cout<<”\n \n EL TOTAL DE GASTOS DEL AÑO ES...”<<TotalGastos; } //PROGRAMA PRINCIPAL void main(void) { MATRIZ GASTOS ; clrscr() ; Lee_Gastos (GASTOS, 12, 4) ; //lee gastos para 12 meses y 4 gastos Imprime_Gastos (GASTOS, 12,4); //Imprime los gastos de la matriz Suma_Gastos(GASTOS, 12,4); getch(); }

Programación II 19 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

• Nombre (cadena de caracteres) • Dirección (cadena de caracteres) • Edad (entero) • Sexo (carácter) • Antigüedad (entero) Si lo vemos gráficamente estos datos tenemos;

Nombre Direccion Nom Pat Mat Calle Nro Zona Edad Sexo Antig Juan Maria

RodríguezAlvarez

Salas Vargas

Av. Arce Calle 12

123 1345

Central Obrajes

25 29

M F

2 3

Si se quisiera almacenar estos datos no sería posible usar un arreglo, ya que sus componentes deben ser todos del mismo tipo.

1.11.1. Definición De Registro y declaración de la variable de registro.

Un registro es un dato estructurado, donde cado uno de sus componentes se denomina campo. Los campos de un registro pueden ser todos de diferentes tipos. Por lo tanto también podrán ser registros o arreglos. Cada campo se identifica por un nombre único (el identificador de campo). No se establece orden entre los campos. Para definir un registro en el lenguaje C, utilizamos la siguiente sintaxis: Donde: Nom_Registro: Es el nombre o identificador del registro.

struct Nom_Registro { tipo_dato1 campo1; tipo_dato1 campo2;

tipo_daton campon; }; //declaración de la variable Variable_registro de tipo // Nom_Registro Nom_Registro Variable_registro ;

Programación II 20 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

campoi: Es el nombre del campo i tipo_datoi: es el tipo de dato del campo i. EJEMPLO Definir la estructura para los datos de un empleado y luego declarar la variable de registro.

1.11.2. Acceso a los Campos De Un Registro Como un registro es un dato estructurado no puede accecsarse directamente como un todo, sino que debe especificarse qué elemento (campo) del registro interesa. Para ello, en la mayoría de los lenguajes se sigue la siguiente sintaxis: Variable_registro.campo

Ejemplo: Escribir un programa para leer los registros de N empleados de una empresa.

struct EMPLEADO { char nombre[20]; char direccion[30]; int edad;

int antigüedad: char sexo;

}; EMPLEADO E ;

Programación II 21 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

#include<iostream.h> #include<conio.h> struct EMPLEADO { char nombre[20]; char direccion[30]; int edad;

int antiguedad: char sexo;

}; //PROCEDIMIENTO PARA LEER LOS DATOS DE UN EMPLEADO EMPLEADO LeeEmpleado (void) { EMPLEADO E; cout<<”\n Nombre del Empleado “; cin>> E.nombre; cout<<”\n Dirección del Empleado “; cin>> E.direccion; cout<<”\n Edad del Empleado “; cin>> E.edad; cout<<”\n Antigüedad del Empleado “; cin>> E.antiguedad; cout<<”\n Sexo del Empleado “; cin>> E.sexo; return ( E ); } // PROCEDIMIENTO PARA IMPRIMIR LOS DATOS PARA UN EMPLEADO void ImprimeEmpleado (EMPLEADO E) { cout<<”\n NOMBRE : “<<E.nombre; cout<<”\n DIRECCION : “<<E.direccion; cout<<”\n EDAD : “<<E.edad; cout<<”\n ANTIGUEDAD : “<<E.antiguedad; cout<<”\n SEXO : “<<E.sexo; } // PROCEDIMIENTO LEER LOS DATOS DE LOS EMPLEADOS EN UN VECTOR void Lee_N_Empleados (EMPLEADO VE[50], int N) { //llena el vector de empleados VE con registros de N empleados for (int i=1; i<= N; i++) { cout<<”\n INTRODUZCA LOS DATOS DEL EMPLEADO “<<i<<endl; VE[i]= LeeEmpleado(); } } // PROCEDIMIENTO IMPRIMIR LOS DATOS DE LOS EMPLEADOS EN UN VECTOR void Imprime_N_Empleados (EMPLEADO VE[50], int N) { for (int i=1; i<= N; i++) { cout<<”\n EMPLEADO “<<i<<endl; ImprimeEmpleado( VE[i]); } getch(); }

Programación II 22 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1.11.3. Funciones en el interior de estructuras: C++, al contrario que C, permite incluir funciones en el interior de las estructuras. Normalmente estas funciones tienen la misión de manipular los datos incluidos en la estructura. Aunque esta característica se usa casi exclusivamente con las clases, como veremos más adelante, también puede usarse en las estructuras. Dos funciones muy particulares son las de inicialización, o constructor, y el destructor. Veremos con más detalle estas funciones cuando asociemos las estructuras y los punteros.

1.11.4. Asignación de estructuras: La asignación de estructuras está permitida, pero sólo entre variables del mismo tipo de estructura, salvo que se usen constructores, y funciona como la intuición dice que debe hacerlo. Veamos un ejemplo:

//PROGRAMA PRINCIPAL void main (void) { EMPLEADO ve[50]; int N; clrscr( );

cout<<”\n Cuantos empleados desea registrar ?”; cin >>N;

Leer_N_Empleados (ve , N); Imprimir_N_Empleados (ve, N); getch( ); }

struct Punto { int x, y; Punto() {x = 0; y = 0;} } ; Punto Punto1, Punto2; int main() { Punto1.x = 10; Punto1.y = 12; Punto2 = Punto1; }

Programación II 23 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

La línea: Punto2 = Punto1;

equivale a:

Punto2.x = Punto1.x;

Punto2.y = Punto1.y;

1.11.5. Arrays de estructuras: La combinación de las estructuras con los arrays proporciona una potente herramienta para el almacenamiento y manipulación de datos. Ejemplo: Vemos en este ejemplo lo fácil que podemos declarar el array Plantilla que contiene los datos relativos a doscientas personas. Podemos acceder a los datos de cada uno de ellos: cout << Plantilla[43].Direccion;

O asignar los datos de un elemento de la plantilla a otro: Plantilla[0] = Plantilla[99];

1.11.6. Estructuras anidadas: También está permitido anidar estructuras, con lo cual se pueden conseguir superestructuras muy elaboradas. Ejemplo: Definiremos una estructura para persona con los datos: Nombre (nombre de pila, paterno, materno), Direccion (Calle, nro y zona) y telefono.

struct Persona { char Nombre[65]; char Direccion[65]; int AnyoNacimiento; }; Persona Plantilla[200];

Programación II 24 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

En general, no es una práctica corriente definir estructuras dentro de estructuras, ya que resultan tener un ámbito local, y para acceder a ellas se necesita hacer referencia a la estructura más externa. Por ejemplo para declarar una variable del tipo stNombre hay que utilizar el operador de acceso (::):

stPersona::stNombre NombreAuxiliar;

Sin embargo para declarar una variable de tipo stDireccion basta con declararla:

stDireccion DireccionAuxiliar;

1.12. Conjuntos El conjunto es también un tipo de dato estructurado. Puede definirse un conjunto como una colección de objetos del mismo tipo base. El tipo base puede ser solamente un tipo ordinal (enteros, caracteres, enumerados y subrangos).

1.12.1. Definición De Conjuntos Los conjuntos serán definidos de la siguiente manera:

struct stDireccion { char Calle[64]; int Nro; char Zona[32]; }; Struct stNombre { char nom[20]; char pat[20]; char mat[25]; }; struct stPersona { stNombre Nomb; stDireccion Direccion; long int Telefono; }; ...

Programación II 25 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Ident_conjunto = CONJUNTO DE tipo_base Donde: Tipo_base es cualquier tipo ordinal de dato. Ejemplo: Se definen los conjuntos NUMEROS, MAYUSCULAS y ALUMNOS. NUMEROS es el tipo conjunto formado por todos los números enteros comprendidos entre el 1 y el 50 inclusive. MAYUSCULAS es el tipo conjunto formado por las letras mayúsculas, y finalmente ALUMNOS es el tipo conjunto formado por todos los elementos del tipo enumerado NOMBRES. NOMBRES = (Juan, Jose, Julio,Javier) NUMEROS = CONJUNTO DE 1..50

MAYUSCULAS = CONJUNTO DE ‘A’..’Z’ ALUMNOS = CONJUNTO DE nombres

1.13. Matrices Poco Densas Matriz es un término matemático utilizado para definir un conjunto de elementos organizados por medio de renglones y columnas, equivalente al término arreglo bidimensional utilizado en computación. Poco Densa indica una proporción muy alta de ceros entre los elementos de la matríz. Es decir una matriz poco densa es aquella que tiene gran cantidad de elementos ceros. Ejemplo: La matriz A de 4 filas por 4 columnas, del total de elementos que es 16, solo 4 de ellos son diferentes de cero.

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

Existen diversos métodos para almacenar los valores diferentes de cero de una matríz poco densa. A continuación presentamos uno de ellos.

A =

Programación II 26 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Arreglo de Registros Se utiliza un arreglo unidimensional, donde cada elemento es un registro formado por tres campos: uno para guardar la fila donde se encontró el valor diferente de cero, otro para guardar la columna, y el tercero para guardar el valor del elemento distinto de cero de la matriz. El siguiente programa nos permite registrar una matriz poco densa

#include<iostream.h> #include<conio.h> struct PocoDensa { int fila; int col; int valor; }; //PROCEDIMIENTO PARA LLENAR LA MATRIZ POCO DENSA Void LeePocoDensa( PocoDensa PD[20]) { int F, C, k; int e; cout<< “\n Cual es la dimensión de la matríz poco densa (filas y columnas) ” cin>>M>>N; k=1; for (int i= 1; i<= F; i++) for (int j=1; j<=C;j++) { cout<< “\n Ingrese el elemento ”<<i<<” “<<j<<” “; cin>>e; if (e != 0) { PD[k].fila = i; PD[k].col =j; PD[k].valor = e; k++; } PD[0].fila = F ; PD[0].col = C; PD[0].valor= k-1; } //PROGRAMA PRINCIPAL Void main (void) { PocoDensa M[20]; //M es un vector de registros clrscr() ; LeePocoDensa(M) ; getch() ; }

Programación II 27 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1.14. Archivos • El almacenamiento de datos en variables y arreglos es temporal; al terminar un

programa todos estos datos se pierden. Para la conservación permanente de grandes cantidades de datos se utilizan los archivos.

• Las computadoras almacenan los archivos en dispositivos de almacenamiento

secundario, especialmente en discos.

• C ve cada uno de los archivos simplemente como un flujo secuencial de bytes. Cada archivo termina con un marcador de fin de archivo.

• Abrir un archivo regresa un apuntador a una estructura FILE (definida en

<stdio.h>) que contiene información utilizada para procesar dicho archivo.

Modos de apertura de archivos w :Para crear un archivo, o para descartar el contenido de un archivo antes de escribir

datos. r :Para leer un archivo existente. a :Para añadir registros al final de un archivo existente. r+ :Para abrir un archivo de tal forma que pueda ser escrito y leído (abre un

archivo de lectura y escritura para actualizar) w+ :Genera un archivo para lectura y escritura. Si el archivo ya existe, se abre y el

contenido se descarta a+ :Abre un archivo para lectura y escritura - toda escritura se efectuará al final del

archivo. Si no existe, será creado. Si al abrir un archivo en cualquiera de los modos anteriores ocurre un error, fopen regresará NULL. EJEMPLO ARCHIVO DE TEXTO CREACION Y LECTURA. Lee y escribe en un archivo de texto de n números sus cubos.

Programación II 28 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

//ARCHIVOS DE TEXTO #include<iostream.h> #include<conio.h> #include<process.h> #include<stdio.h> FILE *Miarch; void crear(void) { int a,n; if ( (Miarch=fopen("C:\\prueba.dat","w"))==NULL) { cout<<"no se puede abrir el archivo \n"; exit(1); } else { cout<<"Cuantos numeros quieres introducir al archivo "<<endl; cin>>n; for (int i=1; i<=n; i++) { a=i*i*i; fprintf(Miarch,"\n %d %d",i,a); } fclose(Miarch); } } void leer(void) { int a,b; if ((Miarch=fopen("c:\\prueba.dat","r"))==NULL) { cout<<"no se puede abrir el archivo "<<endl; exit(1); } else { cout<<"\n n n^3"; fscanf(Miarch,"%d%d",&a,&b); while (!feof(Miarch)) { cout<<"\n "<<a<<" "<<b; fscanf(Miarch,"%d%d",&a,&b); } fclose(Miarch); } } void main(void) { clrscr(); crear(); leer(); }

Programación II 29 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

// Ejemplo de ficheros de acceso aleatorio. #include <stdio.h> #include <stdlib.h> struct stRegistro { char valido; // Campo que indica si el registro es válido S->Válido, N->Inválido char nombre[34]; int dato[4]; }; int Menu(); void Leer(struct stRegistro *reg); void Mostrar(struct stRegistro *reg); void Listar(long n, struct stRegistro *reg); long LeeNumero(); void Empaquetar(FILE **fa); int main() { struct stRegistro reg; FILE *fa; int opcion; long numero; fa = fopen("alea.dat", "r+b"); // Este modo permite leer y escribir if(!fa) fa = fopen("alea.dat", "w+b"); // si el fichero no existe, lo crea. do { opcion = Menu(); switch(opcion) { case '1': // Añadir registro Leer(&reg); // Insertar al final: fseek(fa, 0, SEEK_END); fwrite(&reg, sizeof(struct stRegistro), 1, fa); break; case '2': // Mostrar registro system("cls"); printf("Mostrar registro: "); numero = LeeNumero(); fseek(fa, numero*sizeof(struct stRegistro), SEEK_SET); fread(&reg, sizeof(struct stRegistro), 1, fa); Mostrar(&reg); break; case '3': // Eliminar registro system("cls"); printf("Eliminar registro: "); numero = LeeNumero(); fseek(fa, numero*sizeof(struct stRegistro), SEEK_SET); fread(&reg, sizeof(struct stRegistro), 1, fa); reg.valido = 'N'; fseek(fa, numero*sizeof(struct stRegistro), SEEK_SET);

Programación II 30 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

fwrite(&reg, sizeof(struct stRegistro), 1, fa); break; case '4': // Mostrar todo rewind(fa); numero = 0; system("cls"); printf("Nombre Datos\n"); while(fread(&reg, sizeof(struct stRegistro), 1, fa)) Listar(numero++, &reg); system("PAUSE"); break; case '5': // Eliminar marcados Empaquetar(&fa); break; } } while(opcion != '0'); fclose(fa); return 0; } // Muestra un menú con las opciones disponibles y captura una opción del usuario int Menu() { char resp[20]; do { system("cls"); printf("MENU PRINCIPAL\n"); printf("--------------\n\n"); printf("1- Insertar registro\n"); printf("2- Mostrar registro\n"); printf("3- Eliminar registro\n"); printf("4- Mostrar todo\n"); printf("5- Eliminar registros marcados\n"); printf("0- Salir\n"); fgets(resp, 20, stdin); } while(resp[0] < '0' && resp[0] > '5'); return resp[0]; } // Permite que el usuario introduzca un registro por pantalla void Leer(struct stRegistro *reg) { int i; char numero[6]; system("cls"); printf("Leer registro:\n\n"); reg->valido = 'S'; printf("Nombre: "); fgets(reg->nombre, 34, stdin);

Programación II 31 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

// la función fgets captura el retorno de línea, hay que eliminarlo: for(i = strlen(reg->nombre)-1; i && reg->nombre[i] < ' '; i--) reg->nombre[i] = 0; for(i = 0; i < 4; i++) { printf("Dato[%1d]: ", i); fgets(numero, 6, stdin); reg->dato[i] = atoi(numero); } } // Muestra un registro en pantalla, si no está marcado como borrado void Mostrar(struct stRegistro *reg) { int i; system("cls"); if(reg->valido == 'S') { printf("Nombre: %s\n", reg->nombre); for(i = 0; i < 4; i++) printf("Dato[%1d]: %d\n", i, reg->dato[i]); } system("PAUSE"); } // Muestra un registro por pantalla en forma de listado, // si no está marcado como borrado void Listar(long n, struct stRegistro *reg) { int i; if(reg->valido == 'S') { printf("[%6ld] %-34s", n, reg->nombre); for(i = 0; i < 4; i++) printf(", %4d", reg->dato[i]); printf("\n"); } } // Lee un número suministrado por el usuario long LeeNumero() { char numero[6]; fgets(numero, 6, stdin); return atoi(numero); } // Elimina los registros marcados como borrados void Empaquetar(FILE **fa) { FILE *ftemp; struct stRegistro reg; ftemp = fopen("alea.tmp", "wb");

PrograIngeni

CON

A

r w f f r r r *}

amación II ieria de Sistemas

NTESTA LA1. ¿Cuá

a) b) c)

2. Los cpuedocurr

3. ¿Cuá

ascea) b) c) d)

ARREGLOS

rewind(*fawhile(frea if(reg. fwrifclose(ftefclose(*faremove("alrename("alrename("alfa = fope

s

AS SIGUIENál de las sig

Es un leEs un leEs un le

compiladoreden ensambre con el sig

a) a+++b) No coc) Imprimd) No horiginalee) Indica

ál de las sigendentemen

char, intlong, intchar, lonchar, sh

S DE UNA D

a); ad(&reg, svalido ==ite(&reg, emp); a); lea.bak");lea.dat", lea.tmp", en("alea.d

Ejer

NTES PREGguientes deenguaje peqenguaje paraenguaje seges siguen lablarse en unguiente cód+++++b; ompila. me el valor ace ningun

es. a un warninguientes listnte por tamat, short, longt, char, shorng, int, shorort, int, long

DIMENSIÓ

sizeof(str= 'S') sizeof(st

"alea.bak"alea.datdat", "r+b

32

rcicios Prop

GUNTAS Dscripciones

queño (de ina la enseña

guro, con rea regla de lena construcdigo?

de a + b. a operación

ng. tas de tiposaño, calculag rt rt g

N

ruct stReg

truct stRe

k"); t"); b");

puestos

DE OPCIÓNs de C++ esnstruccioneanza como colección deer el mayocción sintáct

n con a y b

s de datos dado con size

gistro), 1

egistro),

MÚLTIPLEs verdaderas) como C.Pascal.

de basura, cor número dtica válida.

e imprime l

de C++ estáeof()?.

1, *fa))

1, ftemp)

Lic. Katya PérLic. Glady

E: ?

como Java. de caráctereEntonces, ¿

los valores

án ordenada

;

rez Martínez ys Chuquimia

es que ¿qué

s

as

 

Programación II 33 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1. Declare e inicialice un vector de N elementos de modo que los componentes de indice par valgan 0 y los de indice impar valgan 1. Ejm. V(1,0,1,0, . . . . .)

2. Escriba un programa que almacene en un vector los N primeros números de

Fibonacci. Una vez calculados, el programa los mostrará por pantalla en orden inverso.

3. Escriba un programa que almacene en un vector los N primeros números de

Fibonacci. Una vez calculados, el programa pedirá al usuario que introduzca un número y dirá si es o no es uno de los N primeros números de Fibonacci.

4. Hallar la mediana, en el anterior planteado en la pagina 6 del texto 5. Modifica el programa anterior para que permita efectuar cálculos con N personas. 6. Modifica el programa del ejercicio anterior para que muestre, además, cuántas

edades hay entre 0 y 9 años, entre 10 y 19, entre 20 y 29, etc. Considera que ninguna edad es igual o superior a 150. Ejemplo: si el usuario introduce las siguientes edades correspondientes a 12 personas:

10 23 15 18 20 18 57 12 29 31 78 28 el programa mostrará (además de la media, desviación estándar, moda y mediana), la siguiente tabla:

0 - 9: 0 10 - 19: 5 20 - 29: 4 30 - 39: 1 40 - 49: 0 50 - 59: 1 60 - 69: 0 70 - 79: 1 80 - 89: 0 90 - 99: 0 100 - 109: 0 110 - 119: 0 120 - 129: 0 130 - 139: 0 140 - 149: 0

4 Modifica el programa para que muestre un histograma de edades. La tabla anterior se mostrará ahora como este histograma:

0 - 9: 10 - 19: ***** 20 - 29: **** 30 - 39: * 40 - 49: 50 - 59: * 60 - 69:

Programación II 34 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

70 - 79: * 80 - 89: 90 - 99: 100 - 109: 110 - 119: 120 - 129: 130 - 139: 140 - 149:

Como puedes ver, cada asterisco representa la edad de una persona.

5 Modifica el programa anterior para que el primer y último rangos de edades mostrados en el histograma correspondan a tramos de edades en los que hay al menos una persona. El histograma mostrado antes aparecerá ahora así:

10 - 19: ***** 20 - 29: **** 30 - 39: * 40 - 49: 50 - 59: * 60 - 69: 70 - 79: *

6 Modifica el programa del ejercicio anterior para que muestre el mismo histograma de esta otra forma:

| ####### | | | | | | | | ####### | ####### | | | | | | | ####### | ####### | | | | | | | ####### | ####### | | | | | | | ####### | ####### | ####### | | ####### | | ####### | +----------+-----------+-----------+-----------+-----------+-----------+-----------+ | 10 – 19 | 20 - 29 | 30 – 39 | 40 – 49 | 50 – 59 | 60 - 69 | 70 – 79 |

7 Diseñe un programa que pida el valor de N números enteros distintos y los almacene en un vector. Si se da el caso, el programa advertirá al usuario, tan pronto sea posible, si introduce un número repetido y solicitará nuevamente el número hasta que sea diferente de todos los anteriores. A continuación, el programa mostrará los N números por pantalla

8 Diseñe un programa C que lea y almacene en un vector N números enteros

asegurándose de que sean positivos. A continuación, el programa pedirá que se introduzca una serie de números enteros y nos dirá si cada uno de ellos está o no en el vector. El programa finaliza cuando el usuario introduce un número negativo. Luego ordenar el vector, por el método de la burbuja.

Programación II 35 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

9 En un arreglo se ha almacenado el número total de toneladas de cereales

cosechadas durante cada mes del año anterior. Se desea la siguiente información:

i. El promedio anual de toneladas cosechadas ii. ¿Cuántos meses tuvieron una cosecha superior al promedio anual?

iii. ¿Cuántos meses tuvieron una cosecha inferior al promedio anual?Escriba un programa que proporcione estos datos.

ARREGLOS MULTIDIMENSIONALES 20. Escriba un programa que intercambie por renglón los elementos de un arreglo

bidimensional. Los elementos del renglón 1 deben intercambiarse con los del renglón N, los del renglón 2 con los del N-1, y así sucesivamente.

21. Escriba un programa que asigne valores a A, a partir de B teniendo en cuenta los

siguientes criterios: iv. Aij = (bi) si i <= j v. Aij = 0 si i >j

REGISTROS 22. Una compañía distribuye N productos a distintos comercios de la ciudad. Para ellos almacena en un arreglo toda la información relacionada con su mercancía: • Clave • Descripción • Existencia • Mínimo a mantener de existencia • Precio unitario Escriba un programa que pueda llevar a cabo las siguientes opraciones: a) Venta de un producto: se deben actualizar los campos que correspondan, y

verificar que la nueva existencia no esté por debajo del mínimo. (datos: clave, cantidad_vendida)

b) Reabastecimientos de un producto: se deben actualizar los campos que correspondan. (Datos: clave, cantidad comprada)

c) Actualizar el precio de un producto: se deben proporcionar todos los datos relacionados con un producto. (Dato: clave)

Programación II 36 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Capitulo II Pilas

2.1. Introducción La pila es una lista de elementos caracterizada porque las operaciones de inserción y eliminación se realizan solamente en un extremo de la estructura. El extremo donde se realizan estas operaciones se denomina habitualmente 'cima' o tope (top en nomenclatura inglesa). Dada una pila P = (a, b, c, ... k ), se dice que a, es el elemento más inaccesible de la pila, está en el fondo de la pila (bottom) y que k, por el contrario, el más accesible, está en el Tope. Las restricciones definidas para la pila implican que si una serie de elementos A, B, C, D, E se añaden, en este orden a una pila, entonces el primer elemento que se borre de la estructura deberá ser el E. Por tanto, resulta que el último elemento que se inserta en una pila es el primero que se borra. Por esta razón, se dice que una pila es una lista LIFO (Last Input First Output, es decir, el último que entra es el primero que sale). Un ejemplo típico de pila lo constituye un montón de platos, cuando se quiere introducir un nuevo plato, éste se pone en la posición más accesible, encima del último plato. Cuando

El pequeño pez "Usted perdone", le dijo un pez a otro, "es usted más viejo y con más experiencia que yo y probablemente podrá usted ayudarme. Dígame: ¿dónde puedo encontrar eso que llaman Océano? He estado buscándolo por todas partes, sin resultado". "El Océano", respondió el viejo pez, "es donde está ahora mismo". "¿Esto? Pero si esto no es más que agua... Lo que yo busco es el Océano, replicó el joven pez, totalmente decepcionado, mientras se marchaba nadando a buscar en otra parte. Deja de buscar, pequeño pez. No hay nada que buscar. Sólo tienes que estar tranquilo, abrir tus ojos y mirar. No puedes dejar de verlo.

de Anthony de Mello, libro: "El canto del pájaro".

Programación II 37 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

se coge un nuevo plato, éste se extrae, igualmente, del punto más accesible, el último que se ha introducido.

Una pila de papeles Una pirámide de personas

Otro ejemplo natural de la aplicación de la estructura pila aparece durante la ejecución de un programa de ordenador, en la forma en que la máquina procesa las llamadas a los procedimientos. Cada llamada a un procedimiento (o función) hace que el sistema almacene toda la información asociada con ese procedimiento (parámetros, variables, constantes, dirección de retorno, etc..) de forma independiente a otros procedimientos y permitiendo que unos procedimientos puedan invocar a otros distintos (o a si mismos) y que toda esa información almacenada pueda ser recuperada convenientemente cuando corresponda. Como en un procesador sólo se puede estar ejecutando un procedimiento, esto quiere decir que sólo es necesario que sean accesibles los datos de un procedimiento (el último activado que está en la cima). De ahí que la estructura pila sea muy apropiada para este fin. Como en cualquier estructura de datos, asociadas con la estructura pila existen una serie de operaciones necesarias para su manipulación, éstas son:

• Crear la pila. • Comprobar si la pila está vacía. Es necesaria para saber si es posible eliminar

elementos. • Acceder al elemento situado en la cima. • Añadir elementos a la cima. • Eliminar elementos de la cima.

La especificación correcta de todas estas operaciones permitirá definir adecuadamente una pila.

2.2. Representación de Las Pilas

Programación II 38 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Las pilas no son estructuras fundamentales de datos, es decir, no están definidas como tales en los lenguajes de programación (como lo están por ejemplo los arreglos). Las pilas pueden representarse mediante el uso de:

• Arreglos • Listas Enlazadas

Aquí se utilizarán arreglos. En consecuencia deberá definirse cuál será el tamaño máximo de la pila, y además una variable auxiliar a la que se denominará TOPE, que será un apuntador al último elemento insertado en la pila. En la figura 2.1 se presentan dos alternativas de representación de una pila, utilizando arreglos.

2.3. Operaciones Con Pilas La definición de una estructura de datos queda completa al incluir las operaciones que se pueden realizar en ella. Las operaciones principales con pilas son:

• PONER un elemento en la pila (push) • QUITAR un elemento de la pila (pop)

Para implementar una pila, utilizaremos una estructura o un registro de la siguiente manera, implementaremos para una pila de números enteros:

444 222 434 555

555 434 222 4444 PILA =

PILA

1 2 3 4 … MAX

TOPE

MAX 4 3 2 1

E TOPE

struct PILA { int V[MAX]; int Tope;

//OPERACIONES

void PONER(int elem); int QUITAR(void); void CREAR (void); void IMPRIMIR(void); };

Programación II 39 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

A continuación implementamos las operaciones más básicas de la estructura de datos Pila

2.4. Operaciones Adicionales - CREAR PILA Es necesario definir el arreglo que permitirá almacenar la información y la variable que indica la posición de la cima. Además tendremos que inicializar cima al valor 0, indicando que la creación implica que la pila está vacía. void PILA::CREAR (void)

{ tope = 0; cout<< “La pila ha sido creada…”; }

void PILA::PONER (int elem) {// Este procedimiento pone el elemento elem en // la pila representada por el vector V. Actualiza el valor de tope. MAX es // el número máximo de elementos que puede almacenar PILA

If ( tope < MAX ) { tope = tope + 1; V[ tope ] = elem ; } else { cout<<" PILA LLENA "; exit(1); } }

int PILA ::QUITAR ( void ) { // Este procedimiento saca el elemento de la pila representado por V que // está en la posición indicada por tope. Actualiza el // valor de tope int elem; if ( tope > 0 ) { elem = V[tope]; tope = tope - 1 ; } else { cout<<"PILA VACIA"; exit(1); } return(elem); }

Programación II 40 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

- IMPRIME PILA Permite listar los elementos de la pila.

Ejemplo: Realizar un procedimiento para intercalar los elementos de una pila A con los elementos de una pila B en una pila C. Es decir: C =

El programa completo para manejo de pilas se codifica a continuación:

3 4 77 89 23 11 44 33

89 33 77 44 4 11 3 23

void PILA::IMPRIMIR ( void ) { for(int i = tope ; i >=1 ; i -- ) cout<<" "<< V [ i ]; getch(); }

A = B =1 2 3 4 5 1 2 3 4 5

TOPE TOPE

1 2 3 4 5 6 7 8

TOPE

void INTERCALA (PILA A, PILA B, PILA C ) { int dato; while ( A.tope > 0 ) { dato = A.QUITAR ( ); C.PONE ( dato ); dato = B.QUITAR ( ); C.PONER (dato); } }

Programación II 41 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

//PROGRAMA PARA MANEJO DE PILAS #include <iostream.h> #include<conio.h> #include<process.h> #define MAX 40 struct PILA { int V[MAX]; int Tope;

//OPERACIONES

void PONER(int elem); int QUITAR(void); void CREAR (void); void IMPRIMIR(void); };

Programación II 42 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

void PILA::PONER (int elem) {// Este procedimiento pone el elemento elem en // la pila representada por el vector V. Actualiza el valor de tope. MAX es // el número máximo de elementos que puede almacenar PILA

If ( tope < MAX ) { tope = tope + 1; V[ tope ] = elem ; } else { cout<<" PILA LLENA "; exit(1); } } int PILA ::QUITAR ( void ) { // Este procedimiento saca el elemento de la pila representado por V que // está en la posición indicada por tope. Actualiza el // valor de tope int elem; if ( tope > 0 ) { elem = V[tope]; tope = tope - 1 ; } else { cout<<"PILA VACIA"; exit(1); } return(elem); }

void PILA::CREAR (int tope) { tope = 0; cout<< “La pila ha sido creada…”; } void PILA::IMPRIMIR ( void ) { for(int i = tope ; i >=1 ; i -- ) cout<<" "<< V [ i ]; getch(); } void LLENAR_PILA ( PILA P, int N); { int e; for (int i=1; i<=N; i++) {

Programación II 43 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.5. Aplicaciones

void INTERCALA (PILA A, PILA B, PILA *C) { int e; while (A.tope != 0) { E=A.QUITAR( ); (*C).PONER(e); E=B.QUITAR( ); (*C).PONER(e); } }

Programación II 44 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Las pilas son una estructura de datos muy usada en la solución de diversos tipos de problemas. Ahora se verán algunos de los casos más representativos de aplicación de pilas:

• Llamadas a subprogramas • Recursión • Tratamiento de expresiones aritméticas • Ordenación

2.5.1. Llamadas a subprogramas Cuando se tiene un programa que llama a un subprograma, internamente se usan pilas para guardar el estado de las variables del programa en el momento que se hace la llamada. Así, cuando termina la ejecución del subprograma, los valores almacenados en la pila pueden recuperarse para continuar con la ejecución del programa en el punto en el cual fue interrumpido. Además de las variables debe guardarse la dirección del programa en la que se hizo la llamada, porque es a esa posición a la que regresa el control del proceso. Por ejemplo, se tiene un programa principal (PP) que llama a los subprogramas UNO y DOS. A su vez, el subprograma DOS llama al subprograma TRES. Cada vez que la ejecución de uno de los subprogramas concluye, se regresa el control al nivel inmediato superior. Cuando el programa PP llama a UNO, se guarda en una pila la posición en la que se hizo la llamada. Al terminar UNO, el control se regresa a PP recuperando previamente la dirección de la pila. Al llamar a DOS, nuevamente se guarda la dirección de PP en la pila. Cuando DOS llama a TRES, se pone en la pila la dirección de DOS. Después de procesar TRES, se recupera la posición de DOS para continuar con su ejecución. Al terminar DOS se regresa el control a PP, obteniendo previamente la dirección guardada en la pila. PP UNO DOS

… UNO…. DOS ….

…… ….

… …. TRES ….

Programación II 45 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

TRES

Finalmente podemos concluir que las pilas son necesarias en esta área de aplicaciones por lo siguiente:

• Permiten guardar la dirección del programa (subprograma) desde donde se hizo la llamada a otros subprogramas, para poder regresar y seguir ejecutándolo a partir de la instrucción inmediata a la llamada.

• Permiten guardar el estado de las variables en el momento que se hace la llamada, para poder seguir ocupándolas al regresar del subprograma.

2.5.2. Recursión Se Tratará en el tema 5 el tema de recursión. Se dejará para entonces la aplicación de pilas en procesos recursivos.

2.5.3. Tratamiento de expresiones aritméticas Un problema interesante en computación es poder convertir expresiones en notación infija a su equivalente en notación postfija.(o prefija). Revisaremos algunos conceptos:

PP

5 4 3 2 1

TOPE

PP

5 4 3 2 1

TOPE

5 4 3 2 1 0

PP

5 4 3 2 1

TOPE

PP

5 4 3 2 1

DOS PP

5 4 3 2 1 TOPE

PP

54 3 2 1

PP

54 3 2 1TOPE

PP

54 3 2 1

5 4 3 2 1 0

TOPE

Programación II 46 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

• Dada la expresión A+B se dice que está en notación infija, y su nombre se debe a que el operador (+) está entre los operandos ( A y B).

• Dada la expresión AB+ se dice que está en notación postfija, y su nombre se debe a que el operador (+) está después de los operandos (A y B).

• Dada la expresión +AB se dice que está en notación prefija, y su nombre se debe a que el operador (+) está antes que los operando (A y B).

• La ventaja de usar expresiones en notación polaca postfija o prefija radica en que no son necesarios los paréntesis para indicar orden de operación, ya que éste queda establecido por la ubicación de los operadores con respecto a los operandos.

• Para convertir una expresión dada en notación infija a una notación postfija (o prefija) deberán establecerse previamente ciertas condiciones:

• Solamente se manejarán los siguientes operadores (están dados ordenadamente de mayor a menor según su prioridad de ejecución):

^ (potencia) * / (multiplicación y división) + - (suma y resta)

• Los operadores de más alta prioridad se ejecutan primero. • Si hubiera en una expresión dos o más operadores de igual prioridad, entonces se

procesarán de izquierda a derecha. • La subexpresiones parentizadas tendrán más prioridad que cualquier operador. • Presentaremos, paso a paso, algunos ejemplos de conversión de expresiones

infijas a notación polaca postfija. EJEMPLO En este ejemplo se presentan dos casos de traducción de notación infija a postfija. El primero de ellos es una expresión simple, mientras que el segundo presenta un mayor grado de complejidad.

a) expresión infija: X + Z * W

X + [ZW*] Expresión postfija XZW*+

b) transformamos a notación prefija la misma expresión anterior X + Z * W X + [* Z W]

Notación prefija + X*ZW

PrograIngeni

3.

3.1. 3.2. 3.3. 4.

5. 6.

7.

8.

amación II ieria de Sistemas

Traduzca levaluación

X*(Z+W)Z-W*Y+XW*(Z/(K-

Escriba un postfija. Realizar unRealizar uprimer elemEscriba un repetidos oEscriba un

s

las siguiete. )/(T-V) XˆK -T)) programa q

n programa n procedim

mento. programa q

ocupan posisubprogram

EJERCI

es expresio

que lea una

para hallarmiento para

que elimineiciones sucema que invi

47

CICIOS PRO

ones a not

a expresión

r el mayor e intercamb

e los elemenesivas. erta los ele

OPUESTOS

tación postf

en notació

elemento debiar el últim

ntos repetid

mentos de

S

fija utilizan

n infija, y la

e una pila Pmo elemento

dos de una

una pila.

Lic. Katya PérLic. Glady

do el algo

a traduzca a

. o de la pil

pila. Los el

rez Martínez ys Chuquimia

ritmo de

anotación

la por el

lementos

 

Programación II 48 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Capitulo III

Colas

3.1. Introducción La estructura de datos "cola" también llamada "Queue", es un tipo de datos abstracto "TDA". Una cola TDA permite a una lista de cosas ser removidos en el orden en que fueron almacenados. Una cola es una lista de elementos en la que éstos se introducen por un extremo y se eliminan por otro. Los elementos se eliminan en el mismo orden en el que se insertaron. Por lo tanto, el primer elemento que entra a la cola será el primero en salir. Debido a esta característica, las colas también reciben el nombre de estructuras FIFO (First In, First Out: Primero en entrar, primero en salir). Las colas en computación son muy similares a las colas de la vida real. Ejemplos:

a. cola de clientes esperando pagar en una caja de supermercado b. cola de clientes esperando ser atendidos por algún cajero en un banco c. cola de procesos esperando ser ejecutados por una CPU

Al igual que la pila, la cola es una estructura de datos dinámica, pero puede ser representado en forma estática (arreglos).

El camino del Tigre

Un hombre caminaba por el bosque cuando vió una zorra lisiada. "¿Cómo hará para alimentarse?", pensó.

En ese momento, se acercó un tigre, con un animal entre los dientes. Sació su apetito, y le dejó a la zorra lo que había sobrado. "Si Dios ayuda a la zorra, también me va a ayudar", reflexionó.

Volvió a su casa, se encerró en ella, y se quedó esperando que los Cielos le proveyeran de alimento. Nada pasó. Cuando ya se estaba quedando demasiado débil para salir a trabajar, se le apareció un ángel.

- ¿Por qué decidiste imitar a la zorra lisiada? -preguntó el ángel.

- ¡Levántate, toma tus herramientas, y sigue el camino del tigre! de Paulo Coelho

Programación II 49 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

3.2. Características

• Todos los elementos de la cola son del mismo tipo. • Existe un orden de elementos ya que es una estructura lineal, pero los elementos

no están ordenados por su valor sino por orden de introducción en la cola. • Existen dos extremos en la estructura lineal cola, el frente y el final de la cola. • Sólo se puede acceder y eliminar al dato que está en el frente de la cola. • Sólo se puede añadir información al final de la cola.

3.3. Representación De Las Colas Al igual que las pilas, las colas no existen como estructuras de datos estándares en los lenguajes de programación. Las colas pueden representarse mediante el uso de:

• Arreglos • Listas Enlazadas.

Como en el caso de las pilas, utilizaremos arreglos. Debe definirse el tamaño máximo para la cola y dos variables auxiliares. Una de ellas para que guarde la posición del primer elemento de la cola (FRENTE) y otra para que guarde la posición del último elemento de la cola (FINAL). En el ejemplo siguiente se muestra una representación de una cola en la cual se ha insertado cuatro elementos: 11, 22, 33 y 44, en ese orden. El elemento 11 está en el frente ya que fue el primero que entró en la cola. Mientras que el elemento 44, que fue el último en entrar, está en el FINAL de la cola.

Ejemplo: sea una cola Q de números enteros:

11  22  33  44     

 

 

3.4. Estructura de Una cola Implementamos en el lenguaje C++ la estructura de una cola, y para ello utilizaremos REGISTROS como sigue:

Q =

1 2 3 4 5 6

MAX=6

Frente Final

Programación II 50 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

3.5. Operaciones Con Colas Análogamente a las pilas, es necesario definir el conjunto de operaciones básicas para especificar adecuadamente una estructura cola. Estas operaciones serían:

• Crear una cola vacía. • Insertar elementos al final de la cola. • Eliminar elementos al inicio de la cola. • Imprimir los elementos de una cola

Para determinar correctamente cada una de estas operaciones, es necesario especificar un tipo de representación para las colas. Como se puede notar, una cola tiene dos operaciones primitivas:

• Insertar un elemento en la cola (Add/Insert) • Eliminar un elemento de la cola (Remove)

y operaciones auxiliares:

• Crear cola • Imprimir Cola

Crear cola: Esta operación consistirá en definir la variable de tipo array que permitirá almacenar la información y las variables que apuntarán a los extremos de la

#define MAX 40 Struct COLA { int Q[MAX]; int FRENTE;

int FINAL; //OPERACIONES void INSERTAR(int elem); void ELIMINAR (void); void CREAR(void); void IMPRIMIR (void); };

Programación II 51 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

 

 

 

Insertar un elemento en la cola: Debido a la implementación estática de la estructura cola es necesario determinar si existen huecos libres donde poder insertar antes de hacerlo. Esto puede hacerse de varias formas:

Eliminar un elemento de la cola: Como vera la eliminación no borra nada 'físicamente', sólo se encarga de actualizar el puntero 'frente' en una posición y devolver el valor x como mucho, aunque esto último no sea estrictamente necesario. Aunque esa posición aun contenga datos será considerada como vacía a efectos del otro puntero 'final'.

void COLA::CREAR (void) { FRENTE = 0; FINAL = 0; cout<< “ \n La cola ha sido creada “; getch(); }

void COLA ::INSERTAR (int elem) {

//este procedimiento inserta el elemento ELEM al final de la cola V // FRE y FIN son los punteros que indican respectivamente el inicio y //fin de la cola. MAX es el máximo número de elementos que puede //almacenar la cola if (FINAL < MAX) // verifica si hay espacio libre { FINAL = FINAL + 1; Q [FINAL] = elem; If (FINAL = = 1) FRENTE = 1; } else { cout<<”cola llena…”; exit(1); }

}

int COLA ::ELIMINAR (void) { // elimina el primer elemento de la Cola Q, y lo almacena en ELEM. FRENTE // y FINAL son los punteros que indican respectivamente el inicio y fin de // la cola V. int elem ; if ( FRENTE != 0) // verifica que la cola no este vacia { elem = Q[FRENTE]; if ( FRENTE = = FINAL ) //si hay un solo elemento { FRENTE = 0; FINAL = 0; } else FRENTE = FRENTE +1 ; } else{ cout<<” COLA VACIA ”;

exit (1);

Programación II 52 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Imprimir una cola El siguiente procedimiento permite imprimir los elementos de la cola: Ejercicio A continuación implementamos el código correspondiente al programa de manejo de colas lineales

#include <iostream.h> #include <conio.h> #include <process.h> #define MAX 40 Struct COLA { int Q[MAX]; int FRENTE;

int FINAL; //OPERACIONES void INSERTAR(int elem); void ELIMINAR (void); void CREAR(void); void IMPRIMIR (void); }; void COLA::CREAR (void) { FRENTE = 0; FINAL = 0; cout<< “ \n La cola ha sido creada “; getch(); } void COLA ::INSERTAR (int elem) {

//este procedimiento inserta el elemento ELEM al final de la cola V // FRE y FIN son los punteros que indican respectivamente el inicio y //fin de la cola. MAX es el máximo número de elementos que puede //almacenar la cola if (FINAL < MAX) // verifica si hay espacio libre { FINAL = FINAL + 1; Q [FINAL] = elem; If (FINAL = = 1) FRENTE = 1; } else { cout<<”cola llena…”; exit(1); }

}

void COLA::IMPRIMIR(void) { for (int i = FRENTE ; i<=FINAL; i++) cout<< “\n “ << Q[i]; }

Programación II 53 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

int COLA ::ELIMINAR (void) { // elimina el primer elemento de la Cola Q, y lo almacena en ELEM. FRENTE // y FINAL son los punteros que indican respectivamente el inicio y fin de // la cola V. int elem ; if ( FRENTE ! = 0) // verifica que la cola no este vacia elem = Q[FRENTE]; if ( FRENTE = = FINAL) //si hay un solo elemento { FRENTE = 0; FINAL = 0; } else FRENTE = FRENTE +1 ; else{ cout<<” COLA VACIA ”;

exit (1); } return(elem); } void COLA::IMPRIMIR(void) { for (int i = FRENTE ; i<=FINAL; i++) cout<< “\n “ << Q[i]; } //PROGRAMA PRINCIPAL void main (void) { COLA Q1 ; int e, n ; Q1.CREAR( ) ; cout<< « \n cuantos elementos desea introducir a la cola ? “; cin>> n; for (int i=1 ; i<=n; i++) { cout<<” \n Ingrese elemento ”; cin >> e; Q1.INSERTAR(e); } cout<<”\n\n los elementos de la cola son : “; Q1.IMPRIMIR( ); getch( ); }

PrograIngeni

3.6. Paraestruse m

Paraanter

1

1

F

amación II ieria de Sistemas

Colas

a hacer un uctura circu

muestra la re Ejemp

a el siguientrior estructu

Fina

2 3

(a)

Final

(

2 3

F

s

Circular

uso más elar. Es dec

epresentació

plo

e ejemplo (ura, se prop

1

3

2 6al

4 5

D E

Frente Fi

Frente

(a)

4 5

D E

res

ficiente de ir, el elemeón de una c

(a), en el cupone la alte

22

33

9

4

67

5

E

inal

5

E

54

la memoriaento anteriocola circular

ual ya no esernativa (b):

45

8

7

6

5

85

a disponibleor al primeror.

s posible ad

Frente

e se trata ao es el últim

dicionar elem

e

Lic. Katya PérLic. Glady

a la cola como. En la f

mentos sigu

(b)

rez Martínez ys Chuquimia

omo una igura 3.1

uiendo la

 

Programación II 55 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

3.7. Operaciones Con Colas Circulares Las operaciones basicas con colas circulares son: insertar, eliminar y mostrar una cola circular. EJERCICIO: Eliminar el primer elemento impar de una cola circular Q de números enteros

void COLA :: INSERTA_CIRCULAR(int e) { if ( ( ( FINAL == MAX ) && (FRENTE == 1 ) ) || ( FRENTE == FINAL +1 ) ) { cout<<"\n\t..........cola llena........."; exit(1); } else { if ( FINAL == MAX ) FINAL = 1; else if ( FRENTE == 0 ) { FRENTE =1; FINAL =1; } else FINAL = FINAL +1; Q[ FINAL ] = e ; } }

int COLA ::ELIMINA_CIRCULAR (void ) { int e; if(FRENTE ==0) { cout<<"............cola vacia......."; exit(1); } else { e= Q[FRENTE] ; if ( FRENTE == MAX ) FRENTE=1; else if(FRENTE ==FINAL) { FRENTE = 0; FINAL = 0; } else FRENTE = FRENTE +1; } return( e ); }

void COLA ::COLA_IMPRIME_CIRCULAR ( void) { while (FRENTE ! = FINAL ) { cout<< Q[ FRENTE ];

if (FRENTE == MAX) FRENTE =1 ; FRENTE = FRENTE + 1 ; }

cout<< “\n ”<<Q [FRENTE]; getch(); }

Programación II 56 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

3.8. Aplicación De Pilas Y Colas Hasta ahora se ha tratado solamente con la representación en memoria y manipulación de una única pila o cola. Se han visto dos representaciones secuenciales eficientes para dichas estructuras. Sin embargo, en ocasiones, es preciso representar varias estructuras utilizando el mismo espacio de memoria. Supongamos que seguimos transformando las estructuras de datos en representaciones Secuenciales, tipo array. Si sólo hay que representar dos pilas sobre un mismo array A[1..n], la solución puede resultar simple. Se puede hacer crecer las dos pilas partiendo desde los extremos opuestos del array, de forma que A[1] será el elemento situado en el fondo de la primera pila y A[n] el correspondiente para la segunda pila. Entonces, la pila 1 crecerá incrementando los índices hacia A[n] y la pila 2 lo hará decrementando los índices hacia A[1]. De esta manera, es posible utilizar eficientemente todo el espacio disponible. Si se plantea representar más de dos pilas sobre ese mismo array A, no es posible seguir la misma estrategia, ya que un array unidimensional sólo tiene dos puntos fijos, A[1] y A[n], y cada pila requiere un punto fijo para representar el elemento más profundo.

void ELI_IMPAR ( COLA Q) { int e, sw = 0 ; int m = Q.FINAL + 1 ; while (Q.FRENTE ! = m && sw == 0 ) { e = Q.ELIMINA_CIRCULAR() ; if ( e % 2 != 0) sw = 1; else Q.INSERTA_CIRCULAR(e);

} Q.IMPRIME_CIRCULAR();

getch(); }

PrograIngeni

Cuanes nede loen bainformse utdóndreal dvacía

1. 2.

3. 4. 5.

6. 7. 8. 9.

¿

¿

4 Im

amación II ieria de Sistemas

ndo se requecesario divos segmentase al tamamación, el tilizará un í

de está su tde la pila, da.

RealizarEscribir

UtilizandCon colaUtilice unde esperEscriba Escriba Escriba Sea C u(FRENTsiguiente

a. Inb. Ec. Ind. Ine. Inf. E

¿Con cuánto

¿Hubo algún

mplemente

s

uiere represvidir en m stos a una paño esperadarray A se ndice f(i) patope. En gede forma qu

r un programun program

cola do colas circas circularesna estructura de un baun procedimun programun procedimna cola circE=FINAL=0es operacionsertar los eEliminar el ensertar los ensertar el ensertar el eEliminar los

os elemento

n caso de e

las operac

sentar secusegmentos pila. La divisdo de cada puede divid

ara represeeneral, se hue se cump

ma para elimma para inte

culares resus un procedra de cola p

anco. miento para

ma que inviemiento paracular de 6 e0). Dibuje eones: elementos:

elemento: Aelementos: lemento: Glemento: Helementos:

os qudó C?

error (Desbo

iones de Co

57

uencialmentla memoriasión inicial una de las dir en segm

entar el fondhace que f(pla la condic

EJERCICI

minar los elercambiar co

uelva el prodimiento papara simula

a insertar unerta los elema eliminar unlementos. Il estado de

A,B y C A

D, E y F

: B y C

?

ordamiento

olas Simple

te más de da disponiblede A[1..n] eestructuras

mentos de ido de la pilai) esté una ción f(i)=c(i)

IOS

ementos poon su adyac

oblema de Jra manejo dr el movimi

n elemento mentos de un elemento nicialmente Cluego de

o subdesbo

es.

dos pilas, pe, A[1..n], y en segmens. Si no es pgual tamaña i y un índposición p

) si y solam

ositivos de ucente los el

Josefo. de colas deento de clie

en una dobuna cola.

de una dobe la ola esta

realizar ca

ordamiento

Lic. Katya PérLic. Glady

por ejemploasignar a ctos se puedposible conño. Para cadice c(i) paror debajo d

mente si la p

una cola. ementos de

e prioridadesentes en un

ble cola.

ble cola. a vacía da una de l

)?. Explique

rez Martínez ys Chuquimia

o m pilas, cada uno de hacer

nocer esa da pila i,

ra indicar del fondo pila i está

e una

s. a cola

as

e.

 

Programación II 58 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Sugerencia: Use el siguiente programa principal

void main() {int C[MAX],fr,fi,opcion,d; for(;;) { cout<<" OPERACIONES CON TDAs COLA"<<endl; cout<<"1. Crear "<<endl; cout<<"2. Insertar "<<endl; cout<<"3. Eliminar "<<endl; cout<<"4. Mostrar "<<endl; cout<<"5. Salir "<<endl; cin>>opcion; switch(opcion){ case 1: inicializaC(&fr,&fi); break; case 2: cout<<"Ingrese un dato -> "; cin>>d; insertaC(C,fi,d,&fi); break; case 3: eliminaC(C,fr,fi,&d,&fr); cout<<"Se eliminó el -> "<<d; break; case 4: mostrarC(C,fr,fi); break; case 5: cout<<"Fin de programa !!!"; exit(0); break; }}}

5 Escriba un programa que cree una pila a partir de una cola. 6 Ecriba un programa que permita animar las operaciones de colas gráficamente para

una empresa de autos. 7 Implemente las operaciones de Colas Circulares.

Programación II 59 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Capítulo IV

Recursividad

4.1. Introducción Definición de Recursividad: Técnica de programación muy potente que puede ser usada en lugar de la iteración. Se dice que un objeto es recursivo, si en parte está formado por sí mismo o se define en función de sí mismo El concepto de recursividad va ligado al de repetición. Son recursivos aquellos algoritmos que, estando encapsulados dentro de una función, son llamados desde ella misma una y otra vez, en contraposición a los algoritmos iterativos, que hacen uso de bucles while, do-while, for, etc.

4.2. Ambito de Aplicación: – General – Problemas cuya solución se puede hallar solucionando el mismo problema pero con un caso de menor tamaño.

4.3. Razones de uso: – Problemas “casi” irresolubles con las estructuras iterativas. – Soluciones elegantes. – Soluciones más simples. • Condición necesaria: ASIGNACIÓN DINÁMICA DE MEMORIA

Aprende a nacer desde el dolor y a ser más grande que el más grande de los obstáculos, mírate en el espejo de ti mismo y serás libre y fuerte y dejarás de ser un títere de las circunstancias, porque tu mismo eres tu destino.

Pablo Neruda

Programación II 60 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

4.4. ¿En qué consiste la recursividad? En el cuerpo de sentencias del subalgoritmo se invoca al propio subalgoritmo para

resolver “una versión más pequeña” del problema original. Habrá un caso (o varios) tan simple que pueda resolverse directamente sin necesidad de hacer otra llamada recursiva.

• Aspecto de un subalgoritmo recursivo. ALGORITMO Recursivo(...) INICIO ... Recursivo(...); ... FIN

“... Como el costo de la programación aumenta con regularidad y el costo de la

computación disminuye, hemos llegado al punto en donde la mayoría de los casos no vale la pena que un programador desarrolle laboriosamente una solución no recursiva para un problema que se resuelve con mayor naturalidad en forma recursiva”. La recursividad desde el punto de vista de la programación es una poderosa herramienta

que facilita la implementación de algunos algoritmos que de otro modo se harían muy complejos. El ejemplo clásico de esto último es el recorrido de arboles.

4.5. Forma de Enfrentar Problemas Recursivos.

a) Debe existir un caso BASE o Fin de recursión ete debe ser un caso en que la función se puede computar en forma simple (sin llamar a ninguna "copia" de ella). Este es el caso en que se DETIENE la recursión b) b) Parte puramente recursiva Se debe confiar en que, al llamar a una COPIA de la función, ESA función VA A RESOLVER EL PROBLEMA QUE LE ENTREGAMOS. No importa cuan difícil sea ese problema, debemos creer que lo soluciona. Hay que verificar que el problema "más pequeño" sea efectivamente MÁS PEQUEÑO, es decir, que el problema esté más cerca de solucionarse. La idea es que sepamos que en cada llamada recursiva estamos mas cerca del caso BASE

Programación II 61 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

+ Ejemplo definición de nº natural: el N º 0 es natural El Nº n es natural si n-1 lo es El factorial de un número natural esta definido como:

n! = n*(n-1)*(n-2)* ... *3*2*1 con 0!= 1 claramente se ve que: n! = n * (n-1)! ^ ^ ^ | | | | | Problema de tamaño (n-1) | Calculo Problema de tamaño n

En la siguiente figura vemos el proceso de la recursividad

PROBLEMA DE TAMAÑO N

PEQUEÑO CALCULO

PROBLEMA DE TAMAÑO N-1

//ITERATIVO: int Factorial( int n ) { int i, res=1; for (i=1; i <= n; i++ ) res = res * i; return ( res ); }

//RECURSIVO: int Factorial ( int n ) { if (n == 0) return (1); return (n * Factorial (n-1) ); }

Programación II 62 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Es necesario llevar un registro de todas las operaciones que se dejan pendientes hasta

que se llega al caso base. El patrón que sigue este registro es el de una pila, en donde la operación original se deja

en el fondo y en el tope aparecería el caso base. Una vez que se llenó la pila, se efectúa la operación de vaciar la pila efectuando todos

los cálculos que previamente se habían dejado pendientes Por lo general, los lenguajes de alto nivel como el C, enmascaran todos los detalles de la

implementación. A pesar de lo anterior es necesario tomarlo en cuenta debido a que la pila exige una gran cantidad de recursos (memoria y tiempo) de la máquina a la hora de ponerla a funcionar.

4.6. Tipos de recursividad.

♦ Recursividad simple: Aquella en cuya definición sólo aparece una llamada recursiva. Se puede transformar con facilidad en algoritmos iterativos.

♦ Recursividad múltiple: Se da cuando hay más de una llamada a sí misma dentro

del cuerpo de la función, resultando más dificil de hacer de forma iterativa.

Programación II 63 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Ejemplo ♦ Recursividad anidada: En algunos de los arg. de la llamada recursiva hay una nueva llamada a sí misma.

♦ Recursividad cruzada o indirecta: Son algoritmos donde una función provoca una llamada a sí misma de forma indirecta, a través de otras funciones. Ejemplo: Par o Impar:

4.7. La pila de recursion

La memoria del ordenador se divide (de manera lógica, no física) en varios segmentos (4):

int Fib( int n ) /* ej: Fibonacci */ { if ( n <=1) return(1); return ( Fib( n-1) + Fib(n-2) ); }

int Ack ( int n, int m ) /* ej: Ackerman */ { if (n == 0 ) return (m+1) else if ( m == 0 ) return (Ack (n-1,1) ); return( Ack (n-1, Ack(n,m-1) ) ); }

int par ( int nump ) { if ( nump == 0 ) return(1); return (impar (nump-1) ); }

int impar ( int numi ) { if ( numi == 0 ) return(0); return( par ( numi -1) ); }

Programación II 64 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Segmento de código: Parte de la memoria donde se guardan las instrucciones del programa en cod. Máquina. Segmento de datos: Parte de la memoria destinada a almacenar las variables estáticas. Montículo: Parte de la memoria destinada a las variables dinámicas. Pila del programa: Parte destinada a las variables locales y parámetros de la función que está siendo ejecutada.

4.8. La llamada a una función Se reserva espacio en la pila para los parámetros de la función y sus variables locales. Se guarda en la pila la dirección de la línea de código desde donde se ha llamado a la

función. Se almacenan los parámetros de la función y sus valores en la pila. Al terminar la función, se libera la memoria asignada en la pila y se vuelve a la

instrucción actual. En el caso recursivo, cada llamada genera un nuevo ejemplar de la función con sus

correspondientes objetos locales: La función se ejecutará normalmente hasta la llamada a sí misma. En ese momento se

crean en la pila nuevos parámetros y variables locales. El nuevo ejemplar de función comieza a ejecutarse. Se crean más copias hasta llegar a los casos bases, donde se resuelve directamente el

valor, y se va saliendo liberando memoria hasta llegar a la primera llamada (última en cerrarse)

EJERCICIOS

1) Calcular x elevado a n de forma recursiva: 3^2 = 9 3^0 = 1

float xelevn ( float base, int exp ) { if (exp == 0 ) return(1); return( base * xelevn (base , exp-1)); }

Programación II 65 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

b). Multiplicar 2 nºs con sumas sucesivas recursiva: 3*2 = 6 C) ¿Qué hace este programa?:

int multi( int a, int b ) { if ( b == 0 ) return(0); return( a + multi(a, b-1) ); }

void cosa( char *cad, int i) { if ( cad[i] != ‘ \0 ' ) { cosa (cad ,i+1); cout<< cad[i] ; } }

Programación II 66 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Capitulo V Listas Enlazadas

5.1. Introducción a las Estructuras de Datos Dinámica

Hasta ahora, todos los tipos de datos que se han visto, ya sean simples o estructurados, tienen una propiedad común: son estáticos. Esto significa que las variables que se declaran en un programa de alguno de estos tipos mantendrán la misma estructura durante la ejecución del mismo. Son variables estáticas y se definen en tiempo de compilación.

Ejemplo 1:

Si se declara un Vector de 5 elementos de tipo int, éste podrá cambiar su contenido, pero no su estructura.

Hay muchas situaciones en las que se desea cambiar el tamaño de las estructuras usadas.

La técnica usada para manejar estas situaciones es la asignación dinámica de memoria. Con este tipo de asignación se tendrán variables dinámicas o referenciadas, que pueden crearse y destruirse en tiempo de ejecución.

Ejemplo 2:

Si se pide diseñar un programa para gestionar una agenda de teléfonos con los datos de personas (nombre, apellidos, dirección, cumpleaños, teléfono, email, etc...). ¿Qué estructura puede utilizarse para realizarla?

Muere lentamente, quien abandona un proyecto antes de iniciarlo, no preguntando de un asunto que desconoce

o no respondiendo cuando le indagan sobre algo que sabe.

Evitemos la muerte en suaves cuotas, recordando siempre que estar vivo exige un esfuerzo mucho mayor

que el simple hecho de respirar. Pablo Neruda

Programación II 67 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Una respuesta inmediata pareciera ser la de usar un array (vector o matriz), de la siguiente forma:

Si se usa un arreglo de dimensión MAX, pueden surgir los siguientes problemas:

¿Qué pasa si se quieren insertar más personas de MAX en la agenda? Hay que recompilar el programa para ampliar MAX Si el número de personas de la agenda es mucho menor que MAX se está

desperdiciando memoria que podría ser utilizada por otros programas

En muchos otros casos no es posible conocer anteladamente el numero de “elementos” que se van a usar.

Ejemplo 3:

Bases de datos para almacenar los datos de los estudiantes de la universidad

Elementos de un dibujo de pantalla, cuyo tamaño depende del dibujo trazado

5.2. Mecanismos Para Enlazar Información

a. LISTAS o Listas simplemente enlazadas

Solo hay un enlace por nodo Solo se puede recorrer en una dirección

o Listas doblemente enlazadas o Lista circular simplemente enlazada o Lista circular doblemente enlazada

b. ÁRBOLES Y GRAFOS

#define MAX 50 Struct Persona { char nombre[10]; char apellidos[10]; char dirección[15]; char email[20] }; Persona per[MAX];

Programación II 68 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

5.3. El Tipo Puntero. Siempre que se habla de punteros hay que diferenciar claramente entre lo que es:

• Variable referencia o apuntadora (o simplemente puntero) • Variable referenciada o apuntada.

Una variable de tipo Puntero (variable referencia) contendrá una referencia, es decir la dirección en memoria de una variable de un tipo determinado (variable referenciada). Por tanto, siempre existirá asociado al tipo Puntero, otro tipo que es el de la variable referenciada. En C la definición será:

Tipo *TipoP tipo de la variable referenciada. Un puntero es una representación simbólica de una dirección de memoria, es decir, contiene la dirección de un objeto o variable. También se define como una variable que contiene una dirección de memoria. Un puntero apunta a una posición en memoria

Operadores * y &

& permite devolver la dirección de memoria donde se encuentra almacenada el valor de una variable & Contiene la dirección o posición de memoria en la cual se ha almacenado una variable. El operador & es unario, es decir, tiene un solo operando, y devuelve la dirección de memoria de dicho operando. Supongamos el siguiente ejemplo: Para int x = 4; se tiene * (operador de indirección) Representa el contenido de una dirección de memoria del mismo tipo que ha sido declarada.

Programación II 69 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

El operador * es unuario y toma a su operando como una dirección de memoria, entonces el operador accesa al contenido de esa dirección. Por ejemplo, suponga las variables declaradas anteriormente y la asignación, Declaración de puntero: Ejemplo 4:

• int *p; // declaración de puntero a una variable entera • flota *pf; // declaración de puntero a una variable real

Ejemplo 5: int *p; // Declaración puntero a una variable entera int y; // Declaración de variable de tipo entera p=&x; // a p se le asigna la dirección de almacenamiento de la variable y=*p; // se asigna a y el contenido donde apunta p Note que solamente un a puntero se le puede asignar una dirección de memoria. Gráficamente tenemos: Ejemplo 6:

En un momento dado, la variable p será un puntero, o lo que es lo mismo, contendrá la dirección en memoria de una variable de tipo entero, que a su vez, contendrá un entero.

Para acceder a la variable apuntada (referenciada) hay que hacerlo a través de la variable puntero, ya que aquella no tiene nombre (por esto, también se le denomina variable anónima).

TipoDato *NombrePuntero;

Programación II 70 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

NOTA Para intercambiar los valores de los parámetros actuales, las funciones deben recibir la dirección de memoria donde se encuentran las variables.

main() { TipoDato a,b; ...... ....... funcion(&a,&b); /*Traspaso de direcciones de las variables*/ ....... ....... } funcion( TipoDato *x, TipoDato *y) /*Los parámetros apuntan a las direcciones */ { ............. }

Ejemplo :

void intercambia(int *x, int *y) { int temp; temp=*x; *x=*y; *y=temp; }

EJERCICIOS: PROGRAMAS DE PUNTEROS

// Programa PUNTEROS. Uso de & y * #include <iostream.h> void main (void ) { int a; // a es un entero int *P; // P es un puntero a un entero a = 7; P = &a; // P asignado a la dirección de a cout << "La dirección de a es " << &a << endl

<< "\n El valor de P es " << P << endl << endl; cout << "El valor de a es " << a << endl << "El valor de *P es " << *P << endl << endl; cout << "Demostrando que * y & son complementos de " << "cada uno. " << endl << "&*P = " << &*P << endl << "*&P = " << *&P <<endl; return 0; }

PrograIngeni

amación II ieria de Sistemas

// Program #include < main ( ) { int int a = aPt cou

<< cou << cou << << ret}

// Progra #include #include< void main{ int int A = B = P1 P2 C = Cou get}

s

ma PUNTEROS

<iostream.h

t a; t *aPtr;

= 7; tr = &a;

ut << "La d"El valor

ut << "El v"El valor

ut << "Demo"cada uno.endl << "*

turn 0;

Las listnúmero d

ama Nº2 PU

<iostream<conio.h>

n (void)

t A, B, C;t *P1, *P2

= 4; = 6; = &A; = &B;

= (*P1) + ut<<”el retch();

S.

h>

// //

//

dirección dde aPtr es

valor de ade *aPtr e

ostrando qu. " << endl*&aPtr = "

tas enlazadade elemento

caso, la

UNTEROS.su

m.h>

; // 2; //

// //

(*P2); esultado d

71

Uso de

a es un enaPtr es un

aPtr asign

de a es " <s " << aPtres " << a

es " << *aPue * y & sol << "&*aPt<< *&aPtr

as son estruos que la in cantidad de

uma de dos

A, B y CP1 y P2 s

a P1 se la P2 se l

de la suma

& y *

ntero n apuntador

nado a la d

<< &a << enr << endl <<< endl Ptr << endlon complemetr = " << &<<endl;

uctura de dntegran noe los mismo

s números

son enterson punter

le asigna le asigna

a de “<< *

r a un ente

dirección d

ndl << endl;

l << endl;entos de "&*aPtr

datos natuo es conocios puede var

A y B y

ros ros a ente

la direccla direcc

*P1 <<”+”<

Lic. Katya PérLic. Glady

ero

de a

rales dondeido, o en suriar.

resultado

eros

ción de A.ción de B.

<<*P2<<” =

rez Martínez ys Chuquimia

e el mejor

en C

.

.

= “<<C;

 

Programación II 72 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Info enlace

Características:

Las listas son una secuencia ordenada de elementos llamados nodo

Toda lista tiene una cabecera y una cola. Todos los nodos de la lista son del mismo tipo. Los nodo pueden ser agregados o suprimidos en cualquier momento en la lista

• Una lista enlazada es una estructura de datos en la que los objetos están ubicados

linealmente. • En lugar de índices de arreglo aquí se emplean punteros para agrupar linealmente

los elementos. • La lista enlazada permite implementar todas las operaciones de un conjunto

dinámico. • Si el predecesor de un elemento es NULL, se trata de la cabeza de la lista. • Si el sucesor de un elemento es NULL, se trata de la cola de la lista. • Cuando la cabeza es NULL, la lista está vacía o no existe la lista

Gráficamente una lista simplemente enlazada se representa de la siguiente forma:

   

 

  NOTA Las listas almacenadas en arreglos están sujetos al tamaño del arreglo para su crecimiento. Si estos están vacíos se esta ocupando memoria sin uso. Además las listas reflejan el orden lógico y el orden físico de los elementos en el arreglo. Una lista enlazada es una secuencia ordenada de elementos llamados nodos

Toda lista cuenta con un encabezado y nodos.

Nodo Cabeza de la Lista

Ultimo elemento de la Lista

P 23 12 4

Dirección de memoria

Programación II 73 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

5.4. Tipo De Dato Abstracto Nodo

La implementación de un nodo podría ser de la siguiente forma:

5.5. Punteros La construcción y manipulación de una lista enlazada requiere el acceso a los nodos de la lista a través de uno o más punteros a nodos. Normalmente, un programa incluye un puntero al primer nodo (cabeza) y un puntero al último nodo (cola). De cualquier forma el último elemento de la lista contiene un valor de 0, esto es, un puntero nulo (NULL) que señala el final de la lista.

5.6. Operador “->” Si p es un puntero a una estructura y m es un elemento de esa estructura entonces p->m accede al miembro m de la estructura puntada por p

5.7. Operaciones Básicas Sobre Listas Enlazadas Existen cinco operaciones básicas que se pueden hacer sobre listas enlazadas:

El nodo puede contener objetos de cualquier tipo.

struct Nodo { int info; nodo *sig; }; typedef Nodo *LISTA;

Todo nodo contará con un apuntador hacia el

siguiente nodo. Valor que

almacenará el nodo.

Un NODO es una estructura compuesta básicamente de dos partes: • Un campo de información, en cual se almacenan datos o

estructuras • Un campo de dirección, en el cual se almacena la dirección

del nodo siguiente.

Programación II 74 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

• Creación: de una lista vacía • Vacía: Retorna verdadero si la lista L está vacía y falso en caso contrario. • Inserción: Agregar un elemento al final de la lista. • Acceso: Examinar el primer elemento de la lista. • Eliminar: Se puede quitar el primer elemento de la lista, o remover todos los

elementos de la misma. • Buscar: ver si la lista contiene un elemento determinado. • Anula: Permite convertir la lista en una lista vacía • Imprime Lista: Imprime los elementos de la lista L

Creación de una lista vacía

A continuación implementamos el procedimiento para crear una lista vacía.

Inserción

a) Inserción al Final de la lista

En el siguiente segmento de código se ejemplifica solamente el caso en el que el elemento se adiciona al final de la lista tal y como si se tratase de una estructura Pila o Cola.

void Inserta_Final (LISTA *p, int elem) {LISTA Q, T; T = new Nodo; T -> Info = elem; T -> sig = NULL; if(*p != NULL) { Q = *p; while ( Q-> sig != NULL) Q = Q -> sig; Q -> sig = T; } else { *p = T; } }

Recorrido hasta el final de la lista

Se realiza el enlace al final de la lista

Se crea un nuevo nodo asociado al puntero T

void CREAR_LISTA (LISTA *L) { *L = NULL; cout<<”\n lista creada…”; }

Programación II 75 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

b) Inserción al Final de la lista

En el siguiente segmento de código se implementa el caso en el que el nuevo nodo se inserta en la cabeza de la lista:

Para el siguiente ejemplo se ilustra el proceso de adición de un nuevo nodo p = Inserta_Inicio(&p,10);

Recorrido de una Lista: Para mostrar los elementos de la lista se realiza el recorrido de la misma, a partir del nodo cabeza de la lista hasta encontrar el último nodo. Recordemos que el último nodo tiene la propiedad que su miembro SIG es igual a NULL.

void Inserta_Inicio(LISTA *p, int elem) { LISTA Q; Q = new Nodo; Q - > Info = elem; Q - > sig = *p; *p = Q; }

void Recorrido (LISTA p) { LISTA Q; Q = p; while ( Q != NULL) { cout<<” \n “<< Q - > Info; Q= Q-> sig; } }

Programación II 76 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Eliminar el nodo del inicio de la Lista

La siguiente función recibe una lista y devuelve esa misma lista, sin el nodo que ocupaba inicialmente la posición de la cabeza. La función será la siguiente:

Eliminar el nodo del Final de la Lista

A continuación realizamos la implementación del programa completo de listas simples:

void Elimina_Inicio( LISTA *P) { LISTA Q; Q = *P; if ( Q->sig !=NULL) *P = Q -> sig; delete Q; *P = NULL; }

void Elimina_Final( LISTA *P) { LISTA Q, T ; Q = *P; if ( Q->sig !=NULL) { while (Q->sig != NULL) { T = Q; Q = Q-> sig;

} T->sig = NULL;

} else *p=NULL; delete Q; }

Programación II 77 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

#include<iostream.h> #include<conio.h> struct Nodo { int info; nodo *sig; }; typedef Nodo *LISTA; void CREAR_LISTA (LISTA *L) { *L = NULL; cout<<”\n lista creada…”; } void Inserta_Inicio(LISTA *p, int elem) { LISTA Q; Q = new Nodo; Q - > Info = elem; Q - > sig = *p; *p = Q; } void Recorrido (LISTA p) { LISTA Q; Q = p; while ( Q != NULL) { cout<<” \n “<< Q - > Info; Q= Q-> sig; } } void Elimina_Inicio( LISTA *P) { LISTA Q; Q = *P; if ( Q->sig !=NULL) *P = Q -> sig; delete Q; *P = NULL; }

void Elimina_Final( LISTA *P) { LISTA Q, T ; Q = *P; if ( Q->sig !=NULL) { while (Q->sig != NULL) { T = Q; Q = Q-> sig;

} T->sig = NULL;

} else *p=NULL; delete Q; } //PROGRAMA PRINCIPAL void main (void) { LISTA A ; int n , e; CREAR_LISTA(&A) ; clrscr() ;

cout<< « cuantos elementos desea ingresar a la lista « ;

for (int i = 1; i<=n; i++) { cout<<”ingrese elemento”; cin>>e; Inserta_Inicio(&A, e); } Recorrido (A); getch(); }

PrograIngeni

EJEREscral fin

1

2

3

4

voi{ }

amación II ieria de Sistemas

RCICIO ibir un procal de L1.

1. Escriba decir antno se en

2. Escriba ordenad

3. Escriba

4. Escriba

id CONCATE

LISTA Q = L1while Q->sigRecorr

s

cedimiento p

un programtes de inse

ncuentre en

las subrutia

un program

un program

ENA (LISTA

Q; 1; ( Q - >siQ = Q->sig = L2; rido (L1)

para conca

ma que permertar un dete la lista.

inas que p

ma que perm

ma que perm

A L1, LIST

ig != NULLig;

;

78

tenar dos li

EJERCICI

mita adicionerminado e

permitan ad

mita elimina

mita contar l

TA L2)

L)

istas L1 y L

IOS

nar elementelemento se

dicionar ele

ar el elemen

los element

L2. L2 debe

tos no repete debe verif

ementos a

nto de la co

tos de una l

Lic. Katya PérLic. Glady

e estar conc

tidos a unaficar antes

una lista d

la de una li

lista

rez Martínez ys Chuquimia

catenado

a lista. Es que éste

de forma

sta

 

Programación II 79 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Capitulo VI Árboles y Grafos

6.1. Definición

6.2. Conceptos Básicos

a. Nodo hijo: cualquiera de los nodos apuntados por uno de los nodos del árbol. En el ejemplo, 'L' y 'M' son hijos de 'F'.

b. Nodo padre: nodo que contiene un puntero al nodo actual. En el ejemplo, el nodo 'A' es padre de 'B', 'C' y 'D'. Los árboles con los que

trabajaremos tienen otra característica importante: cada nodo sólo puede ser apuntado

Un árbol es una estructura no lineal en la que cada nodo puede apuntar a uno o varios nodos.

Tu vida no cambia cuando cambia tu jefe, Cuando tus amigos cambian, Cuando tus padres cambian, Cuando tu pareja cambia.

Tu vida cambia, cuando tu cambias, Eres el único responsable por ella.

"examínate.. Y no te dejes vencer"

Programación II 80 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

por otro nodo, es decir, cada nodo sólo tendrá un padre. Esto hace que estos árboles estén fuertemente jerarquizados, y es lo que en realidad les da la apariencia de árboles. En cuanto a la posición dentro del árbol:

c. Nodo raíz: nodo que no tiene padre. Este es el nodo que usaremos para referirnos al árbol. En el ejemplo, ese nodo es el 'A'.

d. Nodo hoja: nodo que no tiene hijos. En el ejemplo hay varios: 'G', 'H', 'I', 'K', 'L', 'M', 'N' y 'O'.

e. Nodo rama: aunque esta definición apenas la usaremos, estos son los nodos que no pertenecen a ninguna de las dos categorías anteriores. En el ejemplo: 'B', 'C', 'D', 'E', 'F' y 'J'.

Otra característica que normalmente tendrán nuestros árboles es que todos los nodos contengan el mismo número de punteros, es decir, usaremos la misma estructura para todos los nodos del árbol. Esto hace que la estructura sea más sencilla, y por lo tanto también los programas para trabajar con ellos. Tampoco es necesario que todos los nodos hijos de un nodo concreto existan. Es decir, que pueden usarse todos, algunos o ninguno de los punteros de cada nodo. Un árbol en el que en cada nodo o bien todos o ninguno de los hijos existe, se llama árbol completo. En una cosa, los árboles se parecen al resto de las estructuras que hemos visto: dado un nodo cualquiera de la estructura, podemos considerarlo como una estructura independiente. Es decir, un nodo cualquiera puede ser considerado como la raíz de un árbol completo. Existen otros conceptos que definen las características del árbol, con relación a su tamaño:

f. Orden: es el numero potencial de hijos que puede tener cada elemento de árbol. De este modo, diremos que un árbol en el que cada nodo puede apuntar a otros dos es de orden dos, si puede apuntar a tres será de orden tres, etc.

g. Grado: el número de hijos que tiene el elemento con más hijos dentro del árbol. En el árbol del ejemplo, el grado es tres, ya que tanto 'A' como 'D' tienen tres hijos, y no existen elementos con más de tres hijos.

h. Nivel: se define para cada elemento del árbol como la distancia a la raíz, medida en nodos. El nivel de la raíz es cero y el de sus hijos uno. Así sucesivamente. En el ejemplo, el nodo 'D' tiene nivel 1, el nodo 'G' tiene nivel 2, y el nodo 'N', nivel 3.

i. Altura: la altura de un árbol se define como el nivel del nodo de mayor nivel. Como cada nodo de un árbol puede considerarse a su vez como la raíz de un árbol,

Programación II 81 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

también podemos hablar de altura de ramas. El árbol del ejemplo tiene altura 3, la rama 'B' tiene altura 2, la rama 'G' tiene altura 1, la 'H' cero, etc.

Los árboles de orden dos son bastante especiales, de hecho les dedicaremos varios capítulos. Estos árboles se conocen también como árboles binarios. Frecuentemente, aunque tampoco es estrictamente necesario, para hacer más fácil moverse a través del árbol, añadiremos un puntero a cada nodo que apunte al nodo padre. De este modo podremos avanzar en dirección a la raíz, y no sólo hacia las hojas. Es importante conservar siempre el nodo raíz ya que es el nodo a partir del cual se desarrolla el árbol, si perdemos este nodo, perderemos el acceso a todo el árbol. El nodo típico de un árbol difiere de los nodos que hemos visto hasta ahora para listas, aunque sólo en el número de nodos.

6.3. Árboles Binarios. Un arbol binario es un conjunto finito de nodos que puede estar vacío o está compuesto por un nodo raíz y dos subárboles binarios disjuntos llamados subárbol izquierdo y subárbol derecho.. Un subárbol izquierdo o derecho puede esta vacío. Es decir en un árbol binario cada nodo puede tener 0, 1 o 2 nodos hijos.

• Un Árbol Binario es Completo si es Pleno y todas sus hojas tienen exactamente la misma profundidad.

• Un Árbol Binario es Casi-Completo si todos sus niveles están completos,

excepto posiblemente el más profundo, en cuyo caso las hojas ocupan las posiciones de más a la izquierda.

• Propiedades de un AB Completo:

o El número de nodos en el nivel k-ésimo es 2k. o El número total de nodos desde la raíz hasta el nivel k-ésimo es 2k+1-1, o El número total de nodos internos desde la raíz hasta el nivel k-ésimo es

2k-1. • Notación

o izq(v): Hijo (o subárbol) izquierdo de v o der(v): Hijo (o subárbol) derecho de v.

Programación II 82 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

6.4. Representación De Árboles Binarios Estructura estática representada mediante un vector T Al igual que las listas, los árboles también están constituidos por un conjunto de nodos del siguiente modo: La definición en C del nodo es muy parecida a la de listas doblemente encadenadas: El movimiento a través de árboles, salvo que implementemos punteros al nodo padre, será siempre partiendo del nodo raíz hacia un nodo hoja. Cada vez que lleguemos a un nuevo nodo podremos optar por cualquiera de los nodos a los que apunta para avanzar al siguiente nodo. En general, intentaremos que exista algún significado asociado a cada uno de los punteros dentro de cada nodo, los árboles que estamos viendo son abstractos, pero las aplicaciones no tienen por qué serlo. Ejemplo

a. Un ejemplo de estructura en árbol es el sistema de directorios y ficheros de un sistema operativo. Aunque en este caso se trata de árboles con nodos de dos tipos, nodos directorio y nodos fichero, podríamos considerar que los nodos hoja son ficheros y los nodos rama son directorios.

b. Otro ejemplo podría ser la tabla de contenido de un libro, por ejemplo de este mismo curso, dividido en capítulos, y cada uno de ellos en subcapítulos. Aunque el libro sea algo lineal, como una lista, en el que cada capítulo sigue al anterior, también es posible acceder a cualquier punto de él a través de la tabla de contenido.

c. También se suelen organizar en forma de árbol los organigramas de mando en empresas o en el ejército, y los árboles genealógicos.

6.5. Recorridos El modo evidente de moverse a través de las ramas de un árbol es siguiendo los punteros, del mismo modo en que nos movíamos a través de las listas. Esos recorridos

struct nodo { int info; nodo *izq; nodo *der; }; typedef nodo *ARBOL;

Programación II 83 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

dependen en gran medida del tipo y propósito del árbol, pero hay ciertos recorridos que usaremos frecuentemente. Se trata de aquellos recorridos que incluyen todo el árbol. Hay tres formas de recorrer un árbol completo, y las tres se suelen implementar mediante recursividad. En los tres casos se sigue siempre a partir de cada nodo todas las ramas una por una, estos recorridos son:

PreOrden u “Orden Previo'' IinOrden u “Orden Interno o Simétrico'" PostOrden u “Orden Posterior'"

Los cuales se ejemplifican a continuación Ejemplo: Ejemplo: Un recorrido de un árbol (binario) A es una secuencia o lista de nodos que visita todos los n nodos de A de alguna forma sistemática. Supongamos que tenemos un árbol binario y queremos recorrerlo por completo. Partiremos del nodo raíz:

RecorrerArbol(raiz); La función RecorrerArbol, aplicando recursividad, será tan sencilla como invocar de nuevo a la función RecorrerArbol para cada una de las ramas: Lo que diferencia los distintos métodos de recorrer el árbol no es el sistema de hacerlo, sino el momento que elegimos para procesar el valor de cada nodo con relación a los recorridos de cada una de las ramas. La implementación de los tres tipos es la siguiente:

Preorden: <A, B, C> Inorden: <B, A, C> Postorden: <B, C, A>

void RecorrerArbol(ARBOL raiz) { if(a == NULL) return; RecorrerArbol(raiz->izq); RecorrerArbol(raiz->der); }

Programación II 84 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

In-orden: En este tipo de recorrido, el valor del nodo se procesa después de recorrer la primera rama y antes de recorrer la última. Esto tiene más sentido en el caso de árboles binarios,

void InOrden(ARBOL raiz) { if (raiz == NULL) return; InOrden (raiz->izq); cout<<raiz->info; InOrden (raiz->der); }

El siguiente árbol sería recorrido así: 5,15,17,9,3,1,2,19,25,23

1 15 2 5 9 19 17 3 23 25

Si el árbol no esta vació se realizan los siguientes pasos: 1º.- Recorrer en INORDEN el subarbol izquierdo. 2º.- Procesar la Raiz. 3º.- Recorrer en INORDEN el subarbol derecho.

Pre-orden: En este tipo de recorrido, el valor del nodo se procesa antes de recorrer las ramas: El árbol sería recorrido así: 1,15,5,9,17,3,2,19,23,25

Si el árbol no esta vacio se realizan los siguientes pasos:

void PreOrden( ARBOL raiz) { if ( raiz == NULL) return; cout<<raiz->info; PreOrden (raiz->izq); PreOrden (raiz->der ); }

Programación II 85 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1º.- Procesar Raiz. 2º.- Recorrer en PREORDEN el izquierdo. 3º.- Recorrer en PREORDEN el derecho.

Post-orden: En este tipo de recorrido, el valor del nodo se muestra después de recorrer todas las ramas:

void PostOrden(ARBOL raiz) { if (raiz == NULL) return; PostOrden (raiz->izq); PostOrden (raiz->der); cout<<raiz->info; }

El árbol sería recorrido así: 5,17,3,9,15,25,23,19,2,1 Si el árbol no esta vacio se realizan los siguientes pasos: 1º.- Recorrer en POSTORDEN el izquierdo. 2º.- Recorrer en POSTORDEN el derecho. 3º.- Procesar raiz.

6.6. Árboles Binarios De Busqueda (ABB) Árbol binario en el cual todos sus nodos cumplen que los nodos de su subárbol izquierdo tienen un valor inferior a él, y los nodos de su subárbol derecho tienen un valor superior a él. No existen valores repetidos.

25 12 37 1 30 63 9 29 48 11 40

6.7. Operaciones Sobre Árboles Binarios De Búsqueda De nuevo tenemos casi el mismo repertorio de operaciones de las que disponíamos con las listas:

Programación II 86 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

• Creación de un nuevo nodo • Inserción de nuevos nodos • Búsqueda • Eliminación

Creación De Un Nuevo Nodo Inserción De Un Elemento. Hacer un procedimiento que reciba como parámetro el puntero a la raíz de un ABB y el elemento ELEM y lo inserte en su sitio correspondiente.

ARBOL getnodo() { ARBOL p; p=(ARBOL)malloc(sizeof(struct tiponodo)); return p; } ARBOL creaArbol(int dato) { ARBOL p; p=getnodo(); p->info=dato; p->der=NULL; p->izq=NULL; p->padre=NULL; return p; } void inserta(PN raiz, int dato) {int sw=0; PN q; q=getnodo(); q->info=dato; q->izq=NULL; q->der=NULL; q->padre=raiz; while(sw==0) { if(q->info<raiz->info) { if(raiz->izq==NULL) {raiz->izq=q; sw=1; } else raiz=raiz->izq; } if(raiz->izq==NULL) {raiz->der=q; sw=1; } else raiz=raiz->der; } }

Programación II 87 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Búsqueda De Un Elemento. Hacer un procedimiento que se llama BUSCAR que reciba como parámetro el puntero raiz de un ABB, un elemento a buscar en la variable ELEM del mismo tipo que los que forman el arbol y una variable POS en la que devolverá la dirección de memoria del nodo que contiene el elemento ELEM, en caso de no encontrarse ese elemento devolverá NULL

Buscar y Eliminar Un Elemento Para eliminar un determinado elemento de un árbol, primero se debe encontrar dicho elemento y luego se debe considerar uno de los siguientes casos:

• Si el nodo buscado es una hoja • Si el nodo buscado tiene un hijo • Si el nodo buscado tiene dos hijos

void insertar (ARBOL raiz, int elem) { if(raiz==NULL) { raiz=new tiponodo(); raiz->info=elem; raiz->izq= NULL; raiz->der= NULL; } else if( raiz->info==elem ) cout<<”repetido”; else if(elem < raiz->info) insertar (raiz->izq, elem); else insertar (raiz->der, elem);

}

void buscar (ARBOL raiz, int elem, int *pos) {int auxpos;

if (raiz==NULL) *pos=NULL; else if(raiz->info==elem) *pos =raiz; else if(elem < raiz->info) buscar ( raiz->izq, elem, &auxpos); else buscar (raiz->der, elem, &auxpos); }

Programación II 88 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Con 2 hijos: se sustituye el elemento a borrar por su anterior en un recorrido en INORDEN, y una vez hecho hay que deshacerse de este último. Dentro del subarbol izquierdo del elemento a suprimir, el que esté más a la derecha es el que ocupa su posición, ya que es su anterior en un recorrido en INORDEN.

6.8. Árboles ordenados Un árbol ordenado, en general, es aquel a partir del cual se puede obtener una secuencia ordenada sigue uno de los recorridos posibles del árbol: inorden, preorden o postorden.

void encontar(ARBOL raiz, int elem) { ARBOL p, ant; ant = NULL ; p = raiz ; while (( p-> info != elem ) && ( p<> NULL ) ) { ant = p ; if( elem<p-> info) p= p-> izq ; else p = p-> der ; } if(p != NULL ) { if(p == raiz) suprimir ( raiz ) ; else if(p=ant->izq ) suprimir(ant->izq); else suprimir ( ant -> der ) ; } }

void suprimir ( var p : arbol ) ; { ARBOL temp , ant; if(p->izq == NULL) p = p->der; else if(p -> der == NULL) p = p->izq; else { ant = p; temp = p->izq; while ( temp -> der != NULL ) { ant = temp; temp = temp->der; } p -> info = temp->info ; if(ant==p) ant->izq = temp->izq; else ant->der = temp->izq; } }

Programación II 89 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

En estos árboles es importante que la secuencia se mantenga ordenada aunque se añadan o se eliminen nodos. Existen varios tipos de árboles ordenados, que veremos a continuación:

• Árboles binarios de búsqueda (ABB): son árboles de orden 2 que mantienen una secuencia ordenada si se recorren en inorden.

• Árboles AVL: son árboles binarios de búsqueda equilibrados, es decir, los niveles de cada rama para cualquier nodo no difieren en más de 1.

• Árboles perfectamente equilibrados: son árboles binarios de búsqueda en los que el número de nodos de cada rama para cualquier nodo no defieren en más de 1. Son por lo tanto árboles AVL también.

• Árboles 2-3: son árboles de orden 3, que contienen dos claves en cada nodo y que están también equilibrados. También generan secuencias ordenadas al recorrerlos en inorden.

• Árboles-B: caso general de árboles 2-3, que para un orden M, contienen M-1 claves.

Salvo que trabajemos con árboles especiales, como los que veremos más adelante, las inserciones serán siempre en punteros de nodos hoja o en punteros libres de nodos rama. Con estas estructuras no es tan fácil generalizar, ya que existen muchas variedades de árboles. •Añadir o insertar elementos. •Buscar o localizar elementos. •Borrar elementos. •Moverse a través del árbol. •Recorrer el árbol completo. Los algoritmos de inserción y borrado dependen en gran medida del tipo de árbol que estemos implementando, de modo que por ahora los pasaremos por alto y nos centraremos más en el modo de recorrer árboles.

6.9. Grafos 6.9.1. Definiciones Básicas Un grafo G es un par (V,E) donde V es un conjunto (llamado conjunto de vértices) y E un subconjunto de VxV (conjunto de aristas).

Programación II 90 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Gráficamente representaremos los vértices por puntos y las aristas por líneas que los unen. Un vértice puede tener 0 o más aristas, pero toda arista debe unir exactamente 2 vértices. Llamaremos orden de un grafo a su número de vértices, |V|. Si |V| es finito se dice que el grafo es finito. En este curso estudiaremos los grafos finitos, centrándonos sobre todo en grafos no dirigidos. También supondremos, a no ser que se diga lo contrario, que entre dos vértices hay, como mucho, una arista y que toda arista une dos vértices distintos.

Aristas Si la arista carece de dirección se denota indistintamente {a,b} o {b,a}, si}o a y b los vértices que une. Si {a,b} es una arista, a los vértices a y b se les llama sus extremos.

Vértices Dos vértices v, w se dice que son adyacentes’ si {v,w}∈A (o sea, si existe una arista entre ellos). Llamaremos grado de un vértice al número de aristas de las que es extremo. Se dice que un vértice es ‘par’ o ‘impar’ según lo sea su grado.

Caminos Sean x, y ∈ V, se dice que hay un camino en G de x a y si existe una sucesión finita no vacía de aristas {x,v1}, {v1,v2},..., {vn,y}. En este caso

x e y se llaman los extremos del camino El número de aristas del camino se llama la longitud del camino. Si los vértices no se repiten el camino se dice propio o simple. Si hay un camino no simple entre 2 vértices, también habrá un camino simple

entre ellos. Cu&&o los dos extremos de un camino son iguales, el camino se llama circuito o

camino cerrado. - Llamaremos ciclo a un circuito simple - Un vértice a se dice accesible desde el vértice b si existe un camino entre ellos.

Todo vértice es accesible respecto a si mismo

Programación II 91 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

Ejemplos De Grafos 1.- Grafo regular: Aquel con el mismo grado en todos los vértices. Si ese grado es k lo llamaremos k-regular. Por ejemplo, el primero de los siguientes grafos es 3-regular, el segundo es 2-regular y el tercero no es regular 2.- Grafo bipartito: Es aquel con cuyos vértices pueden formarse dos conjuntos disjuntos de modo que no haya adyacencias entre vértices pertenecientes al mismo conjunto Ejemplo.- de los dos grafos siguientes el primero es bipartito y el segundo no lo es 3.- Grafo completo: Aquel con una arista entre cada par de vértices. Un grafo completo con n vértices se denota Kn. A continuación pueden verse los dibujos de K3, K4, K5 y K6 Todo grafo completo es regular porque cada vértice tiene grado |V|-1 al estar conectado con todos los otros vértices. Un grafo regular no tiene por qué ser completo. 4.- Un grafo bipartido regular se denota Km,n donde m, n es el grado de cada conjunto disjunto de vértices. A continuación ponemos los dibujos de K1,2, K3,3, y K2,5

PrograIngeni

Repr

1.

amación II ieria de Sistemas

resentació

Hacer un

void ejerci{ if( raiz!= { if( raiz

c e e if

} }

s

n De Grafo

seguimiento

icio(ARBOL

=NULL)

z->info % 2 cout<<raiz->ejercicio(raizejercicio(raizf(raiz->info> cout<<ra

os

o del siguie

L raiz)

== 0) >info; z->izq); z->der); >0) aiz->info;

92

EJERCICI

ente algoritm

IOS

mo con el árrbol que se

Lic. Katya PérLic. Glady

da.

rez Martínez ys Chuquimia

 

Programación II 93 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

1 2 9 3 8 10 15 11 16 4 5 12 6 13 7 14 ¿Obtendriamos: 2, 4, 4, 6, 7, 6, 5, 3, 8, 8, 2, 10, 12, 14, 14, 13, 12, 11, 10, 16, 16, 15, 9,1? 2. Dada la siguiente secuencia de números: 25, 12, 1, 9, 37, 30, 63, 48, 29, 11, 4

construir el ABB que genera dicha secuencia si el orden en el que entran en el árbol es el que se establece.

25 12 37 1 30 63 9 29 48 11 40

3. Dado un árbol binario de búsqueda, que contiene el nombre, el apellido y la edad de un grupo de personas , ordenados por edades . Se pide : Hacer un algoritmo que visualice los datos de las personas de mayor a menor edad .

Programación II 94 Lic. Katya Pérez Martínez Ingenieria de Sistemas Lic. Gladys Chuquimia  

4. Dado un ABB que contiene números enteros.

Se pide : - Hacer un procedimiento que reciba como parámetros: el puntero a raiz del arbol , y un número entero . El procedimiento debe buscar el elemento NUM y una vez encontrado visualizar todos sus antecesores (los anteriores).