06.05.2013 Views

CONTENIDO DE LA LECCIÓN 18

CONTENIDO DE LA LECCIÓN 18

CONTENIDO DE LA LECCIÓN 18

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

MIGUEL Á. TOLEDO MARTÍNEZ<br />

<strong>CONTENIDO</strong> <strong>DE</strong> <strong>LA</strong> <strong>LECCIÓN</strong> <strong>18</strong><br />

ALMACENAMIENTO <strong>DE</strong> DATOS EN ARREGLOS<br />

1. Introducción 3<br />

2. Estructura de un arreglo 3<br />

2.1. Elementos del arreglo 5<br />

2.2. Índice del arreglo 5<br />

3. Examen breve 34 76<br />

4. Definición de arreglos unidimensionales en C++ 5<br />

4.1. Formato para un arreglo unidimensional 5<br />

4.2. Ejemplo <strong>18</strong>.1 6<br />

5. Examen breve 35 76<br />

6. El acceso a los arreglos 6<br />

6.1. Inserción de elementos en los arreglos unidimensionales 6<br />

6.1.1. Asignación directa 6<br />

6.1.1.1. Formato de asignación directa (inserción de elementos en un arreglo) 6<br />

6.1.2. Sugerencia de depuración 7<br />

6.1.3. Lectura de los elementos del arreglo 7<br />

6.1.4. Inserción de elementos del arreglo con el uso de ciclos 8<br />

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

7. Extracción de elementos de los arreglos unidimensionales 9<br />

7.1. Asignación directa 9<br />

7.1.1. Formato de asignación directa (extracción de elementos del arreglo) 9<br />

7.1.2. Escritura de elementos del arreglo 10<br />

7.1.3. Extracción de elementos del arreglo con el uso de ciclos 10<br />

7.1.4. Sugerencia de depuración 11<br />

8. Examen breve 36 76<br />

9. Ejemplos <strong>18</strong>.2, <strong>18</strong>.3, <strong>18</strong>.4, <strong>18</strong>.5, <strong>18</strong>.6, <strong>18</strong>.7, <strong>18</strong>.8, <strong>18</strong>.9, <strong>18</strong>.10, <strong>18</strong>.11, <strong>18</strong>.12, <strong>18</strong>.13, <strong>18</strong>.14,<br />

<strong>18</strong>.15, <strong>18</strong>.16, <strong>18</strong>.17. 12<br />

10. Paso de arreglos y elementos de arreglos a las funciones 21<br />

10.1. Ejemplos <strong>18</strong>.<strong>18</strong>, <strong>18</strong>.19, <strong>18</strong>.20, <strong>18</strong>.21, <strong>18</strong>.22, <strong>18</strong>.23, <strong>18</strong>.24, <strong>18</strong>.25 21<br />

11. Examen breve 37 76<br />

12. Ordenamiento de arreglos 31<br />

12.1. Ejemplos <strong>18</strong>.26, <strong>18</strong>.27, <strong>18</strong>.28, <strong>18</strong>.29, <strong>18</strong>.30, <strong>18</strong>.31, <strong>18</strong>.32 32<br />

13. Búsqueda en arreglos: Búsqueda lineal y búsqueda binaria 40<br />

13.1. Ejemplos <strong>18</strong>.33, <strong>18</strong>.34, <strong>18</strong>.35 40<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-1


MIGUEL Á. TOLEDO MARTÍNEZ<br />

14. Solución de problemas en acción: Búsqueda en un arreglo con iteración<br />

(búsqueda secuencial) 45<br />

14.1. Problema 45<br />

14.2. Definición del problema 45<br />

14.3. Planeación de la solución 46<br />

14.4. Codificación del programa 46<br />

15. Solución de problemas en acción: Como ordenar un arreglo con iteración<br />

(ordenación por inserción) 47<br />

15.1. Problema 47<br />

15.2. Definición del problema 47<br />

15.3. Planeación de la solución 48<br />

15.4. Codificación del problema 50<br />

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

(búsqueda binaria) 51<br />

16.1. Problema 51<br />

16.2. Definición del problema 51<br />

16.3. Planeación de la solución 52<br />

16.4. Codificación del problema 55<br />

17. Ejemplos <strong>18</strong>.36, <strong>18</strong>.37 56<br />

<strong>18</strong>. Iniciación de arreglos 61<br />

<strong>18</strong>.1. Iniciación predeterminada de arreglos globales y estáticos 63<br />

19. Examen breve 38 77<br />

20. Arreglos que rebasan los 64 kbytes de memoria 65<br />

20.1. Ejemplos <strong>18</strong>.38 y <strong>18</strong>.39 65<br />

21. Lo que necesita saber 66<br />

22. Preguntas y problemas 68<br />

22.1. Preguntas 68<br />

22.2. Problemas 70<br />

22.3. Problemas de recursividad 75<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-2


MIGUEL Á. TOLEDO MARTÍNEZ<br />

<strong>LECCIÓN</strong> <strong>18</strong><br />

ALMACENAMIENTO <strong>DE</strong> DATOS EN ARREGLOS<br />

INTRODUCCIÓN<br />

Esta lección tratará un tema muy importante en cualquier lenguaje de programación: los<br />

arreglos. No es posible enfatizar sobremanera la importancia de los arreglos, pues ellos mismos<br />

dan origen a muchas aplicaciones.<br />

En muchas ocasiones, sus programas requerirán almacenar varios valores, tales como 50<br />

calificaciones, 10 títulos de libros, 1000 nombres de archivos, etc. Cuando sus programas<br />

necesitan almacenar varios valores, entonces define un arreglo, especificando su clase de datos,<br />

nombre y número de elementos que el arreglo almacenará.<br />

Un arreglo es una estructura de datos indexados que se utiliza para almacenar elementos de datos<br />

de la misma clase.<br />

Los arreglos simplemente proporcionan un medio organizado para localizar y almacenar<br />

datos, así como el apartado postal en el correo de su oficina postal local proporciona un medio<br />

organizado de localizar y clasificar el correo. Por esto a un arreglo se le conoce como una<br />

estructura de datos que se usa para almacenar cualquier clase de datos, incluyendo enteros,<br />

flotantes, caracteres, arreglos, apuntadores, y registros (structs) Además, los arreglos son tan<br />

versátiles que se pueden usar para implementar otras estructuras de datos, como pilas, colas,<br />

listas ligadas y árboles binarios, de hecho en algunos lenguajes corno FORTRAN, el arreglo es la<br />

única estructura de datos disponible para el programador, porque, por medio del uso de arreglos<br />

es posible implementar la mayor parte de otras estructuras.<br />

Los objetivos de esta lección son:<br />

• Presentar la estructura de datos de arreglo.<br />

• Declarar un arreglo dentro de su programa.<br />

• Comprender cómo se declaran e inicializan los arreglos y de qué manera se hace referencia a<br />

los elementos de un arreglo.<br />

• Aprender a pasar los arreglos a las funciones<br />

• Aprender a utilizar los arreglos para almacenar, ordenar y hacer búsquedas en listas y tablas<br />

de valores.<br />

• Discernir las técnicas básicas para el ordenamiento.<br />

ESTRUCTURA <strong>DE</strong> UN ARREGLO<br />

Un arreglo es una estructura de datos. En otras palabras, un arreglo consta de elementos<br />

de datos organizados o estructurados en una forma particular. Esta estructura de datos<br />

proporciona un medio conveniente para almacenar grandes cantidades de datos en la memoria<br />

primaria o del usuario. Existen arreglos unidimensionales o listas y multidimensionales.<br />

En esta lección revisaremos los arreglos unidimensionales; en la lección 19<br />

estudiaremos los arreglos multidimensionales.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-3


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Para obtener la idea de un arreglo, observe la ilustración de la figura <strong>18</strong>.1. En esta figura<br />

se puede ver una sola línea de cajas de apartados postales como se encuentran en cualquier<br />

oficina de correos. Como se sabe, cada caja tiene un número de apartado postal (A. P.) En la<br />

figura <strong>18</strong>.1, los números del A. P. empiezan con cero y van hasta un número finito n. ¿Cómo se<br />

localiza una determinada caja? Por medio del número de apartado postal, ¿correcto? Sin<br />

embargo, el número del A. P. no tiene nada que ver con lo que contiene la caja. Simplemente se<br />

usa para localizar una caja determinada. Desde luego, el contenido es la correspondencia enviada<br />

a esta caja. La razón por la que el servicio postal usa el método de apartado postal es que<br />

proporciona un método conveniente y bien organizado para almacenar y tener acceso a la<br />

correspondencia de sus clientes. Un arreglo hace lo mismo en un programa de computadora;<br />

proporciona un método conveniente y bien organizado para almacenar y tener acceso a los datos<br />

para usted, el programador. De modo que, ¿cuántos apartados postales hay en la figura <strong>18</strong>.1?<br />

Debido a que el primer número del apartado es 0 y el último es n, habrá n + 1 apartados.<br />

APARTADO<br />

POSTAL 0<br />

Figura <strong>18</strong>.1.Un arreglo unidimensional o lista es como una fila de buzones de apartados en la<br />

oficina de correos.<br />

Imagine que un arreglo unidimensional, como el que se muestra en la figura <strong>18</strong>.2, es<br />

similar a una línea de cajas de apartados postales. El arreglo unidimensional consiste en una sola<br />

fila de lugares de almacenamiento, cada una etiquetada con un número que se conoce con el<br />

nombre de índice. Cada localización del índice se utiliza para almacenar una clase de datos<br />

determinada. Los datos almacenados en una localización de índice determinada se conoce como<br />

elemento del arreglo. De esta manera, un arreglo unidimensional es una lista secuencial de<br />

lugares de almacenamiento que contiene elementos de datos individuales que se localizan o<br />

accedan por medio de índices.<br />

Elemento<br />

0<br />

APARTADO<br />

POSTAL 1<br />

Elemento<br />

1<br />

APARTADO<br />

POSTAL 2<br />

Elemento<br />

2<br />

APARTADO<br />

POSTAL 3<br />

Elemento<br />

3<br />

[0] [1] [2] [3] [n]<br />

ÍNDICES<br />

Figura <strong>18</strong>.2 Un arreglo unidimensional, también llamado lista, es una lista secuencial de<br />

localizaciones de almacenamiento que contiene los elementos de datos que se<br />

localizan por medio de los índices.<br />

Los dos componentes principales de un arreglo son los elementos almacenados en el<br />

arreglo y los índices que localizan los elementos almacenados. ¡No se confunda con estos dos<br />

componentes del arreglo! Aunque los elementos del arreglo y los índices se relacionan, son<br />

cantidades completamente separadas, al igual que el contenido de una caja de apartado postal es<br />

diferente de su número de apartado. Con esto presente, vamos a explorar los elementos e índices<br />

del arreglo un poco más a fondo.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-4<br />

...<br />

...<br />

APARTADO<br />

POSTAL n<br />

Elemento<br />

n


MIGUEL Á. TOLEDO MARTÍNEZ<br />

ELEMENTOS <strong>DE</strong>L ARREGLO<br />

Los elementos de un arreglo son los datos almacenados en éste y pueden ser de<br />

cualquier clase de datos que se hayan visto. De esta manera, un arreglo dado puede almacenar<br />

elementos enteros, elementos de punto flotante, caracteres, y elementos booleanos. Además,<br />

de estos elementos de clase de datos estándar, un arreglo también es útil para almacenar<br />

elementos de datos enumerados. De hecho, aun los elementos de un arreglo pueden ser otros<br />

arreglos. Sin embargo, existe una restricción importante que se aplica a los elementos del<br />

arreglo: todos los elementos de un arreglo determinado deberán ser de la misma clase de datos.<br />

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

definición es especificar la clase de los elementos que el arreglo almacenará. Una vez que se ha<br />

definido un arreglo determinado para cierta clase de datos, sólo los elementos de esa clase de<br />

datos se almacenarán en ese arreglo.<br />

ÍNDICES <strong>DE</strong>L ARREGLO<br />

Los índices del arreglo localizan a los elementos del arreglo, en C++, el compilador en<br />

forma automática asigna índices enteros a la lista de elementos del arreglo empezando con el<br />

índice 0. Por lo tanto, el primer elemento del arreglo en la figura <strong>18</strong>.2 se localiza en el índice 0,<br />

y el último elemento se localiza en el índice n. Los índices empiezan con 0 y van a n, por tanto<br />

habrá n + 1 elementos en el arreglo. También, debido a que es un arreglo unidimensional, o<br />

lista, decimos que tiene una dimensión de 1 ×××× (n + 1) lo cual significa que hay un renglón de n +<br />

1 elementos. La dimensión de un arreglo indica el tamaño del arreglo, justo como la dimensión<br />

de una pieza de madera indica su tamaño.<br />

<strong>DE</strong>FINICIÓN <strong>DE</strong> ARREGLOS UNIDIMENSIONALES EN C++<br />

cosas:<br />

En C++ todos los arreglos deberán estar definidos. Para definir un arreglo, especifique 3<br />

1. La clase de datos de los elementos del arreglo.<br />

2. El nombre del arreglo.<br />

3. El tamaño del arreglo.<br />

El formato general es como sigue:<br />

EXAMEN BREVE 34<br />

FORMATO PARA UN ARREGLO UNIDIMENSIONAL<br />

[];<br />

Lo primero que se ve en la definición es la clase de datos de los elementos del arreglo.<br />

La clase de datos del arreglo es seguida por el identificador del arreglo, su nombre, el cual es<br />

seguido por el número de elementos que almacena el arreglo dentro de los corchetes, [] La<br />

definición termina con un punto y coma. Por ejemplo, la siguiente es una definición de un<br />

arreglo de 10 caracteres cuyo nombre es caracteres.<br />

char caracteres [10];<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-5


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.1<br />

Escriba las definiciones para los siguientes arreglos:<br />

a. Un arreglo llamado enteros que almacenará 10 enteros.<br />

b. Un arreglo llamado reales que almacenará 5 valores de punto flotante.<br />

c. Un arreglo llamado caracteres que almacenará 11 caracteres.<br />

d. Un arreglo llamado clase que almacenará calificaciones de 25 estudiantes. Suponga que las<br />

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

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

a. int enteros[10];<br />

b. float reales[5];<br />

c. char caracteres[11];<br />

d. enum calificaciones {R, D, C, B, A};<br />

e. calificaciones clase[25];<br />

Solución<br />

El índice que localiza el último elemento en cada uno de los arreglos anteriores es uno<br />

menos que el tamaño definido del arreglo.<br />

En cada una de las definiciones anteriores se menciona primero la clase de datos del elemento, seguida por<br />

el identificador del arreglo, después el tamaño del arreglo encerrado en corchetes. Cada una de las<br />

definiciones es muy obvia, excepto quizá la definición del arreglo clase. En esta definición, la clase de<br />

datos del arreglo es la clase de datos enumerada llamada calificaciones, que deberá anunciarse antes de la<br />

definición del arreglo. Por tanto, diremos que el arreglo clase puede almacenar elementos cuya clase de<br />

datos sean calificaciones. De esta manera, los elementos que se pueden almacenar en el arreglo clase se<br />

limitan a los elementos de la clase de datos enumerados de R, D, C, B y A. Observe que el compilador no<br />

los considera como caracteres, sino como elementos de una clase de datos enumerados llamada<br />

calificaciones.<br />

EL ACCESO A LOS ARREGLOS<br />

Tener acceso al arreglo significa insertar elementos dentro del arreglo para almacenar u<br />

obtener elementos almacenados desde el arreglo.<br />

INSERCIÓN <strong>DE</strong> ELEMENTOS EN LOS ARREGLOS UNIDIMENSIONALES<br />

Hay básicamente 3 formas principales de insertar elementos dentro de un arreglo:<br />

mediante un enunciado de asignación directa, mediante lectura o usando ciclos.<br />

ASIGNACIÓN DIRECTA<br />

EXAMEN BREVE 35<br />

El formato general para insertar un elemento, dentro un arreglo, con asignación directa<br />

es como sigue:<br />

FORMATO <strong>DE</strong> ASIGNACIÓN DIRECTA (INSERCIÓN <strong>DE</strong> ELEMENTOS EN UN ARREGLO)<br />

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

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-6


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Con las siguientes definiciones de arreglos:<br />

char caracteres[6];<br />

int enteros[3];<br />

las asignaciones directas pueden ser como estas:<br />

caracteres [0] ='H';<br />

caracteres [5] ='\0';<br />

enteros [0] = 16;<br />

enteros [2] = -22;<br />

En cada uno de estos ejemplos se coloca un elemento en la primera y en la última<br />

posición de almacenamiento del arreglo respectivamente. El carácter 'H' se coloca en la primera<br />

posición del arreglo caracteres y el terminador nulo se coloca en la última posición de este<br />

