Está en la página 1de 69

ARQUITECTURA DE DATOS

Mgtr. Luis Antonio Salvador


AUTOR DEL CONTENIDO
Unidad I: Arreglos y listas encadenadas
1.1 Definición de arreglos
1.2 Manejo de arreglos multidimensionales
1.3. Creación de arreglos dinámicos
1.4 Relación entre arreglos y punteros
1.5 Operaciones con arreglos
1.6 Recorrido de los elementos de un arreglo
Objetivo

Comprender las ventajas del uso


de los arreglos y de las listas
encadenadas para el manejo de
información dentro de la
programación de aplicaciones
informáticas.
Introducción
Arreglos
Una de las herramientas más comunes
dentro de los lenguajes de programación
para el almacenamiento de datos y su
posterior procesamiento es el uso de
arreglos de datos. Los arreglos de datos
permiten almacenar volúmenes
importantes de datos que puede ser
procesados en forma masiva mediante
estructuras de control repetitivas o lazos.
Este tipo de procesamiento es genérico y
no individual de modo que proporciona el Imagen - Código

mismo conjunto de operaciones sobre


todos los datos almacenados en el arreglo.
1.1 Definición de arreglos
Dentro del lenguaje de programación C,
que es uno de los lenguajes de
programación de mayor popularidad en el
aprendizaje de la informática, la definición
de arreglos de variables se realiza
mediante la incorporación de dos
corchetes conteniendo el número de
elementos del arreglo, inmediatamente
después de especificar el nombre de la
variable. Por ejemplo, para definir un
arreglo de enteros debería escribirse algo
similar a lo siguiente en la función principal Imagen - Código

main:
1.1 Definición de arreglos
Mediante la instrucción: int ae[40]; se está declarando un arreglo de 40
elementos cuyo nombre de variable es ae. Para acceder a cada
elemento del arreglo se debe especificar su posición en el arreglo dentro
de los corchetes que preceden al nombre de la variable. Esta posición se
conoce como índice del elemento. Por ejemplo, para acceder al tercer
elemento del arreglo de enteros anteriormente definido debería
añadirse la siguiente instrucción considerando que el índice se
contabiliza desde cero:

ae[2]=5;
1.1 Definición de arreglos
Cada elemento del arreglo puede ser tratado como una variable
individual. De modo que el elemento ae[2] puede ser utilizado para
almacenar un valor particular y también para realizar operaciones con su
contenido.
El siguiente código muestra el uso del elemento ae[2] para almacenar la
suma de dos valores y su respectivo despliegue en pantalla:

Imagen - Código
1.1 Definición de arreglos
Una forma sencilla de ejecutar este código es mediante el uso de un
compilador en línea. En Internet puede encontrar varios servicios es este
tipo. También puede descargarse las versiones del compilador para
computadora de escritorio como por ejemplo CodeBlocks.
La siguiente imagen muestra la ejecución del programa anterior en un
compilador en línea:

Imagen -
Compilador
en línea
1.1 Definición de arreglos
Para acceder en forma general a todos los elementos del arreglo se
pueden usar estructuras de control repetitivas como: for o while.
El código siguiente muestra el uso de la estructura for para asignar un
valor a cada elemento del arreglo ae. En cada elemento del arreglo se
almacena un número similar al índice multiplicado por 2. El valor
almacenado se muestra en pantalla.

Imagen - Código
1.1 Definición de arreglos
El código anterior muestra como se asigna un valor a cada elemento del
arreglo mientras se imprime su contenido. La siguiente ilustración
muestra el resultado de la ejecución del código.

Imagen - Ejecución del código.


1.1 Definición de arreglos
Es posible también definir arreglos estáticos que almacenan información
pero cuyo contenido no puede ser cambiado. El siguiente código
muestra la definición de un arreglo de contenido fijo:

Imagen - Código

El programa anterior imprime los 6 elementos contenidos en el arreglo.


1.1 Definición de arreglos
La ejecución del código anterior puede observarse en la siguiente
imagen:

Imagen - Ejecución del código


1.2 Manejo de arreglos multidimensionales
También es posible definir arreglos de varias dimensiones. En la
siguiente ilustración se muestra la definición del arreglo ae de 20 filas
por 30 columnas:

Imagen - Código

Observe que en este caso cada elemento del arreglo debe ser accedido
mediante dos índices: uno que identifique la fila y otro que identifique la
columna.
1.2 Manejo de arreglos multidimensionales
También pueden definirse arreglos multidimensionales con elementos
fijos, es decir, elementos cuyo valor no puede ser modificado:

Imagen - Código

En el código anterior se imprime el elemento de la primera fila y


columna del arreglo ae.
1.3. Creación de arreglos dinámicos
Es posible la creación de arreglos
mediante la reserva de memoria
dinámica. Esto quiere decir que el
arreglo puede ser creado y eliminado
según la necesidad del programador.
Para reservar un arreglo de 40
elementos en forma dinámica se
puede usar la función malloc, y para
eliminarlo se puede usar la función
free, como lo muestra la siguiente
ilustración:
Imagen - Código
1.3. Creación de arreglos dinámicos
La siguiente ilustración muestra la ejecución del programa anterior:

Imagen - Ejecución del programa


1.4 Relación entre arreglos y punteros
Cuando se trabaja con arreglos es
posible acceder a los elementos
de un arreglo mediante el uso de
punteros. En el siguiente código
se muestra como es posible
acceder al tercer elemento de un
arreglo mediante el uso de
punteros.

Imagen - Código
1.4 Relación entre arreglos y punteros
La siguiente ilustración muestra la ejecución del código anterior:

Imagen - Ejecución del código


1.4 Relación entre arreglos y punteros
Como se puede observar a partir de la ubicación sobre la cual se define
el puntero, los elementos del arreglo pueden ser accedidos mediante el
puntero como si se tratara de otro arreglo. Observe el siguiente código:

Imagen - Código
1.4 Relación entre arreglos y punteros
La ejecución del código anterior se muestra en la siguiente ilustración:

Imagen - Ejecución del código


1.5 Operaciones con arreglos
El uso de arreglos, como ya se mencionó al principio, es muy común
para el proceso de datos. Es así que, para su manejo adecuado, entre
otras cosas, es necesario conocer algunas operaciones que pueden
realizarse con estos.
Entre las operaciones más destacadas están:
• Buscar un elemento.
• Añadir un elemento.
• Eliminar un elemento.
• Insertar un elemento.
• Imprimir un elemento

Imagen - Programación
1.5 Operaciones con arreglos
Para realizar cada una de estas operaciones es necesario utilizar alguna
estrategia. Por ejemplo, para buscar un elemento dentro de un arreglo
puede ser necesario realizar un recorrido secuencial del arreglo hasta
encontrar el elemento almacenado. De igual manera pueden añadirse
nuevos elementos al final del último elemento almacenado. Pero para
eliminar un elemento es necesario recorrer los elementos del arreglo
sobre el espacio dejado por el elemento descartado. De igual manera,
cuando se inserta un elemento se requiere recorrer el resto de
elementos para dejar libre el espacio que ocupará el nuevo elemento.
1.5 Operaciones con arreglos
El siguiente código muestra cómo se realiza la búsqueda secuencial del
elemento b dentro de un arreglo ae:

Imagen - Código
1.5 Operaciones con arreglos
La siguiente ilustración muestra la ejecución del código anterior:

Imagen - Ejecución del código


1.5 Operaciones con arreglos
Añadir un elemento al final de un arreglo puede resultar simple si se
mantiene un indicador de la posición del elemento final. El siguiente
código muestra como añadir un elemento al final de un arreglo en el
cual la posición del último elemento viene dada por la variable f.

Imagen - Código
1.5 Operaciones con arreglos
Al eliminar un elemento de un arreglo es necesario recorrer todos los
elementos que se encuentran a la derecha hacia la izquierda para
compactar el arreglo. El siguiente código muestra un ejemplo de ello:

Imagen - Código
1.5 Operaciones con arreglos
De igual manera, para insertar un elemento en el arreglo es necesario
dejar una localidad libre desplazando todos los elementos hacia la
derecha de la localidad donde se produce la inserción. Observe el código
siguiente:

Imagen - Código
1.6 Recorrido de los elementos de un arreglo
Un arreglo puede ser recorrido de distintas maneras, dependiendo de lo
que se desee hacer con su contenido. Por ejemplo, un arreglo puede ser
recorrido de adelante hacia atrás o de atrás hacia adelante.
El siguiente código muestra como un arreglo es recorrido de las dos
maneras indicadas anteriormente:

Imagen - Código
La educación es el arma más
poderosa que puedas usar
para cambiar el mundo.
Nelson Mandela

FIN DE LA CLASE 1
UNIDAD 1
ARQUITECTURA DE DATOS

Mgtr. Luis Antonio Salvador


AUTOR DEL CONTENIDO
Unidad I: Arreglos y listas encadenadas
1.7 Listas encadenadas
1.8 Creación de listas encadenadas
1.9 Operaciones con listas encadenadas
1.10 Tipos de listas encadenadas
1.11 Implementación de estructuras de datos
1.12 transformando listas a arreglos
Objetivo

Comprender las ventajas del uso


de los arreglos y de las listas
encadenadas para el manejo de
información dentro de la
programación de aplicaciones
informáticas.
1.7 Listas encadenadas
Otra forma de almacenar y procesar datos es
mediante el uso de listas encadenadas. Las
listas encadenadas es un almacenamiento
dinámico que permite la manipulación de sus
elementos mediante el uso de punteros,
reservando y liberando memoria según la
necesidad de espacio de memoria. Una de las
ventajas de usar listas encadenadas es su
versatilidad para la creación de distintos tipos
de estructuras de datos. Si bien los arreglos
permiten también la definición de los mismos
tipos de estructuras de datos, las listas
encadenadas hacen que las operaciones con
estas estructuras sean más simples.
1.8 Creación de listas encadenadas
Para crear una lista encadenada
es necesario entender como
están dispuestos o como se
relacionan sus elementos. En
lenguaje C, lo común es definir
una estructura donde se
especifiquen los campos que
contiene cada elemento de la
lista. Observe el siguiente código:

Grafica -Código
1.8 Creación de listas encadenadas
Un nodo dentro de una lista contiene una variable para almacenar el
valor del elemento y un puntero al siguiente elemento. El último
elemento de la lista debe contener un puntero nulo.

Imagen - Nodo.

En la implementación de la lista simple se añade un puntero al último


elemento para facilitar la implementación de la función agregar
1.8 Creación de listas encadenadas
El siguiente código muestra como crear una lista con los valores 3, 4 y 5
de la ilustración anterior:

Grafica -Código
1.9 Operaciones con listas encadenadas
Otra forma de crear la lista es definiendo un conjunto de operaciones
que faciliten su uso. El siguiente código muestra la implementación de
las funciones agregar e imprimir.

Grafica -Código
1.9 Operaciones con listas encadenadas

Grafica -Código
1.9 Operaciones con listas encadenadas
• El siguiente programa utiliza las funciones definidas anteriormente
para crear una lista:
int main(){

NODO *lista=NULL;
lista=agregar(lista, 3);
lista=imprimir(lista);
lista=agregar(lista, 4);
lista=imprimir(lista);
lista=agregar(lista, 5);
lista=imprimir(lista);
return(0);
}
Imagen - Código
1.9 Operaciones con listas encadenadas
Al igual que con los arreglos pueden implementarse además de las
operaciones de agregar e imprimir, operaciones como: insertar,
eliminar y buscar. La operación insertar requiere tener control del
puntero al elemento anterior al cual se desea insertar.
1.9 Operaciones con listas encadenadas
El siguiente código muestra la implementación de la función insertar:

Grafica -Código
1.9 Operaciones con listas encadenadas
La operación eliminar requiere tener control del puntero al elemento
anterior al cual se desea eliminar.

Imagen - Control del puntero.


1.9 Operaciones con listas encadenadas
• El código que implementa la función eliminar se muestra a
continuación:

Grafica -Código
1.9 Operaciones con listas encadenadas
• La operación buscar requiere realizar una barrido secuencial por los
elementos de la lista.

Imagen - Control del puntero.


1.9 Operaciones con listas encadenadas
El código que implementa la función buscar retorna la ubicación del
elemento en la lista, de no existir retorna -1:

Grafica -Código
1.10 Tipos de listas encadenadas
Utilizando las operaciones básicas descritas se pueden crear nuevas
operaciones y estructuras de datos basadas en listas.
Otros tipos de listas encadenadas son:

Grafica - tipos.
1.10 Tipos de listas encadenadas
Las listas encadenadas
simples

Poseen nodos o elementos que almacenan un valor y un puntero al


próximo elemento de la lista como ya se observó anteriormente
1.10 Tipos de listas encadenadas
Las listas encadenadas
circulares

Son similares a las listas encadenadas simples pero unen el último


elemento con el primero para permitir un recorrido circular sobre sus
elementos

Imagen - Encadenadas circulares.


1.10 Tipos de listas encadenadas
En esta implementación se usa un apuntador al último elemento para
facilitar la agregación de elementos al final de la lista
NODO *agregar(NODO *lista, int valor){
Agregar NODO *p=lista;
NODO *s=(NODO *) malloc (sizeof(NODO));
if(s!=NULL){
s->valor=valor;
La implementación de la if(lista==NULL){
función agregar en una lista=s;
lista->a=lista;
lista circular se muestra a lista->s=lista;
continuación: }else{
lista->a->s=s;
lista->a=s;
s->s=lista;
}
}
return lista;
}
1.10 Tipos de listas encadenadas
La implementación de la función imprimir en una lista circular se
muestra a continuación:

Imprimir NODO *imprimir(NODO *lista){


NODO *p=lista;
if(p!=NULL){
printf("[");
do {
printf("%d ",p->valor);
p=p->s;
}while(p!=lista);
printf(“]\n");
}
return lista;
}
1.10 Tipos de listas encadenadas
La implementación de la función recorrer en una lista circular permite
recorrer los elementos de la lista un número de saltos. La función se
muestra a continuación: NODO *recorrer(NODO *lista, int n){
NODO *p=lista;
Recorrer int i=0;
if(p!=NULL){
do {
printf("%d ",p->valor);
p=p->s;
i++;
}while(i<n);
printf("\n");
}
return lista;
}
1.10 Tipos de listas encadenadas
La implementación de la función buscar en una lista circular es similar a
la función de una lista simple. int buscar(NODO *lista, int valor){
NODO *p=lista;
int indice=0;
Buscar if(p!=NULL)
do {
if(p->valor==valor)
break;
else{
p=p->s;
indice++;
}
}while(p!=lista);
if(p==lista) indice=-1;
}
return indice;
}
1.10 Tipos de listas encadenadas
NODO *insertar(NODO *lista, int indice, int valor){
NODO *ant, *fin, *p=lista;
int i=0;
La implementación de la NODO *s=(NODO *) malloc (sizeof(NODO));
función insertar en una lista if(s!=NULL){
s->valor=valor;
circular es algo más compleja s->s=NULL;
pues el último elemento if(lista==NULL){
lista=s; s->s=s; s->a=s;
debe apuntar siempre al }else if(lista!=NULL){
principio de la lista. if(indice==0){
lista->a->s=s; s->s=lista; lista=s;
}else{
do{
Insertar ant=p; p=p->s; i++;
}while(p!=lista && i<indice);
s->s=p; ant->s=s;
if(p==lista) lista->a=s;
}
}
}
return lista;
}
1.10 Tipos de listas encadenadas
NODO *eliminar(NODO *lista, int indice){
NODO *ant, *p=lista;
La implementación de la int i=0;
if(p!=NULL){
función eliminar en una lista if(indice==0){
circular requiere considerar if(lista!=lista->s){
lista=lista->s; lista->a=p->a; lista->a->s=lista;
que si se elimina el primer }else
elemento de la lista, el lista=NULL;
free(p);
último elemento debe }else{
apuntar siempre al nuevo do{
ant=p; p=p->s; i++;
principio de la lista. }while(p!=lista && i<indice);
if(p!=lista){
ant->s=p->s;
Eliminar if(ant->s==lista) lista->a=ant;
free(p);
}
}
}
return lista;
}
1.10 Tipos de listas encadenadas
Las listas circulares
doblemente encadenadas

