Leccion 18

78
MIGUEL Á. TOLEDO MARTÍNEZ ARREGLOS, APUNTADORES Y ESTRUCTURAS LECCIÓN 18 18-1 CONTENIDO DE LA LECCIÓN 18 ALMACENAMIENTO DE DATOS EN ARREGLOS 1. Introducción 3 2. Estructura de un arreglo 3 2.1. Elementos del arreglo 5 2.2. Índice del arreglo 5 3. Examen breve 34 76 4. Definición de arreglos unidimensionales en C++ 5 4.1. Formato para un arreglo unidimensional 5 4.2. Ejemplo 18.1 6 5. Examen breve 35 76 6. El acceso a los arreglos 6 6.1. Inserción de elementos en los arreglos unidimensionales 6 6.1.1. Asignación directa 6 6.1.1.1. Formato de asignación directa (inserción de elementos en un arreglo) 6 6.1.2. Sugerencia de depuración 7 6.1.3. Lectura de los elementos del arreglo 7 6.1.4. Inserción de elementos del arreglo con el uso de ciclos 8 6.1.4.1. Inserción dentro de un arreglo unidimensional con el uso de un ciclo for 8 7. Extracción de elementos de los arreglos unidimensionales 9 7.1. Asignación directa 9 7.1.1. Formato de asignación directa (extracción de elementos del arreglo) 9 7.1.2. Escritura de elementos del arreglo 10 7.1.3. Extracción de elementos del arreglo con el uso de ciclos 10 7.1.4. Sugerencia de depuración 11 8. Examen breve 36 76 9. Ejemplos 18.2, 18.3, 18.4, 18.5, 18.6, 18.7, 18.8, 18.9, 18.10, 18.11, 18.12, 18.13, 18.14, 18.15, 18.16, 18.17. 12 10. Paso de arreglos y elementos de arreglos a las funciones 21 10.1. Ejemplos 18.18, 18.19, 18.20, 18.21, 18.22, 18.23, 18.24, 18.25 21 11. Examen breve 37 76 12. Ordenamiento de arreglos 31 12.1. Ejemplos 18.26, 18.27, 18.28, 18.29, 18.30, 18.31, 18.32 32 13. Búsqueda en arreglos: Búsqueda lineal y búsqueda binaria 40 13.1. Ejemplos 18.33, 18.34, 18.35 40

Transcript of Leccion 18

Page 1: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-1

CONTENIDO DE LA LECCIÓN 18

ALMACENAMIENTO DE DATOS EN ARREGLOS 1. Introducción 3 2. Estructura de un arreglo 3

2.1. Elementos del arreglo 5 2.2. Índice del arreglo 5

3. Examen breve 34 76 4. Definición de arreglos unidimensionales en C++ 5

4.1. Formato para un arreglo unidimensional 5 4.2. Ejemplo 18.1 6

5. Examen breve 35 76 6. El acceso a los arreglos 6

6.1. Inserción de elementos en los arreglos unidimensionales 6 6.1.1. Asignación directa 6

6.1.1.1. Formato de asignación directa (inserción de elementos en un arreglo) 6 6.1.2. Sugerencia de depuración 7 6.1.3. Lectura de los elementos del arreglo 7 6.1.4. Inserción de elementos del arreglo con el uso de ciclos 8

6.1.4.1. Inserción dentro de un arreglo unidimensional con el uso de un ciclo for 8

7. Extracción de elementos de los arreglos unidimensionales 9 7.1. Asignación directa 9

7.1.1. Formato de asignación directa (extracción de elementos del arreglo) 9 7.1.2. Escritura de elementos del arreglo 10 7.1.3. Extracción de elementos del arreglo con el uso de ciclos 10 7.1.4. Sugerencia de depuración 11

8. Examen breve 36 76 9. Ejemplos 18.2, 18.3, 18.4, 18.5, 18.6, 18.7, 18.8, 18.9, 18.10, 18.11, 18.12, 18.13, 18.14,

18.15, 18.16, 18.17. 12 10. Paso de arreglos y elementos de arreglos a las funciones 21

10.1. Ejemplos 18.18, 18.19, 18.20, 18.21, 18.22, 18.23, 18.24, 18.25 21

11. Examen breve 37 76 12. Ordenamiento de arreglos 31

12.1. Ejemplos 18.26, 18.27, 18.28, 18.29, 18.30, 18.31, 18.32 32

13. Búsqueda en arreglos: Búsqueda lineal y búsqueda binaria 40 13.1. Ejemplos 18.33, 18.34, 18.35 40

Page 2: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-2

14. Solución de problemas en acción: Búsqueda en un arreglo con iteración (búsqueda secuencial) 45 14.1. Problema 45 14.2. Definición del problema 45 14.3. Planeación de la solución 46 14.4. Codificación del programa 46

15. Solución de problemas en acción: Como ordenar un arreglo con iteración (ordenación por inserción) 47 15.1. Problema 47 15.2. Definición del problema 47 15.3. Planeación de la solución 48 15.4. Codificación del problema 50

16. Solución de problemas en acción: Búsqueda en un arreglo con recursión

(búsqueda binaria) 51 16.1. Problema 51 16.2. Definición del problema 51 16.3. Planeación de la solución 52 16.4. Codificación del problema 55

17. Ejemplos 18.36, 18.37 56 18. Iniciación de arreglos 61

18.1. Iniciación predeterminada de arreglos globales y estáticos 63

19. Examen breve 38 77 20. Arreglos que rebasan los 64 kbytes de memoria 65

20.1. Ejemplos 18.38 y 18.39 65

21. Lo que necesita saber 66 22. Preguntas y problemas 68

22.1. Preguntas 68 22.2. Problemas 70 22.3. Problemas de recursividad 75

Page 3: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-3

LECCIÓN 18

ALMACENAMIENTO DE DATOS EN ARREGLOS INTRODUCCIÓN

Esta lección tratará un tema muy importante en cualquier lenguaje de programación: los arreglos. No es posible enfatizar sobremanera la importancia de los arreglos, pues ellos mismos dan origen a muchas aplicaciones.

En muchas ocasiones, sus programas requerirán almacenar varios valores, tales como 50

calificaciones, 10 títulos de libros, 1000 nombres de archivos, etc. Cuando sus programas necesitan almacenar varios valores, entonces define un arreglo, especificando su clase de datos, nombre y número de elementos que el arreglo almacenará.

Un arreglo es una estructura de datos indexados que se utiliza para almacenar elementos de datos de la misma clase. Los arreglos simplemente proporcionan un medio organizado para localizar y almacenar

datos, así como el apartado postal en el correo de su oficina postal local proporciona un medio organizado de localizar y clasificar el correo. Por esto a un arreglo se le conoce como una estructura de datos que se usa para almacenar cualquier clase de datos, incluyendo enteros, flotantes, caracteres, arreglos, apuntadores, y registros (structs) Además, los arreglos son tan versátiles que se pueden usar para implementar otras estructuras de datos, como pilas, colas, listas ligadas y árboles binarios, de hecho en algunos lenguajes corno FORTRAN, el arreglo es la única estructura de datos disponible para el programador, porque, por medio del uso de arreglos es posible implementar la mayor parte de otras estructuras.

Los objetivos de esta lección son: •••• Presentar la estructura de datos de arreglo. •••• Declarar un arreglo dentro de su programa. •••• Comprender cómo se declaran e inicializan los arreglos y de qué manera se hace referencia a

los elementos de un arreglo. •••• Aprender a pasar los arreglos a las funciones •••• Aprender a utilizar los arreglos para almacenar, ordenar y hacer búsquedas en listas y tablas

de valores. •••• Discernir las técnicas básicas para el ordenamiento.

ESTRUCTURA DE UN ARREGLO

Un arreglo es una estructura de datos. En otras palabras, un arreglo consta de elementos de datos organizados o estructurados en una forma particular. Esta estructura de datos proporciona un medio conveniente para almacenar grandes cantidades de datos en la memoria primaria o del usuario. Existen arreglos unidimensionales o listas y multidimensionales.

En esta lección revisaremos los arreglos unidimensionales; en la lección 19

estudiaremos los arreglos multidimensionales.

Page 4: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-4

Para obtener la idea de un arreglo, observe la ilustración de la figura 18.1. En esta figura se puede ver una sola línea de cajas de apartados postales como se encuentran en cualquier oficina de correos. Como se sabe, cada caja tiene un número de apartado postal (A. P.) En la figura 18.1, los números del A. P. empiezan con cero y van hasta un número finito n. ¿Cómo se localiza una determinada caja? Por medio del número de apartado postal, ¿correcto? Sin embargo, el número del A. P. no tiene nada que ver con lo que contiene la caja. Simplemente se usa para localizar una caja determinada. Desde luego, el contenido es la correspondencia enviada a esta caja. La razón por la que el servicio postal usa el método de apartado postal es que proporciona un método conveniente y bien organizado para almacenar y tener acceso a la correspondencia de sus clientes. Un arreglo hace lo mismo en un programa de computadora; proporciona un método conveniente y bien organizado para almacenar y tener acceso a los datos para usted, el programador. De modo que, ¿cuántos apartados postales hay en la figura 18.1? Debido a que el primer número del apartado es 0 y el último es n, habrá n + 1 apartados.

... ...

Figura 18.1.Un arreglo unidimensional o lista es como una fila de buzones de apartados en la oficina de correos.

Imagine que un arreglo unidimensional, como el que se muestra en la figura 18.2, es

similar a una línea de cajas de apartados postales. El arreglo unidimensional consiste en una sola fila de lugares de almacenamiento, cada una etiquetada con un número que se conoce con el nombre de índice. Cada localización del índice se utiliza para almacenar una clase de datos determinada. Los datos almacenados en una localización de índice determinada se conoce como elemento del arreglo. De esta manera, un arreglo unidimensional es una lista secuencial de lugares de almacenamiento que contiene elementos de datos individuales que se localizan o accedan por medio de índices.

ÍNDICES

Figura 18.2 Un arreglo unidimensional, también llamado lista, es una lista secuencial de

localizaciones de almacenamiento que contiene los elementos de datos que se localizan por medio de los índices.

Los dos componentes principales de un arreglo son los elementos almacenados en el

arreglo y los índices que localizan los elementos almacenados. ¡No se confunda con estos dos componentes del arreglo! Aunque los elementos del arreglo y los índices se relacionan, son cantidades completamente separadas, al igual que el contenido de una caja de apartado postal es diferente de su número de apartado. Con esto presente, vamos a explorar los elementos e índices del arreglo un poco más a fondo.

APARTADO POSTAL 0

APARTADO POSTAL 1

APARTADO POSTAL 2

APARTADO POSTAL 3

APARTADO POSTAL n

[0] [1] [2] [3] [n]

Elemento

0

Elemento

1

Elemento

3

Elemento

2

Elemento

n

Page 5: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-5

ELEMENTOS DEL ARREGLO

Los elementos de un arreglo son los datos almacenados en éste y pueden ser de cualquier clase de datos que se hayan visto. De esta manera, un arreglo dado puede almacenar elementos enteros, elementos de punto flotante, caracteres, y elementos booleanos. Además, de estos elementos de clase de datos estándar, un arreglo también es útil para almacenar elementos de datos enumerados. De hecho, aun los elementos de un arreglo pueden ser otros arreglos. Sin embargo, existe una restricción importante que se aplica a los elementos del arreglo: todos los elementos de un arreglo determinado deberán ser de la misma clase de datos.

Como se verá en breve, es necesario definir los arreglos en un programa C++. Parte de la

definición es especificar la clase de los elementos que el arreglo almacenará. Una vez que se ha definido un arreglo determinado para cierta clase de datos, sólo los elementos de esa clase de datos se almacenarán en ese arreglo.

ÍNDICES DEL ARREGLO

Los índices del arreglo localizan a los elementos del arreglo, en C++, el compilador en forma automática asigna índices enteros a la lista de elementos del arreglo empezando con el índice 0. Por lo tanto, el primer elemento del arreglo en la figura 18.2 se localiza en el índice 0, y el último elemento se localiza en el índice n. Los índices empiezan con 0 y van a n, por tanto habrá n + 1 elementos en el arreglo. También, debido a que es un arreglo unidimensional, o lista, decimos que tiene una dimensión de 1 ×××× (n + 1) lo cual significa que hay un renglón de n + 1 elementos. La dimensión de un arreglo indica el tamaño del arreglo, justo como la dimensión de una pieza de madera indica su tamaño. DEFINICIÓN DE ARREGLOS UNIDIMENSIONALES EN C++

En C++ todos los arreglos deberán estar definidos. Para definir un arreglo, especifique 3 cosas:

1. La clase de datos de los elementos del arreglo. 2. El nombre del arreglo. 3. El tamaño del arreglo.

El formato general es como sigue:

FORMATO PARA UN ARREGLO UNIDIMENSIONAL

<clase de datos del elemento> <nombre del arreglo> [<número de elementos del arreglo>];

Lo primero que se ve en la definición es la clase de datos de los elementos del arreglo. La clase de datos del arreglo es seguida por el identificador del arreglo, su nombre, el cual es seguido por el número de elementos que almacena el arreglo dentro de los corchetes, [] La definición termina con un punto y coma. Por ejemplo, la siguiente es una definición de un arreglo de 10 caracteres cuyo nombre es caracteres.

char caracteres [10];

EXAMEN BREVE 34

Page 6: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-6

Ejemplo 18.1

Escriba las definiciones para los siguientes arreglos: a. Un arreglo llamado enteros que almacenará 10 enteros. b. Un arreglo llamado reales que almacenará 5 valores de punto flotante. c. Un arreglo llamado caracteres que almacenará 11 caracteres. d. Un arreglo llamado clase que almacenará calificaciones de 25 estudiantes. Suponga que las

calificaciones A, B, C, D y R se definen en una clase de datos enumerados denominada calificaciones.

¿Qué índice localiza el último elemento en cada uno de los arreglos anteriores?

Solución

a. int enteros[10]; b. float reales[5]; c. char caracteres[11]; d. enum calificaciones {R, D, C, B, A}; e. calificaciones clase[25];

El índice que localiza el último elemento en cada uno de los arreglos anteriores es uno menos que el tamaño definido del arreglo.

En cada una de las definiciones anteriores se menciona primero la clase de datos del elemento, seguida por el identificador del arreglo, después el tamaño del arreglo encerrado en corchetes. Cada una de las definiciones es muy obvia, excepto quizá la definición del arreglo clase. En esta definición, la clase de datos del arreglo es la clase de datos enumerada llamada calificaciones, que deberá anunciarse antes de la definición del arreglo. Por tanto, diremos que el arreglo clase puede almacenar elementos cuya clase de datos sean calificaciones. De esta manera, los elementos que se pueden almacenar en el arreglo clase se limitan a los elementos de la clase de datos enumerados de R, D, C, B y A. Observe que el compilador no los considera como caracteres, sino como elementos de una clase de datos enumerados llamada calificaciones.

EL ACCESO A LOS ARREGLOS

Tener acceso al arreglo significa insertar elementos dentro del arreglo para almacenar u obtener elementos almacenados desde el arreglo. INSERCIÓN DE ELEMENTOS EN LOS ARREGLOS UNIDIMENSIONALES

Hay básicamente 3 formas principales de insertar elementos dentro de un arreglo: mediante un enunciado de asignación directa, mediante lectura o usando ciclos.

ASIGNACIÓN DIRECTA

El formato general para insertar un elemento, dentro un arreglo, con asignación directa es como sigue:

FORMATO DE ASIGNACIÓN DIRECTA (INSERCIÓN DE ELEMENTOS EN UN ARREGLO)

<nombre del arreglo> [índice del arreglo] = valor del elemento;

EXAMEN BREVE 35

Page 7: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-7

Con las siguientes definiciones de arreglos: char caracteres[6]; int enteros[3]; las asignaciones directas pueden ser como estas: caracteres [0] ='H'; caracteres [5] ='\0'; enteros [0] = 16; enteros [2] = -22; En cada uno de estos ejemplos se coloca un elemento en la primera y en la última

posición de almacenamiento del arreglo respectivamente. El carácter 'H' se coloca en la primera posición del arreglo caracteres y el terminador nulo se coloca en la última posición de este arreglo. Recuerde que la primera posición de este arreglo es siempre [0] y la última posición del arreglo es siempre uno menos que el tamaño del arreglo. El entero 16 se coloca en la primera posición del arreglo enteros y el entero -22 se coloca en la última posición de este arreglo.

Observe que se menciona el nombre respectivo del arreglo, seguido por el índice del

arreglo dentro de corchetes. Entonces se usa un operador de asignación (=) seguido por el elemento que se va a insertar. La clase de datos del elemento que se inserta deberá ser la misma que aquella definida para los elementos del arreglo; de otra manera, se obtendrán resultados impredecibles cuando se trabaje con los elementos del arreglo.

SUGERENCIA DE DEPURACIÓN

