0% encontró este documento útil (0 votos)
25 vistas29 páginas

Estructuras de Datos: Arrays y Structs

Cargado por

Lautaro Diez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
25 vistas29 páginas

Estructuras de Datos: Arrays y Structs

Cargado por

Lautaro Diez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Algoritmos y Estructuras de Datos

UTN Regional Buenos Aires


Prof. Ing. Diego Juan
Contenido

Capítulo 5: Estructuras de datos (array y struct). 2


5.1. Concepto de Estructura de Datos 2
5.2. Registros (structs) 2
5.2.1. Uso de registros en asignaciones 3
5.3. Arrays unidimensionales: vectores 4
5.3.1. Asignación en vectores 4
5.3.2. Recorrido y carga secuencial del vector 4
5.3.3. Carga directa con desplazamiento. 6
5.3.4. Recorrido con corte de control 9
5.3.5. Ordenamiento de vectores 12
5.3.6. Búsqueda en vectores 14
5.3.6.1. Búsqueda secuencial 14
5.3.6.2. Búsqueda binaria 16
5.3.6. Cargar sin repetir en vector. 18
5.4. Arrays bidimensionales: las matrices 20
5.4.1. Inicialización de matrices. 20
5.4.2. Acceso a los elementos de una matriz. 21
5.4.3. Acceso a elementos de una matriz mediante ciclos 21
5.5. Las cadenas de caracteres 22
5.5.1. Declaración de cadenas de caracteres 22
5.5.2. La biblioteca string.h 23
5.5.3. Implementación cin.getline() 25
5.5.4 La biblioteca ctype.h 27
5.6. Utilización de arrays como parámetros 28
Capítulo 5: Estructuras de datos (array y struct).
Hasta ahora vimos los tipos de datos simples, pero en la medida que aumenta la cantidad y
variedad de valores a procesar se dificulta trabajar de esta manera. En muchas situaciones
se necesita procesar un conjunto de valores o datos que están relacionados entre sí. En
este capítulo vamos a estudiar las estructuras de datos que permiten almacenar un conjunto
de valores relacionados.

5.1. Concepto de Estructura de Datos


El tipo de dato compuesto o estructurado nos permite representar un conjunto de variables.
Se crean a partir de tipos de datos simples.

Vamos a definir a una estructura de datos como un conjunto de variables, no


necesariamente del mismo tipo, que están relacionadas entre sí de diversas formas.

Podemos clasificar a las estructuras de datos por varios criterios, uno de ellos es por el tipo
de los datos que las forman. Decimos que es homogénea cuando los datos que la
componen son todos del mismo tipo; y es heterogénea cuando los datos son de distinto tipo.
Otra característica para clasificarlas es por la cantidad de memoria que utilizan durante la
ejecución del programa. Si la cantidad de memoria que utilizan en tiempo de ejecución es
fija, entonces es estática; en cambio decimos que es dinámica si la cantidad de memoria
varía en tiempo de ejecución.
En este capítulo vamos a ver el concepto de vector que es una estructura homogénea
estática, y el concepto de registro que es una estructura heterogénea estática.

5.2. Registros (structs)


Vamos a definir un registro como una estructura de datos que puede contener un conjunto
de variables que denominaremos campos o atributos del registro o struct; los tipos de datos
que pueden almacenar los campos pueden variar. Otra característica es que podemos
acceder a cada atributo mediante un operador de acceso y el nombre o identificador del
atributo.

//Declaración un tipo de datos registro en C


struct tipoEstudiante {
int legajo;
char nombre[20];
int dni;
};

tipoEstudiante estudiante; //Declaración de una variable de tipoEstudiante


estudiante.legajo = 2965; //Se asigna un valor al atributo legajo de la variable estudiante
En el lenguaje C/C++ un registro se declara con la palabra reservada estructura (struct, en
inglés) y se declara utilizando los mismos pasos necesarios para utilizar cualquier variable.
Primero, se debe declarar el registro y a continuación se asignan valores a los atributos
individuales del registro o estructura mediante el operador de acceso punto variable.atributo

Ejercicio: solicitar al usuario dos números enteros, almacenarlos en una estructura de datos
struct, realizar la suma y almacenar el resultado en dicha estructura.

#include <iostream>
using namespace std;

struct TipoNumerosSuma{
int numero1;
int numero2;
int suma;
};

int main(){
TipoNumerosSuma registro;
cout<<"ingrese un nro.: ";
cin>>registro.numero1;
cout<<"ingrese otro nro.: ";
cin>>registro.numero2;
registro.suma = registro.numero1 + registro.numero2;
cout<<"Suma: "<<registro.suma<<endl;
return 0;
}

5.2.1. Uso de registros en asignaciones


Es posible asignar un registro a otro:

#include <iostream>
using namespace std;

struct TipoLibro{
int codigo;
float precio;
};