Permiten un recorrido bidireccional y además el último y el primer


elemento de la lista están conectados por punteros.

Imagen - Doblemente encadenadas circulares.


1.10 Tipos de listas encadenadas
La estructura definida para implementar una lista circular doblemente
encadenada debe considerar un puntero al elemento anterior como lo
muestra el siguiente código:

• Este tipo de listas facilita


el recorrido bidireccional
entre los elementos.

Grafica - Códigos.
1.10 Tipos de listas encadenadas
NODO *agregar(NODO *lista, int valor){
NODO *ant;
NODO *p=(NODO *) malloc (sizeof(NODO));
La función agregar para un if(p!=NULL){
lista circular doblemente p->valor=valor;
if(lista==NULL){
encadenada se muestra a lista=p;
continuación: p->a=p;
p->s=p;
}else{
ant=lista;
while(ant->s!=lista){
ant=ant->s;
}
p->s=lista;
p->a=ant;
ant->s=p;
lista->a=p;
}
}
return lista;
}
1.10 Tipos de listas encadenadas
NODO *imprimir(NODO *lista, int n){
NODO *p=lista;
int i=0;
if(lista!=NULL){
La función imprimir para un do {
lista circular doblemente if(n==0){
printf("%d ",p->valor);
encadenada permite indicar p=p->s;
if(p==lista) break;
el número de elementos a }else if(n>0){
desplegar. Si el número es printf("%d ",p->valor);
p=p->s;
negativo mostrará los i++;
elementos del último al if(i>=n) break;
}else if(n<0){
primero. Si el número es cero p=p->a;
printf("%d ",p->valor);
mostrará todos los i--;
elementos de la lista. if(i<=n) break;
}
}while(1);
printf("\n");
}
return lista;
}
1.10 Tipos de listas encadenadas NODO *insertar(NODO *lista, int n, int valor){
NODO *ant;
int i=0;
NODO *p=(NODO *)malloc(sizeof(NODO));
if(p!=NULL){
La función insertar para un p->valor=valor;
if(lista==NULL ){
lista circular doblemente lista=p;
p->s=p;
encadenada permite insertar p->a=p;
}else{
un elemento en una posición if(n==0){
p->s=lista;
dada dentro de la lista. Si el p->a=lista->a;
lista->a=p;
número de posición excede p->a->s=p;
lista=p;
su tamaño, inserta el }else{
ant=lista;
elemento al final. while(ant->s!=lista && i+1<n){
ant=ant->s;
i++;
}
p->a=ant;
p->s=ant->s;
ant->s=p;
p->s->a=p;
}
}
return lista;
}
}
1.10 Tipos de listas encadenadas NODO *eliminar(NODO *lista, int n){
NODO *ant,*p;
int i=0;
if(lista!=NULL ){
if(n==0){
ant=lista;
La función eliminar para un lista=lista->s;
if(lista==ant)
lista circular doblemente lista=NULL;
else{
encadenada permite eliminar lista->a=ant->a;
lista->a->s=lista;
un elemento en una posición }
free(ant);
dada dentro de la lista. Si el }else{
ant=lista;
número de posición excede su while(ant->s->s!=lista && i+1<n){
ant=ant->s;
tamaño, elimina el elemento i++;
}
del final. p=ant->s;
if(p==ant)
lista=NULL;
else{
ant->s=p->s;
ant->s->a=ant;
}
free(p);
}
}
return lista;
}
1.10 Tipos de listas encadenadas
int buscar(NODO *lista, int valor){
NODO *p=lista;
La función buscar para un lista int indice=0;
circular doblemente encadenada if(p!=NULL){
do {
tiene una implementación if(p->valor==valor)
similar a la función desarrollad break;
para listas encadenadas simples. else{
p=p->s;
indice++;
}
}while(p!=lista);
if(p==lista) indice=-1;
}
return indice;
}
1.11 Implementación de estructuras de datos
❑ A partir de la implementación de las operaciones básicas tanto de
listas encadenadas como de arreglos, es posible crear algunas
estructuras. Entre estas estructuras están Pilas, Colas, Conjuntos, y
Mapas.
❑ Las estructuras descritas tienen un comportamiento particular, sin
embargo, todas ellas pueden ser implementadas a partir de las
operaciones: buscar, agregar, insertar y eliminar.
❑ Estructuras de datos como árboles y grafos son más complejas de
implementar y sus operaciones difieren grandemente de la
operaciones relacionadas con listas y arreglos.
❑ Debido a la similitud entre las operaciones de listas encadenadas y
arreglos, es posible reproducir la implementación de las listas y sus
operaciones usando arreglos estáticos.
1.11 Implementación de estructuras de datos
❑ Por ejemplo, una lista de enteros puede tener su contraparte en un
arreglo de enteros y a su vez la lista de enteros puede ser almacenada
en un archivo de datos sin mayores complicaciones.
❑ Los punteros de las listas encadenadas pueden ser sustituidos por
índices dentro de un arreglo de datos. De tal manera que para facilitar
el traslado de la listas encadenadas a arreglos basta con crear
funciones que sustituyan a malloc y a free.