Recuerde que los índices de un arreglo en C++ son valores enteros. Esto significa que cualquier índice especificado se convierte en su equivalente entero. Por ejemplo, es posible especificar un índice como un carácter como este: arreglo['A’]; sin embargo, C++ trata esto como arreglo[65], porque, desde el código ASCII, el equivalente entero del carácter ‘A’ es 65. De la misma manera, se puede especificar un índice usando un valor de punto flotante, como este: arreglo[1.414], sin embargo, C++ lo ve como arreglo[1], porque la parte entera de 1.414 es 1. Los elementos de datos enumerados también se pueden usar como índices, porque el compilador iguala los elementos enumerados a enteros, de acuerdo con el orden listado en el enunciado de la clase de datos enumerados. Para evitar confusión y problemas potenciales, se sugiere utilizar siempre valores enteros en los índices, a menos que la aplicación específicamente indique otra cosa. LECTURA DE LOS ELEMENTOS DEL ARREGLO

También es posible usar cualquiera de las funciones u objetos de entrada de C o C++ para insertar los elementos del arreglo desde un teclado, como sigue:

cin > caracteres[1]; cin > enteros[0];

En este caso, el usuario deberá escribir el valor del elemento del arreglo respectivo desde

el teclado y oprimir la tecla ENTER para ejecutar cada enunciado. Deberá escribir un carácter para el primer enunciado cin y un entero para el segundo cin. (¿Por qué?) El carácter escrito desde el teclado se almacenará en la segunda posición (índice[1]) del arreglo caracteres, mientras que el entero escrito desde el teclado se almacenará en la primera posición (índice[0]) del arreglo enteros.

Page 8: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-8

INSERCIÓN ELEMENTOS DE ARREGLOS CON EL USO DE CICLOS

La desventaja obvia al usar asignaciones directas para insertar elementos de arreglos es que se requiere un enunciado de asignación separado para llenar cada posición del arreglo. Puede automatizar el proceso de inserción usando una estructura de ciclo. Aunque cualquiera de las tres estructuras de ciclo (while, do/while, for) se puede emplear, la estructura for es la más común. A continuación está el formato general para el uso de un ciclo for: INSERCIÓN DENTRO DE UN ARREGLO UNIDIMENSIONAL CON EL USO DE UN CICLO for

for(int indice = 0; indice < tamaño del arreglo; ++indice) <asigna o lee arreglo[indice]>

Considere el siguiente programa:

//LLENADO DE UN ARREGLO CON UN CICLO for #include <iostream.h> // Para cin y cout // DECLARA EL TAMAÑO DEL ARREGLO const int MAX = 10; void main(void)

{ int muestra[MAX]; // DEFINE UN ARREGLO ENTERO cout << "Escriba una lista de" << MAX << " elementos y presione" " la tecla ENTER después de cada entrada." << endl; for (int i = 0; i < MAX; ++i)

cin >> muestra[i]; } // FINAL DE main()

Primero, observe la declaración de la constante global de nombre MAX. Note dónde se

utiliza MAX en el programa. Éste es el valor del tamaño del arreglo y el valor del contador final en el ciclo for. El uso de una constante como ésta le permite cambiar el tamaño del arreglo fácilmente. Aquí, el tamaño del arreglo tiene 10 elementos. Para cambiar el tamaño del arreglo, sólo necesitará hacer un cambio en un lugar en el programa dentro del enunciado constante.

Después, vea la definición del arreglo. El arreglo muestra se define en forma local como

un arreglo de elementos enteros. Se le pide al usuario: "Escriba una lista de MAX elementos (donde MAX es 10) y presione la tecla ENTER después de cada entrada.". Una vez que se muestra la pregunta, el programa entra a un ciclo for. La variable contador del ciclo es i, con rango desde 0 a MAX. Cuando el contador del ciclo alcance el valor MAX, el ciclo se rompe porque la verificación del ciclo es i < MAX. Es importante usar la verificación menor que (<) en lugar de usar la verificación menor que o igual a (<=); de otra manera, el ciclo se ejecutará demasiadas veces. (¿Por qué?) El contador del ciclo se emplea como el valor del índice para el arreglo. Con cada iteración del ciclo, se ejecuta un solo enunciado cin para insertar un elemento dentro del arreglo en la posición respectiva especificada por el contador del ciclo, i.

Analicemos el enunciado cin. Primero, el identificador muestra se lista con la variable

contador de ciclo i como el índice del arreglo dentro de corchetes. ¿Qué hace i con cada iteración de ciclo? Se incrementa desde 0 a MAX, ¿correcto? Como resultado, la primera iteración de ciclo lee un valor dentro de muestra[0], la segunda iteración lee un valor dentro de muestra[1] y así sucesivamente, hasta que la última iteración de ciclo lee un valor dentro de la última posición

Page 9: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-9

del arreglo muestra[MAX – 1] Cuando el contador de ciclo se incremento al valor MAX al final de la última iteración de ciclo, éste se rompe y no se insertan más elementos dentro del arreglo. ¡Eso es todo! ¡El arreglo está lleno!

También puede usar ciclos para asignar valores a los elementos del arreglo. Por ejemplo,

usando las definiciones anteriores, considere este ciclo: for (int i = 0; i < MAX; ++i)

muestra[i] = 2 * i;

Esta vez, los elementos del arreglo se asignan dos veces al valor del contador de ciclo con cada iteración de ciclo. ¿Qué valores se insertan realmente dentro del arreglo? ¿Qué pasa con los 10 enteros pares de 0 a 18?

EXTRACCIÓN DE ELEMENTOS DE LOS ARREGLOS UNIDIMENSIONALES

Primero, se le advierte que la palabra extraer no es un buen término aquí. ¿Por qué? Porque, en general, la palabra extraer significa eliminar algo, cuando extraemos un elemento de un arreglo, ¡realmente no lo eliminamos! Simplemente copiamos su valor. El elemento permanece almacenado en el arreglo hasta que se reemplaza por otro valor usando una operación de inserción. Como con la inserción, es posible extraer elementos del arreglo usando uno de los tres métodos generales: asignación directa, escritura o ciclo.

ASIGNACIÓN DIRECTA

La extracción de los elementos del arreglo usando enunciados de asignación es lo inverso de la inserción de elementos utilizando un enunciado de asignación. Éste es el formato general:

FORMATO DE ASIGNACIÓN DIRECTA (EXTRACCIÓN DE ELEMENTOS DEL ARREGLO)

<identificador del objeto variable> = <nombre del arreglo>[índice del arreglo]

Como ejemplo, suponga que se hacen las siguientes definiciones: const int MAX = 10; int muestra[MAX]; int x; Como puede observar, el arreglo muestra[] consta de 10 elementos enteros. Ahora,

suponga que el arreglo está lleno, ¿Qué harán los siguientes enunciados? x = muestra[0]; x = muestra[MAX - 1]; x = muestra[3] * muestra[5]; x = 2 * muestra[2] -3 * muestra[7]; El primer enunciado asigna el elemento almacenado en la primera posición del arreglo a

la variable x. El segundo enunciado asigna el elemento almacenado en la última posición del arreglo a la variable x. El tercer enunciado asigna el producto de los elementos localizados en los índices [3] y [5] a x. Por último, el cuarto enunciado asigna a x dos veces el elemento localizado en el índice[2] menos tres veces el elemento localizado en el índice [7] Los últimos dos

Page 10: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-10

enunciados muestran cómo se pueden realizar las operaciones aritméticas con los elementos del arreglo.

En todos los casos anteriores, los valores de los elementos del arreglo no se afectan por

las operaciones de asignación. El principal requerimiento es que x debe estar definida como la misma clase de datos de los elementos del arreglo para no obtener resultados inesperados.

Como ejemplo final, considere estos enunciados de asignación: muestra[0] = muestra[MAX - 1]; muestra[1] = Muestra[2] + Muestra[3]; ¿Puede usted determinar qué pasa aquí? En el primer enunciado, el primer elemento

del arreglo se reemplaza por medio del último elemento del arreglo, ¿se afecta el último elemento del arreglo? No, porque aparece del lado derecho del operador de asignación. En el segundo caso, el segundo elemento del arreglo en el índice [1] se reemplaza por la suma del tercero y cuarto elementos del arreglo en los índices [2] y [3] De nuevo, el tercero y cuarto elementos del arreglo no se afectan por esta operación, porque aparecen del lado derecho del operador de asignación.

ESCRITURA DE ELEMENTOS DEL ARREGLO

Los objetos cout se usan para mostrar los elementos del arreglo. Usemos el mismo arreglo para demostrar cómo escribir elementos del arreglo. Aquí está de nuevo la definición del arreglo:

const int MAX = 10; int muestra[MAX]; ¿Qué se supone que harán los siguientes enunciados? cout << muestra[0] << endl; cout << muestra[MAX - 1] << endl; cout << muestra[1] / muestra[2] << endl; cout << sqrt(muestra[6]) << endl; El primer enunciado mostrará el elemento contenido en el índice [0] del arreglo. El

segundo presentará el último elemento del arreglo, localizado en el índice [MAX – 1] El tercer enunciado dividirá el elemento localizado en el índice [1] entre el elemento en el índice [2] y mostrará el cociente entero. Por último, el cuarto presentará la raíz cuadrada del elemento localizado en el índice [6] Ninguno de los valores del elemento del arreglo se afectará con estas operaciones.

EXTRACCIÓN DE ELEMENTOS DEL ARREGLO CON EL USO DE CICLOS

Al igual que en la inserción de elementos en un arreglo, la extracción de elementos del arreglo con ciclos requiere menos codificación, especialmente cuando se extraen elementos múltiples. De nuevo, se puede usar cualquier estructura de ciclo para este propósito, pero los ciclos for son los más comunes.

Considere el siguiente programa:

Page 11: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-11

// MUESTRA UN ARREGLO CON UN CICLO for #include <iostream.h> // Para cin y cout // ENUNCIA EL TAMAÑO DEL ARREGLO const int MAX = 10; void main(void)

{ int muestra[MAX]; // DEFINE UN ARREGLO ENTERO for(int i = 0; i < MAX; ++i)

muestra[i] = i * i;

for (int i = 0; i < MAX; ++i) cout << muestra[i] <<’\t’;

} // FINAL DE main()

De nuevo, el arreglo se define localmente como un arreglo de valores enteros MAX[10] Su nombre es muestra. Observe que se usa la variable contador de ciclo i como el índice del arreglo en ambos ciclos for. El primer ciclo llenará los lugares del arreglo con el cuadrado del contador de ciclo. Entonces, el segundo ciclo mostrará cada uno de los elementos del arreglo localizados desde el índice [0] hasta el índice [MAX – 1] Se usa un enunciado cout para mostrar los elementos del arreglo en forma horizontal a través de la pantalla. Observe también que cada vez que se muestra un elemento, se escribe un tabulador después del elemento para separarlo del siguiente elemento de la secuencia. Esto es lo que verá en la pantalla:

0 1 4 9 16 25 36 49 64 81

SUGERENCIA DE DEPURACIÓN

Procure no especificar un índice del arreglo fuera de rango. Tal índice puede ser uno que sea menor que 0 o mayor que el valor máximo del índice. Por ejemplo, suponga que define un arreglo como éste:

const int MAX = 10; char arreglo[MAX]; Un error común al usar esta definición sería intentar tener acceso al elemento arreglo[MAX] Sin embargo,

recuerde que los índices de los arreglos en C++ empiezan con 0; por tanto, MAX especifica una posición más allá del valor del índice máximo del arreglo. El valor del índice máximo en este arreglo es MAX - 1. En una situación de ciclo, el siguiente enunciado de ciclo dará origen al mismo problema:

for (int i = 0; i <= MAX, ++i)

cout << A[i];

De nuevo, la última iteración de ciclo especifica un índice del arreglo de MAX, que está fuera de rango del índice. Para evitar este error, la verificación será i < MAX o i <= MAX - 1.

Cuando un índice de arreglo está fuera de rango, no se obtendrá un error de compilación. En el mejor de

los casos se obtendrá basura. En la peor situación, se destruirá algo en la memoria que requiera su programa o sistema operativo, lo que daría origen a la caída del sistema o programa. Errores como éste son muy difíciles de localizar. Así que asegúrese de que sus índices siempre estén dentro de su rango especificado.

EXAMEN BREVE 36

Page 12: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-12

A continuación se darán varios ejemplos en los que se utilicen los conocimientos antes adquiridos.

Ejemplo 18.2

El siguiente programa, INICIALIZA.CPP, utiliza una estructura de repetición for para inicializar a cero los elementos de un arreglo de enteros llamado arreglo de diez elementos; luego imprime dicho arreglo en formato de tabla.

Ejemplo 18.3

El siguiente programa DECLARA.CPP, inicializa los elementos de un arreglo al declararse. Observe como se utilizan el signo de igualdad y una lista de valores separados por coma (encerrados entre corchetes) Si hubiera menos valores que elementos en el arreglo, los elementos restantes se inicializarían automáticamente a cero. Por ejemplo, los elementos del arreglo arreglo[10], del programa INICIALIZA.CPP, se podrían haber inicializado a cero con la declaración:

int arreglo[10] = {0};

/* El siguiente programa: INICIALIZA.CPP, inicializa a ceros a un arreglo de 10 elementos, posteriormente imprime el contenido de dicho arreglo. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() void main(void) { int i, arreglo[10]; for(i = 0; i < 10; i++) arreglo[i] = 0; //Inicializa el arreglo cout << "Elemento" << setw(13) << "Valor" << endl; for(i= 0; i < 10; i++) // Imprime el arreglo cout << setw(7) << i << setw(13) << arreglo[i] << endl; }//Fin de main()

/* El siguiente programa: DECLARA.CPP, inicializa los elementos de un arreglo mediante una declaración. Mediante un for se muestra el contenido de dicho arreglo. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() void main(void) { int i, arreglo[10] = {32, 27, 64, 18, 95, 14, 90, 70, 60, 37}; cout << "Elemento" << setw(13) << "Valor" << endl; for(int i = 0; i < 10; i++) cout << setw(7) << i << setw(13) << arreglo[i] << endl; }//Fin de main()

Page 13: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-13

que explícitamente inicializa a cero el primer elemento e implícitamente inicializa a cero a los otros nueve elementos, pues hay menos valores que elementos en el arreglo. Recuerde que los arreglos automáticos no se inicializan implícitamente a cero. El programador debe inicializar a cero cuando menos el primer elemento para que los demás se inicialicen a cero de manera automática.

La siguiente declaración de arreglo:

int arreglo[5] = {32, 27, 64, 18, 95, 14};

provocaría un error de sintaxis, pues hay 6 inicializadores y únicamente 5 elementos en el arreglo.

Si se omite el tamaño del arreglo en la declaración por medio de una lista de inicio, el numero de elementos del arreglo será el número de elementos de dicha lista. Por ejemplo:

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

creará un arreglo de cinco elementos.

Ejemplo 18.4

El siguiente programa, ELEMENTS.CPP, inicializa el arreglo llamado calificaciones y luego visualiza sus elementos.

Ejemplo 18.5

El siguiente programa MSTARRE.CPP, utiliza el ciclo for para mostrar el contenido del arreglo calificaciones.

/* El siguiente programa: ELEMENTS.CPP, inicializa el arreglo calificaciones y luego visualiza sus elementos. */ #include <iostream.h> //Para cout y cin void main(void) { int calificaciones[5] = {80, 70, 90, 85, 80}; cout << "Valores del arreglo" << endl << endl; cout << "calificaciones[0] = " << calificaciones[0] << endl; cout << "calificaciones[1] = " << calificaciones[1] << endl; cout << "calificaciones[2] = " << calificaciones[2] << endl; cout << "calificaciones[3] = " << calificaciones[3] << endl; cout << "calificaciones[4] = " << calificaciones[4] << endl; }//Fin de main()

/* El siguiente programa: MSTARRE.CPP, utiliza la variable i y el ciclo for para visualizar los elementos del arreglo llamado calificaciones. */ #include <iostream.h> //Para cout y cin

Page 14: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-14

Ejemplo 18.6

El siguiente programa, 5VALORES.CPP, muestra 5 valores de un arreglo mediante una ciclo for.

Ejemplo 18.7

El siguiente programa 5CONSTANTES.CPP, ilustra como declarar un arreglo utilizando una constante.

void main(void) { int calificaciones[5] = {80, 70, 90, 85, 80}; int i; cout << "Valores del arreglo" << endl << endl; for (i = 0; i < 5; i++) cout << "calificaciones[" << i << "] = " << calificaciones[i] << endl; }//Fin de main()

/* El siguiente programa: 5VALORES.CPP, declara un arreglo con cinco valores y luego utiliza el ciclo for para visualizar los valores del arreglo. */ #include <iostream.h> //Para cout y cin void main(void) { int valores[5] = {80, 70, 90, 85, 80}; int i; for (i = 0; i < 5; i++) cout << "Valores[" << i << "] = " << valores[i] << endl; }//Fin de main()

/* El siguiente programa: 5CONSTANTES.CPP, declara un arreglo basado en la constante TAMANO_ARREGLO. */ #include <iostream.h> //Para cout y cin #define TAMANO_ARREGLO 5 void main(void) {

int valores[TAMANO_ARREGLO] = {80, 70, 90, 85, 80}; int i; for (i = 0; i < TAMANO_ARREGLO; i++) cout << "valores[" << i << "] = " << valores[i] << endl; }//Fin de main()

Page 15: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-15

Ejemplo 18.8 El siguiente programa, PARES.CPP, inicializa los elementos de un arreglo llamado arreglo de diez elementos a los enteros 2, 4, 6, ... , 20, e imprime el arreglo en formato de tabla. Estos números se generan multiplicando por 2 cada valor consecutivo del contador del ciclo y sumándole 2.

Ejemplo 18.9

El siguiente programa, TAMARRE.CPP, utiliza el operador sizeof() para visualizar la cantidad de memoria utilizada por los diferentes tipos de arreglos.

/* Este programa: PARES.CPP inicializa un arreglo con los enteros pares comprendidos entre 2 y 20 inclusive. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() void main(void) { const int TAMANO_ARREGLO = 10; int j, arreglo[TAMANO_ARREGLO]; for(j = 0; j < TAMANO_ARREGLO;j++) //Inicializa arreglo arreglo[j] = 2 + 2 * j; cout << "Elemento" << setw(13) << "Valor" << endl; for(j = 0; j < TAMANO_ARREGLO; j++) //Imprime los valores cout << setw(7) << j << setw(13) << arreglo[j] << endl; }//Fin de main()

/* El siguiente programa: TAMARRE.CPP, utiliza el operador sizeof() para visua- lizar la cantidad de memoria utilizada por los diferentes tipos de arreglos. */ #include <iostream.h> //Para cout y cin void main(void) { int calificaciones[100]; float salarios[100]; char cadena[100]; cout << "Bytes utilizados en el arreglo calificaciones[100] = " << sizeof(calificaciones)<< endl; cout << "Bytes utilizados en el arreglo salarios[100] = " << sizeof(salarios) << endl; cout << "Bytes utilizados en el arreglo cadena[100] = " << sizeof(cadena) << endl; }//Fin de main()

Page 16: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-16

Ejemplo 18.10 El siguiente programa, SUMAVALORES.CPP, suma los valores contenidos en el arreglo de enteros de doce elementos.

Ejemplo 18.11 El siguiente programa, FRECUENCIA.CPP, se vale de arreglos para resumir los resultados de la información recolectada en una encuesta. Considere el planteamiento del problema: Se pidió a cuarenta estudiantes que calificaran la calidad de la comida de la cafetería estudiantil en escala del 1 al 10 (1 significa terrible y 10 excelente) Ponga las cuarenta respuestas en un arreglo de enteros y resuma el resultado de la encuesta (cuantos calificaron por cada valor de la escala)

/* El siguiente programa: SUMAVALORES.CPP, Calcula la suma de los elementos de un arreglo. */ #include <iostream.h> //Para cout y cin void main(void) { const int TAMANO_ARREGLO = 12; int arreglo[TAMANO_ARREGLO] = {1, 3, 5, 4, 7, 2, 99, 16, 45, 67, 89, 45}; int total = 0; for(int i = 0; i < TAMANO_ARREGLO; i++) total += arreglo[i]; cout << "El total de los elementos del arreglo es: " << total << endl; }//Fin de main()

/* El siguiente programa: FRECUENCIA.CPP, ilustra una encuesta realizada entre estudiantes. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() void main(void) { const int TAMANO_RESPUESTA = 40, TAMANO_FRECUENCIA = 11; int respuestas[TAMANO_RESPUESTA] = {1, 2, 6, 4, 8, 5, 9, 7, 8, 10, 1, 6, 3, 8, 6, 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7, 5, 6, 6, 5, 6, 7, 5, 6, 4, 8, 6, 8, 10}; int frecuencia[TAMANO_FRECUENCIA] = {0}; for(int numeroRespuestas = 0; numeroRespuestas < TAMANO_RESPUESTA; numeroRespuestas++) ++frecuencia[respuestas[numeroRespuestas]]; cout << "Calidad" << setw(17) << "Frecuencia" << endl; for(int calidad = 1; calidad < TAMANO_FRECUENCIA; calidad++) cout << setw(6) << calidad << setw(17) << frecuencia[calidad] << endl; }//Fin de main()

Page 17: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-17

Ejemplo 18.12

El siguiente programa, PROMEDIO1.CPP, calcula el promedio de los elementos de un arreglo. Los elementos del arreglo son preasignados.

Ejemplo 18.13

El siguiente programa, PROMEDIO2.CPP, calcula el promedio de los elementos de un arreglo. Los valores del arreglo son solicitados por el teclado.

/* El siguiente programa: PROMEDIO1.CPP, calcula el valor promedio de un arreglo. Los elementos del arreglo están preasignados. */ #include <iostream.h> //Para cout y cin const int TAMANO_ARREGLO = 10; void main(void)

{ double arreglo[TAMANO_ARREGLO] = { 12.2, 45.4, 67.2, 12.2, 34.6, 87.4,

83.6, 12.3, 14.8, 55.5 }; double suma = 0; for (int ix = 0; ix < TAMANO_ARREGLO; ++ix) {

suma += arreglo[ix]; cout << "arreglo[" << ix << "]: " << arreglo[ix] << endl;

}//Fin de for cout << endl << "Promedio: " << suma / TAMANO_ARREGLO << endl;

}//Fin de main()

/* El siguiente programa: PROMEDIO2.CPP, calcula el valor promedio del arreglo que es inicializado desde el teclado */ #include <iostream.h> //Para cout y cin const int TAMANO_ARREGLO = 30; void main(void)

{ double arreglo[TAMANO_ARREGLO]; int numeroElementos; // Obtiene la cantidad de puntos de datos do

{ cout << "Introduzca el número de puntos de datos [2 a " << TAMANO_ARREGLO << "]: "; cin >> numeroElementos; cout << endl; } while (numeroElementos < 2 || numeroElementos > TAMANO_ARREGLO);

// Pide datos al usuario for (int ix = 0; ix < numeroElementos; ix++) {

cout << "arreglo[" << ix << "]: "; cin >> arreglo[ix];

}//Fin de for

Page 18: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-18

Ejemplo 18.14

Nuestro siguiente programa, HISTOGRAMA.CPP, lee los números de un arreglo llamado arreglo y traza la información en forma de un gráfico de barras o histograma; cada número se imprime, seguido de una barra consistente en la misma cantidad de asteriscos.

Ejemplo 18.15

El siguiente programa, FRECUENCIA2.CPP, ilustra una nueva forma de codificar el problema del lanzamiento de un dado 6000 veces, determinando la frecuencia de cada cara.

/* El siguiente programa: HISTOGRAMA.CPP, imprime un histograma. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() void main(void) { const int TAMANO_ARREGLO = 10; int arreglo[TAMANO_ARREGLO] = {19, 3, 15, 7, 11, 9, 13, 5, 17, 1}; cout << "Elemento" << setw(13) << "Valor" << setw(17) << "Histograma" << endl; for(int i = 0; i < TAMANO_ARREGLO; i++) { cout << setw(7) << i << setw(13) << arreglo[i] << setw(9); for(int j = 0; j < arreglo[i]; j++) //Imprime una barra cout << '*'; cout << endl; }//Fin del for }//Fin de main()

// Ahora obtiene el promedio double suma = 0; for (int ix = 0; ix < numeroElementos; ++ix) suma += arreglo[ix]; cout << endl << "Promedio: " << suma / numeroElementos << endl;

}//Fin de main()

/* El siguiente programa: FRECUENCIA2.CPP, ilustra la frecuencia de cada cara de un dado al lanzarse 6000 veces. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() #include <stdlib.h> //Para srand() y rand() #include <time.h> //Para time()

Page 19: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-19

Ejemplo 18.16

El siguiente programa, CARACTERES.CPP, muestra la inicialización de un arreglo de caracteres con una literal de cadena; la lectura de una cadena para dejarla en un arreglo de caracteres; la impresión de un arreglo de caracteres como cadena y el acceso a los caracteres individuales de una cadena.

Ejemplo 18.17

El siguiente programa. ESTAUTO.CPP, ilustra el uso de dos arreglos, uno static y el otro automático; en ambos casos se analiza su contenido visualizando en la pantalla.

void main(void) { const int TAMANO_ARREGLO = 7; int cara, frecuencia[TAMANO_ARREGLO] = {0}; srand(time(0)); for(int tirada = 1; tirada <= 6000; tirada++) ++frecuencia[1 + rand() % 6]; //Reemplaza el switch cout << "Cara" << setw(13) << "Frecuencia" << endl; //Ignora el elemento 0 del arreglo frecuencia[] for(cara = 1; cara < TAMANO_ARREGLO; cara++) cout << setw(4) << cara << setw(13) << frecuencia[cara] << endl; }//Fin de main()

/* El siguiente programa: CARACTERES.CPP, ilustra el manejo de arreglos de caracteres como cadenas. */ #include <iostream.h> //Para cout y cin void main(void) { char cadena1[20], cadena2[] = "Cadena literal de caracteres"; cout << "Introduzca una cadena de caracteres: "; cin >> cadena1; //Para estudiar lo que ocurre, escriba: Hola allí. cout << "La cadena1 es: " << cadena1 << "\nLa cadena2 es: " << cadena2 << "\nLa cadena1 con espacios entre caracteres es: "; for(int i = 0; cadena1[i] != '\0'; i++) cout << cadena1[i] << ' '; cin >> cadena1; //Lee allí cout << "\nLa cadena es: " << cadena1 << endl; }//Fin de main()

Page 20: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-20

/* El siguiente programa: ESTAUTO.CPP, ilustra el uso de arreglos estáticos y automáticos. */ #include <iostream.h> //Para cout y cin void arregloEstatico(void); void arregloAutomatico(void); void main(void) { cout << "Primera llamada a cada función:\n"; arregloEstatico(); arregloAutomatico(); cout << "\n\nSegunda llamada a cada función:\n"; arregloEstatico(); arregloAutomatico(); cout << endl; }//Fin de main() //Función que muestra un arreglo estático local void arregloEstatico(void) { static int arreglo1[3]; int i; cout << "\nValores al entrar a arregloEstatico():\n"; for(i = 0; i < 3; i++) cout << "arreglo1[" << i << "] = " << arreglo1[i] << " "; cout << "\nValores al salir de arregloEstatico():\n"; for(i = 0; i < 3; i++) cout << "arreglo1[" << i << "] = " << (arreglo1[i] += 5) << " "; }//Fin de arregloEstatico() void arregloAutomatico(void) { int i, arreglo2[3] = {1, 2, 3}; cout << "\n\nValores al entrar a arregloAutomatico():\n"; for(i = 0; i < 3; i++) cout << "arreglo2[" << i << "] = " << arreglo2[i] << " "; cout << "\nValores al salir de arregloAutomatico():\n"; for(i = 0; i < 3; i++) cout << "arreglo2[" << i << "] = " << (arreglo2[i] += 5) << " "; }//Fin de arregloAutomatico()