int main(){
TipoLibro libro1,libro2;
libro1.codigo=200; libro1.precio = 1500;
libro2.codigo=250; libro2.precio = 2200;
libro1 = libro2;
return 0;
}
5.3. Arrays unidimensionales: vectores
Un vector es una estructura de datos indexada que permite representar un conjunto finito de
elementos del mismo tipo de datos, por esto último se la llama estructura homogénea.

Como la memoria ocupada a lo largo de la ejecución del programa es fija, decimos que la
estructura es estática.

Otra característica importante es que posee un índice que indica la posición relativa de
cada elemento dentro del conjunto, lo que permite acceso directo al dato, indicando la
posición o índice donde se encuentra.

3 6 9 12 15 18 21 24

posición 0 posición 1 posición 2 posición 3 posición 4 posición 5 posición 6 posición 7

Si observamos la posición indica el desplazamiento respecto del primer elemento.


Si un vector tuviese N elementos, la posición del último es N-1.
Los arrays pueden ser de varias dimensiones. La dimensión indica la cantidad de índices
que se necesitan para acceder a un elemento.

5.3.1. Asignación en vectores


La asignación de valores a un vector se realizará con el operador de asignación:
arr[20] ← 5
Asigna el valor 5 en la posición 20 del vector arr.
Si se desea asignar valores a todos los elementos de un vector, se debe recurrir a
estructuras repetitivas (while, for).

//C++ Vector de 3 posiciones, se insertan números enteros.


int vec[3];
vec[0] = 2; //asigna el valor 2 a la posición 0 del vector vec.
vec[1] = 52;
vec[2] = 12;

5.3.2. Recorrido y carga secuencial del vector


Se puede acceder a los elementos de un vector para introducir datos (escribir) en él o bien
para visualizar su contenido (leer). A la operación de efectuar una acción general sobre
todos los elementos de un vector se la denomina recorrido del vector. Estas operaciones se
realizan utilizando estructuras repetitivas, cuyas variables de control (por ejemplo, i) se
utilizan como subíndices del vector (por ejemplo, vec[i]). El incremento del contador del ciclo
producirá el tratamiento sucesivo de los elementos del vector.
//Vector de 8 posiciones, se insertan múltiplos de 3
int vector[8];
for (int i=1;i<=8;i++){
vector[i-1] = i*3;
}

Desarrollar un algoritmo que cargue en un vector e imprima los primeros N números pares.
El valor de N será solicitado al usuario y debe ser menor que el tamaño físico del vector. El
tamaño físico del vector debe ser de 100 elementos.

#include <iostream>
using namespace std;

void imprimir_vector(int[],int);

int main(){
int pares[100];int N;
cin>>N;
for (int i=1;i<=N;i++){
pares[i-1] = 2*i;
}
imprimir_vector(pares,N);
return 0;
}

void imprimir_vector(int vec[],int cant){


for(int i = 0; i < cant; i++){
cout<<vec[i]<<" - ";
}
cout<<endl;
}
En C/C++ no hay control interno para evitar acceder a un índice superior al tamaño físico de
la estructura.

Para inicializar un array, podemos hacer lo siguiente:


for (i = 0; i< 4; i++){
A[i] = i;
}

Otra manera de inicializar un array es asignándole los valores iniciales entre llaves de la
siguiente manera:
int A[4] = {0, 1, 2, 3};

Vector de 10 posiciones con valor inicial en 0:


int autos_vendidos[10] = { 0 };

Si no se inicializa explícitamente el array no se puede estar seguro del valor que


contienen los elementos del mismo.

5.3.3. Carga directa con desplazamiento.


Mediante este patrón se inserta directamente el dato en una posición determinada y
preestablecida. Si en la posición que se desea insertar ya existe un elemento cargado,
realiza un desplazamiento de los elementos del vector hacia la derecha para generar el
hueco donde insertarlo.
Como precondición debemos tener en cuenta que la posición preestablecida debe ser
válida, debe estar dentro del rango de posiciones del vector.

Para realizar el desplazamiento hacia la derecha debemos conocer en dónde está cargado
el último elemento del vector. Si el vector tuviese 10 elementos el último se encontraría en la
posición 9, si tuviese 25, el último estaría en la posición 24, si tuviese cant elementos, el
último estaría en cant-1; luego realizamos el recorrido del vector de atrás hacia adelante.
En el siguiente algoritmo la variable i representa el índice del vector, asimismo cant
representa la cantidad de elementos cargados en el vector. La variable pos representa la
posición del vector donde hay que generar el hueco para insertar el valor v.

Implementación C/C++: funcion insertar implementada con ciclo for

Insertar un valor en una determinada posición del vector o array


La siguiente función inserta el valor v en la posición pos del array arr, desplazando al
i-ésimo elemento hacia la posición i+1, para todo valor de i que verifique: i>=pos e i<cant.

const int MAX_SIZE = 100; // Tamaño máximo del vector


