CONTENIDO DE LA LECCIÓN 18
CONTENIDO DE LA LECCIÓN 18
CONTENIDO DE LA LECCIÓN 18
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
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