Page 21: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-21

PASO DE ARREGLOS Y ELEMENTOS DE ARREGLOS A LAS FUNCIONES

Es posible pasar un arreglo completo a una función o pasar sólo elementos de un arreglo a una función. Lo importante de recordar es que para pasar un arreglo completo, deberá pasar la dirección del arreglo. En C y C++, el nombre del arreglo es la dirección del primer elemento (índice[0]) del arreglo. Empecemos viendo el encabezado de la función. Éste es el prototipo para pasar un arreglo unidimensional a una función:

void raro(char arreglo[TAMANO_ARREGLO]);

o lo que es más correcto

void raro(char arreglo[], int tamanoArreglo);

Observe el prototipo, vea que la función no regresa un valor. Hay un parámetro de

caracteres llamado arreglo[TAMANO_ARREGLO] La única serie de corchetes después del identificador del parámetro implica que el parámetro es un arreglo unidimensional con un tamaño TAMANO_ARREGLO. Cuando se pasan los arreglos a una función, la función deberá saber qué tan grande es el arreglo que aceptará. Ahora, el identificador del arreglo hace referencia a la dirección del arreglo, así el arreglo pasa por referencia a la función. De esta manera, cualquier operación en el arreglo dentro de la función afectará el contenido original del arreglo en el programa llamador. Asimismo, dado que el parámetro es la dirección del arreglo, no se requiere ningún símbolo ampersand, (&), para pasar el arreglo por referencia. De hecho, el uso de un símbolo ampersand antes del parámetro del arreglo originará un error de compilación.

Después, para llamar esta función y pasar el arreglo, simplemente utilice el siguiente

enunciado:

raro(nombreArreglo);

o lo que es más correcto

raro(nombreArreglo, tamanoArreglo);

Desde luego, esta llamada supone que nombreArreglo es la denominación del arreglo en

el programa llamador. (Recuerde que el identificador del argumento real y el identificador del parámetro formal pueden ser diferentes.) La llamada a nombreArreglo da referencia de la dirección del arreglo, de manera que, la dirección del arreglo pasa a la función más que una copia del arreglo. Así, cualquier operación en el arreglo dentro de la función afectará los elementos originales del arreglo. Un programa completo es como sigue:

Ejemplo 18.18

/* El siguiente programa: PASARRE.CPP, ilustra como se pasa un arreglo a una función. */ #include<iostream.h> // Para cin y cout

// DECLARA EL TAMAÑO DEL ARREGLO const int TAMANO_ARREGLO = 3;

Page 22: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-22

El arreglo nombre[] se define como un arreglo de caracteres y se llena en main() usando la asignación directa, con los caracteres 'I', 'B', 'M'. Los caracteres que se almacenan en el arreglo se muestran en la pantalla utilizando un ciclo for. Luego, se llama a la función raro() usando el nombre del arreglo, nombre, como su argumento. Con esto se pasa la dirección del arreglo a la función raro(), en donde se decrementa cada uno de los elementos del arreglo. ¿Qué se supone que verá el usuario en la pantalla después de ejecutar el programa? Bien, las cosas parecen raras porque:

El contenido de nombre[] antes que raro() es: IBM El contenido de nombre[] después de raro() es: HAL

Lo interesante aquí es que la operación decremento dentro de la función afecta a los elementos originales del arreglo. De esta manera, la palabra IBM se convirtió en la palabra HAL. ¿Hay algo raro aquí? Recuerde que HAL fue la computadora con inteligencia artificial en el libro y la película 2001: Odisea en el espacio. ¿Hay un mensaje aquí o sólo es una coincidencia? Tendrá que preguntar al autor, Arthur Clarke, para descubrirlo.

Por último, no es posible pasar el arreglo completo por valor a una función. Si no quiere que las operaciones de la función afecten a los elementos del arreglo, deberá pasar el arreglo a la función, hacer una copia local temporal del arreglo dentro de la función y después operar sobre este arreglo temporal.

Por otra parte, es posible pasar por valor los elementos individuales del arreglo a una función. Vea la siguiente función prototipo:

// FUNCIÓN PROTOTIPO void raro(char arreglo[TAMANO_ARREGLO]);

void main(void)

{ //DEFINE UN ARREGLO DE CARACTERES char nombre[TAMANO_ARREGLO];

//LLENE EL ARREGLO nombre CON CARACTERES nombre[0] ='I'; nombre[1] ='B'; nombre[2] ='M';

//MUESTRA EL ARREGLO nombre cout << “El contenido de nombre[] antes de raro() es: ”; for (int i = 0; i < TAMANO_ARREGLO; ++i)

cout << nombre[i];

//LLAMA A LA FUNCIÓN raro() raro(nombre); //MUESTRA EL ARREGLO nombre cout << “\n\nEl contenido de nombre[] después de raro() es: ”; for (int i = 0; i < TAMANO_ARREGLO; ++i)

cout << nombre[i]; } // Fin de main()

// ESTA FUNCIÓN DECREMENTA CADA UNO DE LOS ELEMENTOS DEL ARREGLO void raro(char arreglo[TAMANO_ARREGLO])

{ for (int i = 0; i < TAMANO_ARREGLO; ++i)

--arreglo[i]; } // Fin de raro()

Page 23: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-23

void pasaPorValor(int elementoArreglo);

El encabezado dice que la función no regresa ningún valor y espera recibir un valor entero desde el programa llamador. Suponga que se hace un llamado a la función como sigue:

pasaPorValor(registros[0]);

Observe que el argumento real en la llamada de la función es registros[0] Esto genera una copia del elemento almacenado en el índice [0] en el arreglo registros[] que será transferido a la función por valor. Como resultado, cualquier operación en este elemento dentro de la función no afectará el valor del elemento en el arreglo registros[] original. Si quiere que el elemento manifieste algún cambio dentro de la función, deberá pasarla por referencia usando el símbolo ampersand en la función prototipo, como ésta:

void pasaPorReferencia(int &elementoArreglo);

Ahora, cualquier llamada a la función pasará la dirección del elemento a la función, pasando de esta manera el elemento por referencia.

Ejemplo 18.19

El siguiente programa muestra cómo los elementos de un arreglo pasan por valor o por referencia.

/* El siguiente programa: PASVALREF.CPP, ilustra el uso de paso de elementos de un arreglo por valor y por referencia. */ #include <iostream.h> // Para cin y cout

//ENUNCIA EL TAMAÑO DEL ARREGLO const int TAMANO_ARREGLO = 3;

//FUNCIONES PROTOTIPO void pasaPorValor(int elementoArreglo); void pasaPorReferencia(int &elementoArreglo); void main(void) {

int registros[TAMANO_ARREGLO];

registros[0] = 10; registros[1] = 20; registros[2] = 30;

cout << “El valor del elemento en registros[0] antes de pasaPorValor es : ”

<< registros[0] << endl;

pasaPorValor(registros[0]);

cout << “El valor del elemento en registros[0] después de pasaPorValor es : ” << registros[0] << endl;

cout << "El valor del elemento en registros[0] antes de pasaPorReferencia es: ” << registros[0] << endl;

pasaPorReferencia(registros[0]);

cout << “Elemento en registros[0] después de pasaPorReferencia es : ” << registros[0] << endl;

} // FINAL DE main()

Page 24: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-24

La salida que produce el programa manifiesta el efecto de las dos funciones en el elemento del arreglo.

El valor del elemento en registro[0] antes de pasaPorValor() es : 10 El valor del elemento en registro[0] después de pasaPorValor() es : 10 El valor del elemento en registro[0] antes de pasaPorReferencia() : 10 El valor del elemento en registro[0] después de pasaPorReferencia(): 11

Estudie los dos programas siguientes para asegurarse que comprende cómo pasan a las funciones los arreglos completos y los elementos individuales del arreglo. Ejemplo 18.20

El siguiente programa, PASOARRE2.CPP, muestra la diferencia entre pasar un arreglo completo y pasar un elemento del arreglo.

void pasaPorValor(int elementoArreglo) {

++elementoArreglo; } // Final de pasaPorValor()

void pasaPorReferencia(int &elementoArreglo)

{ ++elementoArreglo;

} // Final de pasaPorReferencia()

/* El siguiente programa: PASOARRE2.CPP, pasa un arreglo y elementos del arreglo a funciones. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() void modificarArreglo(int[], int); void modificarElemento(int); void main(void) { const int TAMANO_ARREGLO = 5; int i, arreglo[TAMANO_ARREGLO] = {0, 1, 2, 3, 4}; cout << "Efectos de pasar un arreglo completo mediante llamada por referencia:" << "\n\nLos valores del arreglo original son:\n"; for(i = 0; i < TAMANO_ARREGLO; i++) cout << setw(3) << arreglo[i]; cout << endl; //Se pasa el arreglo mediante llamada por referencia modificarArreglo(arreglo, TAMANO_ARREGLO); cout << "Los valores del arreglo modificado son:\n"; for(i = 0; i < TAMANO_ARREGLO; i++) cout << setw(3) << arreglo[i];

Page 25: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-25

Ejemplo 18.21 El siguiente programa, ARRFUNCT.CPP, ilustra el uso de paso de un arreglo a una función. Ejemplo 18.22

El siguiente programa, ARRPARAM.CPP, pasa tres arreglos diferentes (de diferentes tamaños) a la función mostrarValores()

cout << "\n\n\n" << "Efectos de pasar un elemento del arreglo mediante llamada por valor:" << "\n\nEl valor de arreglo[3] es " << arreglo[3] << '\n'; modificarElemento(arreglo[3]); cout << "El valor de arreglo[3] es " << arreglo[3] << endl; }//Fin de main() void modificarArreglo(int b[], int tamanoArreglo) { for(int j = 0; j < tamanoArreglo; j++) b[j] *= 2; }//Fin de modificarArreglo() void modificarElemento(int e) { cout << "El valor en modificarElemento() es " << (e *= 2) << endl; }//Fin de modificarElemento()

/* El siguiente programa: ARRFUNCT.CPP, utiliza la función mostrarArreglo(), para visualizar los valores de un arreglo. */ #include <iostream.h> //Para cout y cin void mostrarArreglo(int valores[], int numeroElementos) { int i; for (i = 0; i < numeroElementos; i++) cout << "valores[" << i << "] = " << valores[i] << endl; }//Fin de mostrarArreglo() void main(void) { int calificaciones[5] = {70, 80, 90, 100, 90}; mostrarArreglo(calificaciones, 5); }//Fin de main()

Page 26: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-26

Ejemplo 18.23

El siguiente programa, MINMAX.CPP, determina los valores máximo y mínimo de un arreglo.

/* El siguiente programa: ARRPARAM.CPP, pasa tres arreglos diferentes (de diferentes tamaños) a la función mostrarValores(). */ #include <iostream.h> //Para cout y cin void mostrarValores(int valores[], int numeroElementos) { int i; cout << endl << "Número de elementos visualizados: " << numeroElementos << endl << endl; for (i = 0; i < numeroElementos; i++) cout << "valores[" << i << "] = " << valores[i] << endl; }//Fin de mostrarValores() void main(void) { int calificaciones[5] = {70, 80, 90, 100, 90}; int contador[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int pequeno[2] = {-33, -44}; mostrarValores(calificaciones, 5); mostrarValores(contador, 10); mostrarValores(pequeno, 2); }//Fin de main()

/* El siguiente programa: MINMAX.CPP, determina los elementos más grandes y más pequeños de un arreglo. */ #include <iostream.h> //Para cout y cin const int MIN = 2; const int MAX = 10; int obtenNumPuntos(int menor, int mayor) { int numPuntos; do { cout << "Introduzca el número de puntos de datos [" << menor << " a " << mayor << "]: "; cin >> numPuntos; } while (numPuntos < menor || numPuntos > mayor); return numPuntos; }//Fin de obtenNumPuntos()

Page 27: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-27

Ejemplo 18.24

Escriba un programa que use un arreglo para almacenar un máximo de 25 calificaciones de exámenes y calcule el promedio. Use una función para llenar el arreglo con las calificaciones, una segunda función para calcular el promedio y una tercera función para mostrar todas las calificaciones junto con el promedio calculado.

Solución

Comencemos con un verdadero estilo estructurado y desarrollemos las interfaces paras las funciones requeridas. Llamaremos a las tres funciones obtenerCalificaciones(), promedio() y mostrarResultados() La función obtenerCalificaciones() deberá obtener del usuario los registros de las calificaciones y las colocará en un arreglo. De esta manera, la función deberá aceptar la estructura del arreglo y regresará el arreglo que contenga las calificaciones de los exámenes. Esto da origen a la siguiente descripción de la interfaz de la función:

int buscaMinimo(int arreglo[], int tamano) { int pequeno = arreglo[0]; for (int i = 1; i < tamano; i++) if (arreglo[i] < pequeno) pequeno = arreglo[i]; return pequeno; }//Fin de buscaMinimo() int buscaMaximo(int arreglo[], int tamano) { int grande = arreglo[0]; for (int i = 1; i < tamano; i++) if (arreglo[i] > grande) grande = arreglo[i]; return grande; }//Fin de buscaMaximo() void main(void) { int arreglo[MAX], numElementos; numElementos = obtenNumPuntos(MIN, MAX); // Pide datos al usuario for (int i = 0; i < numElementos; i++) { cout << "arreglo[" << i << "]: "; cin >> arreglo[i]; }//Fin de for cout << "El valor más pequeño en el arreglo es " << buscaMinimo(arreglo, numElementos) << endl << "El valor más grande en el arreglo es " << buscaMaximo(arreglo, numElementos) << endl; }//Fin de main()

Page 28: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-28

Función obtenerCalificaciones(): Obtiene del usuario los registros de los exámenes y los coloca en un arreglo.

Acepta: El número de calificaciones y el arreglo de las

calificaciones con un tamaño máximo de MAX. Regresa: El número de calificaciones y el arreglo que llena el

usuario con las calificaciones.

Es necesario que la interfaz de la función acepte y regrese la estructura del arreglo. Supongamos que las calificaciones son valores decimales y, por lo tanto, se necesita un arreglo de punto flotante. La función prototipo será:

void obtenerCalificaciones(int &numero, float calificaciones[MAX])

A partir de esto, es fácil la escritura de la función. Emplearemos un enunciado cin dentro de un ciclo for en el cuerpo de la función para llenar el arreglo con las calificaciones. A continuación la función completa:

void obtenerCalificaciones(int &numero, float calificaciones[MAX])

{ cout << “¿Cuántas calificaciones quiere promediar?” ; cin >> numero; cout << “Escriba cada calificación y presione ENTER después de cada entrada.” << endl; for(int i = 0; i < numero; ++i)

{ cout << “Escriba el registro #” << i + 1 << “: ”; cin >> calificaciones[i];

} // Fin del for } // Fin de obtenerCalificaciones

Dentro del cuerpo de la función se pide al usuario que escriba la cantidad de calificaciones y cada calificación individual. Las calificaciones se ingresan y se colocan en el arreglo por medio de un enunciado cin dentro de un ciclo for. Observe que los valores escritos por el usuario para numero se emplean para terminar el ciclo for. También observe que numero es un parámetro por referencia. Como resultado, éste regresará al programa llamador para que otras funciones lo utilicen. Después se escribirá una función llamada promedio para obtener el promedio de las calificaciones que se encuentran en el arreglo. Esta función deberá aceptar el arreglo, obtendrá las calificaciones y regresará un valor sencillo de punto flotante que es el promedio de las calificaciones. Por lo tanto, la descripción de la interfaz de la función es como sigue:

Función promedio(): Calcula el promedio de las calificaciones. Acepta: El número de las calificaciones que se van a promediar y el

arreglo de las calificaciones. Regresa: Un solo valor que es el promedio de las calificaciones.

Esta vez, el arreglo pasará a la función y la función regresará un solo valor. De esta manera, la función prototipo se convierte en:

float promedio(int numero, float calificaciones[MAX]);

Page 29: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-29

El cuerpo de la función simplemente suma todas las calificaciones del arreglo y los divide entre su número. A continuación se ve la función completa: float promedio(int numero, float calificaciones[MAX])

{ float total = 0.0; for (int i = 0; i < numero; ++i)

total +=calificaciones[i];

return total/numero; } // Fin de promedio()

Hay dos variables locales de función definidas: total e i. total actuará como una variable temporal para acumular la suma de las calificaciones y la variable i es la variable contador del ciclo. Primero, la variable total se inicializa a 0. Después se usa el ciclo para obtener los elementos del arreglo, uno por uno y adicionarlos a total. Observe que el contador de ciclo (i) actúa como el índice del arreglo dentro del ciclo. De esta manera se extraen los elementos del arreglo, desde el índice [0] hasta [numero – 1], en forma secuencial con cada ciclo de iteración y se adicionan a total. La última calificación se localiza en el índice [numero – 1]. Una vez que el ciclo calcula la suma total de todas las calificaciones, se usa un enunciado return para regresar el promedio calculado. Por último, la función mostrarResultado() mostrará las calificaciones individuales obtenidas del usuario junto con sus promedios. Para hacer esto, el arreglo deberá pasar a la función para obtener las calificaciones. Aquí está la descripción de la función:

Función mostrarResultado(): Muestra las calificaciones individuales y su

promedio. Acepta: El número de calificaciones para mostrarlas en

pantalla y el arreglo de las calificaciones. Regresa: Nada.

Para mostrar el promedio, sólo se llamará a la función promedio() dentro de esta función como parte de un enunciado cout. La función completa entonces se convierte en: void mostrarResultado(int numero, float calificaciones[MAX])

{ cout << endl << endl; cout << “Muestra calificaciones” << endl; cout << “---------------------------” << endl; cout.setf(ios::fixed); cout.precision(2); for(int i = 0; i < numero; ++i)

cout << calificaciones[i] << endl; cout << “\nEl promedio de los registros anteriores es: ” << promedio(calificaciones) << endl;

} // Fin de mostrarResultados()

De nuevo se emplea un ciclo for para mostrar las calificaciones individuales. Observe cómo la función promedio() se llama al final del enunciado cout para calcular el promedio de las calificaciones.

Ejemplo 18.25 Ahora, reuniendo todo, se obtiene el siguiente programa: ARREGLOS.CPP.

Page 30: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-30

/* El siguiente programa: ARREGLOS.CPP muestra el uso de los arreglos. */ /******************************************************************************* Este programa calculará un promedio de calificaciones desde registros escritos por el usuario en un arreglo. *******************************************************************************/ #include <iostream.h> // Para cin y cout // Constante global const int MAX = 25; // Número máximo de registros. // Funciones prototipo void obtenerCalificaciones(int &numero, float calificaciones[MAX]); float promedio(int numero, float registros[MAX]); void mostrarResultados(int numero, float calificaciones[MAX]); void main(void) { // Define la variable numero y el arreglo int numero = 0; // Número de calificaciones float calificaciones[MAX]; // Definición del arreglo // LLama a las funciones para obtener las calificaciones y mostrar resultados obtenerCalificaciones(numero, calificaciones); mostrarResultados(numero, calificaciones); } // Fin de main() /******************************************************************************* Esta función obtiene los registros del usuario y los coloca en el arreglo calificaciones. *******************************************************************************/ void obtenerCalificaciones(int &numero, float calificaciones[MAX]) { cout << "¿Cuántas calificaciones quiere promediar?" << endl; cin >> numero; cout << "Escriba cada calificacion y presione ENTER después de cada entrada" << endl; for(int i = 0; i < numero; ++i) { cout << "Escriba la calificación #" << (i+1) << ": "; cin >> calificaciones[i]; }// Fin del for } // Fin de obtenerCalificaciones() /******************************************************************************* Esta función calculará el promedio de los registros en el arreglo. *******************************************************************************/ float promedio(int numero, float calificaciones[MAX]) { float total = 0.0; for(int i = 0; i < numero; ++i) total += calificaciones[i]; return total/numero; } // Fin de promedio()

Page 31: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-31

Como puede ver, primero se enuncia una constante global (MAX) Esta será el número máximo de elementos del arreglo. Después de definir la constante global, se listan las tres funciones prototipo. Ahora, mire la sección de enunciados de main() ¿Se sorprende de su simplicidad? La función main() es relativamente corta, porque todo el trabajo se hace en las otras funciones. ¡Esto es lo fascinante de la programación estructurada! Todo lo que hace main() es llamar a las otras dos funciones en el orden que se necesitan. Observe que al principio de main() se defina una variable denominada numero para saber el número de las calificaciones escritas por el usuario. Esta variable pasará a las funciones y desde éstas cuando se necesiten. Después, el arreglo, de nombre calificaciones[], se define como un arreglo de MAX, o 25 elementos de punto flotante. El nombre del arreglo es calificaciones, por lo tanto se utilizará este identificador cuando se quiera tener acceso al mismo. Después que se define el arreglo, se llama a la función obtenerCalificaciones() seguida por la función mostrarResultados() Observe que, en ambos casos, numero y el arreglo calificaciones pasan a la función por medio de una lista de argumentos. Primero se llama a la función obtenerCalificaciones() para obtener del usuario las calificaciones e insertarlas en el arreglo. Después se llama a la función mostrarResultados() De nuevo el número de calificaciones, numero, y el arreglo de calificaciones, calificaciones, pasan a la función por medio de una lista de argumentos en la función llamadora. Esta función muestra los registros de las calificaciones desde el arreglo y llama a la función promedio() para calcular el promedio de las calificaciones.

ORDENAMIENTO DE ARREGLOS

El ordenamiento de información (es decir, la colocación de los datos en algún orden particular, por ejemplo, ascendente o descendente) es una de las aplicaciones más importantes en computación. Un banco ordena todos los cheques por número de cuenta para poder preparar los estados de cuenta al cierre del mes. Las compañías telefónicas ordenan sus listas de cuentas por apellido y luego por nombre, para hacer más fácil la localización de los números telefónicos. Prácticamente todas las organizaciones deben ordenar información y, en muchos casos volúmenes masivos de datos. El ordenamiento de datos es un problema interesante que ha provocado algunos de los esfuerzos de investigación más intensos en las ciencias de la computación.

/******************************************************************************* Esta función muestra las calificaciones del arreglo y el promedio final. *******************************************************************************/ void mostrarResultados(int numero, float calificaciones[MAX]) { cout << endl << endl; cout << "Muestra calificaciones" << endl; cout << "----------------------" << endl; cout.setf(ios::fixed); cout.precision(2); for(int i = 0; i < numero; ++i) cout << calificaciones[i] << endl; cout << "\nEl promedio de los registros anteriores es: " << promedio(numero,calificaciones) << endl; } // Fin de mostrarResultados()

EXAMEN BREVE 37

Page 32: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-32

Ejemplo 18.26 El siguiente programa, BURBUJA1.CPP, ordena los valores del arreglo de diez elementos arreglo en orden ascendente. La técnica de la que nos valemos se llama ordenamiento de burbuja u ordenamiento por hundimiento(dependiendo si se ordena en orden descendente o ascendente), pues los valores más pequeños gradualmente burbujean hacia la parte alta del arreglo como las burbujas de aire que ascienden en el agua, mientras que los valores más grandes se hunden al fondo del arreglo. La técnica es pasar varias veces por el arreglo. En cada pasada, se comparan pares sucesivos de elementos. Si uno de los pares está en orden ascendente (o son idénticos los valores), se queda tal cual. Si está en orden descendente, se intercambian sus valores en el arreglo. Primero el programa compara arreglo[0] con arreglo[1], luego arreglo[1] con arreglo[2], luego arreglo[2] con arreglo[3], y así hasta que completa la pasada comparando arreglo[8] con arreglo[9] Aunque hay 10 elementos, sólo se efectúan nueve comparaciones. Debido a la manera en que se realizan las comparaciones sucesivas, un valor grande puede moverse hacia abajo del arreglo muchas posiciones con una sola pasada, pero un valor pequeño se moverá hacia arriba una sola posición. Durante la primera pasada, se garantiza que el valor mayor se hunda hasta arreglo[9] En la novena pasada, el noveno valor mayor se hunde a arreglo[1] Esto deja en arreglo[0] el valor más pequeño, por lo que sólo se necesitan nueve pasadas para ordenar un arreglo de 10 elementos.

/* El siguiente programa: BURBUJA1.CPP, ordena los valores de un arreglo en orden ascendente utilizando el método de la burbuja. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() void main(void) { const int TAMANO_ARREGLO = 10; int arreglo[TAMANO_ARREGLO] = {2, 6, 4, 8, 10, 12, 89, 68, 45, 37}; int i, temporal; cout << "Datos en el orden original\n"; for(i = 0; i < TAMANO_ARREGLO; i++) cout << setw(4) << arreglo[i]; for(int pasada = 0; pasada < TAMANO_ARREGLO - 1; pasada++) for(i = 0; i < TAMANO_ARREGLO - 1; i++) if(arreglo[i] > arreglo[i+1]) { temporal = arreglo[i]; arreglo[i] = arreglo[i+1]; arreglo[i+1] = temporal; }//Fin de if cout << "\nDatos en orden ascendente\n"; for(i = 0; i < TAMANO_ARREGLO; i++) cout << setw(4) << arreglo[i]; cout << endl; }//Fin de main()

Page 33: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-33

Ejemplo 18.27

El problema anterior puede mejorarse solicitando el número de datos a ordenar, llamaremos a este programa, BURBUJA2.CPP. Es importante que observe algunos cambios que hicimos en la función clasifBurbuja()

/* El siguiente programa: BURBUJA2.CPP, ordena arreglos usando el método de ordenamiento de la burbuja. */ #include <iostream.h> //Para cout y cin const int MIN = 2; const int MAX = 10; int obtenNumPuntos(int minimo, int maximo) { int numPuntos; do { cout << "Introduzca el número de puntos de datos [" << minimo << " a " << maximo << "]: "; cin >> numPuntos; } while (numPuntos < minimo || numPuntos > maximo); return numPuntos; }//Fin de obtenNumPuntos() void entradaArreglo(int intArr[], int num) { // Pide datos al usuario for (int i = 0; i < num; i++) { cout << "arreglo[" << i << "]: "; cin >> intArr[i]; }//Fin de for }//Fin de entradaArreglo() void mostrarArreglo(int intArr[], int num) { for (int i = 0; i < num; i++) { cout.width(5); cout << intArr[i] << " "; }//Fin de for cout << endl; }//Fin de mostrarArreglo() void clasifBurbuja(int intArr[], int num) { for (int i = 1; i < num; ++i) for (int j = 0; j <= i; ++j) if (intArr[i] < intArr[j])

Page 34: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-34

Ejemplo 18.28

El problema anterior, puede variarse una vez más, generando valores aleatorios. Llamemos a este problema, BURBUJA3.CPP

{ int temp = intArr[i]; intArr[i] = intArr[j]; intArr[j] = temp; }//Fin de for }//Fin de clasifBurbuja() void main(void) { int arr[MAX]; int numElementos; numElementos = obtenNumPuntos(MIN, MAX); entradaArreglo(arr, numElementos); cout << "El arreglo desordenado es:" << endl; mostrarArreglo(arr, numElementos); clasifBurbuja(arr, numElementos); cout << endl << "El arreglo ordenado es:" << endl; mostrarArreglo(arr, numElementos); }//Fin de main()

/* El siguiente programa: BURBUJA3.CPP, utiliza el método de la burbuja para ordenar un arreglo que contiene 30 valores creados en forma aleatoria. */ #include <iostream.h> //Para cout y cin #include <stdlib.h> //Para rand() void metodoBurbuja(int arreglo[], int tamano) { int temp, i, j; for (i = 0; i < tamano; i++) for (j = 0; j < tamano; j++) if (arreglo[i] < arreglo[j]) { temp = arreglo[i]; arreglo[i] = arreglo[j]; arreglo[j] = temp; }//Fin de if }//Fin de metodoBlurbuja() void main(void) { int valores[30], i; for (i = 0; i < 30; i++) valores[i] = rand() % 100;

Page 35: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-35

Ejemplo 18.29

El método de la burbuja, puede mejorarse disminuyendo el número de elementos en cada pasada, a este método se le conoce con el nombre de selección. El siguiente programa, SELECCIÓN.CPP, ilustra este método.

Ejemplo 18.30

El siguiente programa: METSHELL.CPP, utiliza el método Shell para ordenar un arreglo que contiene 50 elementos generados aleatoriamente. Estudie el programa y elabore el seudocódigo para dicho método.

metodoBurbuja(valores, 30); for (i = 0; i < 30; i++) cout << valores[i] << ' '; }//Fin de main()