void insertar(int arr[], int& cant, int v, int pos){
if (pos < 0 || pos > cant) {
// La posición no es válida, salir sin hacer nada
return;
}
if (cant == MAX_SIZE) {
// El vector ya está lleno, no se puede insertar más elementos
return;
}
// Desplazar los elementos desde pos hasta el final del arreglo una posición hacia la
derecha
for(int i = cant - 1; i >= pos; i--){
arr[i+1] = arr[i];
}
arr[pos] = v; // Insertar el elemento en la posición pos
cant++; // Incrementar la cantidad de elementos
}
//Implementación C/C++: funcion insertar implementada con ciclo while

void insertar(int arr[], int& cant, int v, int pos){


int i = cant-1;
//realiza corrimiento hasta encontrar la posición donde insertar
while(i >= pos){
arr[i+1]=arr[i];//efectuo el corriemiento
i--; //me desplazo hacia la izquierda
}
arr[pos]=v; //inserto el elemento e incremento la longitud del array
cant++;
return;
}

Esta implementación es similar a la anterior, pero en lugar de utilizar un ciclo for, utilizamos
un while para realizar el corrimiento de los elementos del vector hacia la izquierda hasta
encontrar la posición adecuada para insertar el nuevo valor. La lógica es la misma, solo
cambia la estructura del ciclo. Ambas formas son válidas y funcionarán correctamente.
Veamos un ejemplo completo, con dos funciones para insertar, una desarrollada con ciclo
for y la otra con while.

#include <iostream>
using namespace std;

void insertar_condesplazamientov1(int [], int&, int, int); //ciclo for


void insertar_condesplazamientov2(int [], int&, int, int); //ciclo while
void imprimir_vector(int[],int);

int main(){
int cantidadProductos[20]={20,50,2,2,99,7,24,6,32,71};
int cant_elementos=10; //cantidad de elementos del vector
cout<<"Vector con valores originales:"<<endl;
imprimir_vector(cantidadProductos,cant_elementos);
cout<<"Inserto el 99 en la pos. 8 (posicion comienza en 0)"<<endl;
insertar_condesplazamientov1(cantidadProductos,cant_elementos,99,8);
imprimir_vector(cantidadProductos,cant_elementos);
cout<<"Inserto el 1 en la pos. 3 (posicion comienza en 0)"<<endl;
insertar_condesplazamientov2(cantidadProductos,cant_elementos,1,3);
imprimir_vector(cantidadProductos,cant_elementos);
return 0;
}

void imprimir_vector(int vec[],int cant){


for(int i = 0; i < cant; i++){
cout<<vec[i]<<" - ";
}
cout<<endl;
}
La ejecución del programa:

5.3.4. Recorrido con corte de control


El algoritmo de corte control nos va a permitir recorrer un conjunto de datos que están
ordenados o agrupados por una clave que se repite, el objetivo es generar datos
acumulados o sumarizados por cada agrupamiento y por todo el conjunto datos.
La implementación consta de dos ciclos de repetición, uno externo que garantice que haya
datos en el conjunto y otro interno que cumpla con la condición de que haya datos y que los
mismos pertenezcan al grupo.
Es muy útil para realizar reportes con subtotales, cantidades o promedios parciales.
Hagamos un ejercicio:
El Ministerio de Educación de la provincia de Buenos Aires, de la República Argentina
desea asignar recursos a los Municipios teniendo en cuenta varios aspectos, entre los
cuales se necesita conocer la cantidad de escuelas y estudiantes por cada municipio de la
provincia. Para ello se dispone de un vector, donde cada elemento representa a una
escuela y están agrupados por código de municipio, veamos un ejemplo de algunos datos
del vector:

Código de Municipio Código de escuela Cantidad de estudiantes

1901 5568 198

1901 5571 250

1901 5506 312

1655 2499 356

1655 2415 320

1519 1254 230

1519 1298 195

1519 1245 350

1519 1236 218

Por ejemplo, el código de Municipio 1519 representa a “La Matanza”, 1655 a “Tres de
Febrero”, cada código se corresponde con el nombre del municipio. Es nuestra clave de
agrupamiento, la que se repite, para desarrollar el corte de control; y es lo primero que
debemos identificar.
La provincia de Buenos Aires cuenta con 135 municipios y sabemos que nuestro vector
posee 405 elementos o posiciones, y que cada uno almacena un dato del tipo registro.

struct Tescuela{
int codMunicipio;
int codEscuela;
int cantEstudiantes;
};
Implementación C/C++
int i=0, totalEstudProv=0,claveAnterior, cantidadEscuelas, cantidadEstudiantes;
//ciclo externo para garantizar que haya datos en el vector
while(i<405){
claveAnterior = v[i].codMunicipio;
//inicializamos contadores para cada grupo o corte
cantidadEscuelas=0;
totalEstudiantes=0;
//ciclo interno para controlar cada grupo
while(i<405 && v[i].codMunicipio == claveAnterior){
totalEstudiantes = totalEstudiantes + v[i].cantEstudiantes;
cantEscuelas++;
i++; //modificamos índice del vector para leer el siguiente elemento
}
//acciones propias de cada grupo
cout<<”El Municipio ”,v[i-1].codMunicipio<<”tiene: ”<<endl;
cout<<”Cantidad de escuelas ”<<cantEscuelas<<endl;
cout<<”Total de estudiantes: ”<<totalEstudiantes<<endl;
totalEstudProv=totalEstudProv+totalEstudiantes;
}
//acciones propias de totales generales o globales
cout<<”Total de estudiantes en la provincia: ”<<totalEstudProv<<endl;