Imagen - Estructura de datos.


1.12 Transformando listas a arreglos
El arreglo que sustituye a la memoria se muestra a continuación:
#include <stdio.h>
#include <stdlib.h>
#define tamanoMemoria 500
typedef struct tiponodo{
int valor;
struct tiponodo *a;
struct tiponodo *s;
} NODO;
typedef struct memoria{
int reserva;
NODO *dir;
NODO nodo;
} MEMORIA;
MEMORIA
memoriaTotal[tamanoMemoria];
int finMemoria=1;
1.12 Transformando listas a arreglos
El código que sustituye a la función malloc es el siguiente:
NODO *reservarMemoria(){
int i=0;
NODO *mem=NULL;
if(finMemoria<tamanoMemoria){
for(i=0;i<finMemoria;i++){
if(memoriaTotal[i].reserva==0){
mem=&memoriaTotal[i].nodo;
memoriaTotal[i].dir=mem;
memoriaTotal[i].reserva=1;
break;
}
}
if(mem==NULL){ mem=&memoriaTotal[finMemoria].nodo;
memoriaTotal[finMemoria].dir=mem;
memoriaTotal[finMemoria].reserva=1;
finMemoria++;
}
}
return mem;
}
1.12 Transformando listas a arreglos
El código que sustituye a la función free es el siguiente:
void liberarMemoria(NODO *mem){
int i=0;
for(i=0;i<finMemoria;i++) {
if(mem==&memoriaTotal[i].nodo){
memoriaTotal[i].reserva=0;
memoriaTotal[i].dir=NULL;
break; De esta manera, es posible almacenar las listas
} encadenadas dentro de un arreglo que luego puede ser
} guardado dentro de un archivo. Sin embargo, para
} recuperar el archivo es necesario transformar las
direcciones almacenadas en nuevas direcciones de
memoria, sustituyendo las direcciones almacenadas en
dir, nodo.a y nodo.s por sus equivalentes direcciones de
memoria, mediante un recorrido secuencial de todo el
arreglo.
1.12 Transformando listas a arreglos
El código para actualizar las direcciones de memoria es el siguiente:
void transformarMemoria(){
NODO *p,*ant;
int i, j;
for(i=0;i<finMemoria;i++){
p=&memoriaTotal[i].nodo;
ant=memoriaTotal[i].dir;
memoriaTotal[i].dir=p;
for(j=i;j<finMemoria;j++){
if(memoriaTotal[j].nodo.a==ant)memoriaTotal[j].nodo.a=p;
if(memoriaTotal[j].nodo.s==ant)memoriaTotal[j].nodo.s=p;
}
En la función anterior se toma la dirección recuperada y luego se
}
reemplazan todas las ocurrencias de dicha dirección en el arreglo por la
}
nueva dirección de memoria de cada elemento. La ejecución de esta
función no afecta en absoluto si el arreglo no ha sido recuperado
previamente.
1.12 Transformando listas a arreglos
La siguiente ilustración muestra el manejo de memoria mediante el
uso de arreglos:
La educación es el arma más
poderosa que puedas usar
para cambiar el mundo.
Nelson Mandela

FIN DE LA CLASE 2
UNIDAD 1

También podría gustarte