/* El siguiente programa: SELECCION.CPP, utiliza el método de selección para ordenar un arreglo que contiene 30 valores creados en forma aleatoria. */ #include <iostream.h> //Para cout y cin #include <stdlib.h> //Para rand() void metodoSeleccion(int arreglo[], int tamano) { int temp, actual, j; for (actual = 0; actual < tamano; actual++) for (j = actual + 1; j < tamano; j++) if (arreglo[actual] > arreglo[j]) { temp = arreglo[actual]; arreglo[actual] = arreglo[j]; arreglo[j] = temp; }//Fin de if }//Fin de metodoSeleccion() void main(void) { int valores[30], i; for (i = 0; i < 30; i++) valores[i] = rand() % 100; metodoSeleccion(valores, 30); for (i = 0; i < 30; i++) cout << valores[i] << ' '; }//Fin de main()

Page 36: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-36

Ejemplo 18.31

El siguiente programa: RAPIDO.CPP, utiliza el método de ordenamiento rápido, para ordenar un arreglo que contiene 100 elementos generados en forma aleatoria. Estudie el método y elabore un seudocódigo de su algoritmo.

/* El siguiente programa: METSHELL.CPP, utiliza el método Shell para ordenar un arreglo que contiene 50 elementos generados aleatoriamente. */ #include <iostream.h> //Para cout y cin #include <stdlib.h> //Para rand() void ordenarShell(int arreglo[], int tamano)