Vamos a analizar el algoritmo con más detalle:


1) La precondición que se debe cumplir es que el conjunto de datos debe venir
ordenado o agrupado por los criterios que usaremos para realizar el corte de control;
en nuestro caso es el código de Municipio que se repite y nos permite agrupar los
datos.
2) En cada grupo procesamos los datos, para saber si estamos dentro de un grupo se
tiene que cumplir la condición de la clave sea la misma, es decir, necesitamos
recordar la clave del registro anterior. Esto lo implementamos mediante la variable
claveAnterior.
3) Un while externo para la condición de fin de recorrido (el índice del vector debe ser
menor que la cantidad de elementos del vector), luego tenemos un while interno
para controlar el procesamiento de cada grupo.
4) El incremento del índice del vector para proceder a leer el siguiente elemento del
vector se realiza dentro del while más interno. Ahí es donde debemos avanzar la
lectura del vector.
5) Dentro de cada corte o grupo tendremos variables que se utilizarán como
acumuladores y contadores (totalEstudiantes y cantEscuelas), debemos asignarle
los valores iniciales antes del ciclo correspondiente al corte de control donde serán
incrementados. Asimismo, a la variable claveAnterior se le debe asignar un valor en
el mismo lugar (antes del ciclo), debido a que es en esa sección donde se
establecen las configuraciones iniciales de cada agrupamiento.
6) Cuando se sale de un while interno es porque terminamos el corte determinado, es
ahí donde se pueden imprimir en pantalla las cantidades o totales acumulados.
7) La condición de fin de recorrido se evalúa después del avance de lectura que se
hace en un sólo lugar, dentro del while más interno. Cuando ésta condición se da se
sale de todos los ciclos porque todos arrastran esta condición.
8) Finalmente cuando se sale del while externo se tienen los totales generales
acumulados, en nuestro ejemplo totalEstudProv.

¿Y si el criterio de ordenamiento o agrupamiento fuese más de uno? es decir, tenemos dos


claves de agrupamiento; por ejemplo, tanto el código de Municipio como el código de
Escuela se repiten debido a que en cada registro ya no tenemos la cantidad de estudiantes
por escuela, sino por curso; de esta manera tanto la Escuela como el Municipio se repetirán
en el conjunto de datos.
Para solucionar este problema tendremos un while para la condición de fin de recorrido de
todo el conjunto de datos, luego un while por cada corte de control, es decir, deberíamos
agregar un while dentro del while interno. Y en cada while se arrastra la condición del while
externo inmediato y se le agrega con un operador lógico AND ( &&) la condición del nuevo
corte de control, la cabecera del while que controla al nuevo subgrupo sería algo así:

while (i<405 && v[i].codMunicipio == claveAnterior && v[i].codEscuela == claveAnterior1){


…..
}
El avance de lectura del vector i++ lo deberíamos trasladar a este nuevo while, el más
interior.

5.3.5. Ordenamiento de vectores


Por ordenar se entiende el proceso de reorganizar un conjunto de datos en una cierta
secuencia de acuerdo a un criterio especificado. En general, el objetivo de este proceso es
facilitar la posterior búsqueda de elementos en el conjunto ordenado.
Vamos a ver el patrón algorítmico de ordenamiento burbuja.
El algoritmo consta de un conjunto de pasos, en los cuales va comparando pares de
elementos adyacentes. Al finalizar el proceso el mayor de los elementos queda en el último
lugar; en el último paso acomoda 2 elementos por eso el ciclo se hace entre 1 y N-1. Las
comparaciones son necesarias para poder colocar el mayor al final, en principio se
compara el primero con el segundo y si corresponde se intercambian, luego el segundo con
el tercero, y así hasta llegar al anteúltimo elemento que se lo intercambia con el último.
Comenzando desde el inicio
del vector, se compara cada
par de elementos
adyacentes. Si ambos no
están ordenados (el segundo
es menor que el primero), se
intercambian sus posiciones.
En cada iteración, un
elemento menos necesita
ser evaluado (el último, ver
variable de control del ciclo
interno), ya que no hay más
elementos a su derecha que
necesiten ser comparados,
puesto que ya están
ordenados.

En la medida que se avanza en cada paso corresponde hacer una comparación menos por
eso el ciclo interior el j varía hasta N-i.

Ejemplo: N = 5. El ciclo principal efectúa 4 iteraciones, de 1 a 4. Son 4 pasos.

vector original:
1 6 4 5 2

Pasada 1: N = 5, i = 1, j = [1,4]. Al finalizar el paso 1 el vector queda:


1 4 5 2 6

Pasada 2: N = 5, i = 2, j = [1,3]. Al finalizar el paso 2 el vector queda:


1 4 2 5 6

Pasada 3: N = 5, i = 3, j = [1,2]. Al finalizar el paso 3 el vector queda:


1 2 4 5 6

Pasada 4: N = 5, i = 4, j = [1,1]. Al finalizar el paso 4 el vector queda:


1 2 4 5 6
Desarrollemos un programa completo con módulos de ordenamiento e impresión del vector:

#include<iostream>
using namespace std;

void burbuja(int [],int);//PROTOTIPO INTERFAZ


void imprimir_vector(int[],int);
int main(){
int numeros[8]={51,9,68,1,7,25,14,8};
cout<<"Vector sin orden:"<<endl;
imprimir_vector(numeros,8);
burbuja(numeros,8);
cout<<"Vector ordenado:"<<endl;
imprimir_vector(numeros,8);
return 0;
}

void burbuja(int vec[],int N){


int aux;
for(int i=1;i<=N-1;i++){ //PASOS
for(int j=1;j<=N-i;j++){ //COMPARACIONES
if(vec[j-1]>vec[j]){
aux=vec[j];
vec[j]=vec[j-1];
vec[j-1]=aux;
}
}
}
}

void imprimir_vector(int vec[],int cant){


for(int i = 0; i < cant; i++){
cout<<vec[i]<<" - ";
}
cout<<endl;
}

5.3.6. Búsqueda en vectores

5.3.6.1. Búsqueda secuencial


Los elementos de un vector tienen una relación lineal o secuencial, cada uno se almacena
en una posición relativa a los demás. Estas posiciones relativas son los valores de los
índices de los elementos individuales. Dado que estos valores de los índices están
ordenados, es posible visitarlos en secuencia. Este proceso da lugar a nuestra primera
técnica de búsqueda, la búsqueda secuencial.
#include <iostream>
using namespace std;

int busquedaSecuencial(int,int [],int);

int main(){
int vector[10]={4,3,7,89,0,1,3,5,55,6};
int pos;
pos=busquedaSecuencial(4,vector,10);

cout<<"el valor buscado esta en la pos: "<<pos;


}

int busquedaSecuencial(int buscado,int v[],int n){


int i=0;
while(i<n && v[i]!=buscado){ //Si n es 8 el último elemento está en la pos i=7
i++;
}
if(i<n)
return i;
else
return -1;
}
5.3.6.2. Búsqueda binaria
El patrón algorítmico de búsqueda binaria sólo se puede implementar si el conjunto de
elementos está ordenado. Consiste en ir dividiendo el conjunto de elementos en mitades.
Por ejemplo supongamos que tenemos este vector:

int vector[10] = {2,4,6,8,10,12,14,16,18,20};

El elemento que queremos buscar es 6. El algoritmo funciona de la siguiente manera:


1) Se determinan un índice primero y un índice ultimo, primero=0 y ultimo=9
respectivamente.
2) Se determina un índice medio, donde medio = (primero + ultimo) / 2, en este caso
quedaría medio = 4.
3) Evaluamos si vector[medio] es igual al elemento buscado, si es igual ya
encontramos la clave y devolvemos medio.
4) Si son distintos, evaluamos si vector[medio] es mayor o menor que el elemento
buscado, como el vector está ordenado al hacer esto ya podemos descartar una
mitad del mismo asegurándonos que en esa mitad no está el elemento que
buscamos. En nuestro caso vector[medio] = 4 < 6, entonces la parte del vector
(índices desde 0 a 4) ya puede descartarse.
5) Reasignamos primero o ultimo para obtener la nueva parte del vector en donde
queremos buscar; ultimo, queda igual ya que sigue siendo el tope; primero lo
tenemos subir hasta 5, entonces quedaría ultimo = 9, primero = 5. Y volvemos al
paso 2.
Si el elemento no fuese encontrado en algún momento primero va a ser mayor que
ultimo, con un while vamos a controlar esta condición para salir del ciclo en tal caso y
devolver -1 (elemento no encontrado).
#include <iostream>
using namespace std;
int busquedaBinaria (int [],int,int);

int main (){


int numeros[10]={1,15,63,65,72,99,151,245,336,388};
int posicion = busquedaBinaria(numeros,55,10);
cout<<"el valor se encuentra en la pos "<<posicion;
return 0;
}

int busquedaBinaria(int v[],int buscado,int n){


int primero=0;
int ultimo= n-1;
int medio;
while(primero<=ultimo){
medio=(primero+ultimo)/2;
if (buscado==v[medio]) return medio;
if (buscado>v[medio])
primero = medio + 1;
else
ultimo = medio - 1;
}
return -1;
}

5.3.6. Cargar sin repetir en vector.


Con este patrón de carga pretendemos cargar un vector sin valores repetidos. Para lograr el
objetivo, antes de cargar el valor al vector, debemos hacer una búsqueda en el mismo para
verificar que el valor no esté cargado.