arreglo. Recuerde que la primera posición de este arreglo es siempre [0] y la última posición del<br />

arreglo es siempre uno menos que el tamaño del arreglo. El entero 16 se coloca en la primera<br />

posición del arreglo enteros y el entero -22 se coloca en la última posición de este arreglo.<br />

Observe que se menciona el nombre respectivo del arreglo, seguido por el índice del<br />

arreglo dentro de corchetes. Entonces se usa un operador de asignación (=) seguido por el<br />

elemento que se va a insertar. La clase de datos del elemento que se inserta deberá ser la misma<br />

que aquella definida para los elementos del arreglo; de otra manera, se obtendrán resultados<br />

impredecibles cuando se trabaje con los elementos del arreglo.<br />

SUGERENCIA <strong>DE</strong> <strong>DE</strong>PURACIÓN<br />

Recuerde que los índices de un arreglo en C++ son valores enteros. Esto significa que cualquier índice<br />

especificado se convierte en su equivalente entero. Por ejemplo, es posible especificar un índice como un carácter<br />

como este: arreglo['A’]; sin embargo, C++ trata esto como arreglo[65], porque, desde el código ASCII, el<br />

equivalente entero del carácter ‘A’ es 65. De la misma manera, se puede especificar un índice usando un valor de<br />

punto flotante, como este: arreglo[1.414], sin embargo, C++ lo ve como arreglo[1], porque la parte entera de<br />

1.414 es 1. Los elementos de datos enumerados también se pueden usar como índices, porque el compilador iguala<br />

los elementos enumerados a enteros, de acuerdo con el orden listado en el enunciado de la clase de datos<br />

enumerados. Para evitar confusión y problemas potenciales, se sugiere utilizar siempre valores enteros en los<br />

índices, a menos que la aplicación específicamente indique otra cosa.<br />

LECTURA <strong>DE</strong> LOS ELEMENTOS <strong>DE</strong>L ARREGLO<br />

También es posible usar cualquiera de las funciones u objetos de entrada de C o C++ para<br />

insertar los elementos del arreglo desde un teclado, como sigue:<br />

cin > caracteres[1];<br />

cin > enteros[0];<br />

En este caso, el usuario deberá escribir el valor del elemento del arreglo respectivo desde<br />

el teclado y oprimir la tecla ENTER para ejecutar cada enunciado. Deberá escribir un carácter<br />

para el primer enunciado cin y un entero para el segundo cin. (¿Por qué?) El carácter escrito<br />

desde el teclado se almacenará en la segunda posición (índice[1]) del arreglo caracteres,<br />

mientras que el entero escrito desde el teclado se almacenará en la primera posición (índice[0])<br />

del arreglo enteros.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-7


MIGUEL Á. TOLEDO MARTÍNEZ<br />

INSERCIÓN ELEMENTOS <strong>DE</strong> ARREGLOS CON EL USO <strong>DE</strong> CICLOS<br />

La desventaja obvia al usar asignaciones directas para insertar elementos de arreglos es<br />

que se requiere un enunciado de asignación separado para llenar cada posición del arreglo. Puede<br />

automatizar el proceso de inserción usando una estructura de ciclo. Aunque cualquiera de las tres<br />

estructuras de ciclo (while, do/while, for) se puede emplear, la estructura for es la más común. A<br />

continuación está el formato general para el uso de un ciclo for:<br />

INSERCIÓN <strong>DE</strong>NTRO <strong>DE</strong> UN ARREGLO UNIDIMENSIONAL CON EL USO <strong>DE</strong> UN CICLO for<br />

Considere el siguiente programa:<br />

for(int indice = 0; indice < tamaño del arreglo; ++indice)<br />

<br />

//LLENADO <strong>DE</strong> UN ARREGLO CON UN CICLO for<br />

#include // Para cin y cout<br />

// <strong>DE</strong>C<strong>LA</strong>RA EL TAMAÑO <strong>DE</strong>L ARREGLO<br />

const int MAX = 10;<br />

void main(void)<br />

{<br />

int muestra[MAX]; // <strong>DE</strong>FINE UN ARREGLO ENTERO<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

del arreglo muestra[MAX – 1] Cuando el contador de ciclo se incremento al valor MAX al final<br />

de la última iteración de ciclo, éste se rompe y no se insertan más elementos dentro del arreglo.<br />

¡Eso es todo! ¡El arreglo está lleno!<br />

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

usando las definiciones anteriores, considere este ciclo:<br />

for (int i = 0; i < MAX; ++i)<br />

muestra[i] = 2 * i;<br />

Esta vez, los elementos del arreglo se asignan dos veces al valor del contador de ciclo con<br />

cada iteración de ciclo. ¿Qué valores se insertan realmente dentro del arreglo? ¿Qué pasa<br />

con los 10 enteros pares de 0 a <strong>18</strong>?<br />

EXTRACCIÓN <strong>DE</strong> ELEMENTOS <strong>DE</strong> LOS ARREGLOS UNIDIMENSIONALES<br />

Primero, se le advierte que la palabra extraer no es un buen término aquí. ¿Por qué?<br />

Porque, en general, la palabra extraer significa eliminar algo, cuando extraemos un elemento de<br />

un arreglo, ¡realmente no lo eliminamos! Simplemente copiamos su valor. El elemento<br />

permanece almacenado en el arreglo hasta que se reemplaza por otro valor usando una operación<br />

de inserción. Como con la inserción, es posible extraer elementos del arreglo usando uno de los<br />

tres métodos generales: asignación directa, escritura o ciclo.<br />

ASIGNACIÓN DIRECTA<br />

La extracción de los elementos del arreglo usando enunciados de asignación es lo<br />

inverso de la inserción de elementos utilizando un enunciado de asignación. Éste es el formato<br />

general:<br />

FORMATO <strong>DE</strong> ASIGNACIÓN DIRECTA (EXTRACCIÓN <strong>DE</strong> ELEMENTOS <strong>DE</strong>L ARREGLO)<br />

= [índice del arreglo]<br />

Como ejemplo, suponga que se hacen las siguientes definiciones:<br />

const int MAX = 10;<br />

int muestra[MAX];<br />

int x;<br />

Como puede observar, el arreglo muestra[] consta de 10 elementos enteros. Ahora,<br />

suponga que el arreglo está lleno, ¿Qué harán los siguientes enunciados?<br />

x = muestra[0];<br />

x = muestra[MAX - 1];<br />

x = muestra[3] * muestra[5];<br />

x = 2 * muestra[2] -3 * muestra[7];<br />

El primer enunciado asigna el elemento almacenado en la primera posición del arreglo a<br />

la variable x. El segundo enunciado asigna el elemento almacenado en la última posición del<br />

arreglo a la variable x. El tercer enunciado asigna el producto de los elementos localizados en los<br />

índices [3] y [5] a x. Por último, el cuarto enunciado asigna a x dos veces el elemento localizado<br />

en el índice[2] menos tres veces el elemento localizado en el índice [7] Los últimos dos<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-9


MIGUEL Á. TOLEDO MARTÍNEZ<br />

enunciados muestran cómo se pueden realizar las operaciones aritméticas con los elementos del<br />

arreglo.<br />

En todos los casos anteriores, los valores de los elementos del arreglo no se afectan por<br />

las operaciones de asignación. El principal requerimiento es que x debe estar definida como la<br />

misma clase de datos de los elementos del arreglo para no obtener resultados inesperados.<br />

Como ejemplo final, considere estos enunciados de asignación:<br />

muestra[0] = muestra[MAX - 1];<br />

muestra[1] = Muestra[2] + Muestra[3];<br />

¿Puede usted determinar qué pasa aquí? En el primer enunciado, el primer elemento<br />

del arreglo se reemplaza por medio del último elemento del arreglo, ¿se afecta el último<br />

elemento del arreglo? No, porque aparece del lado derecho del operador de asignación. En el<br />

segundo caso, el segundo elemento del arreglo en el índice [1] se reemplaza por la suma del<br />

tercero y cuarto elementos del arreglo en los índices [2] y [3] De nuevo, el tercero y cuarto<br />

elementos del arreglo no se afectan por esta operación, porque aparecen del lado derecho del<br />

operador de asignación.<br />

ESCRITURA <strong>DE</strong> ELEMENTOS <strong>DE</strong>L ARREGLO<br />

Los objetos cout se usan para mostrar los elementos del arreglo. Usemos el mismo<br />

arreglo para demostrar cómo escribir elementos del arreglo. Aquí está de nuevo la definición del<br />

arreglo:<br />

const int MAX = 10;<br />

int muestra[MAX];<br />

¿Qué se supone que harán los siguientes enunciados?<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

// MUESTRA UN ARREGLO CON UN CICLO for<br />

#include // Para cin y cout<br />

// ENUNCIA EL TAMAÑO <strong>DE</strong>L ARREGLO<br />

const int MAX = 10;<br />

void main(void)<br />

{<br />

int muestra[MAX]; // <strong>DE</strong>FINE UN ARREGLO ENTERO<br />

for(int i = 0; i < MAX; ++i)<br />

muestra[i] = i * i;<br />

for (int i = 0; i < MAX; ++i)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

A continuación se darán varios ejemplos en los que se utilicen los conocimientos antes<br />

adquiridos.<br />

Ejemplo <strong>18</strong>.2<br />

El siguiente programa, INICIALIZA.CPP, utiliza una estructura de repetición for para inicializar a cero los<br />

elementos de un arreglo de enteros llamado arreglo de diez elementos; luego imprime dicho arreglo en<br />

formato de tabla.<br />

/* El siguiente programa: INICIALIZA.CPP, inicializa a ceros a un arreglo de 10 elementos,<br />

posteriormente imprime el contenido de dicho arreglo.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para setw()<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.3<br />

}//Fin de main()<br />

int i, arreglo[10];<br />

for(i = 0; i < 10; i++)<br />

arreglo[i] = 0; //Inicializa el arreglo<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

que explícitamente inicializa a cero el primer elemento e implícitamente inicializa a cero a los otros nueve<br />

elementos, pues hay menos valores que elementos en el arreglo. Recuerde que los arreglos automáticos no<br />

se inicializan implícitamente a cero. El programador debe inicializar a cero cuando menos el primer<br />

elemento para que los demás se inicialicen a cero de manera automática.<br />

La siguiente declaración de arreglo:<br />

int arreglo[5] = {32, 27, 64, <strong>18</strong>, 95, 14};<br />

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

Si se omite el tamaño del arreglo en la declaración por medio de una lista de inicio, el numero de elementos<br />

del arreglo será el número de elementos de dicha lista. Por ejemplo:<br />

Ejemplo <strong>18</strong>.4<br />

creará un arreglo de cinco elementos.<br />

int arreglo[] = {1, 2, 3, 4, 5};<br />

El siguiente programa, ELEMENTS.CPP, inicializa el arreglo llamado calificaciones y luego visualiza sus<br />

elementos.<br />

/* El siguiente programa: ELEMENTS.CPP, inicializa el arreglo calificaciones y<br />

luego visualiza sus elementos.<br />

*/<br />

#include //Para cout y cin<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.5<br />

int calificaciones[5] = {80, 70, 90, 85, 80};<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.6<br />

Ejemplo <strong>18</strong>.7<br />

int calificaciones[5] = {80, 70, 90, 85, 80};<br />

int i;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.8<br />

El siguiente programa, PARES.CPP, inicializa los elementos de un arreglo llamado arreglo de diez<br />

elementos a los enteros 2, 4, 6, ... , 20, e imprime el arreglo en formato de tabla. Estos números se generan<br />

multiplicando por 2 cada valor consecutivo del contador del ciclo y sumándole 2.<br />

/* Este programa: PARES.CPP inicializa un arreglo con los enteros pares comprendidos<br />

entre 2 y 20 inclusive.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para setw()<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.9<br />

const int TAMANO_ARREGLO = 10;<br />

int j, arreglo[TAMANO_ARREGLO];<br />

for(j = 0; j < TAMANO_ARREGLO;j++) //Inicializa arreglo<br />

arreglo[j] = 2 + 2 * j;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.10<br />

El siguiente programa, SUMAVALORES.CPP, suma los valores contenidos en el arreglo de enteros de<br />

doce elementos.<br />

/* El siguiente programa: SUMAVALORES.CPP, Calcula la suma de los elementos de un arreglo. */<br />

#include //Para cout y cin<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.11<br />

const int TAMANO_ARREGLO = 12;<br />

int arreglo[TAMANO_ARREGLO] = {1, 3, 5, 4, 7, 2, 99, 16, 45, 67, 89, 45};<br />

int total = 0;<br />

for(int i = 0; i < TAMANO_ARREGLO; i++)<br />

total += arreglo[i];<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.12<br />

El siguiente programa, PROMEDIO1.CPP, calcula el promedio de los elementos de un arreglo. Los<br />

elementos del arreglo son preasignados.<br />

/* El siguiente programa: PROMEDIO1.CPP, calcula el valor promedio de un arreglo.<br />

Los elementos del arreglo están preasignados.<br />

*/<br />

#include //Para cout y cin<br />

const int TAMANO_ARREGLO = 10;<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.13<br />

double arreglo[TAMANO_ARREGLO] = { 12.2, 45.4, 67.2, 12.2, 34.6, 87.4,<br />

83.6, 12.3, 14.8, 55.5 };<br />

double suma = 0;<br />

for (int ix = 0; ix < TAMANO_ARREGLO; ++ix)<br />

{<br />

suma += arreglo[ix];<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.14<br />

Nuestro siguiente programa, HISTOGRAMA.CPP, lee los números de un arreglo llamado arreglo y traza<br />

la información en forma de un gráfico de barras o histograma; cada número se imprime, seguido de una<br />

barra consistente en la misma cantidad de asteriscos.<br />

/* El siguiente programa: HISTOGRAMA.CPP, imprime un histograma. */<br />

#include //Para cout y cin<br />

#include //Para setw()<br />

void main(void)<br />

{<br />

}//Fin de main()<br />

Ejemplo <strong>18</strong>.15<br />

// Ahora obtiene el promedio<br />

double suma = 0;<br />

for (int ix = 0; ix < numeroElementos; ++ix)<br />

suma += arreglo[ix];<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.16<br />

El siguiente programa, CARACTERES.CPP, muestra la inicialización de un arreglo de caracteres con una<br />

literal de cadena; la lectura de una cadena para dejarla en un arreglo de caracteres; la impresión de un<br />

arreglo de caracteres como cadena y el acceso a los caracteres individuales de una cadena.<br />

Ejemplo <strong>18</strong>.17<br />

const int TAMANO_ARREGLO = 7;<br />

int cara, frecuencia[TAMANO_ARREGLO] = {0};<br />

srand(time(0));<br />

for(int tirada = 1; tirada


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: ESTAUTO.CPP, ilustra el uso de arreglos estáticos y automáticos. */<br />

#include //Para cout y cin<br />

void arregloEstatico(void);<br />

void arregloAutomatico(void);<br />

void main(void)<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

PASO <strong>DE</strong> ARREGLOS Y ELEMENTOS <strong>DE</strong> ARREGLOS A <strong>LA</strong>S FUNCIONES<br />

Es posible pasar un arreglo completo a una función o pasar sólo elementos de un arreglo a<br />

una función. Lo importante de recordar es que para pasar un arreglo completo, deberá pasar la<br />

dirección del arreglo. En C y C++, el nombre del arreglo es la dirección del primer elemento<br />

(índice[0]) del arreglo. Empecemos viendo el encabezado de la función. Éste es el prototipo para<br />

pasar un arreglo unidimensional a una función:<br />

void raro(char arreglo[TAMANO_ARREGLO]);<br />

o lo que es más correcto<br />

void raro(char arreglo[], int tamanoArreglo);<br />

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

caracteres llamado arreglo[TAMANO_ARREGLO] La única serie de corchetes después del<br />

identificador del parámetro implica que el parámetro es un arreglo unidimensional con un<br />

tamaño TAMANO_ARREGLO. Cuando se pasan los arreglos a una función, la función deberá<br />

saber qué tan grande es el arreglo que aceptará. Ahora, el identificador del arreglo hace<br />

referencia a la dirección del arreglo, así el arreglo pasa por referencia a la función. De esta<br />

manera, cualquier operación en el arreglo dentro de la función afectará el contenido original del<br />

arreglo en el programa llamador. Asimismo, dado que el parámetro es la dirección del arreglo, no<br />

se requiere ningún símbolo ampersand, (&), para pasar el arreglo por referencia. De hecho, el<br />

uso de un símbolo ampersand antes del parámetro del arreglo originará un error de compilación.<br />

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

enunciado:<br />

raro(nombreArreglo);<br />

o lo que es más correcto<br />

raro(nombreArreglo, tamanoArreglo);<br />

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

el programa llamador. (Recuerde que el identificador del argumento real y el identificador del<br />

parámetro formal pueden ser diferentes.) La llamada a nombreArreglo da referencia de la<br />

dirección del arreglo, de manera que, la dirección del arreglo pasa a la función más que una<br />

copia del arreglo. Así, cualquier operación en el arreglo dentro de la función afectará los<br />

elementos originales del arreglo. Un programa completo es como sigue:<br />

Ejemplo <strong>18</strong>.<strong>18</strong><br />

/* El siguiente programa: PASARRE.CPP, ilustra como se pasa un arreglo a una función. */<br />

#include // Para cin y cout<br />

// <strong>DE</strong>C<strong>LA</strong>RA EL TAMAÑO <strong>DE</strong>L ARREGLO<br />

const int TAMANO_ARREGLO = 3;<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-21


MIGUEL Á. TOLEDO MARTÍNEZ<br />

// FUNCIÓN PROTOTIPO<br />

void raro(char arreglo[TAMANO_ARREGLO]);<br />

void main(void)<br />

{<br />

//<strong>DE</strong>FINE UN ARREGLO <strong>DE</strong> CARACTERES<br />

char nombre[TAMANO_ARREGLO];<br />

//LLENE EL ARREGLO nombre CON CARACTERES<br />

nombre[0] ='I';<br />

nombre[1] ='B';<br />

nombre[2] ='M';<br />

//MUESTRA EL ARREGLO nombre<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

void pasaPorValor(int elementoArreglo);<br />

El encabezado dice que la función no regresa ningún valor y espera recibir un valor entero desde el<br />

programa llamador. Suponga que se hace un llamado a la función como sigue:<br />

pasaPorValor(registros[0]);<br />

Observe que el argumento real en la llamada de la función es registros[0] Esto genera una copia del<br />

elemento almacenado en el índice [0] en el arreglo registros[] que será transferido a la función por valor.<br />

Como resultado, cualquier operación en este elemento dentro de la función no afectará el valor del<br />

elemento en el arreglo registros[] original. Si quiere que el elemento manifieste algún cambio dentro de la<br />

función, deberá pasarla por referencia usando el símbolo ampersand en la función prototipo, como ésta:<br />

void pasaPorReferencia(int &elementoArreglo);<br />

Ahora, cualquier llamada a la función pasará la dirección del elemento a la función, pasando de esta manera<br />

el elemento por referencia.<br />

Ejemplo <strong>18</strong>.19<br />

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

/* El siguiente programa: PASVALREF.CPP, ilustra el uso de paso de elementos de un arreglo por<br />

valor y por referencia.<br />

*/<br />

#include // Para cin y cout<br />

//ENUNCIA EL TAMAÑO <strong>DE</strong>L ARREGLO<br />

const int TAMANO_ARREGLO = 3;<br />

//FUNCIONES PROTOTIPO<br />

void pasaPorValor(int elementoArreglo);<br />

void pasaPorReferencia(int &elementoArreglo);<br />

void main(void)<br />

{<br />

int registros[TAMANO_ARREGLO];<br />

registros[0] = 10;<br />

registros[1] = 20;<br />

registros[2] = 30;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

void pasaPorValor(int elementoArreglo)<br />

{<br />

++elementoArreglo;<br />

} // Final de pasaPorValor()<br />

void pasaPorReferencia(int &elementoArreglo)<br />

{<br />

++elementoArreglo;<br />

} // Final de pasaPorReferencia()<br />

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

El valor del elemento en registro[0] antes de pasaPorValor() es : 10<br />

El valor del elemento en registro[0] después de pasaPorValor() es : 10<br />

El valor del elemento en registro[0] antes de pasaPorReferencia() : 10<br />

El valor del elemento en registro[0] después de pasaPorReferencia(): 11<br />

Estudie los dos programas siguientes para asegurarse que comprende cómo pasan a las<br />

funciones los arreglos completos y los elementos individuales del arreglo.<br />

Ejemplo <strong>18</strong>.20<br />

El siguiente programa, PASOARRE2.CPP, muestra la diferencia entre pasar un arreglo completo y pasar<br />

un elemento del arreglo.<br />

/* El siguiente programa: PASOARRE2.CPP, pasa un arreglo y elementos del arreglo a funciones. */<br />

#include //Para cout y cin<br />

#include //Para setw()<br />

void modificarArreglo(int[], int);<br />

void modificarElemento(int);<br />

void main(void)<br />

{<br />

const int TAMANO_ARREGLO = 5;<br />

int i, arreglo[TAMANO_ARREGLO] = {0, 1, 2, 3, 4};<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.21<br />

Ejemplo <strong>18</strong>.22<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: ARRPARAM.CPP, pasa tres arreglos diferentes (de diferentes tamaños)<br />

a la función mostrarValores().<br />

*/<br />

#include //Para cout y cin<br />

void mostrarValores(int valores[], int numeroElementos)<br />

{<br />

int i;<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.23<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

int buscaMinimo(int arreglo[], int tamano)<br />

{<br />

int pequeno = arreglo[0];<br />

Ejemplo <strong>18</strong>.24<br />

for (int i = 1; i < tamano; i++)<br />

if (arreglo[i] < pequeno)<br />

pequeno = arreglo[i];<br />

return pequeno;<br />

}//Fin de buscaMinimo()<br />

int buscaMaximo(int arreglo[], int tamano)<br />

{<br />

int grande = arreglo[0];<br />

void main(void)<br />

{<br />

for (int i = 1; i < tamano; i++)<br />

if (arreglo[i] > grande)<br />

grande = arreglo[i];<br />

return grande;<br />

}//Fin de buscaMaximo()<br />

int arreglo[MAX], numElementos;<br />

numElementos = obtenNumPuntos(MIN, MAX);<br />

// Pide datos al usuario<br />

for (int i = 0; i < numElementos; i++)<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Función obtenerCalificaciones(): Obtiene del usuario los registros de los exámenes y los<br />

coloca en un arreglo.<br />

Acepta: El número de calificaciones y el arreglo de las<br />

calificaciones con un tamaño máximo de MAX.<br />

Regresa: El número de calificaciones y el arreglo que llena el<br />

usuario con las calificaciones.<br />

Es necesario que la interfaz de la función acepte y regrese la estructura del arreglo. Supongamos que las<br />

calificaciones son valores decimales y, por lo tanto, se necesita un arreglo de punto flotante. La función<br />

prototipo será:<br />

void obtenerCalificaciones(int &numero, float calificaciones[MAX])<br />

A partir de esto, es fácil la escritura de la función. Emplearemos un enunciado cin dentro de un ciclo for en<br />

el cuerpo de la función para llenar el arreglo con las calificaciones.<br />

A continuación la función completa:<br />

void obtenerCalificaciones(int &numero, float calificaciones[MAX])<br />

{<br />

cout > numero;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

El cuerpo de la función simplemente suma todas las calificaciones del arreglo y los divide entre su número.<br />

A continuación se ve la función completa:<br />

float promedio(int numero, float calificaciones[MAX])<br />

{<br />

float total = 0.0;<br />

for (int i = 0; i < numero; ++i)<br />

total +=calificaciones[i];<br />

return total/numero;<br />

} // Fin de promedio()<br />

Hay dos variables locales de función definidas: total e i. total actuará como una variable temporal para<br />

acumular la suma de las calificaciones y la variable i es la variable contador del ciclo. Primero, la variable<br />

total se inicializa a 0. Después se usa el ciclo para obtener los elementos del arreglo, uno por uno y<br />

adicionarlos a total. Observe que el contador de ciclo (i) actúa como el índice del arreglo dentro del ciclo.<br />

De esta manera se extraen los elementos del arreglo, desde el índice [0] hasta [numero – 1], en forma<br />

secuencial con cada ciclo de iteración y se adicionan a total. La última calificación se localiza en el índice<br />

[numero – 1]. Una vez que el ciclo calcula la suma total de todas las calificaciones, se usa un enunciado<br />

return para regresar el promedio calculado.<br />

Por último, la función mostrarResultado() mostrará las calificaciones individuales obtenidas del usuario<br />

junto con sus promedios. Para hacer esto, el arreglo deberá pasar a la función para obtener las<br />

calificaciones. Aquí está la descripción de la función:<br />

Función mostrarResultado(): Muestra las calificaciones individuales y su<br />

promedio.<br />

Acepta: El número de calificaciones para mostrarlas en<br />

pantalla y el arreglo de las calificaciones.<br />

Regresa: Nada.<br />

Para mostrar el promedio, sólo se llamará a la función promedio() dentro de esta función como parte de un<br />

enunciado cout. La función completa entonces se convierte en:<br />

void mostrarResultado(int numero, float calificaciones[MAX])<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: ARREGLOS.CPP muestra el uso de los arreglos. */<br />

/*******************************************************************************<br />

Este programa calculará un promedio de calificaciones desde registros escritos<br />

por el usuario en un arreglo.<br />

*******************************************************************************/<br />

#include // Para cin y cout<br />

// Constante global<br />

const int MAX = 25; // Número máximo de registros.<br />

// Funciones prototipo<br />

void obtenerCalificaciones(int &numero, float calificaciones[MAX]);<br />

float promedio(int numero, float registros[MAX]);<br />

void mostrarResultados(int numero, float calificaciones[MAX]);<br />

void main(void)<br />

{<br />

// Define la variable numero y el arreglo<br />

int numero = 0; // Número de calificaciones<br />

float calificaciones[MAX]; // Definición del arreglo<br />

// LLama a las funciones para obtener las calificaciones y mostrar resultados<br />

obtenerCalificaciones(numero, calificaciones);<br />

mostrarResultados(numero, calificaciones);<br />

} // Fin de main()<br />

/*******************************************************************************<br />

Esta función obtiene los registros del usuario y los coloca en el arreglo<br />

calificaciones.<br />

*******************************************************************************/<br />

void obtenerCalificaciones(int &numero, float calificaciones[MAX])<br />

{<br />

cout numero;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/*******************************************************************************<br />

Esta función muestra las calificaciones del arreglo y el promedio final.<br />

*******************************************************************************/<br />

void mostrarResultados(int numero, float calificaciones[MAX])<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.26<br />

El siguiente programa, BURBUJA1.CPP, ordena los valores del arreglo de diez elementos arreglo en<br />

orden ascendente. La técnica de la que nos valemos se llama ordenamiento de burbuja u ordenamiento<br />

por hundimiento(dependiendo si se ordena en orden descendente o ascendente), pues los valores más<br />

pequeños gradualmente burbujean hacia la parte alta del arreglo como las burbujas de aire que ascienden en<br />

el agua, mientras que los valores más grandes se hunden al fondo del arreglo. La técnica es pasar varias<br />

veces por el arreglo. En cada pasada, se comparan pares sucesivos de elementos. Si uno de los pares está en<br />

orden ascendente (o son idénticos los valores), se queda tal cual. Si está en orden descendente, se<br />

intercambian sus valores en el arreglo.<br />

/* El siguiente programa: BURBUJA1.CPP, ordena los valores de un arreglo en orden ascendente<br />

utilizando el método de la burbuja.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para setw()<br />

void main(void)<br />

{<br />

const int TAMANO_ARREGLO = 10;<br />

int arreglo[TAMANO_ARREGLO] = {2, 6, 4, 8, 10, 12, 89, 68, 45, 37};<br />

int i, temporal;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.27<br />

El problema anterior puede mejorarse solicitando el número de datos a ordenar, llamaremos a este programa,<br />

BURBUJA2.CPP. Es importante que observe algunos cambios que hicimos en la función clasifBurbuja()<br />

/* El siguiente programa: BURBUJA2.CPP, ordena arreglos usando el método de ordenamiento<br />

de la burbuja.<br />

*/<br />

#include //Para cout y cin<br />

const int MIN = 2;<br />

const int MAX = 10;<br />

int obtenNumPuntos(int minimo, int maximo)<br />

{<br />

int numPuntos;<br />

do<br />

return numPuntos;<br />

}//Fin de obtenNumPuntos()<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

void main(void)<br />

{<br />

Ejemplo <strong>18</strong>.28<br />

}//Fin de clasifBurbuja()<br />

int arr[MAX];<br />

int numElementos;<br />

{<br />

int temp = intArr[i];<br />

intArr[i] = intArr[j];<br />

intArr[j] = temp;<br />

}//Fin de for<br />

numElementos = obtenNumPuntos(MIN, MAX);<br />

entradaArreglo(arr, numElementos);<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.29<br />

El método de la burbuja, puede mejorarse disminuyendo el número de elementos en cada pasada, a este método se<br />

le conoce con el nombre de selección. El siguiente programa, SE<strong>LECCIÓN</strong>.CPP, ilustra este método.<br />

Ejemplo <strong>18</strong>.30<br />

metodoBurbuja(valores, 30);<br />

for (i = 0; i < 30; i++)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: METSHELL.CPP, utiliza el método Shell para ordenar<br />

un arreglo que contiene 50 elementos generados aleatoriamente.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para rand()<br />

void ordenarShell(int arreglo[], int tamano)<br />

{<br />

int temp, espacio, i, banderaCambio;<br />

Ejemplo <strong>18</strong>.31<br />

espacio = tamano / 2;<br />

do<br />

{<br />

do<br />

{<br />

banderaCambio = 0;<br />

for (i = 0; i < tamano - espacio; i++)<br />

if (arreglo[i] > arreglo[i + espacio])<br />

{<br />

temp = arreglo[i];<br />

arreglo[i] = arreglo[i + espacio];<br />

arreglo[i + espacio] = temp;<br />

banderaCambio = 1;<br />

}//Fin de if<br />

} while (banderaCambio);<br />

} while (espacio = espacio / 2);<br />

}//Fin de ordenarShell()<br />

void main(void)<br />

{<br />

int valores[50], i;<br />

for(i = 0; i < 50; i++)<br />

valores[i] = rand() % 100;<br />

ordenarShell(valores, 50);<br />

for (i= 0; i < 50; i++)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: RAPIDO.CPP, utiliza el método de clasificación ordenamiento<br />

rápido, para ordenar un arreglo que contiene 100 elementos generados en forma<br />

aleatoria.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para rand()<br />

void ordenarRapido(int arreglo[], int primero, int ultimo)<br />

{<br />

int temp, bajo, alto, separadorLista;<br />

do<br />

Ejemplo <strong>18</strong>.32<br />

bajo = primero;<br />

alto = ultimo;<br />

separadorLista = arreglo[(primero + ultimo) / 2];<br />

{<br />

while (arreglo[bajo] < separadorLista)<br />

bajo++;<br />

while (arreglo[alto] > separadorLista)<br />

alto--;<br />

if (bajo


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Cada una de las respuestas es un número del 1 al 9. El programa calcula la media, la mediana y la moda de<br />

los 99 valores.<br />

/* El siguiente programa: ESTADISTICA.CPP, presenta el tema del análisis de información<br />

de una encuesta. Calcula la media, la mediana y la moda de los datos.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para setw(w)<br />

void media(const int [], int);<br />

void mediana(int [], int);<br />

void moda(int [], int [], int);<br />

void burbuja(int [], int);<br />

void escribirArreglo(const int[], int);<br />

void main(void)<br />

{<br />

const int TAMANO_RESPUESTAS = 99;<br />

int frecuencia[10] = {0},<br />

respuestas[TAMANO_RESPUESTAS] = {6, 7, 8, 9, 8, 7, 8, 9, 8, 9,7, 8, 9, 5, 9, 8, 7, 8, 7, 8,<br />

6, 7, 8, 9, 3, 9, 8, 7, 8, 7,<br />

7, 8, 9, 8, 9, 8, 9, 7, 8, 9,<br />

6, 7, 8, 7, 8, 7, 9, 8, 9, 2,<br />

7, 8, 9, 8, 9, 8, 9, 7, 5, 3,<br />

5, 6, 7, 2, 5, 3, 9, 4, 6, 4,<br />

7, 8, 9, 6, 8, 7, 8, 9, 7, 8,<br />

7, 4, 4, 2, 5, 3, 8, 7, 5, 6,<br />

4, 5, 6, 1, 6, 5, 7, 8, 7};<br />

media(respuestas, TAMANO_RESPUESTAS);<br />

mediana(respuestas, TAMANO_RESPUESTAS);<br />

moda(frecuencia, respuestas, TAMANO_RESPUESTAS);<br />

}//Fin de main()<br />

void media(const int contestacion[], int tamanoArreglo)<br />

{<br />

int total = 0;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

escribirArreglo(contestacion, tamano);<br />

burbuja(contestacion, tamano);<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

a[j] = a[j + 1];<br />

a[j + 1] = temporal;<br />

}//Fin de if<br />

}//Fin de burbuja()<br />

void escribirArreglo(const int a[], int tamano)<br />

{<br />

for(int j = 0; j < tamano; j++)<br />

{<br />

if(j %20 == 0)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: BUSLINEAL.CPP, ilustra el método de búsqueda lineal en un arreglo. */<br />

#include //Para cout y cin<br />

int busquedaLineal(const int[], int, int);<br />

void main(void)<br />

{<br />

const int TAMANO_ARREGLO = 100;<br />

int a[TAMANO_ARREGLO], claveBusqueda, elemento;<br />

for(int x = 0; x < TAMANO_ARREGLO; x++) //Crea algunos datos<br />

a[x] = 2 * x;<br />

cout > claveBusqueda;<br />

elemento = busquedaLineal(a, claveBusqueda, TAMANO_ARREGLO);<br />

if(elemento != -1)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

búsqueda binaria de cualquier arreglo ordenado puede determinarse encontrando la primera potencia de 2<br />

mayor que el número de elementos del arreglo.<br />

Ejemplo <strong>18</strong>.34<br />

El siguiente programa, BUSBIN.CPP, presenta la versión iterativa de la función busquedaBinaria() La<br />

función recibe cuatro argumentos: un arreglo de enteros b, un entero claveBusqueda, el índice bajo del<br />

arreglo y el índice alto del arreglo. Si la clave de búsqueda no es igual al elemento de la mitad de un<br />

subarreglo, se ajusta el índice bajo o alto para poder hacer la búsqueda en un subarreglo más pequeño. Si la<br />

clave de búsqueda es menor que el elemento central, el índice alto se establece a mitad – 1 y se continúa la<br />

búsqueda en los elementos de bajo a mitad – 1. Si la clave de búsqueda es mayor que el elemento central, el<br />

índice bajo se establece a mitad + 1 y se continúa la búsqueda en los elementos de mitad + 1 a alto. El<br />

programa emplea un arreglo de 15 elementos. La primera potencia de 2 mayor que la cantidad de elementos<br />

de este arreglo es 16 (2 4 ), por lo que se necesitan un máximo de 4 comparaciones para encontrar la clave de<br />

búsqueda. La función imprimirEncabezado() envía a la salida los índices del arreglo y la función<br />

imprimirFila() envía a la salida cada subarreglo generado durante el proceso de búsqueda binaria. El<br />

elemento central de cada subarreglo se marca con un asterisco (*), para indicar el elemento con el que se<br />

compara la clave de búsqueda.<br />

/* El siguiente programa: BUSBIN.CPP, ilustra el uso de la búsqueda binaria en un arreglo. */<br />

#include //Para cout y cin<br />

#include //Para setw()<br />

int busquedaBinaria(int[], int, int, int, int);<br />

void imprimirEncabezado(int);<br />

void imprimirFila(int[], int, int, int, int);<br />

void main(void)<br />

{<br />

const int TAMANO_ARREGLO = 15;<br />

int a[TAMANO_ARREGLO], clave, resultado;<br />

for(int i = 0; i < TAMANO_ARREGLO; i++)<br />

a[i] = 2 * i; //Crea los elementos del arreglo<br />

cout > clave;<br />

imprimirEncabezado(TAMANO_ARREGLO);<br />

resultado = busquedaBinaria(a, clave, 0, TAMANO_ARREGLO - 1, TAMANO_ARREGLO);<br />

if(resultado != -1)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.35<br />

{<br />

mitad = (bajo + alto) / 2;<br />

imprimirFila(b, bajo, mitad, alto, tamano);<br />

if(claveBusqueda == b[mitad]) //coincidencia<br />

return mitad;<br />

else if(claveBusqueda < b[mitad])<br />

alto = mitad - 1; //Busca en la parte baja del arreglo<br />

else<br />

bajo = mitad + 1; //Busca en la parte alta del arreglo<br />

}//Fin de while<br />

return -1; //No se encontró claveBusqueda<br />

}//Fin de busquedaBinaria()<br />

//Imprime el encabezado para la salida<br />

void imprimirEncabezado(int tamano)<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: BINARIO.CPP, utiliza una búsqueda binaria para localizar<br />

valores en el arreglo llamado contador, el cual contiene los valores de 1 a<br />

100. La función busquedaBinaria imprime mensajes que describen el proceso.<br />

*/<br />

#include //Para cout y cin<br />

int busquedaBinaria(int arreglo[], int valor, int tamano)<br />

{<br />

int encontrado = 0;<br />

int alto = tamano, bajo = 0, enmedio;<br />

void main(void)<br />

{<br />

enmedio = (alto + bajo) / 2;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

SOLUCIÓN <strong>DE</strong> PROBLEMAS EN ACCIÓN: Búsqueda en un arreglo con iteración<br />

(Búsqueda secuencial)<br />

PROBLEMA<br />

Diversas aplicaciones requieren de un programa para buscar un determinado elemento en un arreglo. Para<br />

realizar esta tarea se usan dos algoritmos comunes que son búsqueda secuencial o serial y búsqueda<br />

binaria. Por lo común, la búsqueda secuencial se utiliza para arreglos no ordenados y la búsqueda binaria<br />

en arreglos ordenados. En el siguiente problema veremos la búsqueda secuencial; en un problema posterior<br />

veremos la búsqueda binaria.<br />

Desarrolle una función que busque, en forma secuencial, en un arreglo de enteros, el valor de un elemento<br />

dado y que regrese el índice del elemento si éste se encuentra en el arreglo.<br />

<strong>DE</strong>FINICIÓN <strong>DE</strong>L PROBLEMA<br />

Debido a que se trata de una función, la definición del problema se enfocará a la interfaz de la función. Por<br />

consiguiente, se debe considerar que la función aceptará y regresará algo. Llamaremos a la función<br />

busquedaSec() A partir del enunciado del problema, la función deberá buscar en un arreglo de enteros un<br />

valor de un elemento determinado. De esta manera, la función necesita dos cosas para realizar este trabajo:<br />

(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<br />

de valor o de referencia? Bueno, la función no cambiará ni al arreglo ni al elemento a buscar, ¿es<br />

correcto? Por lo tanto, los parámetros serán por valor.<br />

Después, necesitamos determinar que va a regresar la función al programa llamador. A partir del enunciado<br />

del problema, se observa que la función necesita regresar el índice del elemento buscado, si se encuentra en<br />

el arreglo. En C++ todos los índices de los arreglos son enteros, por lo tanto la función regresará un valor<br />

entero. Pero, ¿qué pasa si el elemento que se busca no se encuentra en el arreglo? Se regresará algún<br />

valor entero que aclare esta situación. Dado que, en C++ los índices de los arreglos se encuentran en un<br />

rango de 0 a algún entero positivo finito, si el elemento no se encuentra en el arreglo, regresará el entero -1.<br />

De esta manera usaremos –1 para indicar la condición no encontrado, porque en C++ ningún índice de<br />

arreglo tiene este valor. La descripción de la interfaz de la función será:<br />

Función busquedaSec(): Busca en un arreglo entero un valor de elemento<br />

dado.<br />

Acepta: Un arreglo de enteros y el elemento que se busca.<br />

Regresa: El índice del arreglo del elemento, si se encuentra, o<br />

el valor –1 si no se encuentra el elemento.<br />

La descripción de interfaz de la función anterior proporciona toda la información necesaria para escribir la<br />

interfaz de la función, como sigue:<br />

int busquedaSec(int arreglo[MAX], int elemento)<br />

La interfaz indica que la función aceptará dos cosas: (1) un arreglo de elementos enteros MAX y (2) un<br />

valor entero, llamado elemento, que será el valor que se busca.<br />

La siguiente tarea es desarrollar el algoritmo de búsqueda secuencial.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-45


MIGUEL Á. TOLEDO MARTÍNEZ<br />

P<strong>LA</strong>NEACIÓN <strong>DE</strong> <strong>LA</strong> SOLUCIÓN<br />

¿Qué quiere decir exactamente búsqueda secuencial? Rastreo en el arreglo en forma secuencial, de un<br />

elemento al siguiente, empezando en la primera posición del arreglo y deteniéndose al encontrar el<br />

elemento o al llegar al final del arreglo. De esta manera, el algoritmo deberá verificar el elemento<br />

almacenado en la primera posición del arreglo, después en la segunda posición del arreglo, enseguida en la<br />

tercera y así sucesivamente, hasta que encuentre el elemento o hasta que se agoten los elementos del<br />

arreglo. Desde luego, ésta es una tarea repetitiva de verificación de un elemento en el arreglo, luego<br />

moverse al siguiente elemento y verificar otra vez y así sucesivamente. Considere el siguiente algoritmo<br />

que emplea un ciclo while para realizar la operación de verificación repetitiva:<br />

Algoritmo busquedaSec()<br />

busquedaSec()<br />

INICIO<br />

encuentra = falso.<br />

indice = primer índice del arreglo.<br />

while(elemento no se Encuentra) AND (Indice


MIGUEL Á. TOLEDO MARTÍNEZ<br />

// Busca en el arreglo hasta encontrar o alcanzar el final del arreglo<br />

while((!encuentra) && (i < MAX))<br />

{<br />

if (a[i] == elemento) // Verifica el elemento del arreglo<br />

encuentra = Verdadero; // Si es igual, establece<br />

else // encuentra a Verdadero<br />

++i;<br />

} // Fin del while<br />

// Si se encuentra el elemento, regresa la posición del elemento en el arreglo<br />

// Si no regresa –1<br />

if(encuentra)<br />

return i;<br />

else<br />

return –1;<br />

} // Fin de busquedaSec()<br />

No debe haber sorpresas en este código. En la parte superior de la función, verá el encabezado que es<br />

idéntico a la interfaz de la función desarrollada anteriormente. Después verá una clase de datos enumerada<br />

creada para definir Falso y Verdadero. Recuerde que los valores predeterminados para elementos de datos<br />

enumerados son enteros, empezando con 0. De esta manera, Falso se define con el valor 0 y Verdadero se<br />

define con el de 1. Esto permite la utilización de los identificadores Falso y Verdadero dentro del programa<br />

para representar los valores enteros 0 y 1 respectivamente. Además, las verificaciones booleanas se pueden<br />

hacer contra estos valores, porque C++ interpreta un 0 como un Falso lógico y 1 como un Verdadero<br />

lógico.<br />

La variable encuentra se define como un objeto de clases de datos booleanos enumerados y se establece a<br />

Falso. Utilizaremos la variable i como variable índice del arreglo. Esta variable se define como un entero y<br />

establece a 0 el primer índice del arreglo. Recuerde que los arreglos en C++ siempre empiezan con el índice<br />

0. El ciclo while emplea el operador AND (&&) para verificar los valores de encuentra e i. El ciclo se<br />

repetirá hasta que se encuentre el elemento (encuentra) y el valor de i sea menor que el tamaño MAX del<br />

arreglo. Recuerde que cuando el tamaño del arreglo es MAX, el último índice del arreglo es MAX – 1. Por<br />

lo tanto, cuando i excede el índice máximo del arreglo, MAX – 1, el ciclo se rompe. Cuando el ciclo se<br />

rompe, el valor de encuentra se verifica. Si encuentra es Verdadero, se regresa el valor de i; si encuentra<br />

es Falso, el valor –1 se regresa para indicar que el elemento no se encontró en el arreglo.<br />

SOLUCIÓN <strong>DE</strong> PROBLEMAS EN ACCIÓN: Como ordenar un arreglo con iteración<br />

(Ordenación por inserción)<br />

PROBLEMA<br />

Ordenar un arreglo significa colocar los elementos del arreglo en orden ascendente o descendente desde el<br />

principio al final del arreglo. Hay muchos algoritmos comunes utilizados para ordenación. Hay<br />

clasificación por inserción, clasificación por burbuja, clasificación por selección, clasificación rápida,<br />

clasificación combinada y clasificación apilada, sólo por mencionar algunas. En un curso de estructura de<br />

datos, es muy probable que aprenda y analice todos estos algoritmos de ordenación. En este problema se<br />

desarrollará el algoritmo de clasificación por inserción y se codificará como una función en C++.<br />

Desarrolle una función que se pueda llamar para ordenar un arreglo de caracteres en orden ascendente<br />

utilizando el algoritmo clasificación por inserción.<br />

<strong>DE</strong>FINICIÓN <strong>DE</strong>L PROBLEMA<br />

De nuevo, codificaremos el algoritmo como una función C++, así la definición del problema se enfocará en<br />

la interfaz de la función, guiándonos a la función prototipo. Llamaremos a la función clasifPorInser()<br />

Piense que necesita clasifPorInser() para hacer un trabajo. Bueno, deberá recibir un arreglo de caracteres<br />

no clasificados y regresar el mismo arreglo como un arreglo clasificado, ¿correcto? ¿Necesita algo más?<br />

No, la función no requiere datos adicionales, porque lo único que se opera es el arreglo mismo.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-47


MIGUEL Á. TOLEDO MARTÍNEZ<br />

¿Qué hay de los valores de regreso? ¿Necesita regresar la función un valor sencillo o una serie de<br />

valores? La función no necesita regresar ningún valor sencillo, deberá regresar el arreglo ordenado. Por lo<br />

tanto, la clase de valor regresado será void y el arreglo será un parámetro de referencia, ¿correcto?<br />

Recuerde que cuando se pasan los arreglos a las funciones C++, siempre son tratados como parámetros de<br />

referencia porque el nombre del arreglo representa una dirección en memoria. Por lo tanto, aquí está la<br />

descripción de la interfaz de la función clasifPorInser():<br />

Función clasifPorInser(): Clasifica un arreglo de caracteres en orden ascendente.<br />

Acepta: Un arreglo de caracteres sin clasificar.<br />

Regresa: Un arreglo de caracteres clasificados.<br />

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

void clasifPorInser(char arreglo[MAX])<br />

La interfaz dice que clasifPorInser() recibirá un arreglo de caracteres de tamaño MAX. El tipo regresado es<br />

void, porque no se regresa ningún valor sencillo. Sin embargo, debido a que el arreglo completo se pasa a la<br />

función, cualquier operación de clasificación sobre el arreglo dentro de la función se reflejará en el<br />

programa llamador.<br />

P<strong>LA</strong>NEACIÓN <strong>DE</strong> <strong>LA</strong> SOLUCIÓN<br />

Antes de establecer el algoritmo, vamos a ver cómo trabaja la clasificación por inserción. Vea la figura<br />

<strong>18</strong>.3. Suponga que vamos a clasificar un arreglo de 5 caracteres en orden ascendente. Antes de entrar en<br />

detalle, vea la figura de arriba abajo y de izquierda a derecha. El arreglo sin ordenar se muestra en la parte<br />

superior de la figura <strong>18</strong>.3 y el arreglo ordenado se muestra en la parte inferior de la figura <strong>18</strong>.3. Observe<br />

que el sombreado se emplea en la figura para mostrar el proceso de clasificación de arriba a abajo.<br />

Conforme procedemos desde el arreglo sin clasificar en la parte superior, el sombreado se incrementa,<br />

mostrando la porción del arreglo que está clasificado, hasta que el arreglo completo está sombreado en la<br />

parte inferior de la figura <strong>18</strong>.3.<br />

La secuencia descendente muestra que se harán cuatro pasos a lo largo del arreglo para obtener al arreglo<br />

clasificado mostrado en la parte inferior de la figura. Con cada paso, un elemento se coloca dentro de su<br />

posición clasificada relativa a los elementos que se encuentran antes en el arreglo. El primer paso empieza<br />

con el primer elemento ‘E’, clasificado como se indica por medio del sombreado. Se considera que el<br />

carácter individual ‘E’ se clasifica por sí mismo, porque no tiene ningún elemento antes que él. De esta<br />

manera, la tarea en este primer paso es clasificar el segundo elemento, ‘D’, relativo al carácter ‘E’ que lo<br />

precede.<br />

El segundo paso empieza con los caracteres clasificados ‘D’ y ‘E’, como se indica por medio del<br />

sombreado. La tarea en este paso es clasificar el tercer carácter, ‘C’, en relación con estos dos caracteres.<br />

En el tercer paso se empieza con los caracteres clasificados ‘C’, ‘D’ y ‘E’, y la tarea es clasificar el carácter<br />

‘B’ en relación con estos caracteres. Recuerde, en cada paso, la tarea es clasificar el primer carácter de la<br />

parte no clasificada del arreglo en relación con los caracteres que le preceden. El proceso continúa hasta<br />

que se clasifican todos los caracteres, como se muestra en la parte inferior de la figura <strong>18</strong>.3. En cada paso<br />

se repite prácticamente lo que se hizo en el paso anterior. Como resultado, es posible identificar un proceso<br />

de repeticiones paso a paso, de arriba hacia debajo de la figura <strong>18</strong>.3. Esta repetición dará origen a una<br />

estructura de ciclo en nuestro algoritmo.<br />

Ahora, la pregunta es: ¿qué sucede durante cada paso para clasificar finalmente al arreglo completo?<br />

Bueno, durante cada paso, el primer elemento en la parte no clasificada (sin sombrear) del arreglo se<br />

examina comparándolo a la secuencia clasificada de los elementos que le preceden. Si este elemento es<br />

menor que el anterior, estos dos elementos se intercambian y de nuevo el elemento anterior se compara con<br />

su predecesor, si es menor se intercambian y así sucesivamente.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-48


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Paso 1<br />

Paso 2<br />

[0] [1] [2] [3] [4]<br />

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4]<br />

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4]<br />

CICLO EXTERNO<br />

Paso 3<br />

Paso 4<br />

E D C B A<br />

E D C B A D E C B A<br />

D E C B A D C E B A<br />

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2] [3] [4]<br />

[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]<br />

[0] [1] [2] [3] [4]<br />

ARREGLO C<strong>LA</strong>SIFICADO<br />

C D E B A<br />

C D E B A C D B E A C B D E A<br />

CICLO INTERNO<br />

B C D E A<br />

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

A B C D E<br />

Figura <strong>18</strong>.3. La clasificación por inserción es un proceso de repeticiones anidadas<br />

Este proceso se repite hasta que sucede una de dos cosas: (1) el elemento es mayor o igual a su predecesor,<br />

o (2) el elemento está en la primera posición del arreglo (índice [0]) En otras palabras, el proceso de<br />

comparación o intercambio de izquierda a derecha que se muestra en la figura <strong>18</strong>.3 termina cuando el<br />

elemento examinado se ha insertado en su posición adecuada en la parte clasificada del arreglo. Este<br />

proceso de comparación o intercambio que representa la repetición de izquierda a derecha en la figura <strong>18</strong>.3<br />

dará como resultado otra estructura de ciclo en nuestro algoritmo. Por lo tanto, es posible identificar dos<br />

procesos repetitivos en la figura <strong>18</strong>.3, uno de arriba hacia abajo y otro de izquierda a derecha. ¿Cómo se<br />

relacionan los dos procesos repetitivos? Bien, parece que por cada paso de arriba abajo a través del<br />

arreglo, el proceso de comparación o intercambio se ejecuta de izquierda a derecha. De esta manera, el<br />

proceso de izquierda a derecha deberá anidarse dentro del proceso de arriba abajo. Esto se reflejará en<br />

nuestro algoritmo por medio de dos estructuras de ciclo: una controla el proceso de comparación o<br />

intercambio de izquierda a derecha que deberá anidarse dentro del segundo ciclo que controla el proceso de<br />

arriba abajo. Observe de nuevo la figura <strong>18</strong>.3 para asegurarse que ve esta repetición anidada. Ahora que<br />

tiene una idea de cómo funciona la ordenación por inserción, analice el algoritmo formal:<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-49


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Algoritmo clasifPorInser()<br />

clasifPorInser()<br />

INICIO<br />

Establece i = segundo índice del arreglo.<br />

mientras(i primer índice del arreglo) AND (A[j] < A[j – 1]))<br />

Inicio<br />

Intercambia A[j] y A[j – 1].<br />

Decrementa j.<br />

Fin.<br />

Incrementa i.<br />

Fin.<br />

FIN.<br />

La variable i controla el ciclo externo y j controla el ciclo interno. Observe que i empieza en el segundo<br />

índice del arreglo. ¿Por qué no en el primer índice del arreglo? Porque el primer elemento en el arreglo<br />

se clasifica siempre relativo a cualquier elemento precedente, ¿correcto? Por lo tanto, el primer paso<br />

comienza con el segundo elemento del arreglo. El primer enunciado en el ciclo externo establece a j = i. De<br />

esta manera, i y j localizan el primer elemento en la parte no clasificada del arreglo al principio de cada<br />

paso. Ahora, el ciclo interno intercambiará al elemento localizado por j, el cual es A[j], con su elemento<br />

predecesor, el cual es A[j – 1], siempre y cuando j sea mayor que el primer índice del arreglo y el elemento<br />

A[j] sea menor que el elemento A[j – 1] Una vez que se hace el intercambio, se decrementa j. Esto obliga a<br />

j a seguir siendo el elemento insertado dentro de la parte del arreglo ordenado. El intercambio continúa<br />

hasta que no hay ningún elemento que preceda al elemento A[j] que sea menor que el A[j]<br />

Una vez que se rompe el ciclo interno, el elemento A[j] se inserta en su posición correcta con relación con<br />

los elementos que le preceden. Después, se hace otro paso incrementando la variable externa para el control<br />

del ciclo i, estableciendo j a i, y ejecutando de nuevo el ciclo interno. Este proceso de ciclo anidado<br />

continúa hasta que i se incrementa más allá de la última posición del arreglo.<br />

Estudie el algoritmo anterior y compárelo con la figura <strong>18</strong>.3 hasta que esté seguro que comprende<br />

clasifPorInser() A continuación el código en C++.<br />

CODIFICACIÓN <strong>DE</strong>L PROBLEMA<br />

Hemos desarrollado la interfaz de la función clasifPorInser() En C++, el algoritmo se codifica fácilmente<br />

como una función semejante a ésta:<br />

// Función de intercambio()<br />

void interCambio(char &x, char &y)<br />

{<br />

char temp; // Crea una variable temporal<br />

temp = x;<br />

x = y;<br />

y = temp;<br />

} // Fin de interCambio()<br />

// Función clasificación por inserción<br />

void clasifPorInser(char A[MAX])<br />

{<br />

int i; // Variable que controla el ciclo exterior<br />

int j; // Variable que controla el ciclo interior<br />

i = 1; // Establece i al índice del segundo elemento<br />

while(i < MAX)<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-50


MIGUEL Á. TOLEDO MARTÍNEZ<br />

{<br />

j = i; // Localiza el primer elemento del arreglo no clasificado<br />

while((j > 0) && (A[j] < A[j – 1]))<br />

{<br />

interCambio(A[j], A[j – 1]);<br />

--j; // Hace que j siga al elemento insertado<br />

} // Final del while interno<br />

++i; // Hace que i localice el primer elemento de la parte no<br />

clasificada.<br />

} // Fin del while externo<br />

} // Fin de clasifPorInser()<br />

En este caso se pueden ver dos funciones codificadas en C++. Recuerde que el algoritmo clasifPorInser()<br />

requiere una operación de intercambio. Para llevar a cabo esta tarea se ha codificado una función llamada<br />

interCambio() Observe que esta función tiene dos parámetros de referencia que son caracteres. De esta<br />

manera, la función recibe dos caracteres que se intercambian utilizando la variable local temporal (temp)<br />

dentro de la función. Los caracteres de intercambio se envían de regreso al programa llamador por medio de<br />

los parámetros de referencia. Desde luego, el programa llamador será la función clasifPorInser()<br />

El código de clasifPorInser() debe ser directo a partir del algoritmo que se analizó. Estudie el código y<br />

compárelo con el algoritmo. Encontrará que son idénticos desde un punto de vista lógico y estructural.<br />

Debe notar cómo se llama a la función intercambio() dentro de clasifPorInser() Los elementos del arreglo<br />

A[j] y A[j – 1] se pasan a la función. Estos elementos son caracteres sencillos, ¿correcto? Por lo tanto, la<br />

función recibe dos caracteres y los intercambia. Los caracteres respectivos en el arreglo reflejan la<br />

operación de intercambio, porque intercambio() emplea parámetros de referencia.<br />

SOLUCIÓN <strong>DE</strong> PROBLEMAS EN ACCIÓN: Búsqueda en un arreglo con recursión<br />

(Búsqueda binaria)<br />

PROBLEMA<br />

En este problema desarrollaremos otro popular algoritmo de búsqueda, llamado búsqueda binaria. El<br />

algoritmo de búsqueda binaria que desarrollaremos empleará recursividad, aunque también se puede hacer<br />

utilizando iteración. Una de las principales diferencias entre búsqueda binaria y búsqueda secuencial es que<br />

la primera requiere que el arreglo se ordene antes de la búsqueda, mientras que la segunda no tiene este<br />

requerimiento. Sin embargo, si va a empezar con un arreglo ordenado, la búsqueda binaria es mucho más<br />

rápida que la secuencial, especialmente para arreglos grandes. Por ejemplo, si fuera a aplicar una búsqueda<br />

secuencia a un arreglo de 1000 enteros, el algoritmo de búsqueda secuencial hará un promedio de 500<br />

comparaciones para encontrar el elemento deseado. Peor aún, si el elemento deseado está en la última<br />

posición del arreglo, la búsqueda secuencial hará mil comparaciones para encontrar el elemento. Por otra<br />

parte, la búsqueda binaria requerirá un máximo de 10 comparaciones para encontrar el elemento, ¡aún si<br />

está en la última posición del arreglo! Desde luego, se debe pagar un precio por esta mayor eficiencia. El<br />

precio que se paga es un algoritmo más complejo. Por lo tanto, cuando busque en un arreglo ordenado, la<br />

ventaja de la búsqueda secuencial es simplicidad, mientras que la ventaja de la búsqueda binaria es<br />

eficiencia.<br />

Desarrolle una función en C++ que se pueda llamar para buscar en un arreglo ordenado de enteros un<br />

elemento con determinado valor y que regrese el índice del elemento si se encuentra en el arreglo. Utilice<br />

una búsqueda binaria recursiva para realizar esta tarea.<br />

Se desarrollará una función en C++, así la definición del problema se enfocará de nuevo a la interfaz de la<br />

función. Sin embargo, antes de considerar la interfaz de la función, veamos cómo funciona una búsqueda<br />

binaria recursiva, porque el algoritmo de búsqueda dictará los parámetros de la función. Por lo tanto,<br />

primero trataremos el algoritmo y después desarrollaremos la interfaz de la función.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-51


MIGUEL Á. TOLEDO MARTÍNEZ<br />

P<strong>LA</strong>NEACIÓN <strong>DE</strong> <strong>LA</strong> SOLUCIÓN<br />

La búsqueda binaria representa una operación recursiva natural. Recuerde que la idea detrás de la<br />

recursividad es dividir un problema en subproblemas. Dividir un problema en subproblemas más simples de<br />

exactamente el mismo tipo hasta que ocurra una condición primitiva. Esto no es lo mismo que el diseño de<br />

software descendente, que divide los problemas en subproblemas más simples. La diferencia con la<br />

recursividad es que los subproblemas son exactamente del mismo tipo de problema que el problema<br />

original. Por ejemplo, suponga que está buscando un nombre en un directorio. Imagine que empieza al<br />

principio del directorio y ve cada nombre hasta que encuentra el correcto. Esto es exactamente lo que hace<br />

la búsqueda secuencial. ¿No sería más rápido, en promedio, abrir el directorio a la mitad? Después,<br />

determinar qué mitad del directorio contiene el nombre que está buscando, dividir esta sección del<br />

directorio a la mitad y así sucesivamente, hasta que obtiene la página en la cual aparece el nombre deseado.<br />

Aquí está un algoritmo que describe la búsqueda en el directorio como se describió.<br />

UN ALGORITMO <strong>DE</strong> BÚSQUEDA RECURSIVA EN UN DIRECTORIO<br />

buscaTel()<br />

INICIO<br />

si(el directorio telefónico sólo tiene una página) entonces<br />

Busca el nombre en la página.<br />

sino<br />

Abre el libro a la mitad.<br />

si(el nombre está en la primera mitad) entonces<br />

buscaTel(primera mitad del directorio para el nombre)<br />

sino<br />

buscaTel(segunda mitad del directorio para el nombre)<br />

FIN.<br />

¿Observa cómo esta búsqueda es recursiva? Se mantiene realizando las mismas operaciones básicas<br />

hasta que llega a la página que contiene el nombre que está buscando. En otras palabras, la función<br />

buscaTel() se mantiene llamándose a sí misma en el enunciado anidado si/sino hasta que se encuentra la<br />

página correcta. La razón por la que se llama al proceso de búsqueda binaria es que deberá dividir el<br />

directorio entre 2 (bi) cada vez que se llama a sí mismo el algoritmo.<br />

Ahora, vamos a ver cómo se puede aplicar este proceso a la búsqueda en un arreglo de enteros. Se llamará a<br />

la función de búsqueda binaria recursiva busquedaBin() y se desarrollará el algoritmo en varios pasos.<br />

Aquí esta el primer nivel del algoritmo.<br />

Algoritmo busquedaBin(): Primer nivel<br />

busquedaBin()<br />

INICIO<br />

si(el arreglo tiene sólo un elemento) entonces<br />

Determine si este elemento es el elemento buscado.<br />

sino<br />

Encuentre el punto medio del arreglo.<br />

si(el elemento está en la primera mitad) entonces<br />

busquedaBin(primera mitad)<br />

sino<br />

busquedaBin(segunda mitad)<br />

FIN.<br />

Observe cómo este algoritmo es casi idéntico al algoritmo buscaTel() Aquí el proceso de búsqueda se<br />

continúa hasta que el arreglo se reduce a un elemento que se verifica contra el elemento que se está<br />

buscando. ¿Observa cómo la búsqueda se mantiene llamándose a sí misma hasta que ocurre la<br />

condición primitiva? Aunque este algoritmo proporciona una idea general de búsqueda binaria, se necesita<br />

mayor detalle para codificar el algoritmo. Para hacerlo, debemos preguntarnos que datos necesita<br />

busquedaBin() para realizar esta tarea. Bueno, al igual que la búsqueda secuencial, ésta necesita un arreglo<br />

en dónde buscar y el elemento que se va a buscar ¿correcto? Sin embargo, la búsqueda secuencial trata con<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-52


MIGUEL Á. TOLEDO MARTÍNEZ<br />

un tamaño de arreglo dado, mientras que la búsqueda binaria necesita tratar con arreglos de tamaños<br />

diferentes conforme sigue dividiendo al arreglo original a la mitad. No solamente son estos arreglos de<br />

diferente tamaño, sino el primero y el último índices de cada mitad son diferentes. Como resultado,<br />

debemos proporcionar a busquedaBin() los límites del arreglo que está tratando en cualquier momento<br />

dado. Esto se puede hacer pasando el primero y último índices del arreglo dado a la función. Llamemos a<br />

estos índices primero y ultimo.<br />

Estamos listos para escribir la descripción de la interfaz de la función:<br />

Función busquedaBin(): Busca en un arreglo de enteros ordenado un valor<br />

determinado.<br />

Acepta: Un arreglo de enteros, un elemento de búsqueda, el primero<br />

y el último índices del arreglo en el cual se busca.<br />

Regresa: El índice del elemento, si se encuentra, o el valor -1 si el<br />

elemento no se encuentra.<br />

Esta descripción da suficiente información para escribir la interfaz de la función C++, como sigue:<br />

int busquedaBin(int A[], int elemento, int primero, int ultimo)<br />

Aquí, busquedaBin() regresará un valor entero que representa el índice del elemento que se busca. De<br />

nuevo, verá que regresará el valor -1 si el elemento no se encuentra en el arreglo. La función recibe el<br />

arreglo de enteros que se busca (A[]), el elemento que se busca (elemento), el primer índice del arreglo<br />

(primero) y el último índice del arreglo (ultimo) Observe que ningún tamaño se proporciona para el arreglo<br />

en que se busca porque la función buscará en forma recursiva en arreglos de diferentes tamaños.<br />

El siguiente problema es determinar qué valor de primero y ultimo se utilizarán para cualquier arreglo<br />

durante la búsqueda. Bueno, recuerde que se deberá dividir cualquier arreglo dado a la mitad para producir<br />

dos nuevos arreglos cada vez que se hace una llamada recursiva busquedaBin() Dado cualquier arreglo<br />

donde el primer índice es primero y el último índice es ultimo, se puede determinar el índice medio, como<br />

sigue:<br />

mitad = (primero + ultimo) / 2<br />

Con este cálculo, la primera mitad del arreglo empieza en primero y finaliza en mitad - 1, y la segunda<br />

mitad del arreglo empieza en mitad + 1 y finaliza en ultimo. Esta idea se muestra en la figura <strong>18</strong>.4.<br />

Pero, observe que ninguna mitad del arreglo contiene el elemento medio. Por me- dio de la utilización de<br />

esta técnica, las dos mitades no hacen un todo, ¿correcto? Por lo tanto, antes que se haga la división,<br />

suponga que verificamos el elemento medio para ver si es el elemento que se está buscando. La siguiente<br />

verificación realiza el trabajo.<br />

si (A[mitad] == elemento) entonces<br />

regresar mitad.<br />

Si esta verificación es verdadera antes de la división, se habrá encontrado el elemento que se está buscando<br />

y es posible terminar las llamadas recursivas. De otra manera, el elemento almacenado en A[mitad] no es el<br />

elemento que se está buscando, y esta posición del arreglo se puede ignorar durante el resto de la búsqueda.<br />

Si éste es el caso, se dividirá el arreglo y continuará el proceso recursivo. Sin embargo, se tiene que<br />

adicionar una segunda condición primitiva para el algoritmo recursivo. Aquí están las dos condiciones<br />

primitivas que se han hecho:<br />

1. El arreglo que se busca tiene sólo un elemento.<br />

2. A [mitad] == elemento.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-53


MIGUEL Á. TOLEDO MARTÍNEZ<br />

mitad = (primero + ultimo) / 2<br />

[primero] [ultimo]<br />

[primero] [mitad – 1] [mitad + 1] [ultimo]<br />

Figura <strong>18</strong>.4. La búsqueda binaria recursiva requiere que un arreglo sea dividido en mitades con cada llamada recursiva.<br />

Cualquiera de estas condiciones primitivas hará que termine la llamada recursiva. Ahora, vamos a<br />

considerar la primera condición primitiva más de cerca. ¿Cómo saber si el arreglo que se busca tiene<br />

solamente un elemento? Bueno, conforme continúan las llamadas recursivas sin encontrar el elemento,<br />

finalmente el arreglo se reducirá a un elemento sencillo. Si éste es el que se está buscando, la verificación si<br />

A[mitad] == elemento será verdadera, y las llamadas recursivas se detendrán. Si no lo es, el valor de<br />

primero se hará más grande que el valor de ultimo en la siguiente división. ¿Por qué? Porque si piensa en<br />

la acción de división del algoritmo, se dará cuenta que cada llamada recursiva hace que primero se<br />

incremento y ultimo se decremente. De esta manera, si el elemento no está en el arreglo, el valor de<br />

primero se hace finalmente más grande que el valor de ultimo. Por lo tanto se puede usar esta idea para<br />

verificar que el elemento no está en el arreglo, también para usarlo como una condición primitiva. De esta<br />

manera, se reemplazará la condición primitiva original con el siguiente enunciado:<br />

si (primero > ultimo) entonces<br />

regresar – 1<br />

Si ocurre esta condición, se regresa el valor -1, indicando que no se encontró el elemento y terminan las<br />

llamadas recursivas.<br />

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

Algoritmo busquedaBin(): Segundo nivel<br />

busquedaBin(A, elemento, primero, ultimo)<br />

INICIO<br />

si(primero > ultimo) entonces<br />

regresar -1.<br />

sino<br />

Establecer mitad = (primero + ultimo) / 2.<br />

si (A[mitad] == elemento) entonces<br />

regresar mitad.<br />

sino<br />

si (el elemento está en la primera mitad) entonces<br />

busquedaBin(A, elemento, primero, mitad - l)<br />

sino<br />

busquedaBin(A, elemento, mitad + 1, ultimo)<br />

FIN.<br />

Es evidente ahora que el algoritmo realiza la recursividad, porque se puede ver la función llamándose a sí<br />

misma en uno o dos lugares, dependiendo de en qué mitad del arreglo dividido es probable que se encuentre<br />

el elemento. También, observe en dónde se verifican los dos casos primitivos. Si, al inicio de una llamada<br />

recursiva, primero > ultimo, el elemento no está en el arreglo y las llamadas recursivas terminan. Además,<br />

si después de calcular mitad, se encuentra el elemento en A[mitad], terminará la llamada recursiva. En<br />

ambos casos, la función ha terminado su ejecución y regresa un valor al programa llamador. Lo último que<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-54


MIGUEL Á. TOLEDO MARTÍNEZ<br />

necesita el algoritmo es una forma para determinar si el elemento que se busca es muy probable que se<br />

encuentre en la primera mitad o en la segunda mitad del arreglo dividido. Aquí es donde viene el<br />

requerimiento para un arreglo ordenado. Si el arreglo está ordenado, es muy probable que el elemento se<br />

encuentre en la primera mitad del arreglo cuando elemento < A[mitad]; de otra manera, es muy probable<br />

que el elemento se encuentre en la segunda mitad del arreglo. Observe que usamos el término es muy<br />

probable. Nosotros no podemos garantizar que el elemento se encuentre en cualquier mitad, ¡ya que,<br />

inclusive, podría no estar en el arreglo! Todo lo que se puede hacer es dirigir la búsqueda a la mitad<br />

donde es muy probable que se encuentre el elemento, dependiendo del orden de clasificación de los<br />

elementos. Por lo tanto, se puede completar el algoritmo usando esta idea. Aquí está el algoritmo final:<br />

Algoritmo busquedaBin()<br />

busquedaBin(A, elemento, primero, ultimo)<br />

INICIO<br />

si(primero > ultimo) entonces<br />

regresar -1.<br />

sino<br />

Establecer mitad = (primero + ultimo) / 2.<br />

si (A[mitad] == elemento) entonces<br />

regresar mitad.<br />

sino<br />

si (elemento < A[mitad]) entonces<br />

busquedaBin(A, elemento, mitad - l).<br />

sino<br />

busquedaBin(A, elemento, mitad + 1, ultimo)<br />

FIN.<br />

Observe qué elegante es el algoritmo. Por elegante se debe entender que más que un proceso complicado<br />

de búsqueda binaria, se trata de sólo unos cuantos enunciados. Se sabe que hay mucho por recorrer todavía,<br />

pero la recursividad permite expresar todo este procedimiento en sólo unos cuantos enunciados. Como<br />

puede ver, a menudo los algoritmos recursivos proporcionan soluciones simples a problemas de gran<br />

complejidad, en donde una solución iterativa equivalente puede ser compleja. Éste no es siempre el caso,<br />

porque algunas soluciones recursivas son relativamente poco prácticas para la eficiencia de velocidad y de<br />

memoria. Recuerde la siguiente regla cuando considere la recursividad: considere una solución recursiva<br />

para un problema sólo cuando no sea posible una solución iterativa sencilla. Tome en cuenta que la<br />

búsqueda binaria tiene una solución iterativa relativamente sencilla. Se codificará esta solución para uno de<br />

los problemas al final de esta lección.<br />

CODIFICACIÓN <strong>DE</strong>L PROGRAMA<br />

La función que requiere C++ ahora se puede codificar con facilidad a partir del algoritmo final. Como<br />

sigue:<br />

int busquedaBin(int[A], int elemento, int primero, int ultimo)<br />

{<br />

int mitad; // PUNTO MEDIO <strong>DE</strong>L ARREGLO<br />

if (primero > ultimo) // SI EL ELEMENTO NO ESTÁ EN EL ARREGLO<br />

return -l; // REGRESA -1, SI NO CONTINÚA <strong>LA</strong> BÚSQUEDA<br />

else<br />

{<br />

mitad = (primero + ultimo) / 2; // ENCUENTRA EL PUNTO<br />

// MEDIO <strong>DE</strong>L ARREGLO<br />

if (elemento == A[mitad]) // Si EL ELEMENTO ESTÁ EN<br />

// A[mitad]<br />

return mitad; // REGRESA mitad<br />

else // Si NO, BUSCA <strong>LA</strong> MITAD APROPIADA<br />

if (elemento < A[mitad])<br />

return busquedaBin(A, elemento, primero, mitad - l);<br />

else<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-55


MIGUEL Á. TOLEDO MARTÍNEZ<br />

return busquedaBin(A, elemento, mitad + 1, ultimo);<br />

} / /FINAL <strong>DE</strong> else EXTERNO<br />

} // FINAL <strong>DE</strong> busquedaBin()<br />

No deberá tener ningún problema para comprender este código, ya que refleja la interfaz de la función y<br />

al algoritmo que se desarrolló. Aquí la única diferencia es que las llamadas recursivas en busquedaBin()<br />

forman parte de un enunciado return. Recuerde que C++ requiere que todos los caminos de ejecución de<br />

una función sin void den origen a un enunciado return.<br />

Ejemplo <strong>18</strong>.36<br />

El siguiente programa, BÚSQUEDA.CPP, ilustra los métodos de búsqueda lineal y binaria. Es bastante<br />

interesante la solución que se propone, por lo que se le pide al lector que lea con detenimiento la<br />

codificación.<br />

/* El siguiente programa: BUSQUEDA.CPP, busca en arreglos usando los métodos de búsqueda lineal<br />

y binaria.<br />

*/<br />

#include <br />

#include <br />

#include <br />

typedef int (*funcBusqueda)(int,int[],int);<br />

const int MIN = 2;<br />

const int MAX = 10;<br />

const int NO_ENCONTRADO = -1;<br />

// Pide al usuario la cantidad de elementos del arreglo<br />

int obtenNumPuntos(int minimo, int maximo)<br />

{<br />

int numPuntos;<br />

do<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

{<br />

for (int i = 0; i < num; i++)<br />

{<br />

cout.width(5);<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo <strong>18</strong>.37<br />

while (debeBuscar(tipoBusqueda))<br />

{<br />

cout > valorBuscar;<br />

index = buscar(valorBuscar, intArr, num);<br />

if (index != NO_ENCONTRADO)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

// Compara dos enteros<br />

int cmpEnt(const void *item1, const void *item2)<br />

{<br />

return *(int*)item1 - *(int*)item2;<br />

}//Fin de cmpEnt()<br />

// Pide al usuario el número de elementos del arreglo<br />

int obtenNumPuntos(int minimo, int maximo)<br />

{<br />

int numPuntos;<br />

do<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

}//Fin de busquedaBinaria()<br />

// Verificar la continuidad de búsqueda<br />

bool debeBuscar(const char *tipoBusqueda)<br />

{<br />

char ch;<br />

cout valorBuscar;<br />

indice = buscar(valorBuscar, intArr, num);<br />

if (indice)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

INICIACIÓN <strong>DE</strong> ARREGLOS<br />

Antes de terminar esta lección, es necesario saber cómo inicializar los arreglos en el<br />

momento en que se definen. Los arreglos, como las variables, se pueden inicializar cuando se<br />

crean. Se pueden suministrar valores de iniciación para cualquier arreglo, mientras esté definido<br />

en el programa. Consideremos algún ejemplo de definición de arreglo para mostrar cómo se<br />

inicializan los arreglos.<br />

int enteros[3] = {10,20,30};<br />

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

elementos enteros se han inicializado con los valores 10, 20 y 30, respectivamente. Observe la<br />

sintaxis. La definición del arreglo es seguida por un operador de asignación, al cual le suceden<br />

los valores de inicialización encerrados dentro de llaves. Esto es lo que verá si inspecciona al<br />

arreglo usando un depurador (debugger):<br />

RESULTADOS <strong>DE</strong>L <strong>DE</strong>PURADOR<br />

Inspección de enteros<br />

[0] 10<br />

[1] 20<br />

[2] 30<br />

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

arreglo; el valor 20 se coloca en el índice 1; y el valor 30 se coloca en la última posición del<br />

índice, que es 2. Ahora, ¿qué se supone que sucedería si proporcionara menos valores de<br />

inicialización que las posiciones en el arreglo? Bueno, suponga que define al arreglo como<br />

sigue:<br />

int enteros[3] = {10,20};<br />

Enseguida se presenta lo que encontrará cuando inspeccione al arreglo usando un<br />

depurador:<br />

RESULTADOS <strong>DE</strong>L <strong>DE</strong>PURADOR<br />

Inspección de enteros<br />

[0] 10<br />

[1] 20<br />

[2] 00<br />

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

Cero es el valor de iniciación predeterminado para arreglos de enteros cuando no se<br />

proporcionan suficientes valores para llenar al arreglo.<br />

La siguiente pregunta obvia es: ¿qué sucede si proporciona demasiados valores de<br />

iniciación? Por ejemplo, suponga que define el arreglo como sigue:<br />

int enteros[3] = {10,20,30,40};<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-61


MIGUEL Á. TOLEDO MARTÍNEZ<br />

En este caso, se obtendrá un error de compilación, demasiados inicializadores (too many<br />

initializers) Una forma de solucionar este problema es incrementar el tamaño del arreglo. Otra<br />

forma preferida, es definir al arreglo sin ningún tamaño específico, como sigue:<br />

int enteros[] = {10,20,30,40};<br />

Con esta definición, el compilador establecerá suficiente lugar de almacenamiento para<br />

contener todos los valores de inicialización. Aquí está lo que verá si inspecciona esta definición<br />

de arreglo usando un depurador:<br />

RESULTADOS <strong>DE</strong>L <strong>DE</strong>PURADOR<br />

Inspección de enteros<br />

[0] 10<br />

[1] 20<br />

[2] 30<br />

[3] 40<br />

Ahora, vamos a considerar arreglos de caracteres. Suponga que define un arreglo de<br />

caracteres de tamaño cinco, como éste:<br />

char caracteres[5] = {‘H’, ‘E’, ‘L’, ‘L’, ‘O’}<br />

De nuevo se ve que los valores de inicio se encierran en llaves después de un operador de<br />

asignación. Esto mostrará el depurador:<br />

RESULTADOS <strong>DE</strong>L <strong>DE</strong>PURADOR<br />

Inspección de caracteres<br />

[0] ‘H’<br />

[1] ‘E’<br />

[2] ‘L’<br />

[3] ‘L’<br />

[4] ‘O’<br />

Aquí se ve que los cinco caracteres de inicio se colocan en el arreglo empezando en el<br />

índice cero y finalizando en la última posición del índice, cuatro. ¿Qué pasa si proporciona<br />

unos cuantos caracteres de iniciación menos que los requeridos para llenar el arreglo?<br />

Bueno, suponga que define el arreglo de esta forma:<br />

char caracteres[5] = {'H','E'};<br />

El contenido del arreglo ahora será corno sigue:<br />

RESULTADOS <strong>DE</strong>L <strong>DE</strong>PURADOR<br />

Inspección de caracteres<br />

[0] ‘H’<br />

[1] ‘E’<br />

[2] ‘\0’<br />

[3] ‘\0’<br />

[4] ‘\0’<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-62


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Esta vez, el compilador ha insertado un carácter terminador nulo como un carácter<br />

predeterminado para llenar el arreglo. De otra manera, si proporciona demasiados caracteres,<br />

el compilador generará un mensaje de error demasiados inicializadores. De nuevo, la forma<br />

segura para manejar este problema es dejar que el compilador determine el tamaño del arreglo<br />

para ajustar el número de valores inicializados.<br />

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

cadenas. Recuerde, una cadena no es más que un arreglo de caracteres, terminada con un<br />

terminador nulo. Así es como se puede inicializar un arreglo de caracteres con un valor de<br />

cadena:<br />

char caracteres[6] = "HELLO";<br />

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

comillas más que entre llaves. Otra cosa que se ve es que el tamaño del arreglo es uno más<br />

grande que el número de caracteres en la cadena. La razón para esto parece obvia cuando<br />

inspecciona el arreglo usando un depurador. Aquí esta lo que verá:<br />

RESULTADOS <strong>DE</strong>L <strong>DE</strong>PURADOR<br />

Inspección de caracteres<br />

[0] ‘H’<br />

[1] ‘E’<br />

[2] ‘L’<br />

[3] ‘L’<br />

[4] ‘O’<br />

[5] ‘\0’<br />

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

arreglo uno más grande que el número de caracteres en la cadena, permite espacio para que el<br />

compilador inserte el terminador nulo. Si no se deja espacio para el terminador nulo, éste<br />

quedará truncado (eliminado) del arreglo y no se obtendrá un mensaje de error. De nuevo, la<br />

mejor manera de evitar este problema es permitir que el compilador determine el tamaño del<br />

arreglo, como éste:<br />

char caracteres[] = "HELLO";<br />

Con esta definición, el compilador creará suficientes posiciones en el arreglo para<br />

contener todos los caracteres de cadenas con el terminador nulo insertado como el último<br />

carácter en el arreglo.<br />

INICIACIÓN PRE<strong>DE</strong>TERMINADA <strong>DE</strong> ARREGLOS GLOBALES Y ESTÁTICOS<br />

Se pueden definir e inicializar arreglos en cualquier parte del programa C++. El ámbito<br />

de un arreglo funciona justo como el ámbito de una variable o constante. Un arreglo definido<br />

antes de main() es visible en todo el archivo fuente en el cual se define. Un arreglo definido<br />

dentro de un bloque tiene ámbito de bloque y, por lo tanto, es visible sólo dentro del bloque en<br />

el cual se define.<br />

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

ningún valor de inicialización, el compilador inicializará el arreglo con el valor predeterminado<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-63


MIGUEL Á. TOLEDO MARTÍNEZ<br />

respectivo (ceros para arreglos de enteros y de punto flotante y terminadores nulos para arreglos<br />

de caracteres) El siguiente es un ejemplo:<br />

int enteros[5];<br />

void main(void)<br />

{<br />

static char caracteres[5];<br />

} // FINAL <strong>DE</strong> main()<br />

Se ha definido en forma global el arreglo enteros y también como un arreglo estático<br />

local al arreglo caracteres dentro de main() La inspección de estos arreglos con un depurador<br />

revelará lo siguiente:<br />

RESULTADOS <strong>DE</strong>L <strong>DE</strong>PURADOR<br />

Inspección de enteros Inspección de caracteres<br />

[0] 0 [0] ‘\0’<br />

[1] 0 [1] ‘\0’<br />

[2] 0 [2] ‘\0’<br />

[3] 0 [3] ‘\0’<br />

[4] 0 [4] ‘\0’<br />

El depurador muestra que el arreglo global de enteros se ha inicializado con ceros,<br />

mientras que el arreglo de caracteres estático se ha inicializado con caracteres de terminador<br />

nulo. Si define un arreglo con ámbito de bloque local que no es estático y no lo inicializa, el<br />

compilador no suministrará ningún valor de inicialización predeterminado. ¡El arreglo<br />

contendrá basura! De esta manera, si elimináramos la palabra clave static de la definición<br />

anterior del arreglo caracteres, el depurador revelará valores arbitrarios de memoria en el<br />

arreglo.<br />

Aquí está un resumen de la explicación anterior:<br />

• Los arreglos de enteros, de punto flotante y de caracteres se inicializan por medio de un<br />

operador de asignación después de la definición del arreglo, seguido por una lista de valores<br />

individuales de inicialización dentro de llaves.<br />

• Menos valores de iniciación darán como resultado valores predeterminados (ceros para<br />

arreglos de enteros y de punto flotante y terminadores nulos para arreglos de caracteres)<br />

insertados en las posiciones adicionales del arreglo.<br />

• Más valores de iniciación provocan un error de compilación.<br />

• Los arreglos de caracteres se pueden inicializar encerrando una cadena con comillas dobles.<br />

• El tamaño de un arreglo de cadena deberá ser uno más grande que el número de caracteres<br />

dentro de la cadena para dejar espacio para el carácter terminador nulo.<br />

• Si no se especifica un tamaño en la definición del arreglo, el compilador creará el suficiente<br />

espacio de almacenaje para los valores de inicialización.<br />

• Los arreglos globales y arreglos estáticos se inicializan siempre con los valores<br />

predeterminados respectivos cuando ningún valor de iniciación se suministra en la definición<br />

del arreglo.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-64


MIGUEL Á. TOLEDO MARTÍNEZ<br />

• Los arreglos locales que no son estáticos no se inicializarán con ningún valor específico, a<br />

menos que se proporcionen en la definición del arreglo.<br />

ARREGLOS QUE REBASAN LOS 64 KBYTES <strong>DE</strong> MEMORIA<br />

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

(Array size too large) en tiempo de compilación.<br />

Ejemplo <strong>18</strong>.38<br />

Ejemplo <strong>18</strong>.39<br />

EXAMEN BREVE 38<br />

El siguiente programa, <strong>DE</strong>MG<strong>DE</strong>.CPP, ilustra esta situación.<br />

/* El siguiente programa: <strong>DE</strong>MG<strong>DE</strong>.CPP, no se compila correctamente ya que el<br />

arreglo utiliza mas de 64 kbytes de memoria.<br />

*/<br />

void main(void)<br />

{<br />

char cadena[66000L]; // 66,000 bytes<br />

int valores[33000L]; // 33,000 * 2 = 66,000 bytes<br />

float numeros[17000]; // 17,000 * 4 = 68,000 bytes<br />

}//Fin de main()<br />

El siguiente programa ENORME.CPP, ilustra como resolver esta situación.<br />

/* El siguiente programa: ENORME.CPP crea un arreglo de más de 64 kbytes<br />

de datos de punto flotante.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para halloc()<br />

void main (void)<br />

{<br />

int i;<br />

float huge *valores;<br />

if ((valores = (float huge *) halloc (17000, sizeof(float))) == NULL)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

}//Fin de main()<br />

LO QUE NECESITA SABER<br />

for (i = 0; i < 17000; i++)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Es posible utilizar un arreglo de clase char para almacenar una cadena de caracteres.<br />

Los elementos de un arreglo pueden inicializarse por declaración, por asignación o por<br />

entrada.<br />

Si hay menos inicializadores que elementos en el arreglo, los elementos restantes se<br />

inicializan a cero.<br />

C++ no evita que se haga referencia a elementos que estén fuera de los límites de un<br />

arreglo.<br />

Es posible inicializar un arreglo de caracteres por medio de una literal de cadena.<br />

Todas las cadenas terminan con el carácter nulo (‘\0’)<br />

Los arreglos de caracteres se pueden inicializar con constantes de carácter en una lista<br />

de iniciación.<br />

Es posible acceder a los caracteres de una cadena almacenada en un arreglo por medio<br />

de la notación de índices.<br />

Para pasar un arreglo a una función, hay que pasar su nombre. Para pasar un solo elemento<br />

del arreglo a una función, simplemente hay que pasar el nombre del arreglo seguido por el<br />

índice (entre corchetes cuadrados) de dicho elemento.<br />

Los arreglos se pasan a las funciones simulando una llamada por referencia; las<br />

funciones llamadas pueden modificar los valores de los elementos de los arreglos originales.<br />

El nombre del arreglo es la dirección de su primer elemento. Debido a que se pasa la dirección<br />

de inicio del arreglo, la función llamada sabe exactamente dónde está almacenado dicho<br />

arreglo.<br />

Para recibir un arreglo como argumento, la lista de parámetros de la función debe<br />

especificar que se recibirá un arreglo. No es necesario el tamaño del arreglo entre los<br />

corchetes.<br />

Cuando una función recibe un arreglo como parámetro, la función debe especificar la<br />

clase del arreglo y su nombre, pero no el tamaño del arreglo.<br />

C++ tiene el calificador de clase const, que permite a los programas evitar la modificación,<br />

en una función, de los valores de un arreglo. Cuando un parámetro de arreglo está precedido<br />

por el calificador const, los elementos del arreglo se vuelven constantes en el cuerpo de la<br />

función y cualquier intento por modificarlos provoca un error de sintaxis.<br />

La búsqueda y ordenación son operaciones comunes realizadas sobre arreglos.<br />

Los arreglos pueden ordenarse mediante la técnica de ordenamiento de burbuja. Se<br />

realizan varias pasadas al arreglo. Con cada pasada se comparan los pares consecutivos de los<br />

elementos. Si un par está ordenado (o sus valores son idénticos), se deja tal cual. Si está fuera<br />

de orden, se intercambian. El ordenamiento de la burbuja es aceptable en los arreglos<br />

pequeños, pero en los mayores es ineficiente en comparación con otros algoritmos de<br />

ordenamiento más complejos.<br />

La búsqueda secuencial es una búsqueda iterativa que busca un valor dado en un arreglo,<br />

comparando en forma secuencial el valor con los elementos del arreglo, empezando con el<br />

primer elemento del arreglo, hasta que se encuentra el valor en el arreglo o hasta que se<br />

alcanza el final del arreglo.<br />

La búsqueda lineal compara todos los elementos de un arreglo con la clave de búsqueda. Si el<br />

arreglo no está en ningún orden en particular, existe la misma probabilidad de que el valor se<br />

encuentre en el primer elemento como que esté en el último. Por lo tanto, en promedio, el<br />

programa tendrá que comparar la clave de búsqueda con la mitad de los elementos del arreglo.<br />

El método de búsqueda lineal funciona bien con los arreglos pequeños y es aceptable si se<br />

trata de arreglos desordenados.<br />

Una búsqueda binaria puede ser iterativa o recursiva. Una búsqueda binaria divide el arreglo<br />

en mitades, dirigiéndose a sí misma hacia la mitad en donde es muy probable que se encuentre<br />

el valor.<br />

La búsqueda binaria requiere que el arreglo esté ordenado, mientras que la búsqueda<br />

secuencial no tiene este requerimiento.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-67


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Por otra parte, la búsqueda binaria es mucho más rápida que la secuencial, especialmente en<br />

grandes arreglos ordenados.<br />

La búsqueda binaria elimina de su consideración la mitad de los elementos del arreglo tras<br />

cada comparación; esto lo logra localizando el elemento central del arreglo y comparándolo<br />

con la clave de búsqueda. Si son iguales, entonces se encuentra la clave de búsqueda y se<br />

devuelve el índice de dicho elemento. De otra manera, se reduce el problema a la búsqueda en<br />

una mitad del arreglo.<br />

En el peor caso, la búsqueda en un arreglo de 1024 elementos sólo necesitaría 10<br />

comparaciones y se efectúa mediante búsqueda binaria.<br />

Muchas aplicaciones en el mundo real requieren que la información esté ordenada. Hay<br />

algunos algoritmos de clasificación comunes, incluyendo clasificación por inserción,<br />

clasificación por burbuja, clasificación por selección y clasificación rápida.<br />

Todos estos algoritmos operan sobre arreglos. El algoritmo de clasificación por inserción<br />

es un proceso iterativo que inserta un elemento dado en el arreglo en su lugar correcto relativo<br />

a los elementos que lo preceden en el arreglo. Usted se familiarizará con la clasificación de<br />

burbuja y la clasificación de selección en los problemas de esta lección.<br />

PREGUNTAS Y PROBLEMAS<br />

PREGUNTAS<br />

1. Llene los siguientes espacios en blanco:<br />

a. Las listas y tablas de valores se guardan en ________________________.<br />

b. Los elementos de un arreglo se relacionan por el hecho de que tienen el mismo __________ y<br />

_____________.<br />

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

_____________.<br />

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

programa.<br />

e. El proceso de colocar en orden los elementos en un arreglo se llama _______________ del arreglo.<br />

f. El proceso con el que se determina si un arreglo contiene cierto valor clave se llama ____________.<br />

g. C++ almacena las listas de valores en _________________.<br />

h. Al referirse a un elemento de un arreglo, el número de posición contenido entre paréntesis se llama<br />

_____________.<br />

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

___________.<br />

j. La denominación de un arreglo, indicación de su clase y especificación de la cantidad de elementos que<br />

hay en él se llama _____________ del arreglo.<br />

k. El proceso de colocación de los elementos de un arreglo en orden ascendente o descendente se llama<br />

_____________.<br />

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

a. Un arreglo puede contener diferentes clases de valores.<br />

b. Los índices de los arreglos normalmente deben de ser de clase float.<br />

c. Si hay menos inicializadores en una lista de iniciación que el número de elementos que hay en el<br />

arreglo, los elementos restantes se inicializan automáticamente al último valor de dicha lista.<br />

d. Es un error que una lista de iniciación contenga más inicializadores que la cantidad de elementos que<br />

hay en el arreglo.<br />

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

cuando termine la ejecución de la función llamada.<br />

f. Para hacer referencia a una localidad particular o elemento de un arreglo, se especifica el nombre del<br />

arreglo y el valor del elemento.<br />

g. Una declaración de arreglo reserva espacio para el arreglo.<br />

h. Para indicar que se deben reservar 100 localidades para el arreglo de enteros p, el programador<br />

escribirá la declaración<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-68


MIGUEL Á. TOLEDO MARTÍNEZ<br />

p[100];<br />

i. Un programa C++ que inicialice a 0 los elementos de un arreglo de 15 elementos debe contener cuando<br />

menos una instrucción for.<br />

3. Conteste las siguientes preguntas relacionadas con un arreglo llamado fracciones.<br />

a. Defina una variable constante tamanoArreglo que se inicialice a 10.<br />

b. Declare un arreglo con elementos tamanoArreglo de la clase float e inicialice dichos elementos a 0.<br />

c. Nombre el cuarto elemento a partir del inicio del arreglo.<br />

d. Haga referencia al elemento 4.<br />

e. Asígnele el valor 1.667 al elemento 9.<br />

f. Asígnele el valor 3.333 al séptimo elemento del arreglo.<br />

g. Imprima los elementos 6 y 9 del arreglo con una precisión de dos dígitos a la derecha del punto decimal y<br />

muestre la salida que se desplegará en la pantalla.<br />

h. Imprima todos los elementos del arreglo mediante una estructura de repetición for. Defina la variable<br />

entera x como la variable de control del ciclo. Muestre la salida.<br />

4. Encuentre los errores en los siguientes segmentos de programa y corríjalos.<br />

a. #include ;<br />

b. tamanoArreglo = 10; //tamanoArreglo se declaró como const<br />

c. Suponga que int b[10] = {0};<br />

5.<br />

for(int i = 0; i


MIGUEL Á. TOLEDO MARTÍNEZ<br />

16. Escriba un enunciado para definir un arreglo de punto flotante que se inicializará con ceros y es local para<br />

main()<br />

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

<strong>18</strong>. Dada la siguiente definición de arreglo,<br />

int números[4] = {0,1,2,3,4};<br />

int valores[10];<br />

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

posición del último elemento.<br />

Use la siguiente definición de arreglo para contestar las preguntas 19 - 23:<br />

const int MAX = 4;<br />

char cadena[MAX] =“C++”;<br />

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

regresa la longitud de la cadena.<br />

20. Escriba un prototipo para una función de nombre elementoCadena() que recibirá un elemento sencillo de la<br />

cadena, de manera que cualquier operación sobre ese elemento dentro de la función no afectará el valor del<br />

elemento dentro del arreglo.<br />

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

función.<br />

22. Escriba un prototipo para una función de nombre cambiaElemento() que recibirá un solo elemento de la<br />

cadena, de manera que cualquier modificación en este elemento dentro de la función cambie el valor del<br />

elemento en el arreglo.<br />

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

función.<br />

24. En general, ¿qué posición del elemento se regresará por medio de las funciones de búsqueda secuencial y<br />

binaria desarrolladas en esta lección, si un elemento se presenta varias veces en el arreglo?<br />

25. ¿Por qué, en promedio, es más rápida la búsqueda binaria que la secuencial?<br />

26. ¿Cuándo es más rápida la búsqueda secuencial que la binaria?<br />

27. Revise el algoritmo clasifPorInser() para ordenar el arreglo de modo descendente.<br />

PROBLEMAS<br />

1. Escriba un programa para llenar un arreglo con todos los enteros impares desde 1 hasta 99. Escriba una<br />

función para llenar el arreglo y otra para mostrar éste, desplegando en la pantalla los enteros impares<br />

separados por comas.<br />

2. Escriba una función para leer el nombre del usuario desde la entrada del teclado y colóquelo en un arreglo<br />

de caracteres. Escriba otra función para mostrar el nombre del usuario almacenado en el arreglo. Verifique<br />

sus funciones por medio de un programa de aplicación.<br />

3. Escriba un programa para leer una lista de 25 elementos de caracteres desde una entrada de teclado y<br />

muéstrelos en orden inverso. Use una función para llenar la lista con los elementos escritos y otra para<br />

mostrarla.<br />

4. Escriba un programa que use seis arreglos de caracteres para almacenar el nombre del usuario, domicilio,<br />

ciudad, estado, código postal y número telefónico. Proporcione una función para llenar los arreglos y<br />

otra para mostrar el contenido del arreglo usando un formato de direccionamiento apropiado.<br />

5. (La coladera de Eratóstenes) Un entero primo es cualquier entero que es divisible sólo entre él mismo y<br />

entre 1. La coladera de Eratóstenes es un método para encontrar números primos. Opera como sigue:<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-70


MIGUEL Á. TOLEDO MARTÍNEZ<br />

a) Cree un arreglo con todos los elementos inicializados a 1 (verdadero) Los elementos del arreglo que<br />

tengan índices primos permanecerán en 1. Los demás elementos del arreglo en algún momento se<br />

establecerán a cero.<br />

b) Comenzando por índice 2 (el índice 1 debe ser primo), cada vez que se encuentre un elemento del<br />

arreglo que sea 1, haga un ciclo por el resto del arreglo y establezca a cero todos los elementos cuyo<br />

índice sea un múltiplo de dicho índice. Para el índice 2, todos los elementos por encima de 2 que sean<br />

múltiplos de 2 se establecerán a cero (los índices 4, 6, 8, 10, etc.); en el caso del índice 3, todos los<br />

elementos por encima de 3 que sean múltiplos de 3 se establecerán a cero (índices 6, 9, 12, 15, etc.); y<br />

así sucesivamente.<br />

Terminado este proceso, los elementos del arreglo que aún estén establecidos a 1 indicarán que el índice<br />

es un número primo, así que podrán imprimirse. Escriba un programa con un arreglo de 1000<br />

elementos que determine e imprima los números primos entre 1 y 999. Ignore el elemento 0 del<br />

arreglo.<br />

6. Por medio de un arreglo de un solo índice resuelva el siguiente problema. Una compañía paga a sus<br />

vendedores con base en una comisión. Los vendedores reciben $200 a la semana más 9% de sus ventas<br />

netas durante la semana. Por ejemplo, un vendedor cuyas ventas brutas son de $5000, a la semana recibe<br />

$200 más 9% de $5000, es decir un total de $650. Escriba un programa (con un arreglo de contadores) que<br />

determine la cantidad de vendedores que ganaron salarios dentro de los siguientes rangos (suponga que el<br />

salario de cada vendedor se cierra a una cifra entera):<br />

a) $200 - $299<br />

b) $300 - $399<br />

c) $400 - $499<br />

d) $500 - $599<br />

e) $600 - $699<br />

f) $700 - $799<br />

g) $800 - $899<br />

h) $900 - $999<br />

i) $1000 o más<br />

7. El ordenamiento por el método de la burbuja elaborado en el ejemplo <strong>18</strong>.26. es ineficiente en el caso de<br />

arreglos grandes. Haga las siguientes modificaciones sencillas que mejorarán el desempeño del<br />

ordenamiento de burbuja.<br />

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

arreglo, tras la segunda pasada, los dos números mayores están en su lugar, etc. En lugar de hacer<br />

nueve comparaciones con cada pasada, modifique el ordenamiento de burbuja para que efectúe ocho<br />

comparaciones en la segunda pasada, siete en la tercera, etc.<br />

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

pasadas si tal vez baste con menos? Modifique el ordenamiento para comprobar al final de cada pasada<br />

si se han hecho intercambios. Si no ha sucedido ninguno, entonces los datos ya estarán en orden y<br />

deberá terminar el programa. Si han sucedido intercambios, entonces se necesita cuando menos otra<br />

pasada.<br />

8. Escriba instrucciones que lleven a cabo las siguientes operaciones sobre arreglos de un solo índice. Debe ser<br />

una sola instrucción por cada una.<br />

a) Inicialice a cero los 10 elementos del arreglo de enteros contador.<br />

b) Sume 1 a cada uno de los 15 elementos del arreglo de enteros bonos.<br />

c) Lea del teclado 12 valores para el arreglo float temperaturaMensual.<br />

d) Imprima los 5 valores del arreglo de enteros mejoresCalificaciones en formato de columnas<br />

9. Encuentre el error o los errores en las siguientes instrucciones:<br />

a) Suponga que: char cadena[5];<br />

cin >> cadena; //El usuario teclea bello<br />

b) Suponga que: int a[3];<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

c) float f[3] = {1.1, 10.01, 100.001, 1000.0001};<br />

10. Modifique el ejemplo <strong>18</strong>.32 para que la función moda() pueda manejar empates en el valor de la moda.<br />

También modifique la función mediana() de modo que se promedien los dos elementos centrales cuando se<br />

trate de un arreglo con un número par de elementos.<br />

11. Con un arreglo de un solo índice resuelva el siguiente problema. Lea 20 números entre 10 y 100, inclusive. A<br />

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

cuenta el peor caso, cuando todos los números son diferentes. Resuelva este problema empleado el arreglo<br />

más pequeño posible.<br />

12. Escriba un programa que simule el lanzamiento de dos dados. El programa deberá valerse de rand() para<br />

lanzar el primer dado y nuevamente rand() para lanzar el segundo. Luego debe calcular la suma de ambos<br />

valores. Nota: debido a que cada dado puede tener un valor entero de 1 a 6, entonces la suma de ambos<br />

valores variará de 2 a 12, siendo 7 la suma más frecuente y 2 y 12 las menos frecuentes. La figura <strong>18</strong>.5<br />

muestra las 36 combinaciones de dados posibles. Su programa deberá lanzar ambos dados 36,000 veces.<br />

Mediante un arreglo de un solo índice, registre la cantidad de veces que aparece cada suma. Imprima el<br />

resultado en formato de tabla. Además determine si los totales son razonables, es decir, hay seis maneras de<br />

lanzar 7, por lo que aproximadamente una sexta parte de los lanzamientos debe ser 7.<br />

1 2 3 4 5 6<br />

1 2 3 4 5 6 7<br />

2 3 4 5 6 7 8<br />

3 4 5 6 7 8 9<br />

4 5 6 7 8 9 10<br />

5 6 7 8 9 10 11<br />

6 7 8 9 10 11 12<br />

Figura <strong>18</strong>.5. Las 36 posibles combinaciones de lanzamiento de dos dados.<br />

13. ¿Qué hace el siguiente problema?<br />

// Nombre del programa: PROBLEMA11.CPP<br />

#include <br />

int queEsEsto(int[], int);<br />

void main(void)<br />

{<br />

const int TAMANO_ARREGLO = 10;<br />

int a[TAMANO_ARREGLO] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};<br />

}<br />

int resultado = queEsEsto(a, TAMANO_ARREGLO);<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

d) ¿cuál es la duración promedio de un juego de dados?<br />

e) ¿Mejoran las posibilidades de ganar a medida que aumenta la duración del juego?<br />

15. (Sistema de reservaciones de una aerolínea) Una línea aérea pequeña acaba de comprar una computadora para<br />

su nuevo sistema automatizado de reservaciones. Se le ha pedido a usted que programa este sistema. Deberá<br />

escribir un programa que asigne asientos en cada vuelo del único avión de la compañía (capacidad: 10<br />

asientos)<br />

Su programa deberá presentar el siguiente menú de alternativas:<br />

Por favor teclee 1 para fumar.<br />

Por favor teclee 2 para no fumar.<br />

Si se teclea 1, el programa deberá asignar un asiento en la sección de fumadores (asientos 1 a 5) Si se<br />

teclea 2, se asignará un asiento en la sección de no fumar (asientos 6 a 10). El programa deberá imprimir un<br />

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

fumar.<br />

Mediante un arreglo de un solo índice, represente la gráfica de asientos del avión. Inicialice a cero todos<br />

los elementos del arreglo, indicando que todos los asientos están vacíos. A medida que se asigne un asiento,<br />

establezca a 1 el elemento correspondiente, indicando que ya no está disponible.<br />

El programa, claro está, no deberá volver a asignar un asiento ya asignado. Cuando la sección de<br />

fumadores esté llena, el programa deberá preguntar si es aceptable asignar un asiendo en la sección de no<br />

fumar (y viceversa) Si la respuesta es sí, entonces haga la asignación. Si no, imprima el mensaje el próximo<br />

vuelo parte en tres horas.<br />

16. ¿Qué hace el siguiente programa?<br />

// PROGRAMA14.CPP<br />

#include <br />

void ciertaFunción(int[], int);<br />

void main(void)<br />

{<br />

const int TAMANO_ARREGLO = 10;<br />

int a[TAMANO_ARREGLO] = {32, 27, 64, <strong>18</strong>, 95, 14, 90, 70, 60, 37};<br />

}<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

srand(1);<br />

for(int l = 0; 1 < 10; ++1)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

medio de la función clasifPorBur() para intercambiar dos elementos del arreglo como se muestra<br />

en el algoritmo. Escriba sus funciones para clasificar un arreglo de caracteres. Incorpórelas dentro<br />

de un programa para que verifique el procedimiento de clasificación. ¿Por qué la clasificación<br />

por burbuja es menos eficiente que la clasificación por inserción?<br />

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

clasifPorSelec()<br />

Inicio<br />

para indice1 = (primer índice del arreglo) hasta (ultimo índice del arreglo) hacer<br />

posición = indice1.<br />

masPequeno = A[Posicion].<br />

para indice2 = (indice1 + 1) hasta (ultimo índice del arreglo) hacer<br />

si A[indice2] < masPequeno<br />

posicion = indice2.<br />

masPequeno = A[posicion].<br />

A[posicion] = A[indice1].<br />

A[indice1] = masPequeno.<br />

Fin.<br />

Como en la clasificación por burbuja, la clasificación por selección realiza diversos pasos a través<br />

del arreglo. En el primero de ellos examina al arreglo completo y coloca los elementos más<br />

pequeños en la primera posición del arreglo. En el segundo paso se examina el arreglo empezando<br />

en el segundo elemento. Se encuentra el elemento más pequeño en el segmento del arreglo y se<br />

coloca en la segunda posición del arreglo. El tercer paso examina el arreglo empezando en el tercer<br />

elemento, encuentra el elemento más pequeño en el segmento del arreglo y lo coloca en la tercera<br />

posición del elemento. El proceso continúa hasta que no quedan más segmentos del arreglo.<br />

Su trabajo es codificar el algoritmo anterior como una función C++ de nombre clasifPorSelec()<br />

Escriba su función para clasificar un arreglo de caracteres. Después úsela en un programa que<br />

verifique el procedimiento de clasificación.<br />

¿Por qué la clasificación por selección es menos eficiente que la clasificación por inserción?<br />

22. Escriba un programa que tome un arreglo entero sin ordenar y encuentre la localización del valor<br />

máximo en el arreglo. (Sugerencia: copie el arreglo dentro de otro y clasifique este segundo arreglo<br />

para determinar su valor máximo. Después busque el arreglo original para este valor)<br />

Problemas de recursividad<br />

23. (Ordenamiento por selección) El ordenamiento por selección busca en un arreglo el elemento más pequeño, el<br />

cual se intercambia con el primero del arreglo. El proceso se repite con el subarreglo que comienza en el<br />

segundo elemento del arreglo. Cada pasada por el arreglo da como resultado la colocación de un elemento en su<br />

lugar correcto. El desempeño de este ordenamiento es comparable con el ordenamiento de burbuja: para un<br />

arreglo de n elementos, se necesita hacer n-1 pasadas y por cada subarreglo, hay que hacer n-1 comparaciones<br />

para encontrar el valor más pequeño. Cuando el subarreglo que se está procesando sólo contiene un elemento, el<br />

arreglo está ordenado. Escriba la función recursiva clasifOrdenamiento()<br />

24. (Palíndromos) Un palíndromo es una cadena que se pronuncia igual hacia delante que hacia atrás. Algunos<br />

ejemplos son: radar, anilina y dábale arroz a la zorra el abad. Escriba una función recursiva<br />

pruebaPalindrome() que devuelva verdadero si la cadena almacenada en el arreglo es un palíndromo y falso si<br />

no. La función deberá ignorar los espacios y la puntuación en la cadena.<br />

25. (Búsqueda lineal) Modifique el ejemplo <strong>18</strong>.33 para que emplee la función recursiva busquedaBinaria() para<br />

llevar a cabo una búsqueda lineal en el arreglo. La función deberá recibir como argumentos un arreglo de<br />

enteros y su tamaño. Si se encuentra la clave de búsqueda, devuelva el índice del arreglo; de otro modo,<br />

devuelva –1.<br />

26. (Búsqueda binaria) Modifique el programa del ejemplo <strong>18</strong>.34 para que utilice una función recursiva<br />

busquedaBinaria() que lleve a cabo una búsqueda binaria en el arreglo. La función deberá recibir como<br />

argumentos un arreglo de enteros y el índice inicial y el final. Si se encuentra la clave de búsqueda, devuelva el<br />

índice del arreglo; de otro modo, devuelva –1.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-75


MIGUEL Á. TOLEDO MARTÍNEZ<br />

27. (Impresión de un arreglo) Escriba una función recursiva imprimirArreglo() que tome como argumentos un<br />

arreglo y su tamaño y no devuelva nada. La función deberá terminar el proceso y regresar cuando reciba un<br />

arreglo de tamaño cero.<br />

28. (Impresión inversa de una cadena) Escriba una función recursiva cadenaInvertida() que tome un arreglo de<br />

caracteres que contenga como argumento una cadena, la imprima al revés y no devuelva nada. La función<br />

deberá terminar el proceso y regresar cuando se encuentre el carácter nulo de terminación.<br />

29. (Encuentre el valor mínimo dentro de un arreglo) Escriba una función recursiva minimoRecursivo() que tome<br />

como argumentos un arreglo de enteros y su tamaño y devuelva su elemento más pequeño. La función deberá<br />

terminar el proceso y regresar cuando reciba un arreglo de 1 elemento<br />

EXAMEN BREVE 34<br />

1. Los dos componentes principales de un arreglo son: ______________ y ___________.<br />

2. Verdadero o falso: Los elementos dentro de un arreglo determinado pueden ser de cualquier<br />

combinación de clase de datos.<br />

EXAMEN BREVE 35<br />

1. Defina un arreglo de nombre registrosDeExamen que almacene hasta 15 registros de exámenes.<br />

2. ¿Cuál es la dimensión del arreglo en la pregunta 1?<br />

3. ¿Cuál es el índice del primer elemento del arreglo en la pregunta 1?<br />

4. ¿Cuál es el índice del último elemento del arreglo en la pregunta 1?<br />

5. Defina un arreglo de nombre esteSemestre que almacene elementos de una clase enumerada de<br />

nombre cursos, que incluya los cursos que usted está tomando este semestre.<br />

6. Suponga que el índice del último elemento en un arreglo es [25] ¿Cuántos elementos almacenará el<br />

arreglo?<br />

EXAMEN BREVE 36<br />

1. Escriba un ciclo for que llene el siguiente arreglo desde la entrada del usuario: char caracteres[15];<br />

2. Escriba un ciclo for que muestre el contenido del arreglo de la pregunta 1.<br />

EXAMEN BREVE 37<br />

1. Verdadero o falso: el nombre del arreglo es la dirección del índice[1] del arreglo.<br />

2. Escriba un prototipo para una función de nombre muestra() que modifique al siguiente arreglo:<br />

char caracteres[15];<br />

Suponga que la función no regresa ningún valor excepto el arreglo modificado.<br />

3. Escriba un prototipo para una función de nombre prueba() que modifique un solo elemento del<br />

arreglo que se definió en la pregunta 2.<br />

4. Escriba un enunciado que llamará a la función prototipo elaborada en la pregunta 3 para modificar<br />

el elemento almacenado en el índice[5] del arreglo definido en la pregunta 2.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-76


MIGUEL Á. TOLEDO MARTÍNEZ<br />

EXAMEN BREVE 38<br />

1. Defina un arreglo e inicialícelo con los valores enteros de -3 hasta +3.<br />

2. ¿Cuál es la dimensión del arreglo que definió en la pregunta 1?<br />

3. Muestre el contenido del siguiente arreglo:<br />

char lenguaje[51 = {‘C’, ‘+’, ‘+’};<br />

4. Muestre el contenido del siguiente arreglo:<br />

char lenguaje[] = " C++";<br />

5. Suponga que define globalmente un arreglo de caracteres sin valores de iniciación. ¿Qué almacena<br />

el compilador en el arreglo?<br />

RESPUESTA EXAMEN BREVE 34<br />

1. Los dos componentes principales de un arreglo son: indice y elemento.<br />

2. Falso: Todos los elementos dentro de un arreglo deben de ser de la misma clase de datos.<br />

1. float registrosDeExamen[15];<br />

RESPUESTA EXAMEN BREVE 35<br />

2. La dimensión del arreglo de la pregunta 1 es 1 ××××15.<br />

3. El índice del primer elemento del arreglo de la pregunta 1 es [0]<br />

4. El índice del último elemento en el arreglo de la pregunta 1 es [14]<br />

5. enum cursos {Computacion, Matematicas, Física, Ingles, Oratoria};<br />

cursos esteSemestre[5];<br />

6. El arreglo almacenará 26 elementos, dado que el primer índice de elemento es [0]<br />

RESPUESTA EXAMEN BREVE 36<br />

1. El ciclo for que se necesita para llenar el arreglo char caracteres[15]; es:<br />

for(int indice = 0; indice < 15; ++indice)<br />

cin >> caracteres[indice];<br />

2. El ciclo for que mostrará el contenido del arreglo anterior es:<br />

for(int indice = 0; indice < 15; ++indice)<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

3. Un prototipo para una función llamada prueba() que modificará sólo un elemento en el arreglo de la<br />

pregunta 2 es:<br />

void prueba(char &elementoDelArreglo);<br />

4. Un enunciado que llamará a la función anterior para modificar el elemento almacenado en el<br />

índice[5] del arreglo en la pregunta 2 es:<br />

prueba(caracteres[5]);<br />

RESPUESTA EXAMEN BREVE 38<br />

1. La definición de un arreglo inicializado con los valores enteros de –3 a +3 es:<br />

int numeros[7] = [-3, -2, -1, 0, 1, 2, 3};<br />

o<br />

int numeros[] = [-3, -2, -1, 0, 1, 2, 3};<br />

2. La dimensión del arreglo antes mencionado es 1 x 7.<br />

3. El contenido del arreglo char lenguaje[51 = {‘C’, ‘+’, ‘+’}; es:<br />

[‘C] [‘+’] [‘+’] [‘\0’] [‘\0’]<br />

4. El contenido del arreglo char lenguaje[] = "C++"; es;<br />

[‘C] [‘+’] [‘+’] [‘\0’]<br />

5. Un carácter terminador nulo (‘\0’) se coloca en cada posición de un arreglo de caracteres definido<br />

globalmente sin ningún valor de inicialización.<br />

ARREGLOS, APUNTADORES Y ESTRUCTURAS – <strong>LECCIÓN</strong> <strong>18</strong> <strong>18</strong>-78

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!