{ int temp, espacio, i, banderaCambio; espacio = tamano / 2; do { do { banderaCambio = 0; for (i = 0; i < tamano - espacio; i++) if (arreglo[i] > arreglo[i + espacio]) { temp = arreglo[i]; arreglo[i] = arreglo[i + espacio]; arreglo[i + espacio] = temp; banderaCambio = 1; }//Fin de if } while (banderaCambio); } while (espacio = espacio / 2); }//Fin de ordenarShell() void main(void)

{ int valores[50], i; for(i = 0; i < 50; i++) valores[i] = rand() % 100; ordenarShell(valores, 50); for (i= 0; i < 50; i++) cout << valores[i] << ' '; }//Fin de main()

Page 37: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-37

Ejemplo 18.32 El siguiente programa, ESTADÍSTICA.CPP, muestra un ejemplo en los que se utiliza la computadora para recolectar y analizar los resultados de las encuestas y sondeos de opinión. Utiliza el arreglo respuestas, inicializado con 99 respuestas (representadas por la constante TAMANO_RESPUESTAS de una encuesta.

/* El siguiente programa: RAPIDO.CPP, utiliza el método de clasificación ordenamiento rápido, para ordenar un arreglo que contiene 100 elementos generados en forma aleatoria. */ #include <iostream.h> //Para cout y cin #include <stdlib.h> //Para rand() void ordenarRapido(int arreglo[], int primero, int ultimo) { int temp, bajo, alto, separadorLista; bajo = primero; alto = ultimo; separadorLista = arreglo[(primero + ultimo) / 2]; do

{ while (arreglo[bajo] < separadorLista) bajo++; while (arreglo[alto] > separadorLista) alto--; if (bajo <= alto) { temp = arreglo[bajo]; arreglo[bajo++] = arreglo[alto]; arreglo[alto--] = temp; }//Fin de if } while (bajo <= alto); if (primero < alto) ordenarRapido(arreglo, primero, alto); if (bajo < ultimo) ordenarRapido(arreglo, bajo, ultimo); }//Fin de ordenarRapido() void main(void) { int valores[100], i; for (i = 0; i < 100; i++) valores[i] = rand() % 100; ordenarRapido(valores, 0, 99); for (i = 0; i < 100; i++) cout << valores[i] << ' '; }//Fin de main()

Page 38: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-38

Cada una de las respuestas es un número del 1 al 9. El programa calcula la media, la mediana y la moda de los 99 valores.

/* El siguiente programa: ESTADISTICA.CPP, presenta el tema del análisis de información de una encuesta. Calcula la media, la mediana y la moda de los datos. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw(w) void media(const int [], int); void mediana(int [], int); void moda(int [], int [], int); void burbuja(int [], int); void escribirArreglo(const int[], int); void main(void) { const int TAMANO_RESPUESTAS = 99; int frecuencia[10] = {0}, respuestas[TAMANO_RESPUESTAS] = {6, 7, 8, 9, 8, 7, 8, 9, 8, 9,7, 8, 9, 5, 9, 8, 7, 8, 7, 8, 6, 7, 8, 9, 3, 9, 8, 7, 8, 7, 7, 8, 9, 8, 9, 8, 9, 7, 8, 9, 6, 7, 8, 7, 8, 7, 9, 8, 9, 2, 7, 8, 9, 8, 9, 8, 9, 7, 5, 3, 5, 6, 7, 2, 5, 3, 9, 4, 6, 4, 7, 8, 9, 6, 8, 7, 8, 9, 7, 8, 7, 4, 4, 2, 5, 3, 8, 7, 5, 6, 4, 5, 6, 1, 6, 5, 7, 8, 7}; media(respuestas, TAMANO_RESPUESTAS); mediana(respuestas, TAMANO_RESPUESTAS); moda(frecuencia, respuestas, TAMANO_RESPUESTAS); }//Fin de main() void media(const int contestacion[], int tamanoArreglo) { int total = 0; cout << "********\n Media\n********\n"; for(int j = 0; j < tamanoArreglo; j++) total += contestacion[j]; cout << "La media es el valor promedio de los datos.\n" << "La media es igual al total de todos\n" << "los datos divididos entre el número\n" << "de datos (" << tamanoArreglo << "). La media para\nesta corrida es: " << total << " / " << tamanoArreglo << " = " << setprecision(5) << (float) total / tamanoArreglo << "\n\n"; }//Fin de media() void mediana(int contestacion[], int tamano) { cout << "\n********\n Mediana\n********\n" << "El arreglo de respuestas desordenado es: ";

Page 39: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-39

escribirArreglo(contestacion, tamano); burbuja(contestacion, tamano); cout << "\n\nEl arreglo ordenado es:"; escribirArreglo(contestacion, tamano); cout << "\n\nLa mediana es el elemento " << tamano /2 << " del\narreglo ordenado de " << tamano << " elementos.\nPara esta corrida la mediana es " << contestacion[tamano /2] << "\n\n"; }//Fin de mediana void moda(int freq[], int contestacion[], int tamano) { int apariciones, masGrande = 0, valorModal = 0; cout << "\n********\n Moda\n********\n"; for(apariciones = 1; apariciones <= 9; apariciones++) freq[apariciones] = 0; for(int j = 0; j < tamano; j++) ++freq[contestacion[j]]; cout << "Respuesta" << setw(11) << "Frecuencia" << setw(19) << "Histograma\n\n" << setw(53) << "1 1 2 2\n" << setw(54) << "5 0 5 0 5\n\n"; for(apariciones = 1; apariciones <= 9; apariciones++) { cout << setw(9) << apariciones << setw(11) << freq[apariciones] << " "; if(freq[apariciones] > masGrande) { masGrande = freq[apariciones]; valorModal = apariciones; }//Fin de if for(int h = 1; h <= freq[apariciones]; h++) cout << '*'; cout << '\n'; }//Fin del for externo cout << "La moda es el valor más frecuente.\n" << "Para esta corrida la moda es " << valorModal << " que ocurrió " << masGrande << " veces." << endl; }//Fin de moda() void burbuja(int a[], int tamano) { int temporal; for(int pasada = 1; pasada < tamano; pasada++) for(int j = 0; j < tamano - 1; j++) if(a[j] > a[j + 1])

Page 40: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-40

BÚSQUEDA EN ARREGLOS: Búsqueda lineal y búsqueda binaria Con frecuencia el programador trabajará con grandes cantidades de información almacenada en arreglos. Podría ser necesario determinar si algún arreglo contiene un valor que sea igual a cierto valor clave. El proceso para encontrar un elemento particular en un arreglo se llama búsqueda. En esta sección estudiaremos dos técnicas de búsqueda: la técnica simple de búsqueda lineal y la técnica más eficiente de búsqueda binaria. La búsqueda lineal compara todos los elementos del arreglo con la clave de búsqueda. Debido a que el arreglo no está en ningún orden en particular, existe la misma posibilidad de que el valor esté en el primer elemento o en el último. Por lo tanto, para encontrar un valor en el arreglo, en promedio el programa debe comparar la clave de búsqueda con la mitad de los elementos del arreglo. Para determinar que un valor no está en el arreglo, el programa de comparar la clave de búsqueda con todos los elementos de dicho arreglo. Ejemplo 18.33

El siguiente programa BUSLINEAL.CPP, ilustra el método de búsqueda lineal.

{ temporal = a[j]; a[j] = a[j + 1]; a[j + 1] = temporal; }//Fin de if }//Fin de burbuja() void escribirArreglo(const int a[], int tamano) { for(int j = 0; j < tamano; j++) { if(j %20 == 0) cout << endl; cout << setw(2) << a[j]; }//Fin de for }//Fin de imprimirArreglo()

Page 41: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-41

El método de búsqueda lineal funciona bien con arreglos pequeños y con los no ordenados. Sin embargo, en los arreglos mayores la búsqueda lineal no es eficiente. Si el arreglo está ordenado, se puede emplear la técnica de búsqueda binaria, que es de alta velocidad. El algoritmo de búsqueda binaria elimina, tras cada comparación, la mitad de los elementos del arreglo en los que se efectúa la búsqueda. Primero localiza el elemento central del arreglo y luego lo compara con la clave de búsqueda. Si son iguales, se ha encontrado dicha clave y se devuelve el índice de ese elemento. De otro modo, el problema se reduce a buscar en una mitad del arreglo. Si la clave de búsqueda es menor que el elemento central del arreglo, se busca en la primera mitad; de otro modo, se busca en la segunda mitad. Si la clave de búsqueda no está en el elemento central del subarreglo especificado (parte del arreglo original), se repite el algoritmo en un cuarta parte del arreglo original. La búsqueda continúa hasta que la clave de búsqueda sea igual al elemento central de un subarreglo o hasta que el subarreglo consista de un elemento, el cual no es igual a la clave de búsqueda (es decir, no se encuentra la clave de búsqueda)

En el peor caso, la búsqueda en un arreglo de 1024 elementos sólo se llevará 10 comparaciones mediante la búsqueda binaria. La división repetida de 1024 entre 2 (dado que tras cada comparación se puede descartar la mitad del arreglo) nos da los valores 512, 256, 128, 64, 32, 16, 8, 4, 2, y 1. El número 1024 (210) se divide entre 2 sólo diez veces para llegar al valor 1. La división entre 2 es equivalente a una comparación en el algoritmo de búsqueda binaria. Un arreglo de 1048576 (220) elementos tarda un máximo de 20 comparaciones en encontrar la clave de búsqueda. Un arreglo de mil millones de elementos necesita un máximo de 30 comparaciones. Esta es una mejora enorme en relación con la búsqueda lineal, que requeriría comparar la clave de búsqueda con un promedio de la mitad de los elementos del arreglo. ¡Para un arreglo de mil millones de elementos, esta diferencia es entre un promedio de 500 millones de comparaciones y un máximo de 30!. La cantidad máxima de comparaciones necesarias para la

/* El siguiente programa: BUSLINEAL.CPP, ilustra el método de búsqueda lineal en un arreglo. */ #include <iostream.h> //Para cout y cin int busquedaLineal(const int[], int, int); void main(void) { const int TAMANO_ARREGLO = 100; int a[TAMANO_ARREGLO], claveBusqueda, elemento; for(int x = 0; x < TAMANO_ARREGLO; x++) //Crea algunos datos a[x] = 2 * x; cout << "Introduzca una clave entera de búsqueda: "; cin >> claveBusqueda; elemento = busquedaLineal(a, claveBusqueda, TAMANO_ARREGLO); if(elemento != -1) cout << "Se encontró el valor en el elemento " << elemento << endl; else cout << "Valor no encontrado" << endl; }//Fin de main() int busquedaLineal(const int arreglo[], int clave, int tamanoArreglo) { for(int n = 0; n < tamanoArreglo; n++) if(arreglo[n] == clave) return n; return -1; }//Fin de busquedaLineal()

Page 42: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-42

búsqueda binaria de cualquier arreglo ordenado puede determinarse encontrando la primera potencia de 2 mayor que el número de elementos del arreglo.

Ejemplo 18.34

El siguiente programa, BUSBIN.CPP, presenta la versión iterativa de la función busquedaBinaria() La función recibe cuatro argumentos: un arreglo de enteros b, un entero claveBusqueda, el índice bajo del arreglo y el índice alto del arreglo. Si la clave de búsqueda no es igual al elemento de la mitad de un subarreglo, se ajusta el índice bajo o alto para poder hacer la búsqueda en un subarreglo más pequeño. Si la clave de búsqueda es menor que el elemento central, el índice alto se establece a mitad – 1 y se continúa la búsqueda en los elementos de bajo a mitad – 1. Si la clave de búsqueda es mayor que el elemento central, el índice bajo se establece a mitad + 1 y se continúa la búsqueda en los elementos de mitad + 1 a alto. El programa emplea un arreglo de 15 elementos. La primera potencia de 2 mayor que la cantidad de elementos de este arreglo es 16 (24), por lo que se necesitan un máximo de 4 comparaciones para encontrar la clave de búsqueda. La función imprimirEncabezado() envía a la salida los índices del arreglo y la función imprimirFila() envía a la salida cada subarreglo generado durante el proceso de búsqueda binaria. El elemento central de cada subarreglo se marca con un asterisco (*), para indicar el elemento con el que se compara la clave de búsqueda.

/* El siguiente programa: BUSBIN.CPP, ilustra el uso de la búsqueda binaria en un arreglo. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() int busquedaBinaria(int[], int, int, int, int); void imprimirEncabezado(int); void imprimirFila(int[], int, int, int, int); void main(void) { const int TAMANO_ARREGLO = 15; int a[TAMANO_ARREGLO], clave, resultado; for(int i = 0; i < TAMANO_ARREGLO; i++) a[i] = 2 * i; //Crea los elementos del arreglo cout << "Introduzca un número entre 0 y 28: "; cin >> clave; imprimirEncabezado(TAMANO_ARREGLO); resultado = busquedaBinaria(a, clave, 0, TAMANO_ARREGLO - 1, TAMANO_ARREGLO); if(resultado != -1) cout << '\n' << clave << " encontrado en el elemento del arreglo número " << resultado << endl; else cout << '\n' << clave << " no encontrado" << endl; }//Fin de main() //Busqueda binaria int busquedaBinaria(int b[], int claveBusqueda, int bajo, int alto, int tamano) { int mitad; while(bajo <= alto)

Page 43: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-43

Ejemplo 18.35

Una solución ligeramente diferente al mismo problema es el siguiente programa, BINARIO.CPP, el cual utiliza una búsqueda binaria para localizar valores en el arreglo llamado contador, que contiene los valores del 1 a 100. La función busquedaBinaria() imprime mensajes que describen el proceso.

{ mitad = (bajo + alto) / 2; imprimirFila(b, bajo, mitad, alto, tamano); if(claveBusqueda == b[mitad]) //coincidencia return mitad; else if(claveBusqueda < b[mitad]) alto = mitad - 1; //Busca en la parte baja del arreglo else bajo = mitad + 1; //Busca en la parte alta del arreglo }//Fin de while return -1; //No se encontró claveBusqueda }//Fin de busquedaBinaria() //Imprime el encabezado para la salida void imprimirEncabezado(int tamano) { cout << "\nIndices:\n"; for(int i = 0; i < tamano; i++) cout << setw(3) << i << ' '; cout << '\n'; for(int i = 1; i <= 4 * tamano; i++) cout << '-'; cout << endl; }//Fin de imprimirEncabezado() //Imprime una fila de la salida, indicando la parte del arreglo que se está procesando. void imprimirFila(int b[], int bajo, int mit, int alto, int tamano) { for(int i = 0; i < tamano; i++) if(i < bajo || i > alto) cout << " "; else if(i == mit) //Marca el valor central cout << setw(3) << b[i] << '*'; else cout << setw(3) << b[i] << ' '; cout << endl; }//Fin de imprimirFila()

Page 44: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-44

/* El siguiente programa: BINARIO.CPP, utiliza una búsqueda binaria para localizar valores en el arreglo llamado contador, el cual contiene los valores de 1 a 100. La función busquedaBinaria imprime mensajes que describen el proceso. */ #include <iostream.h> //Para cout y cin int busquedaBinaria(int arreglo[], int valor, int tamano) { int encontrado = 0; int alto = tamano, bajo = 0, enmedio; enmedio = (alto + bajo) / 2; cout << endl << endl << "Buscando el número: " << valor << endl; while ((! encontrado) && (alto >= bajo)) { cout << "Bajo: " << bajo << ' ' << "Enmedio: " << enmedio << ' ' << "Alto: " << alto << endl; if (valor == arreglo[enmedio]) encontrado = 1; else if (valor < arreglo[enmedio]) alto = enmedio - 1; else bajo = enmedio + 1; enmedio = (alto + bajo) / 2; }//Fin de while return((encontrado) ? enmedio: -1); }//Fin de busquedaBinaria() void main(void) { int arreglo[100], i; for (i = 0; i < 100; i++) arreglo[i] = i; cout << "Resultado de la búsqueda: " << busquedaBinaria(arreglo, 33, 100) << endl; cout << "Resultado de la búsqueda: " << busquedaBinaria(arreglo, 75, 100) << endl; cout << "Resultado de la búsqueda: " << busquedaBinaria(arreglo, 1, 100) << endl; cout << "Resultado de la búsqueda: " << busquedaBinaria(arreglo, 1001, 100) << endl; }//Fin de main()

Page 45: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-45

SOLUCIÓN DE PROBLEMAS EN ACCIÓN: Búsqueda en un arreglo con iteración (Búsqueda secuencial)

Diversas aplicaciones requieren de un programa para buscar un determinado elemento en un arreglo. Para realizar esta tarea se usan dos algoritmos comunes que son búsqueda secuencial o serial y búsqueda binaria. Por lo común, la búsqueda secuencial se utiliza para arreglos no ordenados y la búsqueda binaria en arreglos ordenados. En el siguiente problema veremos la búsqueda secuencial; en un problema posterior veremos la búsqueda binaria.

PROBLEMA

Desarrolle una función que busque, en forma secuencial, en un arreglo de enteros, el valor de un elemento dado y que regrese el índice del elemento si éste se encuentra en el arreglo.

DEFINICIÓN DEL PROBLEMA

Debido a que se trata de una función, la definición del problema se enfocará a la interfaz de la función. Por consiguiente, se debe considerar que la función aceptará y regresará algo. Llamaremos a la función busquedaSec() A partir del enunciado del problema, la función deberá buscar en un arreglo de enteros un valor de un elemento determinado. De esta manera, la función necesita dos cosas para realizar este trabajo: (1) el arreglo y (2) el elemento que se va a buscar. Estos serán los parámetros de la función, ¿deberán ser de valor o de referencia? Bueno, la función no cambiará ni al arreglo ni al elemento a buscar, ¿es correcto? Por lo tanto, los parámetros serán por valor. Después, necesitamos determinar que va a regresar la función al programa llamador. A partir del enunciado del problema, se observa que la función necesita regresar el índice del elemento buscado, si se encuentra en el arreglo. En C++ todos los índices de los arreglos son enteros, por lo tanto la función regresará un valor entero. Pero, ¿qué pasa si el elemento que se busca no se encuentra en el arreglo? Se regresará algún valor entero que aclare esta situación. Dado que, en C++ los índices de los arreglos se encuentran en un rango de 0 a algún entero positivo finito, si el elemento no se encuentra en el arreglo, regresará el entero -1. De esta manera usaremos –1 para indicar la condición no encontrado, porque en C++ ningún índice de arreglo tiene este valor. La descripción de la interfaz de la función será:

Función busquedaSec(): Busca en un arreglo entero un valor de elemento

dado. Acepta: Un arreglo de enteros y el elemento que se busca. Regresa: El índice del arreglo del elemento, si se encuentra, o

el valor –1 si no se encuentra el elemento.

La descripción de interfaz de la función anterior proporciona toda la información necesaria para escribir la interfaz de la función, como sigue:

int busquedaSec(int arreglo[MAX], int elemento)

La interfaz indica que la función aceptará dos cosas: (1) un arreglo de elementos enteros MAX y (2) un valor entero, llamado elemento, que será el valor que se busca.

La siguiente tarea es desarrollar el algoritmo de búsqueda secuencial.

Page 46: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-46

PLANEACIÓN DE LA SOLUCIÓN

¿Qué quiere decir exactamente búsqueda secuencial? Rastreo en el arreglo en forma secuencial, de un elemento al siguiente, empezando en la primera posición del arreglo y deteniéndose al encontrar el elemento o al llegar al final del arreglo. De esta manera, el algoritmo deberá verificar el elemento almacenado en la primera posición del arreglo, después en la segunda posición del arreglo, enseguida en la tercera y así sucesivamente, hasta que encuentre el elemento o hasta que se agoten los elementos del arreglo. Desde luego, ésta es una tarea repetitiva de verificación de un elemento en el arreglo, luego moverse al siguiente elemento y verificar otra vez y así sucesivamente. Considere el siguiente algoritmo que emplea un ciclo while para realizar la operación de verificación repetitiva:

Algoritmo busquedaSec()

busquedaSec() INICIO

encuentra = falso. indice = primer índice del arreglo. while(elemento no se Encuentra) AND (Indice <= último índice del arreglo) Inicio

si (arreglo[indice] == elemento) entonces encuentra = verdadero.

sino incremente indice.

Fin.

si(encuentra == verdadero) entonces regresar indice

sino regresar –1

FIN.

Aquí, la idea es emplear una variable booleana, llamada encuentra, para indicar si el elemento se encontró durante la búsqueda. La variable encuentra se inicializa a falso y una variable de nombre indice se inicializa para el índice del primer elemento en el arreglo. Observe la verificación del ciclo while. Debido al uso de un operador AND, el ciclo continuará en tanto no se encuentre el elemento y el valor de indice sea menor o igual al último valor de índice del arreglo. Otra forma de decir esto es que el ciclo se repetirá hasta que se encuentre el elemento o el valor de indice exceda el último valor del índice del arreglo. Dentro del ciclo, el valor almacenado en la localidad [indice] se compara al valor de elemento, recibido por medio de la función. Si los dos valores son iguales, la variable booleana encuentra se establece a verdadero. De otra manera, el valor de indice se incrementa para moverse a la siguiente posición del arreglo.

Si el elemento fue encontrado o no, el ciclo termina. Si se trata del primer caso, el valor de encuentra será verdadero y el valor de indice será la posición del arreglo, o índice, en el cual se encontró el elemento. De esta manera si encuentra es verdadero, el valor indice regresa al programa llamador. Si el elemento no fue encontrado, el valor de encuentra se mantendrá en falso desde su estado inicial y se regresará el valor –1 al programa llamador.

CODIFICACIÓN DEL PROGRAMA

A continuación se escribe el código C++ que refleja el algoritmo anterior:

int busquedaSec(int a[MAX], int elemento) {

enum boolean {Falso, Verdadero}; // Define Falso = 0 y Verdadero = 1 boolean encuentra = Falso; // Inicializa encuentra a Falso int i = 0; // Variable índice del arreglo

Page 47: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-47

// Busca en el arreglo hasta encontrar o alcanzar el final del arreglo while((!encuentra) && (i < MAX))

{ if (a[i] == elemento) // Verifica el elemento del arreglo

encuentra = Verdadero; // Si es igual, establece else // encuentra a Verdadero

++i; } // Fin del while

// Si se encuentra el elemento, regresa la posición del elemento en el arreglo // Si no regresa –1 if(encuentra)

return i; else

return –1; } // Fin de busquedaSec()

No debe haber sorpresas en este código. En la parte superior de la función, verá el encabezado que es idéntico a la interfaz de la función desarrollada anteriormente. Después verá una clase de datos enumerada creada para definir Falso y Verdadero. Recuerde que los valores predeterminados para elementos de datos enumerados son enteros, empezando con 0. De esta manera, Falso se define con el valor 0 y Verdadero se define con el de 1. Esto permite la utilización de los identificadores Falso y Verdadero dentro del programa para representar los valores enteros 0 y 1 respectivamente. Además, las verificaciones booleanas se pueden hacer contra estos valores, porque C++ interpreta un 0 como un Falso lógico y 1 como un Verdadero lógico. La variable encuentra se define como un objeto de clases de datos booleanos enumerados y se establece a Falso. Utilizaremos la variable i como variable índice del arreglo. Esta variable se define como un entero y establece a 0 el primer índice del arreglo. Recuerde que los arreglos en C++ siempre empiezan con el índice 0. El ciclo while emplea el operador AND (&&) para verificar los valores de encuentra e i. El ciclo se repetirá hasta que se encuentre el elemento (encuentra) y el valor de i sea menor que el tamaño MAX del arreglo. Recuerde que cuando el tamaño del arreglo es MAX, el último índice del arreglo es MAX – 1. Por lo tanto, cuando i excede el índice máximo del arreglo, MAX – 1, el ciclo se rompe. Cuando el ciclo se rompe, el valor de encuentra se verifica. Si encuentra es Verdadero, se regresa el valor de i; si encuentra es Falso, el valor –1 se regresa para indicar que el elemento no se encontró en el arreglo.

SOLUCIÓN DE PROBLEMAS EN ACCIÓN: Como ordenar un arreglo con iteración

(Ordenación por inserción)

Ordenar un arreglo significa colocar los elementos del arreglo en orden ascendente o descendente desde el principio al final del arreglo. Hay muchos algoritmos comunes utilizados para ordenación. Hay clasificación por inserción, clasificación por burbuja, clasificación por selección, clasificación rápida, clasificación combinada y clasificación apilada, sólo por mencionar algunas. En un curso de estructura de datos, es muy probable que aprenda y analice todos estos algoritmos de ordenación. En este problema se desarrollará el algoritmo de clasificación por inserción y se codificará como una función en C++.

PROBLEMA

Desarrolle una función que se pueda llamar para ordenar un arreglo de caracteres en orden ascendente utilizando el algoritmo clasificación por inserción.

DEFINICIÓN DEL PROBLEMA

De nuevo, codificaremos el algoritmo como una función C++, así la definición del problema se enfocará en la interfaz de la función, guiándonos a la función prototipo. Llamaremos a la función clasifPorInser() Piense que necesita clasifPorInser() para hacer un trabajo. Bueno, deberá recibir un arreglo de caracteres no clasificados y regresar el mismo arreglo como un arreglo clasificado, ¿correcto? ¿Necesita algo más? No, la función no requiere datos adicionales, porque lo único que se opera es el arreglo mismo.

Page 48: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-48

¿Qué hay de los valores de regreso? ¿Necesita regresar la función un valor sencillo o una serie de valores? La función no necesita regresar ningún valor sencillo, deberá regresar el arreglo ordenado. Por lo tanto, la clase de valor regresado será void y el arreglo será un parámetro de referencia, ¿correcto? Recuerde que cuando se pasan los arreglos a las funciones C++, siempre son tratados como parámetros de referencia porque el nombre del arreglo representa una dirección en memoria. Por lo tanto, aquí está la descripción de la interfaz de la función clasifPorInser():

Función clasifPorInser(): Clasifica un arreglo de caracteres en orden ascendente. Acepta: Un arreglo de caracteres sin clasificar. Regresa: Un arreglo de caracteres clasificados.

De la descripción anterior, la interfaz de la función se codifica fácilmente como:

void clasifPorInser(char arreglo[MAX])

La interfaz dice que clasifPorInser() recibirá un arreglo de caracteres de tamaño MAX. El tipo regresado es void, porque no se regresa ningún valor sencillo. Sin embargo, debido a que el arreglo completo se pasa a la función, cualquier operación de clasificación sobre el arreglo dentro de la función se reflejará en el programa llamador.

PLANEACIÓN DE LA SOLUCIÓN

Antes de establecer el algoritmo, vamos a ver cómo trabaja la clasificación por inserción. Vea la figura 18.3. Suponga que vamos a clasificar un arreglo de 5 caracteres en orden ascendente. Antes de entrar en detalle, vea la figura de arriba abajo y de izquierda a derecha. El arreglo sin ordenar se muestra en la parte superior de la figura 18.3 y el arreglo ordenado se muestra en la parte inferior de la figura 18.3. Observe que el sombreado se emplea en la figura para mostrar el proceso de clasificación de arriba a abajo. Conforme procedemos desde el arreglo sin clasificar en la parte superior, el sombreado se incrementa, mostrando la porción del arreglo que está clasificado, hasta que el arreglo completo está sombreado en la parte inferior de la figura 18.3. La secuencia descendente muestra que se harán cuatro pasos a lo largo del arreglo para obtener al arreglo clasificado mostrado en la parte inferior de la figura. Con cada paso, un elemento se coloca dentro de su posición clasificada relativa a los elementos que se encuentran antes en el arreglo. El primer paso empieza con el primer elemento ‘E’, clasificado como se indica por medio del sombreado. Se considera que el carácter individual ‘E’ se clasifica por sí mismo, porque no tiene ningún elemento antes que él. De esta manera, la tarea en este primer paso es clasificar el segundo elemento, ‘D’, relativo al carácter ‘E’ que lo precede.

El segundo paso empieza con los caracteres clasificados ‘D’ y ‘E’, como se indica por medio del sombreado. La tarea en este paso es clasificar el tercer carácter, ‘C’, en relación con estos dos caracteres. En el tercer paso se empieza con los caracteres clasificados ‘C’, ‘D’ y ‘E’, y la tarea es clasificar el carácter ‘B’ en relación con estos caracteres. Recuerde, en cada paso, la tarea es clasificar el primer carácter de la parte no clasificada del arreglo en relación con los caracteres que le preceden. El proceso continúa hasta que se clasifican todos los caracteres, como se muestra en la parte inferior de la figura 18.3. En cada paso se repite prácticamente lo que se hizo en el paso anterior. Como resultado, es posible identificar un proceso de repeticiones paso a paso, de arriba hacia debajo de la figura 18.3. Esta repetición dará origen a una estructura de ciclo en nuestro algoritmo.

Ahora, la pregunta es: ¿qué sucede durante cada paso para clasificar finalmente al arreglo completo? Bueno, durante cada paso, el primer elemento en la parte no clasificada (sin sombrear) del arreglo se examina comparándolo a la secuencia clasificada de los elementos que le preceden. Si este elemento es menor que el anterior, estos dos elementos se intercambian y de nuevo el elemento anterior se compara con su predecesor, si es menor se intercambian y así sucesivamente.

Page 49: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-49

[0] [1] [2] [3] [4] Paso 1

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4]

Paso 2

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4] CICLO EXTERNO Paso 3

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4] Paso 4

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4]

[0] [1] [2] [3] [4] ARREGLO CLASIFICADO CICLO INTERNO

Figura 18.3. La clasificación por inserción es un proceso de repeticiones anidadas

Este proceso se repite hasta que sucede una de dos cosas: (1) el elemento es mayor o igual a su predecesor, o (2) el elemento está en la primera posición del arreglo (índice [0]) En otras palabras, el proceso de comparación o intercambio de izquierda a derecha que se muestra en la figura 18.3 termina cuando el elemento examinado se ha insertado en su posición adecuada en la parte clasificada del arreglo. Este proceso de comparación o intercambio que representa la repetición de izquierda a derecha en la figura 18.3 dará como resultado otra estructura de ciclo en nuestro algoritmo. Por lo tanto, es posible identificar dos procesos repetitivos en la figura 18.3, uno de arriba hacia abajo y otro de izquierda a derecha. ¿Cómo se relacionan los dos procesos repetitivos? Bien, parece que por cada paso de arriba abajo a través del arreglo, el proceso de comparación o intercambio se ejecuta de izquierda a derecha. De esta manera, el proceso de izquierda a derecha deberá anidarse dentro del proceso de arriba abajo. Esto se reflejará en nuestro algoritmo por medio de dos estructuras de ciclo: una controla el proceso de comparación o intercambio de izquierda a derecha que deberá anidarse dentro del segundo ciclo que controla el proceso de arriba abajo. Observe de nuevo la figura 18.3 para asegurarse que ve esta repetición anidada. Ahora que tiene una idea de cómo funciona la ordenación por inserción, analice el algoritmo formal:

E D C B A

E D C B A D E C B A

D E C B A D C E B A

C D E B A C D B E A C B D E A

C D E B A

B C D E A

B C D E A B C D A E B C A D E B A C D E A B C D E

A B C D E

Page 50: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-50

Algoritmo clasifPorInser() clasifPorInser()

INICIO Establece i = segundo índice del arreglo. mientras(i <= último índice del arreglo)

Inicio Establece j = i. mientras(j > primer índice del arreglo) AND (A[j] < A[j – 1]))

Inicio Intercambia A[j] y A[j – 1]. Decrementa j.

Fin. Incrementa i.

Fin. FIN.

La variable i controla el ciclo externo y j controla el ciclo interno. Observe que i empieza en el segundo índice del arreglo. ¿Por qué no en el primer índice del arreglo? Porque el primer elemento en el arreglo se clasifica siempre relativo a cualquier elemento precedente, ¿correcto? Por lo tanto, el primer paso comienza con el segundo elemento del arreglo. El primer enunciado en el ciclo externo establece a j = i. De esta manera, i y j localizan el primer elemento en la parte no clasificada del arreglo al principio de cada paso. Ahora, el ciclo interno intercambiará al elemento localizado por j, el cual es A[j], con su elemento predecesor, el cual es A[j – 1], siempre y cuando j sea mayor que el primer índice del arreglo y el elemento A[j] sea menor que el elemento A[j – 1] Una vez que se hace el intercambio, se decrementa j. Esto obliga a j a seguir siendo el elemento insertado dentro de la parte del arreglo ordenado. El intercambio continúa hasta que no hay ningún elemento que preceda al elemento A[j] que sea menor que el A[j] Una vez que se rompe el ciclo interno, el elemento A[j] se inserta en su posición correcta con relación con los elementos que le preceden. Después, se hace otro paso incrementando la variable externa para el control del ciclo i, estableciendo j a i, y ejecutando de nuevo el ciclo interno. Este proceso de ciclo anidado continúa hasta que i se incrementa más allá de la última posición del arreglo. Estudie el algoritmo anterior y compárelo con la figura 18.3 hasta que esté seguro que comprende clasifPorInser() A continuación el código en C++.

CODIFICACIÓN DEL PROBLEMA

Hemos desarrollado la interfaz de la función clasifPorInser() En C++, el algoritmo se codifica fácilmente como una función semejante a ésta:

// Función de intercambio() void interCambio(char &x, char &y)

{ char temp; // Crea una variable temporal temp = x; x = y; y = temp;

} // Fin de interCambio()

// Función clasificación por inserción void clasifPorInser(char A[MAX])

{ int i; // Variable que controla el ciclo exterior int j; // Variable que controla el ciclo interior i = 1; // Establece i al índice del segundo elemento while(i < MAX)

Page 51: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-51

{ j = i; // Localiza el primer elemento del arreglo no clasificado while((j > 0) && (A[j] < A[j – 1]))

{ interCambio(A[j], A[j – 1]); --j; // Hace que j siga al elemento insertado

} // Final del while interno ++i; // Hace que i localice el primer elemento de la parte no

clasificada. } // Fin del while externo

} // Fin de clasifPorInser()

En este caso se pueden ver dos funciones codificadas en C++. Recuerde que el algoritmo clasifPorInser() requiere una operación de intercambio. Para llevar a cabo esta tarea se ha codificado una función llamada interCambio() Observe que esta función tiene dos parámetros de referencia que son caracteres. De esta manera, la función recibe dos caracteres que se intercambian utilizando la variable local temporal (temp) dentro de la función. Los caracteres de intercambio se envían de regreso al programa llamador por medio de los parámetros de referencia. Desde luego, el programa llamador será la función clasifPorInser() El código de clasifPorInser() debe ser directo a partir del algoritmo que se analizó. Estudie el código y compárelo con el algoritmo. Encontrará que son idénticos desde un punto de vista lógico y estructural. Debe notar cómo se llama a la función intercambio() dentro de clasifPorInser() Los elementos del arreglo A[j] y A[j – 1] se pasan a la función. Estos elementos son caracteres sencillos, ¿correcto? Por lo tanto, la función recibe dos caracteres y los intercambia. Los caracteres respectivos en el arreglo reflejan la operación de intercambio, porque intercambio() emplea parámetros de referencia.

SOLUCIÓN DE PROBLEMAS EN ACCIÓN: Búsqueda en un arreglo con recursión

(Búsqueda binaria)

En este problema desarrollaremos otro popular algoritmo de búsqueda, llamado búsqueda binaria. El algoritmo de búsqueda binaria que desarrollaremos empleará recursividad, aunque también se puede hacer utilizando iteración. Una de las principales diferencias entre búsqueda binaria y búsqueda secuencial es que la primera requiere que el arreglo se ordene antes de la búsqueda, mientras que la segunda no tiene este requerimiento. Sin embargo, si va a empezar con un arreglo ordenado, la búsqueda binaria es mucho más rápida que la secuencial, especialmente para arreglos grandes. Por ejemplo, si fuera a aplicar una búsqueda secuencia a un arreglo de 1000 enteros, el algoritmo de búsqueda secuencial hará un promedio de 500 comparaciones para encontrar el elemento deseado. Peor aún, si el elemento deseado está en la última posición del arreglo, la búsqueda secuencial hará mil comparaciones para encontrar el elemento. Por otra parte, la búsqueda binaria requerirá un máximo de 10 comparaciones para encontrar el elemento, ¡aún si está en la última posición del arreglo! Desde luego, se debe pagar un precio por esta mayor eficiencia. El precio que se paga es un algoritmo más complejo. Por lo tanto, cuando busque en un arreglo ordenado, la ventaja de la búsqueda secuencial es simplicidad, mientras que la ventaja de la búsqueda binaria es eficiencia.

PROBLEMA

Desarrolle una función en C++ que se pueda llamar para buscar en un arreglo ordenado de enteros un elemento con determinado valor y que regrese el índice del elemento si se encuentra en el arreglo. Utilice una búsqueda binaria recursiva para realizar esta tarea.