#include <iostream>
using namespace std;

const int MAX_SIZE = 10; // Tamaño máximo del vector


int busquedaSecuencial(int,int[],int);
int cargarSinRepetir (int [],int,int&);
void imprimir_vector(int[],int);
int main (){
int vector[MAX_SIZE]={1,20,63,65,72,99,388};
int n=7; // Número actual de elementos en el vector
imprimir_vector(vector,n);
int pos = cargarSinRepetir(vector,55,n);
imprimir_vector(vector,n);
pos = cargarSinRepetir(vector,63,n); // el 63 no lo carga
imprimir_vector(vector,n);
pos = cargarSinRepetir(vector,21,n);
imprimir_vector(vector,n);
return 0;
}

int busquedaSecuencial(int buscado,int v[],int n){


int i=0;
while(i<n && v[i]!=buscado){ //Si n es 8 el ultimo elemento esta en la pos (i) 7
i++;
}
if(i<n)
return i;
else
return -1;
}

int cargarSinRepetir(int vector[],int busc,int& n){


if (n >= MAX_SIZE) { // Verificar si el vector está lleno
return -1; // Indicar que no se pudo insertar el elemento
}
int p = busquedaSecuencial(busc,vector,n);
if (p == -1) {
// Si el elemento no está en el vector y el vector no está lleno, insertarlo
vector[n] = busc;
n++;
return n - 1; // Devolver la posición del elemento insertado
} else {
// Si el elemento ya está presente, devolver su posición
return p;
}
}
void imprimir_vector(int vec[],int cant){
for(int i = 0; i < cant; i++){
cout<<vec[i]<<" - ";
}
cout<<endl;
}
5.4. Arrays bidimensionales: las matrices
El vector con 2 o más índices o dimensiones es una matriz. Un grupo de elementos
homogéneo con un orden interno en el que se necesitan 2 o más índices para referenciar a
un elemento de la estructura.

Un array de dos dimensiones equivale a una tabla con múltiples filas y múltiples columnas.

0 1 2 3 4

0
Matriz de 4 filas x 5 columnas
1

Si las filas se etiquetan de 0 a m y las columnas de 0 a n, el número de elementos que


tendrá la matriz será (m+1) x (n + 1).

Un ejemplo de declaración de matriz:


int equipos[6][8];

5.4.1. Inicialización de matrices.


Podemos asignar valores iniciales a la matriz cuando se declara:
int tabla[2][3]={
{4,8,2},
{3,2,9}
};

También con el siguiente formato:


int tabla[2][3]={4,8,2,3,2,9};

4 8 2

3 2 9

Para inicializar toda la matriz con un valor → int tabla[2][3]={0};


5.4.2. Acceso a los elementos de una matriz.
Se puede acceder a los elementos de un array bidimensional de igual forma que a los
elementos de un vector; la diferencia reside en que en los elementos bidimensionales deben
especificarse los índices de la fila y la columna.

El formato para asignación directa a los elementos es:


<nombre_array>[indice_fila][indice_columna] = valor;

Ejemplo: tabla[0][0] = 18;


Podemos extraer valores: ventas = tabla[2][1];

5.4.3. Acceso a elementos de una matriz mediante ciclos


Se puede acceder a los elementos de una matriz mediante ciclos anidados:

for(int indiceFila=0; indiceFila < numFilas; indiceFila++){


for(int indiceColumna=0; indiceColumna < numColumnas; indiceColumna++){
procesar matriz[indiceFila][indiceColumna]
}
}

Ejemplo: una matriz que contiene los resultados de las elecciones de 5 distritos electorales
(filas) de los 6 candidatos que se postulan (columnas).

0 1 2 …… 5
Cada elemento de la matriz
contiene la cantidad de votos
0 que obtuvo el candidato en el
distrito.
1

Los códigos de candidatos van del 0 al 5, son los índices de las columnas y los códigos de
distritos van del 0 al 4, son los índices de las filas.
#include<iostream>
using namespace std;

int main(){
int elecciones[5][6]={
{100,55,540,201,7,25},
{90,66,54,21,22,250},
{105,51,4,20,37,205},
{81,27,50,1,17,125},
{181,11,20,81,117,45},
};
int i,j;//i representa distritos y j a los candidatos
for(i=0;i<5;i++){ //por cada distrito
cout<<"--votos distrito "<<i<<endl;
for(j=0;j<6;j++){ //itero candidatos
cout<<"votos de "<<j<<" en dist. "<<i<<":"<<elecciones[i][j]<<endl;
}
}
return 0;
}

5.5. Las cadenas de caracteres


En C++ (y su lenguaje padre, C) no existe un tipo predefinido para manipular cadenas de
caracteres (string). Sin embargo, el estándar de C define algunas funciones de biblioteca
para tratamiento de cadenas (string.h)
Una cadena en C es un array de caracteres de una dimensión (vector de caracteres) que
termina con el carácter especial '\0' (cero). El valor real de esta cadena es la dirección de
su primer carácter y su tipo es un puntero a char, tema que veremos más adelante.
El formato para declarar una cadena es:
char nombre[n];
donde: n >= 1 y representa a la longitud-1 real de la cadena.