Se desarrollará una función en C++, así la definición del problema se enfocará de nuevo a la interfaz de la función. Sin embargo, antes de considerar la interfaz de la función, veamos cómo funciona una búsqueda binaria recursiva, porque el algoritmo de búsqueda dictará los parámetros de la función. Por lo tanto, primero trataremos el algoritmo y después desarrollaremos la interfaz de la función.

Page 52: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-52

PLANEACIÓN DE LA SOLUCIÓN

La búsqueda binaria representa una operación recursiva natural. Recuerde que la idea detrás de la recursividad es dividir un problema en subproblemas. Dividir un problema en subproblemas más simples de exactamente el mismo tipo hasta que ocurra una condición primitiva. Esto no es lo mismo que el diseño de software descendente, que divide los problemas en subproblemas más simples. La diferencia con la recursividad es que los subproblemas son exactamente del mismo tipo de problema que el problema original. Por ejemplo, suponga que está buscando un nombre en un directorio. Imagine que empieza al principio del directorio y ve cada nombre hasta que encuentra el correcto. Esto es exactamente lo que hace la búsqueda secuencial. ¿No sería más rápido, en promedio, abrir el directorio a la mitad? Después, determinar qué mitad del directorio contiene el nombre que está buscando, dividir esta sección del directorio a la mitad y así sucesivamente, hasta que obtiene la página en la cual aparece el nombre deseado. Aquí está un algoritmo que describe la búsqueda en el directorio como se describió.

UN ALGORITMO DE BÚSQUEDA RECURSIVA EN UN DIRECTORIO

buscaTel() INICIO

si(el directorio telefónico sólo tiene una página) entonces Busca el nombre en la página.

sino Abre el libro a la mitad. si(el nombre está en la primera mitad) entonces

buscaTel(primera mitad del directorio para el nombre) sino

buscaTel(segunda mitad del directorio para el nombre) FIN.

¿Observa cómo esta búsqueda es recursiva? Se mantiene realizando las mismas operaciones básicas hasta que llega a la página que contiene el nombre que está buscando. En otras palabras, la función buscaTel() se mantiene llamándose a sí misma en el enunciado anidado si/sino hasta que se encuentra la página correcta. La razón por la que se llama al proceso de búsqueda binaria es que deberá dividir el directorio entre 2 (bi) cada vez que se llama a sí mismo el algoritmo. Ahora, vamos a ver cómo se puede aplicar este proceso a la búsqueda en un arreglo de enteros. Se llamará a la función de búsqueda binaria recursiva busquedaBin() y se desarrollará el algoritmo en varios pasos. Aquí esta el primer nivel del algoritmo.

Algoritmo busquedaBin(): Primer nivel

busquedaBin() INICIO

si(el arreglo tiene sólo un elemento) entonces Determine si este elemento es el elemento buscado.

sino Encuentre el punto medio del arreglo. si(el elemento está en la primera mitad) entonces

busquedaBin(primera mitad) sino

busquedaBin(segunda mitad) FIN.

Observe cómo este algoritmo es casi idéntico al algoritmo buscaTel() Aquí el proceso de búsqueda se continúa hasta que el arreglo se reduce a un elemento que se verifica contra el elemento que se está buscando. ¿Observa cómo la búsqueda se mantiene llamándose a sí misma hasta que ocurre la condición primitiva? Aunque este algoritmo proporciona una idea general de búsqueda binaria, se necesita mayor detalle para codificar el algoritmo. Para hacerlo, debemos preguntarnos que datos necesita busquedaBin() para realizar esta tarea. Bueno, al igual que la búsqueda secuencial, ésta necesita un arreglo en dónde buscar y el elemento que se va a buscar ¿correcto? Sin embargo, la búsqueda secuencial trata con

Page 53: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-53

un tamaño de arreglo dado, mientras que la búsqueda binaria necesita tratar con arreglos de tamaños diferentes conforme sigue dividiendo al arreglo original a la mitad. No solamente son estos arreglos de diferente tamaño, sino el primero y el último índices de cada mitad son diferentes. Como resultado, debemos proporcionar a busquedaBin() los límites del arreglo que está tratando en cualquier momento dado. Esto se puede hacer pasando el primero y último índices del arreglo dado a la función. Llamemos a estos índices primero y ultimo. Estamos listos para escribir la descripción de la interfaz de la función:

Función busquedaBin(): Busca en un arreglo de enteros ordenado un valor

determinado. Acepta: Un arreglo de enteros, un elemento de búsqueda, el primero

y el último índices del arreglo en el cual se busca. Regresa: El índice del elemento, si se encuentra, o el valor -1 si el

elemento no se encuentra. Esta descripción da suficiente información para escribir la interfaz de la función C++, como sigue:

int busquedaBin(int A[], int elemento, int primero, int ultimo)

Aquí, busquedaBin() regresará un valor entero que representa el índice del elemento que se busca. De nuevo, verá que regresará el valor -1 si el elemento no se encuentra en el arreglo. La función recibe el arreglo de enteros que se busca (A[]), el elemento que se busca (elemento), el primer índice del arreglo (primero) y el último índice del arreglo (ultimo) Observe que ningún tamaño se proporciona para el arreglo en que se busca porque la función buscará en forma recursiva en arreglos de diferentes tamaños.

El siguiente problema es determinar qué valor de primero y ultimo se utilizarán para cualquier arreglo durante la búsqueda. Bueno, recuerde que se deberá dividir cualquier arreglo dado a la mitad para producir dos nuevos arreglos cada vez que se hace una llamada recursiva busquedaBin() Dado cualquier arreglo donde el primer índice es primero y el último índice es ultimo, se puede determinar el índice medio, como sigue:

mitad = (primero + ultimo) / 2

Con este cálculo, la primera mitad del arreglo empieza en primero y finaliza en mitad - 1, y la segunda mitad del arreglo empieza en mitad + 1 y finaliza en ultimo. Esta idea se muestra en la figura 18.4.

Pero, observe que ninguna mitad del arreglo contiene el elemento medio. Por me- dio de la utilización de esta técnica, las dos mitades no hacen un todo, ¿correcto? Por lo tanto, antes que se haga la división, suponga que verificamos el elemento medio para ver si es el elemento que se está buscando. La siguiente verificación realiza el trabajo.

si (A[mitad] == elemento) entonces regresar mitad.

Si esta verificación es verdadera antes de la división, se habrá encontrado el elemento que se está buscando y es posible terminar las llamadas recursivas. De otra manera, el elemento almacenado en A[mitad] no es el elemento que se está buscando, y esta posición del arreglo se puede ignorar durante el resto de la búsqueda. Si éste es el caso, se dividirá el arreglo y continuará el proceso recursivo. Sin embargo, se tiene que adicionar una segunda condición primitiva para el algoritmo recursivo. Aquí están las dos condiciones primitivas que se han hecho: 1. El arreglo que se busca tiene sólo un elemento. 2. A [mitad] == elemento.

Page 54: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-54

mitad = (primero + ultimo) / 2

[primero] [ultimo]

[primero] [mitad – 1] [mitad + 1] [ultimo]

Figura 18.4. La búsqueda binaria recursiva requiere que un arreglo sea dividido en mitades con cada llamada recursiva.

Cualquiera de estas condiciones primitivas hará que termine la llamada recursiva. Ahora, vamos a considerar la primera condición primitiva más de cerca. ¿Cómo saber si el arreglo que se busca tiene solamente un elemento? Bueno, conforme continúan las llamadas recursivas sin encontrar el elemento, finalmente el arreglo se reducirá a un elemento sencillo. Si éste es el que se está buscando, la verificación si A[mitad] == elemento será verdadera, y las llamadas recursivas se detendrán. Si no lo es, el valor de primero se hará más grande que el valor de ultimo en la siguiente división. ¿Por qué? Porque si piensa en la acción de división del algoritmo, se dará cuenta que cada llamada recursiva hace que primero se incremento y ultimo se decremente. De esta manera, si el elemento no está en el arreglo, el valor de primero se hace finalmente más grande que el valor de ultimo. Por lo tanto se puede usar esta idea para verificar que el elemento no está en el arreglo, también para usarlo como una condición primitiva. De esta manera, se reemplazará la condición primitiva original con el siguiente enunciado:

si (primero > ultimo) entonces regresar – 1

Si ocurre esta condición, se regresa el valor -1, indicando que no se encontró el elemento y terminan las llamadas recursivas.

Ahora, vamos a aplicar este conocimiento a un segundo nivel del algoritmo. Como sigue:

Algoritmo busquedaBin(): Segundo nivel

busquedaBin(A, elemento, primero, ultimo) INICIO

si(primero > ultimo) entonces regresar -1.

sino Establecer mitad = (primero + ultimo) / 2. si (A[mitad] == elemento) entonces

regresar mitad. sino

si (el elemento está en la primera mitad) entonces busquedaBin(A, elemento, primero, mitad - l)

sino busquedaBin(A, elemento, mitad + 1, ultimo)

FIN.

Es evidente ahora que el algoritmo realiza la recursividad, porque se puede ver la función llamándose a sí misma en uno o dos lugares, dependiendo de en qué mitad del arreglo dividido es probable que se encuentre el elemento. También, observe en dónde se verifican los dos casos primitivos. Si, al inicio de una llamada recursiva, primero > ultimo, el elemento no está en el arreglo y las llamadas recursivas terminan. Además, si después de calcular mitad, se encuentra el elemento en A[mitad], terminará la llamada recursiva. En ambos casos, la función ha terminado su ejecución y regresa un valor al programa llamador. Lo último que

Page 55: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-55

necesita el algoritmo es una forma para determinar si el elemento que se busca es muy probable que se encuentre en la primera mitad o en la segunda mitad del arreglo dividido. Aquí es donde viene el requerimiento para un arreglo ordenado. Si el arreglo está ordenado, es muy probable que el elemento se encuentre en la primera mitad del arreglo cuando elemento < A[mitad]; de otra manera, es muy probable que el elemento se encuentre en la segunda mitad del arreglo. Observe que usamos el término es muy probable. Nosotros no podemos garantizar que el elemento se encuentre en cualquier mitad, ¡ya que, inclusive, podría no estar en el arreglo! Todo lo que se puede hacer es dirigir la búsqueda a la mitad donde es muy probable que se encuentre el elemento, dependiendo del orden de clasificación de los elementos. Por lo tanto, se puede completar el algoritmo usando esta idea. Aquí está el algoritmo final:

Algoritmo busquedaBin()

busquedaBin(A, elemento, primero, ultimo) INICIO

si(primero > ultimo) entonces regresar -1.

sino Establecer mitad = (primero + ultimo) / 2. si (A[mitad] == elemento) entonces

regresar mitad. sino

si (elemento < A[mitad]) entonces busquedaBin(A, elemento, mitad - l).

sino busquedaBin(A, elemento, mitad + 1, ultimo)

FIN.

Observe qué elegante es el algoritmo. Por elegante se debe entender que más que un proceso complicado de búsqueda binaria, se trata de sólo unos cuantos enunciados. Se sabe que hay mucho por recorrer todavía, pero la recursividad permite expresar todo este procedimiento en sólo unos cuantos enunciados. Como puede ver, a menudo los algoritmos recursivos proporcionan soluciones simples a problemas de gran complejidad, en donde una solución iterativa equivalente puede ser compleja. Éste no es siempre el caso, porque algunas soluciones recursivas son relativamente poco prácticas para la eficiencia de velocidad y de memoria. Recuerde la siguiente regla cuando considere la recursividad: considere una solución recursiva para un problema sólo cuando no sea posible una solución iterativa sencilla. Tome en cuenta que la búsqueda binaria tiene una solución iterativa relativamente sencilla. Se codificará esta solución para uno de los problemas al final de esta lección.

CODIFICACIÓN DEL PROGRAMA

La función que requiere C++ ahora se puede codificar con facilidad a partir del algoritmo final. Como sigue: int busquedaBin(int[A], int elemento, int primero, int ultimo)

{ int mitad; // PUNTO MEDIO DEL ARREGLO if (primero > ultimo) // SI EL ELEMENTO NO ESTÁ EN EL ARREGLO

return -l; // REGRESA -1, SI NO CONTINÚA LA BÚSQUEDA else

{ mitad = (primero + ultimo) / 2; // ENCUENTRA EL PUNTO

// MEDIO DEL ARREGLO if (elemento == A[mitad]) // Si EL ELEMENTO ESTÁ EN

// A[mitad] return mitad; // REGRESA mitad

else // Si NO, BUSCA LA MITAD APROPIADA if (elemento < A[mitad])

return busquedaBin(A, elemento, primero, mitad - l); else

Page 56: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-56

return busquedaBin(A, elemento, mitad + 1, ultimo); } / /FINAL DE else EXTERNO

} // FINAL DE busquedaBin()

No deberá tener ningún problema para comprender este código, ya que refleja la interfaz de la función y al algoritmo que se desarrolló. Aquí la única diferencia es que las llamadas recursivas en busquedaBin() forman parte de un enunciado return. Recuerde que C++ requiere que todos los caminos de ejecución de una función sin void den origen a un enunciado return.

Ejemplo 18.36

El siguiente programa, BÚSQUEDA.CPP, ilustra los métodos de búsqueda lineal y binaria. Es bastante interesante la solución que se propone, por lo que se le pide al lector que lea con detenimiento la codificación.

/* El siguiente programa: BUSQUEDA.CPP, busca en arreglos usando los métodos de búsqueda lineal y binaria. */ #include <ctype.h> #include <iostream.h> #include <stdlib.h> typedef int (*funcBusqueda)(int,int[],int); const int MIN = 2; const int MAX = 10; const int NO_ENCONTRADO = -1; // Pide al usuario la cantidad de elementos del arreglo int obtenNumPuntos(int minimo, int maximo) { int numPuntos; do { cout << "Introduzca el número de puntos de datos [" << minimo << " a " << maximo << "]: "; cin >> numPuntos; } while (numPuntos < minimo || numPuntos > maximo); return numPuntos; }//Fin de obtenNumPuntos() // Pedir al usuario los datos del arreglo void entradaArreglo(int intArr[], int num) { for (int i = 0; i < num; i++) { cout << "arreglo[" << i << "]: "; cin >> intArr[i]; }//Fin del for }//Fin de entradaArreglo() // Desplegar el contenido de un arreglo void mostrarArreglo(int intArr[], int num)

Page 57: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-57

{

for (int i = 0; i < num; i++) { cout.width(5); cout << intArr[i] << " "; }//Fin del for cout << endl; }//Fin de mostrarArreglo() // Rutina de búsqueda lineal int busquedaLineal(int valorBuscar, int intArr[], int num) { for (int i = 0; i < num; ++i) if (valorBuscar == intArr[i]) return i; return NO_ENCONTRADO; }//Fin de busquedaLineal() // Subfunción de búsqueda binaria recursiva int busquedaBinaria(int valorBuscar, int intArr[], int minimo, int maximo) { int mitad = (minimo + maximo) / 2; if (minimo > maximo) return NO_ENCONTRADO; else if (valorBuscar == intArr[mitad]) return mitad; else if (valorBuscar > intArr[mitad]) return busquedaBinaria(valorBuscar, intArr, mitad + 1, maximo); else return busquedaBinaria(valorBuscar, intArr, minimo, mitad - 1); }//Fin de busquedaBinaria() // Punto de entrada principal para la búsqueda binaria int busquedaBinaria(int valorBuscar, int intArr[], int n) { return busquedaBinaria(valorBuscar, intArr, 0, n - 1); }//Fin de busquedaBinaria // Revisión para la continuación de la búsqueda bool debeBuscar(const char *tipoBusqueda) { char car; cout << "¿Buscar en el arreglo " << tipoBusqueda << "? (S/N) "; cin >> car; return tolower(car) == 's'; }//Fin de debeBuscar // Maneja la prueba de búsqueda void buscarArreglo( int intArr[], int num, const char *tipoBusqueda, funcBusqueda buscar ) { int valorBuscar, index; char car;

Page 58: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-58

Ejemplo 18.37

Una variante de solución del programa BÚSQUEDA.BPP, es el programa siguiente, BUSQUEDA2.CPP.

while (debeBuscar(tipoBusqueda)) { cout << "Introduzca valor a buscar: "; cin >> valorBuscar; index = buscar(valorBuscar, intArr, num); if (index != NO_ENCONTRADO) cout << "Encontró el elemento en el índice " << index << endl; else cout << "No encontró el elemento" << endl; }//Fin de while }//Fin de buscarArreglo() // Compara dos enteros int cmpEnt(const void *item1, const void *item2) { return *(int*)item1 - *(int*)item2; }//Fin de cmpEnt)= void main(void) { int arr[MAX]; int numElementos; numElementos = obtenNumPuntos(MIN, MAX); entradaArreglo(arr, numElementos); cout << "El arreglo desordenado es:" << endl; mostrarArreglo(arr, numElementos); buscarArreglo(arr, numElementos, "desordenado", busquedaLineal); qsort(arr, numElementos, sizeof(arr[0]), cmpEnt); cout << endl << "El arreglo ordenado es:" << endl; mostrarArreglo(arr, numElementos); buscarArreglo(arr, numElementos, "ordenado", busquedaBinaria); }//Fin de main()

/* El siguiente programa: BUSQUEDA2.CPP, busca en arreglos usando los métodos de búsqueda lineal y binaria. */ #include <ctype.h> #include <iostream.h> #include <search.h> #include <stdlib.h> typedef int* (*funcBusqueda)(int,int[],int); const int MIN = 2; const int MAX = 10;

Page 59: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-59

// Compara dos enteros int cmpEnt(const void *item1, const void *item2) { return *(int*)item1 - *(int*)item2; }//Fin de cmpEnt() // Pide al usuario el número de elementos del arreglo int obtenNumPuntos(int minimo, int maximo) { int numPuntos; do { cout << "Introduzca el número de puntos de datos [" << minimo << " a " << maximo << "]: "; cin >> numPuntos; } while (numPuntos < minimo || numPuntos > maximo); return numPuntos; }//Fin de obtenNumPuntos() // Pide al usuario los datos del arreglo void entradaArreglo(int intArr[], int num) { for (int i = 0; i < num; i++) { cout << "arreglo[" << i << "]: "; cin >> intArr[i]; }//Fin del for }//Fin de entradaArreglo() // Despliega el contenido de un arreglo void mostrarArreglo(int intArr[], int num) { for (int i = 0; i < num; i++) { cout.width(5); cout << intArr[i] << " "; }//Fin del for cout << endl; }//Fin de mostrarArreglo() // Rutina de búsqueda lineal int *busquedaLineal(int valorBuscar, int intArr[], int num) { return (int*)lfind(&valorBuscar, intArr, (size_t*)&num, sizeof(intArr[0]), cmpEnt); }//Fin de busquedaLineal() // Puntos de entrada principal para la búsqueda de datos int *busquedaBinaria(int valorBuscar, int intArr[], int n) { return (int*)bsearch(&valorBuscar, intArr, n, sizeof(intArr[0]), cmpEnt);

Page 60: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-60

}//Fin de busquedaBinaria() // Verificar la continuidad de búsqueda bool debeBuscar(const char *tipoBusqueda) { char ch; cout << "Buscar en el arreglo " << tipoBusqueda << "? (S/N) "; cin >> ch; return tolower(ch) == 's'; }//Fin de debeBuscar() // Manejar la prueba de búsqueda void buscarArreglo(int intArr[], int num, const char* tipoBusqueda, funcBusqueda buscar ) { int valorBuscar; int *indice; char ch; while (debeBuscar(tipoBusqueda)) { cout << "Introduzca el valor de búsqueda: "; cin >> valorBuscar; indice = buscar(valorBuscar, intArr, num); if (indice) cout << "El elemento se encontró en el índice " << indice - intArr << endl; else cout << "No se encontró el elemento" << endl; }//Fin de while }//Fin de buscarArreglo() void main(void) { int arr[MAX]; int numElementos; numElementos = obtenNumPuntos(MIN, MAX); entradaArreglo(arr, numElementos); cout << "El arreglo desordenado es:" << endl; mostrarArreglo(arr, numElementos); buscarArreglo(arr, numElementos, "desordenado", busquedaLineal); qsort(arr, numElementos, sizeof(arr[0]), cmpEnt); cout << endl << "El arreglo ordenado es:" << endl; mostrarArreglo(arr, numElementos); buscarArreglo(arr, numElementos, "ordenado", busquedaBinaria); }//Fin de main()

Page 61: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-61

INICIACIÓN DE ARREGLOS

Antes de terminar esta lección, es necesario saber cómo inicializar los arreglos en el momento en que se definen. Los arreglos, como las variables, se pueden inicializar cuando se crean. Se pueden suministrar valores de iniciación para cualquier arreglo, mientras esté definido en el programa. Consideremos algún ejemplo de definición de arreglo para mostrar cómo se inicializan los arreglos.

int enteros[3] = {10,20,30};

En esta definición, se ha presentado un arreglo entero de tres elementos. Los tres

elementos enteros se han inicializado con los valores 10, 20 y 30, respectivamente. Observe la sintaxis. La definición del arreglo es seguida por un operador de asignación, al cual le suceden los valores de inicialización encerrados dentro de llaves. Esto es lo que verá si inspecciona al arreglo usando un depurador (debugger):

RESULTADOS DEL DEPURADOR

Inspección de enteros

[0] 10 [1] 20 [2] 30

Como puede ver, el primer valor de inicialización, 10, se coloca en el índice 0 del

arreglo; el valor 20 se coloca en el índice 1; y el valor 30 se coloca en la última posición del índice, que es 2. Ahora, ¿qué se supone que sucedería si proporcionara menos valores de inicialización que las posiciones en el arreglo? Bueno, suponga que define al arreglo como sigue:

int enteros[3] = {10,20};

Enseguida se presenta lo que encontrará cuando inspeccione al arreglo usando un

depurador:

RESULTADOS DEL DEPURADOR Inspección de enteros

[0] 10 [1] 20 [2] 00

Como puede observar, el compilador inicializó la última posición del arreglo con cero.

Cero es el valor de iniciación predeterminado para arreglos de enteros cuando no se proporcionan suficientes valores para llenar al arreglo.

La siguiente pregunta obvia es: ¿qué sucede si proporciona demasiados valores de

iniciación? Por ejemplo, suponga que define el arreglo como sigue:

int enteros[3] = {10,20,30,40};

Page 62: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-62

En este caso, se obtendrá un error de compilación, demasiados inicializadores (too many initializers) Una forma de solucionar este problema es incrementar el tamaño del arreglo. Otra forma preferida, es definir al arreglo sin ningún tamaño específico, como sigue:

int enteros[] = {10,20,30,40};

Con esta definición, el compilador establecerá suficiente lugar de almacenamiento para

contener todos los valores de inicialización. Aquí está lo que verá si inspecciona esta definición de arreglo usando un depurador:

RESULTADOS DEL DEPURADOR

Inspección de enteros

[0] 10 [1] 20 [2] 30 [3] 40

Ahora, vamos a considerar arreglos de caracteres. Suponga que define un arreglo de

caracteres de tamaño cinco, como éste:

char caracteres[5] = {‘H’, ‘E’, ‘L’, ‘L’, ‘O’}

De nuevo se ve que los valores de inicio se encierran en llaves después de un operador de asignación. Esto mostrará el depurador:

RESULTADOS DEL DEPURADOR

Inspección de caracteres

[0] ‘H’ [1] ‘E’ [2] ‘L’ [3] ‘L’ [4] ‘O’

Aquí se ve que los cinco caracteres de inicio se colocan en el arreglo empezando en el

índice cero y finalizando en la última posición del índice, cuatro. ¿Qué pasa si proporciona unos cuantos caracteres de iniciación menos que los requeridos para llenar el arreglo? Bueno, suponga que define el arreglo de esta forma:

char caracteres[5] = {'H','E'};

El contenido del arreglo ahora será corno sigue:

RESULTADOS DEL DEPURADOR Inspección de caracteres

[0] ‘H’ [1] ‘E’ [2] ‘\0’ [3] ‘\0’ [4] ‘\0’

Page 63: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-63

Esta vez, el compilador ha insertado un carácter terminador nulo como un carácter predeterminado para llenar el arreglo. De otra manera, si proporciona demasiados caracteres, el compilador generará un mensaje de error demasiados inicializadores. De nuevo, la forma segura para manejar este problema es dejar que el compilador determine el tamaño del arreglo para ajustar el número de valores inicializados.

Por último, vamos a considerar cómo se inicializa el arreglo de caracteres con valores de

cadenas. Recuerde, una cadena no es más que un arreglo de caracteres, terminada con un terminador nulo. Así es como se puede inicializar un arreglo de caracteres con un valor de cadena:

char caracteres[6] = "HELLO";

Observe que la sintaxis es diferente. La cadena de iniciación deberá encerrarse entre

comillas más que entre llaves. Otra cosa que se ve es que el tamaño del arreglo es uno más grande que el número de caracteres en la cadena. La razón para esto parece obvia cuando inspecciona el arreglo usando un depurador. Aquí esta lo que verá: RESULTADOS DEL DEPURADOR

Inspección de caracteres

[0] ‘H’ [1] ‘E’ [2] ‘L’ [3] ‘L’ [4] ‘O’ [5] ‘\0’

Recuerde que una cadena terminará con un terminador nulo. Al hacer el tamaño del

arreglo uno más grande que el número de caracteres en la cadena, permite espacio para que el compilador inserte el terminador nulo. Si no se deja espacio para el terminador nulo, éste quedará truncado (eliminado) del arreglo y no se obtendrá un mensaje de error. De nuevo, la mejor manera de evitar este problema es permitir que el compilador determine el tamaño del arreglo, como éste:

char caracteres[] = "HELLO";

Con esta definición, el compilador creará suficientes posiciones en el arreglo para

contener todos los caracteres de cadenas con el terminador nulo insertado como el último carácter en el arreglo. INICIACIÓN PREDETERMINADA DE ARREGLOS GLOBALES Y ESTÁTICOS

Se pueden definir e inicializar arreglos en cualquier parte del programa C++. El ámbito de un arreglo funciona justo como el ámbito de una variable o constante. Un arreglo definido antes de main() es visible en todo el archivo fuente en el cual se define. Un arreglo definido dentro de un bloque tiene ámbito de bloque y, por lo tanto, es visible sólo dentro del bloque en el cual se define.

Si se define un arreglo en forma global o como un arreglo estático y no se proporciona

ningún valor de inicialización, el compilador inicializará el arreglo con el valor predeterminado

Page 64: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-64

respectivo (ceros para arreglos de enteros y de punto flotante y terminadores nulos para arreglos de caracteres) El siguiente es un ejemplo:

int enteros[5]; void main(void)

{ static char caracteres[5];

} // FINAL DE main()

Se ha definido en forma global el arreglo enteros y también como un arreglo estático local al arreglo caracteres dentro de main() La inspección de estos arreglos con un depurador revelará lo siguiente:

RESULTADOS DEL DEPURADOR

Inspección de enteros Inspección de caracteres [0] 0 [0] ‘\0’ [1] 0 [1] ‘\0’ [2] 0 [2] ‘\0’ [3] 0 [3] ‘\0’ [4] 0 [4] ‘\0’ El depurador muestra que el arreglo global de enteros se ha inicializado con ceros,

mientras que el arreglo de caracteres estático se ha inicializado con caracteres de terminador nulo. Si define un arreglo con ámbito de bloque local que no es estático y no lo inicializa, el compilador no suministrará ningún valor de inicialización predeterminado. ¡El arreglo contendrá basura! De esta manera, si elimináramos la palabra clave static de la definición anterior del arreglo caracteres, el depurador revelará valores arbitrarios de memoria en el arreglo.

Aquí está un resumen de la explicación anterior:

•••• Los arreglos de enteros, de punto flotante y de caracteres se inicializan por medio de un operador de asignación después de la definición del arreglo, seguido por una lista de valores individuales de inicialización dentro de llaves.

•••• Menos valores de iniciación darán como resultado valores predeterminados (ceros para

arreglos de enteros y de punto flotante y terminadores nulos para arreglos de caracteres) insertados en las posiciones adicionales del arreglo.

•••• Más valores de iniciación provocan un error de compilación. •••• Los arreglos de caracteres se pueden inicializar encerrando una cadena con comillas dobles. •••• El tamaño de un arreglo de cadena deberá ser uno más grande que el número de caracteres

dentro de la cadena para dejar espacio para el carácter terminador nulo. •••• Si no se especifica un tamaño en la definición del arreglo, el compilador creará el suficiente

espacio de almacenaje para los valores de inicialización. •••• Los arreglos globales y arreglos estáticos se inicializan siempre con los valores

predeterminados respectivos cuando ningún valor de iniciación se suministra en la definición del arreglo.

Page 65: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-65

•••• Los arreglos locales que no son estáticos no se inicializarán con ningún valor específico, a menos que se proporcionen en la definición del arreglo.

ARREGLOS QUE REBASAN LOS 64 KBYTES DE MEMORIA

Si un arreglo rebasa los 64 kbytes de longitud, el mismo enviará un mensaje de error

(Array size too large) en tiempo de compilación.

Ejemplo 18.38

El siguiente programa, DEMGDE.CPP, ilustra esta situación.

Ejemplo 18.39

El siguiente programa ENORME.CPP, ilustra como resolver esta situación.

EXAMEN BREVE 38

/* El siguiente programa: DEMGDE.CPP, no se compila correctamente ya que el arreglo utiliza mas de 64 kbytes de memoria. */ void main(void)

{ char cadena[66000L]; // 66,000 bytes int valores[33000L]; // 33,000 * 2 = 66,000 bytes float numeros[17000]; // 17,000 * 4 = 68,000 bytes

}//Fin de main()

/* El siguiente programa: ENORME.CPP crea un arreglo de más de 64 kbytes de datos de punto flotante. */ #include <iostream.h> //Para cout y cin #include <malloc.h> //Para halloc() void main (void) { int i; float huge *valores; if ((valores = (float huge *) halloc (17000, sizeof(float))) == NULL) cout << "Error al reservar memoria para el arreglo" << endl; else { cout << "Llenando el arreglo" << endl; for (i = 0; i < 17000; i++) valores[i] = i * 1.0;

Page 66: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-66

LO QUE NECESITA SABER Antes de continuar con la siguiente lección, asegúrese de haber comprendido los siguientes conceptos:

!!!!""""Un arreglo es una variable que puede almacenar uno o más valores del mismo tipo. !!!!""""Un arreglo es una estructura de datos importante que se usa para localizar y almacenar

elementos de una clase de datos dados. !!!!""""Los dos componentes de cualquier arreglo son los elementos que se almacenan en el arreglo

y los índices que localizan los elementos almacenados. !!!!""""Los elementos del arreglo pueden ser cualquier clase de datos y los índices del arreglo son

siempre enteros de rango desde [0] hasta [MAX - 1 ], en donde MAX es el tamaño del arreglo. !!!!""""Hay arreglos unidimensionales y multidimensionales. Un arreglo unidimensional o lista es

un renglón sencillo de elementos. Tiene dimensiones de 1 x n, donde n es el número de elementos de la lista. En C++, el índice máximo en cualquier dimensión es el tamaño de la dimensión (n) menos 1.

!!!!""""Para declarar un arreglo debe especificar su tipo, el nombre del arreglo y el número de valores que el arreglo almacenará.

!!!!""""Los arreglos se definen en C++ especificando la clase de datos del elemento, el nombre y el tamaño del arreglo.

!!!!""""Para tener acceso a los elementos del arreglo, se usan enunciados de asignación directa, de lectura o escritura o ciclos.

!!!!""""La estructura del ciclo for es la forma más común de tener acceso a los múltiples elementos de un arreglo.

!!!!""""Los valores dentro del arreglo se llaman elementos del arreglo. !!!!""""El primer elemento del arreglo se almacena en el elemento 0 (arreglo[0]); el último

elemento es indexado por un valor menor en 1 que el tamaño del arreglo. !!!!""""Los programas a menudo utilizan variables como índice para acceder los elementos del

arreglo. !!!!""""En los arreglos de C++ se almacenan listas de valores. Un arreglo es un conjunto de

localidades consecutivas de memoria relacionadas. Estas localidades se relacionan por el hecho de que todas tienen el mismo nombre y la misma clase de datos. Para hacer referencia a una localidad particular o elementos de un arreglo, se especifica el nombre del arreglo y su índice.

!!!!""""Un índice puede ser un entero o una expresión entera. Las expresiones de índices se evalúan para determinar el elemento particular del arreglo.

!!!!""""Es importante notar la diferencia que hay entre referirse al séptimo elemento del arreglo e indicar el elemento siete del arreglo. El séptimo elemento tiene el índice 6, mientras que el elemento siete del arreglo tiene el índice 7 (de hecho, se trata del octavo elemento) Esto es fuente de errores por diferencia de uno.

!!!!""""Los arreglos ocupan espacio en memoria. Para reservar 100 elementos para el arreglo de enteros b y 27 elementos para el arreglo x, el programador escribe

int b[100], x[27];

for (i = 0; i < 17000; i++) cout << valores[i] << ' '; hfree(valores); }//Fin de else }//Fin de main()

Page 67: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-67

!!!!""""Es posible utilizar un arreglo de clase char para almacenar una cadena de caracteres. !!!!""""Los elementos de un arreglo pueden inicializarse por declaración, por asignación o por

entrada. !!!!""""Si hay menos inicializadores que elementos en el arreglo, los elementos restantes se

inicializan a cero. !!!!""""C++ no evita que se haga referencia a elementos que estén fuera de los límites de un

arreglo. !!!!""""Es posible inicializar un arreglo de caracteres por medio de una literal de cadena. !!!!""""Todas las cadenas terminan con el carácter nulo (‘\0’) !!!!""""Los arreglos de caracteres se pueden inicializar con constantes de carácter en una lista

de iniciación. !!!!""""Es posible acceder a los caracteres de una cadena almacenada en un arreglo por medio

de la notación de índices. !!!!""""Para pasar un arreglo a una función, hay que pasar su nombre. Para pasar un solo elemento

del arreglo a una función, simplemente hay que pasar el nombre del arreglo seguido por el índice (entre corchetes cuadrados) de dicho elemento.

!!!!""""Los arreglos se pasan a las funciones simulando una llamada por referencia; las funciones llamadas pueden modificar los valores de los elementos de los arreglos originales. El nombre del arreglo es la dirección de su primer elemento. Debido a que se pasa la dirección de inicio del arreglo, la función llamada sabe exactamente dónde está almacenado dicho arreglo.

!!!!""""Para recibir un arreglo como argumento, la lista de parámetros de la función debe especificar que se recibirá un arreglo. No es necesario el tamaño del arreglo entre los corchetes.

!!!!""""Cuando una función recibe un arreglo como parámetro, la función debe especificar la clase del arreglo y su nombre, pero no el tamaño del arreglo.

!!!!""""C++ tiene el calificador de clase const, que permite a los programas evitar la modificación, en una función, de los valores de un arreglo. Cuando un parámetro de arreglo está precedido por el calificador const, los elementos del arreglo se vuelven constantes en el cuerpo de la función y cualquier intento por modificarlos provoca un error de sintaxis.

!!!!""""La búsqueda y ordenación son operaciones comunes realizadas sobre arreglos. !!!!""""Los arreglos pueden ordenarse mediante la técnica de ordenamiento de burbuja. Se

realizan varias pasadas al arreglo. Con cada pasada se comparan los pares consecutivos de los elementos. Si un par está ordenado (o sus valores son idénticos), se deja tal cual. Si está fuera de orden, se intercambian. El ordenamiento de la burbuja es aceptable en los arreglos pequeños, pero en los mayores es ineficiente en comparación con otros algoritmos de ordenamiento más complejos.

!!!!""""La búsqueda secuencial es una búsqueda iterativa que busca un valor dado en un arreglo, comparando en forma secuencial el valor con los elementos del arreglo, empezando con el primer elemento del arreglo, hasta que se encuentra el valor en el arreglo o hasta que se alcanza el final del arreglo.

!!!!""""La búsqueda lineal compara todos los elementos de un arreglo con la clave de búsqueda. Si el arreglo no está en ningún orden en particular, existe la misma probabilidad de que el valor se encuentre en el primer elemento como que esté en el último. Por lo tanto, en promedio, el programa tendrá que comparar la clave de búsqueda con la mitad de los elementos del arreglo. El método de búsqueda lineal funciona bien con los arreglos pequeños y es aceptable si se trata de arreglos desordenados.

!!!!""""Una búsqueda binaria puede ser iterativa o recursiva. Una búsqueda binaria divide el arreglo en mitades, dirigiéndose a sí misma hacia la mitad en donde es muy probable que se encuentre el valor.

!!!!""""La búsqueda binaria requiere que el arreglo esté ordenado, mientras que la búsqueda secuencial no tiene este requerimiento.

Page 68: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-68

!!!!"""" Por otra parte, la búsqueda binaria es mucho más rápida que la secuencial, especialmente en grandes arreglos ordenados.

!!!!""""La búsqueda binaria elimina de su consideración la mitad de los elementos del arreglo tras cada comparación; esto lo logra localizando el elemento central del arreglo y comparándolo con la clave de búsqueda. Si son iguales, entonces se encuentra la clave de búsqueda y se devuelve el índice de dicho elemento. De otra manera, se reduce el problema a la búsqueda en una mitad del arreglo.

!!!!""""En el peor caso, la búsqueda en un arreglo de 1024 elementos sólo necesitaría 10 comparaciones y se efectúa mediante búsqueda binaria.

!!!!""""Muchas aplicaciones en el mundo real requieren que la información esté ordenada. Hay algunos algoritmos de clasificación comunes, incluyendo clasificación por inserción, clasificación por burbuja, clasificación por selección y clasificación rápida.

!!!!""""Todos estos algoritmos operan sobre arreglos. El algoritmo de clasificación por inserción es un proceso iterativo que inserta un elemento dado en el arreglo en su lugar correcto relativo a los elementos que lo preceden en el arreglo. Usted se familiarizará con la clasificación de burbuja y la clasificación de selección en los problemas de esta lección.

PREGUNTAS Y PROBLEMAS PREGUNTAS 1. Llene los siguientes espacios en blanco:

a. Las listas y tablas de valores se guardan en ________________________. b. Los elementos de un arreglo se relacionan por el hecho de que tienen el mismo __________ y

_____________. c. El número con el que se hace referencia a un elemento en particular de un arreglo se llama

_____________. d. Debe usarse una _______________ para declarar el tamaño de un arreglo, pues hace más escalable el

programa. e. El proceso de colocar en orden los elementos en un arreglo se llama _______________ del arreglo. f. El proceso con el que se determina si un arreglo contiene cierto valor clave se llama ____________. g. C++ almacena las listas de valores en _________________. h. Al referirse a un elemento de un arreglo, el número de posición contenido entre paréntesis se llama

_____________. i. Los nombres de los cuatro elementos del arreglo p son __________, _____________, _________ y

___________. j. La denominación de un arreglo, indicación de su clase y especificación de la cantidad de elementos que

hay en él se llama _____________ del arreglo. k. El proceso de colocación de los elementos de un arreglo en orden ascendente o descendente se llama

_____________. 2. Indique si las siguientes oraciones son falsas o verdaderas. Si la respuesta es falso explique por qué.

a. Un arreglo puede contener diferentes clases de valores. b. Los índices de los arreglos normalmente deben de ser de clase float. c. Si hay menos inicializadores en una lista de iniciación que el número de elementos que hay en el

arreglo, los elementos restantes se inicializan automáticamente al último valor de dicha lista. d. Es un error que una lista de iniciación contenga más inicializadores que la cantidad de elementos que

hay en el arreglo. e. Un elemento de un arreglo que se pasa a una función y se modifique ahí contendrá el valor modificado

cuando termine la ejecución de la función llamada. f. Para hacer referencia a una localidad particular o elemento de un arreglo, se especifica el nombre del

arreglo y el valor del elemento. g. Una declaración de arreglo reserva espacio para el arreglo. h. Para indicar que se deben reservar 100 localidades para el arreglo de enteros p, el programador

escribirá la declaración

Page 69: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-69

p[100];

i. Un programa C++ que inicialice a 0 los elementos de un arreglo de 15 elementos debe contener cuando menos una instrucción for.

3. Conteste las siguientes preguntas relacionadas con un arreglo llamado fracciones. a. Defina una variable constante tamanoArreglo que se inicialice a 10. b. Declare un arreglo con elementos tamanoArreglo de la clase float e inicialice dichos elementos a 0. c. Nombre el cuarto elemento a partir del inicio del arreglo. d. Haga referencia al elemento 4. e. Asígnele el valor 1.667 al elemento 9. f. Asígnele el valor 3.333 al séptimo elemento del arreglo. g. Imprima los elementos 6 y 9 del arreglo con una precisión de dos dígitos a la derecha del punto decimal y

muestre la salida que se desplegará en la pantalla. h. Imprima todos los elementos del arreglo mediante una estructura de repetición for. Defina la variable

entera x como la variable de control del ciclo. Muestre la salida.

4. Encuentre los errores en los siguientes segmentos de programa y corríjalos. a. #include <iostream.h>; b. tamanoArreglo = 10; //tamanoArreglo se declaró como const c. Suponga que int b[10] = {0};

for(int i = 0; i <= 10; i++) b[i] = 1;

5. Escriba instrucciones en C++ que lleven a cabo lo siguiente: a. Presente el valor del séptimo elemento de un arreglo de caracteres f. b. Introduzca un valor en el elemento 4 de un arreglo de punto flotante b, de un solo índice. c. Inicialice a 8 los 5 elementos del arreglo de enteros g, de un solo índice. d. Totalice e imprima los elementos del arreglo de punto flotante c, que tiene 100 elementos. e. Copie el arreglo a a la primera parte del arreglo b. Suponga que están declarados como float a[11], b[34]; f. Determine e imprima el valor menor y el mayor contenidos en un arreglo w de punto flotante de 99

elementos. 6. ¿Cuáles son las tres cosas que se deben especificar para definir un arreglo?

Use la siguiente definición de arreglo para contestar las preguntas 7 – 12.

char caracteres[15]; 7. ¿Cuál es el índice del primer elemento del arreglo? 8. ¿Cuál es el índice del último elemento del arreglo? 9. Escriba un enunciado que coloque el carácter 'Z' en la tercera celda del arreglo. 10. Escriba un enunciado que muestre el último elemento del arreglo. 11. Escriba el código necesario para llenar el arreglo desde el teclado. Cerciórese de preguntar al usuario antes de

cada entrada de carácter. 12. Escriba un ciclo que muestre verticalmente en la pantalla todos los elementos del arreglo. 13. Muestre el contenido del siguiente Arreglo:

int enteros[5] = {1,2,3}; 14. Muestre el contenido del siguiente arreglo:

char caracteres[5] = {‘C’, ‘+’, ‘+’};

15. ¿Dónde está el error en la siguiente definición de arreglo? char OOP[3] = “C++”;

¿Cómo corrige el problema en esta definición?

Page 70: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-70

16. Escriba un enunciado para definir un arreglo de punto flotante que se inicializará con ceros y es local para main()

17. ¿Dónde está el error en la siguiente definición del arreglo?

int números[4] = {0,1,2,3,4}; 18. Dada la siguiente definición de arreglo,

int valores[10];

a. Escriba un enunciado para colocar el producto del primero y segundo elementos de arreglo en la

posición del último elemento. Use la siguiente definición de arreglo para contestar las preguntas 19 - 23:

const int MAX = 4; char cadena[MAX] =“C++”;

19. Escriba el prototipo para una función de nombre longitudCadena() que recibirá el arreglo completo y

regresa la longitud de la cadena. 20. Escriba un prototipo para una función de nombre elementoCadena() que recibirá un elemento sencillo de la

cadena, de manera que cualquier operación sobre ese elemento dentro de la función no afectará el valor del elemento dentro del arreglo.

21. Escriba un enunciado para llamar la función de la pregunta 19 y pase el primer elemento del arreglo a la función.

22. Escriba un prototipo para una función de nombre cambiaElemento() que recibirá un solo elemento de la cadena, de manera que cualquier modificación en este elemento dentro de la función cambie el valor del elemento en el arreglo.

23. Escriba un enunciado para llamar a la función de la pregunta 22 y pase el último elemento del arreglo a la función.

24. En general, ¿qué posición del elemento se regresará por medio de las funciones de búsqueda secuencial y binaria desarrolladas en esta lección, si un elemento se presenta varias veces en el arreglo?

25. ¿Por qué, en promedio, es más rápida la búsqueda binaria que la secuencial? 26. ¿Cuándo es más rápida la búsqueda secuencial que la binaria? 27. Revise el algoritmo clasifPorInser() para ordenar el arreglo de modo descendente. PROBLEMAS 1. Escriba un programa para llenar un arreglo con todos los enteros impares desde 1 hasta 99. Escriba una