5.5.1. Declaración de cadenas de caracteres


Un ejemplo de declaración de cadena:
char cadena [5];
Debido a que en la representación interna de una cadena de caracteres es terminada por el
símbolo '\0', para un texto de "n" caracteres, debemos reservar "n+1”. El carácter '\0', indica
el final de la cadena, aunque pertenece a la misma, no aparece al utilizar funciones como
printf.
En el caso especial de los arrays de caracteres, podemos utilizar varias formas de
inicialización:
char cadena[] = "Hola";
char cadena[] = {'H','o','l','a','\0'};
sin especificar el tamaño de la cadena, o especificando el tamaño:
char cadena[5] = "Hola";
char cadena[5] = {'H','o','l','a','\0'};

Durante la inicialización, se reserva automáticamente el número de bytes necesarios para la


cadena, esto es, el número de caracteres más uno. Por ejemplo:

char cadena[ ] = "Hola";

cadena: 'H' 'o' 'l' 'a' '\0'

Para acceder a un elemento de una cadena de caracteres puede hacerse de la misma


manera, igual que el acceso al elemento de un array.
cadena[i];
donde: 0 <=i < n
Por ejemplo:

char A[5] = "Hola";

A: 'H' 'o' 'l' 'a' '\0'

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

5.5.2. La biblioteca string.h


La biblioteca string.h tiene una gran cantidad de funciones prácticas para trabajar con
cadenas de caracteres. Para utilizarlas debemos de incluir el archivo que define dichas
funciones:
#include <string.h>

Algunas de las funciones más importantes son:


• strlen(cadena): Devuelve la longitud de la cadena sin tomar en cuenta el carácter de final
de cadena.
• strcpy(cadena_destino, cadena_origen) : Copia el contenido de cadena_origen en
cadena_destino.
• strcat(cadena_destino, cadena_origen) : Concatena el contenido de cadena_origen al final
de cadena_destino.
• strcmp(cadena1, cadena2) : Compara las dos cadenas y devuelve un 0 si las dos cadenas
son iguales, un número negativo si cadena1 es menor que (precede alfabéticamente a)
cadena2 y un número positivo (mayor que cero) si cadena1 es mayor que cadena2.

A diferencia de los vectores de tipos de datos numéricos (arrays de enteros, de números


con punto decimal, etc.), en donde cada elemento del array se debe considerar como una
variable independiente de los demás, los arrays de caracteres (cadenas) se pueden
manipular de dos maneras: de forma conjunta o separada.

Por ejemplo, para mostrar en pantalla un vector de caracteres podemos hacerlo dentro de
un ciclo, desde el primer carácter (indice 0) hasta el último carácter (lo que nos devuelve la
función strlen):
for(i=0; i<strlen(cadena); i++)
printf("%c",cadena[i]);
Existe una mejor manera de mostrar en pantalla una cadena, y es utilizando el carácter de
conversión %s:
printf("%s",cadena);

En C++:
#include <iostream>
using namespace std;

int main() {
char cadena[] = "Hola mundo";
cout << cadena << endl; // Esto imprimirá la cadena completa
return 0;
}

Veamos otro ejemplo:


char cad[21];
La variable cad es una disposición de caracteres, es un espacio asignado por el compilador
de 21 bytes, las posiciones van desde 0 (cero) hasta la 19, el cual nos indica que la cadena
más larga serán de 20 caracteres para este ejemplo, y el espacio extra, el de la posición 20
reservado para el carácter nulo ‘\0’, siempre y cuando la cadena contenga la cantidad
máxima de caracteres que para este ejemplo es de 20 caracteres. La cadena vacía es la
cadena que no contiene caracteres, y la marca de fin de la cadena ‘\0’, se ubica en la
posición cero.
//se asigna automáticamente la cantidad de caracteres necesarias
char palabra[]=”Esteban”;
char palabra2[]={‘E’,’s’,’t’,’e’,’b’,’a’,’n’};
char nombre[30];
5.5.3. Implementación cin.getline()
El flujo de entrada cin que venimos usando con tipos simples, presenta inconvenientes para
trabajar con cadenas de caracteres:
1) Interpreta el espacio, tabulador y enter como separadores de las cadenas, por lo que
si introducimos una frase, varias palabras separadas con espacios, sólo leeremos la
primera palabra.
2) Deja en el buffer el terminador o separador correspondiente a la lectura realizada. Si
por ejemplo le solicitamos un número al usuario, éste lo ingresa seguido de la tecla
enter que se codifica con el carácter '\n'; esto va a ser tenido en cuenta en la
próxima lectura de una cadena mediante cin.getline(); por lo tanto debemos limpiar
el buffer usando cin.ignore().