función para llenar el arreglo y otra para mostrar éste, desplegando en la pantalla los enteros impares separados por comas.

2. Escriba una función para leer el nombre del usuario desde la entrada del teclado y colóquelo en un arreglo de caracteres. Escriba otra función para mostrar el nombre del usuario almacenado en el arreglo. Verifique sus funciones por medio de un programa de aplicación.

3. Escriba un programa para leer una lista de 25 elementos de caracteres desde una entrada de teclado y muéstrelos en orden inverso. Use una función para llenar la lista con los elementos escritos y otra para mostrarla.

4. Escriba un programa que use seis arreglos de caracteres para almacenar el nombre del usuario, domicilio, ciudad, estado, código postal y número telefónico. Proporcione una función para llenar los arreglos y otra para mostrar el contenido del arreglo usando un formato de direccionamiento apropiado.

5. (La coladera de Eratóstenes) Un entero primo es cualquier entero que es divisible sólo entre él mismo y entre 1. La coladera de Eratóstenes es un método para encontrar números primos. Opera como sigue:

Page 71: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-71

a) Cree un arreglo con todos los elementos inicializados a 1 (verdadero) Los elementos del arreglo que tengan índices primos permanecerán en 1. Los demás elementos del arreglo en algún momento se establecerán a cero.

b) Comenzando por índice 2 (el índice 1 debe ser primo), cada vez que se encuentre un elemento del arreglo que sea 1, haga un ciclo por el resto del arreglo y establezca a cero todos los elementos cuyo índice sea un múltiplo de dicho índice. Para el índice 2, todos los elementos por encima de 2 que sean múltiplos de 2 se establecerán a cero (los índices 4, 6, 8, 10, etc.); en el caso del índice 3, todos los elementos por encima de 3 que sean múltiplos de 3 se establecerán a cero (índices 6, 9, 12, 15, etc.); y así sucesivamente.

Terminado este proceso, los elementos del arreglo que aún estén establecidos a 1 indicarán que el índice es un número primo, así que podrán imprimirse. Escriba un programa con un arreglo de 1000 elementos que determine e imprima los números primos entre 1 y 999. Ignore el elemento 0 del arreglo.

6. Por medio de un arreglo de un solo índice resuelva el siguiente problema. Una compañía paga a sus vendedores con base en una comisión. Los vendedores reciben $200 a la semana más 9% de sus ventas netas durante la semana. Por ejemplo, un vendedor cuyas ventas brutas son de $5000, a la semana recibe $200 más 9% de $5000, es decir un total de $650. Escriba un programa (con un arreglo de contadores) que determine la cantidad de vendedores que ganaron salarios dentro de los siguientes rangos (suponga que el salario de cada vendedor se cierra a una cifra entera):

a) $200 - $299 b) $300 - $399 c) $400 - $499 d) $500 - $599 e) $600 - $699 f) $700 - $799 g) $800 - $899 h) $900 - $999 i) $1000 o más

7. El ordenamiento por el método de la burbuja elaborado en el ejemplo 18.26. es ineficiente en el caso de arreglos grandes. Haga las siguientes modificaciones sencillas que mejorarán el desempeño del ordenamiento de burbuja.

a) Después de la primera pasada, se garantiza que la cifra mayor es el elemento de mayor índice del

arreglo, tras la segunda pasada, los dos números mayores están en su lugar, etc. En lugar de hacer nueve comparaciones con cada pasada, modifique el ordenamiento de burbuja para que efectúe ocho comparaciones en la segunda pasada, siete en la tercera, etc.

b) Los datos en el arreglo tal vez ya estén en el orden adecuado, o casi, así que ¿por qué hacer nueve

pasadas si tal vez baste con menos? Modifique el ordenamiento para comprobar al final de cada pasada si se han hecho intercambios. Si no ha sucedido ninguno, entonces los datos ya estarán en orden y deberá terminar el programa. Si han sucedido intercambios, entonces se necesita cuando menos otra pasada.

8. Escriba instrucciones que lleven a cabo las siguientes operaciones sobre arreglos de un solo índice. Debe ser una sola instrucción por cada una.

a) Inicialice a cero los 10 elementos del arreglo de enteros contador. b) Sume 1 a cada uno de los 15 elementos del arreglo de enteros bonos. c) Lea del teclado 12 valores para el arreglo float temperaturaMensual. d) Imprima los 5 valores del arreglo de enteros mejoresCalificaciones en formato de columnas

9. Encuentre el error o los errores en las siguientes instrucciones:

a) Suponga que: char cadena[5]; cin >> cadena; //El usuario teclea bello

b) Suponga que: int a[3];

cout << a[1] << “ “ << a[2] << “ “ << a[3] << endl;

Page 72: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-72

c) float f[3] = {1.1, 10.01, 100.001, 1000.0001}; 10. Modifique el ejemplo 18.32 para que la función moda() pueda manejar empates en el valor de la moda.

También modifique la función mediana() de modo que se promedien los dos elementos centrales cuando se trate de un arreglo con un número par de elementos.

11. Con un arreglo de un solo índice resuelva el siguiente problema. Lea 20 números entre 10 y 100, inclusive. A medida que se lea cada número, imprímalo sólo si no es un duplicado de algún número ya leído. Tome en cuenta el peor caso, cuando todos los números son diferentes. Resuelva este problema empleado el arreglo más pequeño posible.

12. Escriba un programa que simule el lanzamiento de dos dados. El programa deberá valerse de rand() para lanzar el primer dado y nuevamente rand() para lanzar el segundo. Luego debe calcular la suma de ambos valores. Nota: debido a que cada dado puede tener un valor entero de 1 a 6, entonces la suma de ambos valores variará de 2 a 12, siendo 7 la suma más frecuente y 2 y 12 las menos frecuentes. La figura 18.5 muestra las 36 combinaciones de dados posibles. Su programa deberá lanzar ambos dados 36,000 veces. Mediante un arreglo de un solo índice, registre la cantidad de veces que aparece cada suma. Imprima el resultado en formato de tabla. Además determine si los totales son razonables, es decir, hay seis maneras de lanzar 7, por lo que aproximadamente una sexta parte de los lanzamientos debe ser 7.

1 2 3 4 5 6

1 2 3 4 5 6 7 2 3 4 5 6 7 8 3 4 5 6 7 8 9 4 5 6 7 8 9 10 5 6 7 8 9 10 11 6 7 8 9 10 11 12

Figura 18.5. Las 36 posibles combinaciones de lanzamiento de dos dados.

13. ¿Qué hace el siguiente problema?

// Nombre del programa: PROBLEMA11.CPP #include <iostream.h> int queEsEsto(int[], int); void main(void) { const int TAMANO_ARREGLO = 10;

int a[TAMANO_ARREGLO] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int resultado = queEsEsto(a, TAMANO_ARREGLO); cout << “El resultado es: “ << resultado << endl;

}

int queEsEsto(int b[], int tamano) {

if(tamano == 1) return b[0]; else return b[tamano – 1] + queEsEsto(b, tamano – 1);

} 14. Escriba un programa que ejecute 1000 juegos de dados y conteste las siguientes preguntas:

a) ¿Cuántos juegos se ganan al primer lanzamiento, al segundo, ..., al vigésimo y después de veinte lanzamientos?

b) ¿Cuántos juegos se pierden al primer lanzamiento, al segundo, ..., al vigésimo y después de veinte lanzamientos?

c) ¿Cuáles son las posibilidades de ganar jugando a los dados? (Nota: deberá descubrir que los dados son uno de los juegos de casino más justos. ¿Qué supone que significa esto?)

Page 73: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-73

d) ¿cuál es la duración promedio de un juego de dados? e) ¿Mejoran las posibilidades de ganar a medida que aumenta la duración del juego?

15. (Sistema de reservaciones de una aerolínea) Una línea aérea pequeña acaba de comprar una computadora para su nuevo sistema automatizado de reservaciones. Se le ha pedido a usted que programa este sistema. Deberá escribir un programa que asigne asientos en cada vuelo del único avión de la compañía (capacidad: 10 asientos)

Su programa deberá presentar el siguiente menú de alternativas:

Por favor teclee 1 para fumar. Por favor teclee 2 para no fumar.

Si se teclea 1, el programa deberá asignar un asiento en la sección de fumadores (asientos 1 a 5) Si se teclea 2, se asignará un asiento en la sección de no fumar (asientos 6 a 10). El programa deberá imprimir un pase de abordar que indique el número de asiento del pasajero y si se trata de la sección de fumar o de no fumar. Mediante un arreglo de un solo índice, represente la gráfica de asientos del avión. Inicialice a cero todos

los elementos del arreglo, indicando que todos los asientos están vacíos. A medida que se asigne un asiento, establezca a 1 el elemento correspondiente, indicando que ya no está disponible. El programa, claro está, no deberá volver a asignar un asiento ya asignado. Cuando la sección de

fumadores esté llena, el programa deberá preguntar si es aceptable asignar un asiendo en la sección de no fumar (y viceversa) Si la respuesta es sí, entonces haga la asignación. Si no, imprima el mensaje el próximo vuelo parte en tres horas.

16. ¿Qué hace el siguiente programa?

// PROGRAMA14.CPP #include <iostream.h> void ciertaFunción(int[], int); void main(void) {

const int TAMANO_ARREGLO = 10; int a[TAMANO_ARREGLO] = {32, 27, 64, 18, 95, 14, 90, 70, 60, 37}; cout << “Los valores en el arreglo son: “ << endl; ciertaFuncion(a, TAMANO_ARREGLO); cout << endl;

}

void ciertaFuncion(int b[], int tamano) { if(tamano > 0) { ciertaFuncion(&b[1], tamano – 1); cout << b[0] << “ “; } }

17. Escriba un programa para verificar la función busquedaSec() desarrollado en esta lección. Use las funciones srand() y rand() disponibles en stdlib.h para llenar un arreglo con valores enteros aleatorios antes de aplicar busquedaSec() Primero se deberá hacer una llamada a srand(1) para inicializar el generador de número aleatorio. Después, una llamada a rand() regresará un valor entero entre cero y RAND_MAX, donde su compilador define a RAND_MÁX. Por ejemplo, el siguiente código generará 10 enteros aleatorios entre 0 y 99:

Page 74: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-74

srand(1); for(int l = 0; 1 < 10; ++1)

cout << rand() % 100 << endl;

Primero se llama a la función srand() con un valor de argumento de 1 para inicializar el generador de número aleatorio de manera que no se generan los mismos 10 números cada vez que se ejecuta el ciclo. La operación %100 escala el valor que regresa rand() para producir un rango de valores entre 0 y 99.

18. Escriba un programa para verificar la función busquedaBin() desarrollada en esta lección. Use las funciones estándar srand() y rand() disponibles en stdlibh para llenar un arreglo con valores enteros aleatorios como se describió en el problema 5. Después, aplique clasifPorInser() para clasificar el arreglo antes de usar busquedaBin() Nota: si usa el código clasifPorInser() desarrollado en esta lección, deberá cambiar el código para ordenar un arreglo de enteros en lugar de un arreglo de caracteres.

19. Aquí está la solución iterativa para una búsqueda binaria en forma de seudocódigo:

busquedaBin() Inicio

encuentra = Falso. mientras( !encuentra AND primero <= ultimo) hacer

mitad = (primero + último) / 2. si (elemento == A[mitad]) entonces

encuentra = Verdadero. sino

si (elemento < A[mitad]) entonces ultimo = mitad –1..

sino primero = mitad +1..

si (encuentra) entonces regresar mitad..

sino regresar -1.

Fin. Codifique este algoritmo como una función C++ para buscar un elemento dado en un arreglo de enteros. Escriba un programa de aplicación para verificar la función. Recuerde clasificar el arreglo usando una función de clasificación antes de llamar la función de búsqueda binaria.

20. Otro algoritmo de clasificación iterativa común es clasificación por burbuja; aquí está el algoritmo:

clasifPorBurbuja() Inicio

pasos = 1.. intercambio = Verdadero. mientras(pasos < número de elementos del arreglo) AND (intercambio == Verdadero)

intercambio = Falso. para índice = (primer índice del Arreglo) hasta (último índice del Arreglo - pasos)

si A[índice] > A[índice + 1] Permuta(A[índice], A[índice + 1]. intercambio = Verdadero.

pasos = pasos + 1. Fin.

El algoritmo de clasificación por burbuja hace algunos pasos a través del arreglo, comparando valores adyacentes durante cada paso. Los valores adyacentes se intercambian si el primer valor es más grande que el segundo. El proceso termina cuando se han hecho los pasos n – 1 (en donde n es el número de elementos en el arreglo) o cuando no son posibles más intercambios.

Su trabajo es codificar el algoritmo anterior como una función C++ de nombre clasifPorBur() Además, tendrá que codificar una función permuta() que se pueda llamar por

Page 75: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-75

medio de la función clasifPorBur() para intercambiar dos elementos del arreglo como se muestra en el algoritmo. Escriba sus funciones para clasificar un arreglo de caracteres. Incorpórelas dentro de un programa para que verifique el procedimiento de clasificación. ¿Por qué la clasificación por burbuja es menos eficiente que la clasificación por inserción?

21. Otro algoritmo de clasificación iterativa común es clasificación por selección. El algoritmo es como éste:

clasifPorSelec() Inicio

para indice1 = (primer índice del arreglo) hasta (ultimo índice del arreglo) hacer posición = indice1. masPequeno = A[Posicion]. para indice2 = (indice1 + 1) hasta (ultimo índice del arreglo) hacer

si A[indice2] < masPequeno posicion = indice2. masPequeno = A[posicion].

A[posicion] = A[indice1]. A[indice1] = masPequeno.

Fin. Como en la clasificación por burbuja, la clasificación por selección realiza diversos pasos a través del arreglo. En el primero de ellos examina al arreglo completo y coloca los elementos más pequeños en la primera posición del arreglo. En el segundo paso se examina el arreglo empezando en el segundo elemento. Se encuentra el elemento más pequeño en el segmento del arreglo y se coloca en la segunda posición del arreglo. El tercer paso examina el arreglo empezando en el tercer elemento, encuentra el elemento más pequeño en el segmento del arreglo y lo coloca en la tercera posición del elemento. El proceso continúa hasta que no quedan más segmentos del arreglo. Su trabajo es codificar el algoritmo anterior como una función C++ de nombre clasifPorSelec() Escriba su función para clasificar un arreglo de caracteres. Después úsela en un programa que verifique el procedimiento de clasificación. ¿Por qué la clasificación por selección es menos eficiente que la clasificación por inserción?

22. Escriba un programa que tome un arreglo entero sin ordenar y encuentre la localización del valor máximo en el arreglo. (Sugerencia: copie el arreglo dentro de otro y clasifique este segundo arreglo para determinar su valor máximo. Después busque el arreglo original para este valor)

Problemas de recursividad 23. (Ordenamiento por selección) El ordenamiento por selección busca en un arreglo el elemento más pequeño, el

cual se intercambia con el primero del arreglo. El proceso se repite con el subarreglo que comienza en el segundo elemento del arreglo. Cada pasada por el arreglo da como resultado la colocación de un elemento en su lugar correcto. El desempeño de este ordenamiento es comparable con el ordenamiento de burbuja: para un arreglo de n elementos, se necesita hacer n-1 pasadas y por cada subarreglo, hay que hacer n-1 comparaciones para encontrar el valor más pequeño. Cuando el subarreglo que se está procesando sólo contiene un elemento, el arreglo está ordenado. Escriba la función recursiva clasifOrdenamiento()

24. (Palíndromos) Un palíndromo es una cadena que se pronuncia igual hacia delante que hacia atrás. Algunos ejemplos son: radar, anilina y dábale arroz a la zorra el abad. Escriba una función recursiva pruebaPalindrome() que devuelva verdadero si la cadena almacenada en el arreglo es un palíndromo y falso si no. La función deberá ignorar los espacios y la puntuación en la cadena.

25. (Búsqueda lineal) Modifique el ejemplo 18.33 para que emplee la función recursiva busquedaBinaria() para llevar a cabo una búsqueda lineal en el arreglo. La función deberá recibir como argumentos un arreglo de enteros y su tamaño. Si se encuentra la clave de búsqueda, devuelva el índice del arreglo; de otro modo, devuelva –1.

26. (Búsqueda binaria) Modifique el programa del ejemplo 18.34 para que utilice una función recursiva busquedaBinaria() que lleve a cabo una búsqueda binaria en el arreglo. La función deberá recibir como argumentos un arreglo de enteros y el índice inicial y el final. Si se encuentra la clave de búsqueda, devuelva el índice del arreglo; de otro modo, devuelva –1.

Page 76: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-76

27. (Impresión de un arreglo) Escriba una función recursiva imprimirArreglo() que tome como argumentos un arreglo y su tamaño y no devuelva nada. La función deberá terminar el proceso y regresar cuando reciba un arreglo de tamaño cero.

28. (Impresión inversa de una cadena) Escriba una función recursiva cadenaInvertida() que tome un arreglo de caracteres que contenga como argumento una cadena, la imprima al revés y no devuelva nada. La función deberá terminar el proceso y regresar cuando se encuentre el carácter nulo de terminación.

29. (Encuentre el valor mínimo dentro de un arreglo) Escriba una función recursiva minimoRecursivo() que tome como argumentos un arreglo de enteros y su tamaño y devuelva su elemento más pequeño. La función deberá terminar el proceso y regresar cuando reciba un arreglo de 1 elemento

EXAMEN BREVE 34 1. Los dos componentes principales de un arreglo son: ______________ y ___________. 2. Verdadero o falso: Los elementos dentro de un arreglo determinado pueden ser de cualquier

combinación de clase de datos.

EXAMEN BREVE 35 1. Defina un arreglo de nombre registrosDeExamen que almacene hasta 15 registros de exámenes. 2. ¿Cuál es la dimensión del arreglo en la pregunta 1? 3. ¿Cuál es el índice del primer elemento del arreglo en la pregunta 1? 4. ¿Cuál es el índice del último elemento del arreglo en la pregunta 1? 5. Defina un arreglo de nombre esteSemestre que almacene elementos de una clase enumerada de

nombre cursos, que incluya los cursos que usted está tomando este semestre. 6. Suponga que el índice del último elemento en un arreglo es [25] ¿Cuántos elementos almacenará el

arreglo?

EXAMEN BREVE 36 1. Escriba un ciclo for que llene el siguiente arreglo desde la entrada del usuario: char caracteres[15]; 2. Escriba un ciclo for que muestre el contenido del arreglo de la pregunta 1.

EXAMEN BREVE 37 1. Verdadero o falso: el nombre del arreglo es la dirección del índice[1] del arreglo. 2. Escriba un prototipo para una función de nombre muestra() que modifique al siguiente arreglo:

char caracteres[15]; Suponga que la función no regresa ningún valor excepto el arreglo modificado.

3. Escriba un prototipo para una función de nombre prueba() que modifique un solo elemento del arreglo que se definió en la pregunta 2.

4. Escriba un enunciado que llamará a la función prototipo elaborada en la pregunta 3 para modificar el elemento almacenado en el índice[5] del arreglo definido en la pregunta 2.

Page 77: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-77

EXAMEN BREVE 38

1. Defina un arreglo e inicialícelo con los valores enteros de -3 hasta +3. 2. ¿Cuál es la dimensión del arreglo que definió en la pregunta 1? 3. Muestre el contenido del siguiente arreglo:

char lenguaje[51 = {‘C’, ‘+’, ‘+’}; 4. Muestre el contenido del siguiente arreglo:

char lenguaje[] = " C++"; 5. Suponga que define globalmente un arreglo de caracteres sin valores de iniciación. ¿Qué almacena

el compilador en el arreglo?

RESPUESTA EXAMEN BREVE 34 1. Los dos componentes principales de un arreglo son: indice y elemento. 2. Falso: Todos los elementos dentro de un arreglo deben de ser de la misma clase de datos.

RESPUESTA EXAMEN BREVE 35 1. float registrosDeExamen[15]; 2. La dimensión del arreglo de la pregunta 1 es 1 ××××15. 3. El índice del primer elemento del arreglo de la pregunta 1 es [0] 4. El índice del último elemento en el arreglo de la pregunta 1 es [14] 5. enum cursos {Computacion, Matematicas, Física, Ingles, Oratoria};

cursos esteSemestre[5]; 6. El arreglo almacenará 26 elementos, dado que el primer índice de elemento es [0]

RESPUESTA EXAMEN BREVE 36 1. El ciclo for que se necesita para llenar el arreglo char caracteres[15]; es:

for(int indice = 0; indice < 15; ++indice) cin >> caracteres[indice];

2. El ciclo for que mostrará el contenido del arreglo anterior es: for(int indice = 0; indice < 15; ++indice)

cout << caracteres[indice] << ‘\t’;

RESPUESTA EXAMEN BREVE 37 1. Falso: El nombre del arreglo se localiza en el índice[0] del mismo. 2. Un prototipo para una función llamada muestra() que debe modificar el arreglo char caracteres[15]

es: void muestra(char caracteres[15]);

Page 78: Leccion 18

MIGUEL Á. TOLEDO MARTÍNEZ

ARREGLOS, APUNTADORES Y ESTRUCTURAS – LECCIÓN 18 18-78

3. Un prototipo para una función llamada prueba() que modificará sólo un elemento en el arreglo de la pregunta 2 es:

void prueba(char &elementoDelArreglo); 4. Un enunciado que llamará a la función anterior para modificar el elemento almacenado en el

índice[5] del arreglo en la pregunta 2 es: prueba(caracteres[5]);

RESPUESTA EXAMEN BREVE 38 1. La definición de un arreglo inicializado con los valores enteros de –3 a +3 es:

int numeros[7] = [-3, -2, -1, 0, 1, 2, 3}; o int numeros[] = [-3, -2, -1, 0, 1, 2, 3};

2. La dimensión del arreglo antes mencionado es 1 x 7. 3. El contenido del arreglo char lenguaje[51 = {‘C’, ‘+’, ‘+’}; es:

[‘C] [‘+’] [‘+’] [‘\0’] [‘\0’] 4. El contenido del arreglo char lenguaje[] = "C++"; es;

[‘C] [‘+’] [‘+’] [‘\0’] 5. Un carácter terminador nulo (‘\0’) se coloca en cada posición de un arreglo de caracteres definido

globalmente sin ningún valor de inicialización.