Por estos motivos no vamos a usar el flujo cin para leer cadenas. En su lugar usaremos la
implementación cin.getline(), que nos permite leer una cadena de caracteres hasta un
separador que nosotros indiquemos, por ejemplo '\n'.
Veamos ejemplos de los inconvenientes que se presentan con el uso de cin para leer
cadenas de caracteres en C++:

int main(){
char cadena1[50], cadena2[50];
cout << "Introduzca la cadena: Hola UTN" << endl;
cin >> cadena1; // Sólo lee una palabra: Hola (UTN queda sin leer) y requiere
limpiar el buffer posteriormente para leer cadenas.
cout << "Introduzca de nuevo la cadena: Hola UTN" << endl;
cin.ignore(100,'\n'); // Limpia el buffer
cin.getline(cadena2,50, '\n'); // Ahora lee las dos palabras
cout << "\n- La cadena leida con cin >> : " << cadena1 << endl;
cout << "\n- La cadena leida con getline: " << cadena2 << endl;
return 0;
}

NOTA: cin.ignore(n, delim)


Elimina caracteres del buffer de entrada.
La extracción acaba cuando se han eliminado ya n caracteres, o bien nos encontramos con
el carácter delim (que también es eliminado), lo que ocurra primero.
Capturamos la ejecución del programa anterior:
Otro ejemplo de programa con cin.getline(); obsérvese que en la lectura de la variable x se
almacena en el buffer el carácter de terminación '\n', el cual seria leído posteriormente por
cin.getline(); para evitarlo realizamos una limpieza de buffer con cin.ignore().

#include <iostream>
using namespace std;
int main(){
int x; char nombre[25];
cout<<"ingrese x: ";
cin>>x; // Requiere limpiar el buffer posteriormente para leer cadenas.
cout<<"ingrese nombre:"<<endl;
cin.ignore();//limpia el buffer
cin.getline(nombre,25,'\n'); //lee toda la cadena
cout<<"El nombre ingresado es: "<<nombre<<endl;
return 0;
}

Volvamos a codificar el primer ejemplo usando únicamente cin.getline() en lugar de cin para
leer la primera entrada. De esta manera, cin.getline() leerá la línea completa, incluido el
carácter de nueva línea, y evitará que quede un carácter residual en el buffer.
Así queda el ejemplo modificado para usar cin.getline() en la primera lectura:

#include <iostream>
using namespace std;
int main() {
char cadena1[50], cadena2[50];
cout << "Introduzca la cadena: Hola UTN" << endl;
cin.getline(cadena1, 50); // No deja carácter residual en el buffer
cout << "Introduzca de nuevo la cadena: Hola UTN" << endl;
cin.getline(cadena2, 50);
cout <<endl<< "La cadena leida con cin.getline(): "<< cadena1 << endl;
cout << "La cadena leida con cin.getline(): "<< cadena2 << endl;
return 0;
}
5.5.4 La biblioteca ctype.h
Otra biblioteca que nos permite trabajar con caracteres es ctype.h. Con ella podremos
realizar operaciones simples con caracteres: nos permite mostrar mayúsculas, minúsculas o
pasar de una a otra. Veamos algunas de las funciones de esta biblioteca:
islower: Comprueba si un carácter está en minúscula,
isspace: Comprueba si un carácter es un espacio.
isupper: Comprueba si un carácter está en mayúscula.
Funciones de conversión
tolower: Pasa un carácter a minúsculas.
toupper: Pasa un carácter a mayúsculas.

Ahora vamos a realizar un programa que convierte una palabra a minúsculas. En este
ejercicio pasamos como parámetro de una función a una cadena (array de caracteres),
tema que veremos en el siguiente apartado.

#include<iostream>
#include<ctype.h>
using namespace std;

void convertir_a_minuscula(char []);


int main(){
char palabra[5]="ABCD";
convertir_a_minuscula(palabra);
cout<<palabra<<endl;
return 0;
}
void convertir_a_minuscula(char unaPalabra[]){
for(int i=0;unaPalabra[i]!='\0';i++)
unaPalabra[i]=tolower(unaPalabra[i]);

}
5.6. Utilización de arrays como parámetros
En C++ todos los arrays se pasan por referencia (dirección). C++ trata automáticamente la
llamada a la función como si se hubiera realizado con el operador de dirección & delante
del nombre del array.

#include<iostream>
#include<string.h>
using namespace std;

void cambiar(char[]);//Prototipo de la función

int main(){
char palabra[5]="ABCD";
cambiar(palabra);
cout<<palabra<<endl;
return 0;
}

void cambiar(char unaPalabra[]){


cout<<unaPalabra<<endl;
strcpy(unaPalabra,"JJHF");
}
Bibliografía
Fundamentos de programación 4ta Edición Luis Joyanes Aguilar

Programación en C++. Algoritmos, estructuras de datos y objetos. Luis Joyanes Aguilar

Elementos de diseño y programación con ejemplos en C / Adriana Echeverria y Gustavo


Lopez - 1a Edición.

Material oficial de AyED UTN.BA

También podría gustarte