Está en la página 1de 111

PREFACIO

El curso es de naturaleza teórico-práctico y pertenece al área de formación profesional,


capacita e introduce al estudiante en los conceptos y elementos fundamentales para facilitar
la introducción a la programación y ejercitar el razonamiento, conociendo las técnicas
básicas empleadas durante el proceso de generación intermedio, optimización y generación
de código ampliando las técnicas de programación.

COMPETENCIA

El Unidad Didáctica es de naturaleza teórico-práctico y pertenece al área de formación


profesional técnica, capacita al estudiante en los conceptos y elementos fundamentales para
facilitar su introducción al mundo de la programación y pueda ejercitar su capacidad de
razonamiento. Se aplica en los conceptos de Algoritmos, Mini especificaciones, Diagramas
de Flujo y Estructura de Datos. Asimismo, deberá desarrollarse casos prácticos requeridos
en el ámbito empresarial. Como sabemos no existe una regla precisa para escribir un
programa que resuelva un dado problema practico.
ALGORITMIA

Unidad de Aprendizaje
Unidad de Aprendizaje 1 Algoritmia

PRESENTACIÓN Y CONTEXTUALIZACIÓN

Un algoritmo es un conjunto de instrucciones ordenadas, finitas y delimitadas que se crean con


el fin de describir de forma sistemática la ejecución de una tarea. Los algoritmos son de uso
común en el día a día, y se pueden encontrar en manuales de uso, instrucciones para ejecutar
un plan, o guías para ejecutar procesos. Sin embargo, el uso del término es más común en el
ámbito de la programación. Mientas
un programa es una secuencia lógica de instrucciones para ejecutar tareas específicas en una
computadora. Dichas secuencias están escritas en código y son diseñadas por programadores,
usando uno o más algoritmos.
La diferencia entre un algoritmo y un programa, es que, si bien ambos hacen referencia una
serie de instrucciones, los algoritmos pueden estar escritos en código o en lenguaje natural,
mientras que los programas sólo pueden estar escritos en lenguaje de programación.
Además, los algoritmos pueden ser ejecutados por un ser humano, mientras que los programas
están diseñados para ser ejecutados por máquinas.
Un algoritmo, es una secuencia de pasos que se crea con el fin de explicar un proceso que tiene
un inicio y un fin. Esta serie de instrucciones debe estar expresada en términos concretos, de tal
forma que no quede duda de lo que haya que hacer para que la ejecución sea exitosa. El término
algoritmo viene del griego arithmos (número), y se utiliza comúnmente en la informática, la
ALGORITMIA

programación y las matemáticas.


Sin embargo, un algoritmo no solo puede estar expresado en números, sino también con
palabras. Cualquier actividad o evento con inicio y final que tenga una serie de pasos lógicos
para lograr su ejecución puede ser expresada mediante un algoritmo. Y estos suelen ser
representados mediante diagramas de flujo.
Todo algoritmo está compuesto por tres partes, que son indispensables para que las
instrucciones puedan ejecutarse. Entrada, Proceso y Salida. Todo algoritmo debe tener unas
características básicas para que se ejecute correctamente, son precisos, son finitos, tienen que
estar definidos, deben describir tres elementos: entrada, proceso y salida. y deben ser legibles.
En informática, existen cuatro tipos de algoritmos, clasificados de acuerdo al uso (o no) de
cálculos numéricos y dispositivos computacionales. Algoritmos cualitativos, Algoritmos
cuantitativos, Algoritmos computacionales y Algoritmos no computacionales.

COMPETENCIA
Conoce e identifica la terminología para construir y resolver algoritmos con las
estructuras de datos más adecuadas.

CAPACIDADES
• Analiza y critica la importancia de la algorítmica y la estructura de datos dentro del mundo
actual.
• Diferencia los distintos casos de uso y fundamentos de programación. Comprende la
diferencia entre las diferentes estructuras condicionales.
• Diferencia los distintos casos de uso y fundamentos de programación. Comprende la
diferencia entre las estructuras repetitivas.
• Analiza y critica las técnicas de estudio de las estructuras de datos.

Página 4 de 111
Algoritmia Unidad de Aprendizaje 1

ACTITUDES

• Desarrolla una actitud emprendedora mediante la toma de iniciativas.


• Actúa con responsabilidad personal, al cumplir con los horarios establecidos.
• Respeto a las normas de convivencia.
• Cumple con la presentación de los trabajos encomendados de manera individual y en
equipo.

La Unidad de Aprendizaje 01: Algoritmia

Comprende el desarrollo de los siguientes temas:

Página 5 de 111
Unidad de Aprendizaje 1 Algoritmia

Algoritmos
Un computador es capaz de realizar “sólo” determinadas acciones sencillas, tales como sumar,
comparar o transferir datos, pero los problemas que normalmente interesa resolver son más
complejos. Para resolver un problema real es necesario, en primer lugar,
encontrar un método de resolución y, posteriormente, determinar la sucesión
de acciones sencillas (susceptibles de ser ejecutadas por un computador) en
que se descompone dicho método. No todos los métodos de solución de un
problema pueden ser puestos es práctica en un computador.
Para que un procedimiento pueda ser implantado en un computador
debe ser:
• Preciso: estar compuesto de pasos bien definidos (no
ambiguos) y ordenados.
• Definido: si se sigue dos veces, se obtiene el mismo
resultado cada vez.
• Finito: tener un número finito de pasos.
Un procedimiento o método para resolver un problema que cumpla los requisitos anteriores se
dice que es un algoritmo. Se puede dar por tanto la siguiente definición:
Un algoritmo es un método para resolver un problema mediante una secuencia de pasos
bien definidos, ordenados y finitos.
Para que se pueda ejecutar el algoritmo es
preciso, además, que se disponga de las
“herramientas” adecuadas para llevar a cabo cada
uno de los pasos. Si no es así, estos deberán, a su
vez, ser descompuestos en una secuencia
(algoritmo) de pasos más simples que sí se
puedan llevar a cabo.
Un programa de computador es una sucesión de
´ordenes que describen un algoritmo, escritas de
forma que puedan ser entendidas por el
computador.
En un algoritmo (y por tanto en un programa) se distinguen las
siguientes acciones:
• Entrada: es la información de partida que necesita
el algoritmo para arrancar.
• Proceso: es el conjunto de todas las operaciones a
realizar.
• Salida: son los resultados obtenidos.

Página 6 de 111
Algoritmia Unidad de Aprendizaje 1

¿Qué es un algoritmo?
Algoritmo
En aritmética y programación informática, conjunto
ordenado de instrucciones sistemáticas que permite
hallar la solución de un problema específico: el
algoritmo que se emplea para hallar las raíces
cuadradas; un programa informático es un conjunto
de secuencias de instrucciones elementales en
forma de algoritmo La palabra algoritmo se deriva
de la traducción al latín de la palabra árabe
Alkhowarizmi, nombre de un matemático y
astrónomo árabe que escribió un tratado sobre la
manipulación de números y ecuaciones en el siglo
IX. Son un conjunto de operaciones que se utilizan para resolver problemas específicos. En estas
instrucciones se indica la secuencia de operaciones que se deben realizar para que partiendo de los datos
de entada se pueda obtener el resultado buscado.
Los algoritmos son utilizados en el mundo de la ciencia para la resolución metódica de problemas. Los
algoritmos no siempre están escritos de una forma que conduce al programa más efectivo en términos
de requisitos de tiempo o almacenamiento.
Las principales características que debe tener un buen algoritmo son:
• Debe tener un punto particular de inicio.
• Debe ser completamente definido y no
debe permitir dobles interpretaciones.
• Debe ser general, es decir, soportar la
mayoría de las variantes que se puedan
presentar en la definición del problema.
• Debe ser finito en tamaño y tiempo de
ejecución.
• Debe ser legible, claro y fácil de
interpretar y entender.

TIPOS DE ALGORITMOS
• Cualitativos: son aquellos en los que se
describen los pasos utilizando palabras. Son
todos aquellos pasos o instrucciones descritos
por medio de palabras que sirven para llegar
a la obtención de una respuesta o solución de
un problema cualquier
• Cuantitativos: son aquellos en los que se
utilizan cálculos numéricos para definir los
pasos del proceso. Son aquellos pasos o
instrucciones que involucran cálculos
numéricos para llegar a un resultado
satisfactorio.

TIPOS DE ALGORITMOS DE RAZONAMIENTO:


• Algoritmos Estáticos: son los que funcionan siempre igual, independientemente del tipo de
problema tratado.
• Algoritmos Adaptativos: algoritmos con cierta capacidad de aprendizaje.
• Algoritmos Probabilísticos: son algoritmos que no utilizan valores de verdad booleanos sino
continuos. Existen varios tipos de algoritmos probabilísticos dependiendo de su
funcionamiento, pudiéndose distinguir:

Página 7 de 111
Unidad de Aprendizaje 1 Algoritmia

• Algoritmos numéricos: que proporcionan una solución aproximada del problema.


• Algoritmos de Montecarlo: que pueden dar la respuesta correcta o respuesta erróneas (con
probabilidad baja).
• Algoritmos de Las Vegas: que nunca dan una respuesta incorrecta: o bien dan la respuesta
correcta o informan del fallo.
• Algoritmo Cotidiano: es la serie de pasos que realizamos en nuestra vida diaria para realizar
las diferentes tareas y actividades comunes, desde los pasos al levantarnos, así como ir de
compras, etc.
• Algoritmo Voraz: un algoritmo voraz es aquel que, para resolver un
determinado problema, sigue una meta heurística consistente en elegir la
opción óptima en cada paso local con la esperanza de llegar a una solución
general óptima.
• Algoritmo Determinista: es un algoritmo que, en términos informales, es
completamente predictivo si se conocen sus entradas.
• Algoritmo Heurístico: es un algoritmo que abandona uno o ambos
objetivos; por ejemplo, normalmente encuentran buenas soluciones,
aunque no hay pruebas de que la solución no pueda ser arbitrariamente
errónea en algunos casos; o se ejecuta razonablemente rápido, aunque
no existe tampoco prueba de que siempre será así. Las heurísticas
generalmente son usadas cuando no existe una solución óptima bajo las restricciones dadas
(tiempo, espacio), o cuando no existe del todo.
• Algoritmo de escalada: la idea básica consiste en comenzar con una mala solución a un
determinado problema y, repetidamente, aplicar optimizaciones a la misma hasta que esta sea
óptima o satisfaga algún otro requisito.

¿Ciencias en que se apoya la algoritmia para producir soluciones ingeniosas?

• Ciencias de la Computación.
• Matemáticas.
• Ciencias Sociales.
• Ciencias Políticas.

Lenguajes algorítmicos

Indican una serie de símbolos y reglas que se utilizan


para describir de manera explícita un proceso. Pueden
ser:
• Gráficos o Diagrama de Flujo: Es la
representación gráfica de las operaciones que
realiza un algoritmo.
• No Gráficos o Pseudocódigo: Es la
representación en forma descriptiva de las
operaciones que debe realizar un algoritmo.

Características de un algoritmo
• Finito: Si se sigue un algoritmo, se debe terminar en un número finito de pasos.
• Definible: Un algoritmo debe ser preciso e indicar el orden de realización de cada paso. Si se
sigue un algoritmo dos veces, se debe obtener el mismo resultado cada vez.
• Entradas: El algoritmo debe tener cero o más entradas, es decir cantidades dadas antes de
empezar.

Página 8 de 111
Algoritmia Unidad de Aprendizaje 1
• Salidas: el algoritmo tiene una o más salidas en relación con las entradas.

¿QUÉ SON LAS ESTRUCTURAS DE DATOS?


Es una colección datos que pueden ser caracterizados por su organización y las operaciones que se
definen en ella. Ejemplo de Estructura de datos: Arreglos, Archivos, Cadenas y Listas.

CLASIFICACIÓN DE LAS ESTRUCTURAS DE DATOS


➢ ESTÁTICAS: Su tamaño en memoria es fijo.
Ejemplo, arreglos, conjuntos, cadenas.
➢ DINÁMICAS: Su tamaño en memoria es variable.
Ejemplo, pilas, colas, listas, árboles, grafos, etc.
Estas a su vez se dividen en:
• LINEALES: Son aquellas estructuras donde los
datos se almacenan en zonas continuas
(sucesivas o adyacentes), una detrás de otra.
Ejemplo: pilas, colas, listas.
• NO LINEALES: Son aquellas estructuras
donde los datos no se encuentran en forma continua, es decir hay “bifurcación”. Ejemplo
árboles, grafos.

Metodología para construir un algoritmo

Para que la labor de programación sea una tarea fácil debemos seguir una metodología la cual
comprende los siguientes pasos:
a) Definición del problema (Enunciado)
b) Definición de la solución (Análisis del problema)
c) Diseño del algoritmo
d) Desarrollo del problema (Codificación)
e) Depuración y pruebas (Ejecución – Evaluación de Resultados)
f) Documentación

Ejemplo:
OBJETIVO DEL PROBLEMA: CALCULAR EL ÁREA DE UN RECTÁNGULO
Entrada de Datos Proceso Resultado o Salida
Altura (h)
a=b*h área (a)
Base (b)

Diseño del algoritmo


Definidos exactamente los procesos tendremos que programar y
conocidos los datos que estarán siendo entregados y la
información que debemos generar como resultado del proceso,
estamos en condiciones de diseñar nuestra solución.
Para construir algoritmos se utilizan metodologías como: El
Diagrama de Flujo y Pseudocódigos.
Diagrama De Flujo: Metodología gráfica que permite construir
visualmente el recorrido del flujo de un programa y estructurar el
algoritmo para una solución determinada.
Página 9 de 111
Unidad de Aprendizaje 1 Algoritmia

Simbología usada en un diagrama de flujo

Tipos de algoritmo
Algoritmos secuenciales
Es aquélla en la que una acción (instrucción)
sigue a otra en secuencia. Las tareas se suceden
de tal modo que la salida de una es la entrada de
la siguiente y así sucesivamente hasta el fin del
proceso. La estructura secuencial tiene una
entrada y una salida. Su representación gráfica
es la siguiente:
Algoritmos condicionales
En los lenguajes de programación es común el
uso de condicionales que sirven para denotar
diferentes alternativas que pueden llevarse a
cabo dado el valor de una expresión lógica, el
cual siempre será verdadero o falso. La forma
general que tiene un condicional (una pregunta)
es la siguiente: SI (expresión lógica verdadera)
instrucciones que se realizan si la expresión
lógica es verdadera EN CASO CONTRARIO
instrucciones que se realizan si la expresión
lógica es falsa

Página 10 de 111
Algoritmia Unidad de Aprendizaje 1

Algoritmos repetitivos finitos


Otro paso en la elaboración de un algoritmo es el
de la iteración de una o más instrucciones
involucradas en la solución de un requerimiento,
por tanto, existen dos esquemas el cualitativo y
el cuantitativo en los cuales el programador o
desarrollador debe tener agilidad y destreza para
su selección; estos a su vez se componen de las
siguientes estructuras (mientras, haga mientras
que y para). Los algoritmos finitos deben tener
un número finito de pasos, por lo que debe estar
limitado tanto en tiempo de realización como por
el número de pasos que realiza
Algoritmos repetitivos infinitos
El bucle de la siguiente figura es infinito, ya que
las instrucciones (1), (2) y (3) se ejecutan
indefinidamente, pues no existe salida del bucle,
al no cumplirse una determinada condición

Pseudocódigo
Una de las mejores formas de aprender a programar es empezar por los diagramas de flujo y el
pseudocódigo. Ambos facilitan al estudiante su inmersión en la resolución de problemas mediante
algoritmos.
El pseudocódigo es una forma de expresar los
distintos pasos que va a realizar un programa, de
la forma más parecida a un lenguaje de
programación. Su principal función es la de
representar por pasos la solución a un problema
o algoritmo, de la forma más detallada posible,
utilizando un lenguaje cercano al de
programación. El pseudocódigo no puede
ejecutarse en un computador ya que entonces
dejaría de ser pseudocódigo, como su propio
nombre indica, se trata de un código falso
(pseudo = falso), es un código escrito para que lo entienda el ser humano y no la máquina.
Aprender a escribir pseudocódigo para la resolución de un problema permite hacer mucho más sencilla
su programación en un lenguaje convencional, por lo que, si estás interesado en comenzar tu formación
como programador y no tienes conocimientos previos, resulta muy recomendable y conveniente
formarse en pseudocódigo antes de empezar a estudiar cualquier lenguaje de programación

Diagrama de flujo
Un diagrama de flujo es una representación gráfica de un proceso. Cada paso del proceso es
representado por un símbolo diferente que contiene una breve descripción de la etapa de
proceso. Los símbolos gráficos del flujo del proceso están unidos entre sí con flechas que
indican la dirección de flujo del proceso.

Página 11 de 111
Unidad de Aprendizaje 1 Algoritmia

Beneficios del Diagrama de Flujo


En primer lugar, facilita la obtención de una visión transparente del proceso, mejorando su
comprensión. El conjunto de actividades, relaciones e incidencias de un proceso no es
fácilmente discernible a priori. La diagramación hace posible aprehender ese conjunto e ir más
allá, centrándose en aspectos específicos del
mismo, apreciando las interrelaciones que
forman parte del proceso así como las que se dan
con otros procesos y subprocesos.
• Permiten definir los límites de un
proceso. A veces estos límites no son tan
evidentes, no estando definidos los
distintos proveedores y clientes (internos
y externos) involucrados.
• El diagrama de flujo facilita la
identificación de los clientes, es más
sencillo determinar sus necesidades y
ajustar el proceso hacia la satisfacción de
sus necesidades y expectativas.
• Estimula el pensamiento analítico en el
momento de estudiar un proceso,
haciendo más factible generar alternativas útiles.
Un Diagrama de Flujo, su correcta construcción es sumamente importante porque, a partir del
mismo se escribe un programa en algún Lenguaje de Programación.
Si el Diagrama de Flujo está completo y correcto, el paso del mismo a un Lenguaje de
Programación es relativamente simple y directo. Es importante resaltar que el Diagrama de
Flujo muestra el sistema como una red de procesos funcionales conectados entre sí por "
Tuberías " y "Depósitos" de datos que permite describir el movimiento de los datos a través del
Sistema. Este describirá : Lugares de Origen y Destino de los datos , Transformaciones a las
que son sometidos los datos, Lugares en los que se almacenan los datos dentro del sistema , Los
canales por donde circulan los datos. Además de esto podemos decir que este es una
representación reticular de un Sistema, el cual lo contempla en términos de sus componentes
indicando el enlace entre los mismos.
Ejemplo de un Diagrama de flujo:
El siguiente diagrama de flujo constituye el algoritmo que da solución al caso propuesto.

Página 12 de 111
Algoritmia Unidad de Aprendizaje 1
Pseudocódigo:
Es un lenguaje de especificación (descripción) de algoritmos.
Generalmente se escribe en el idioma natural del usuario, es muy semejante al código fuente de
un programa.
Se considera un primer borrador, dado que el pseudocódigo tiene que traducirse posteriormente
a un lenguaje de programación. El pseudocódigo no puede ser ejecutado por el computador.
Ejemplo de un Pseudicódigo: (Para el desarrollo del Caso propuesto)

Desarrollo del problema (codificación):


Consiste en la codificación del programa. Partiendo del algoritmo desarrollado en el paso
anterior, para este fin se deberá utilizar un lenguaje de programación, para nuestro caso Java.
Codificación en Java del algoritmo anterior

Proceso de Datos
a=b*h;
Salida de Información
System.out.println("El área es: " + a);
}
}

Página 13 de 111
Unidad de Aprendizaje 1 Algoritmia

Nota: String no es un tipo de dato primitivo, sino corresponde a una clase


de Java desde la cual instancia (es decir se crea) todos los tipos de cadenas
de texto. Por otra parte, Java sí hace diferencia entre las mayúsculas y
minúsculas, por lo que debemos ser cuidadosos al escribir el código fuente,
por lo general los nombres de las clases siempre empiezan con
mayúsculas.

Depuración Y Prueba
Consiste en la ejecución, depuración y solución de errores. Los errores que podrían presentarse
pueden ser:
Errores de Sintaxis: Son errores de escritura de código, el
compilador no los puede interpretar y por lo tanto cuando
ejecutamos el programa los señala como un error indicándose la
línea y elemento desconocidos, mientras no arreglemos todos estos
errores el programa no podrá ejecutarse.

Error de Ejecución: El programa puede ejecutarse, pero por un mal


manejo del programa o un ingreso indebido, el programa colapsa o
cierra repentinamente, estos tipos de errores deben evitarse y debe
ser el programa quien pueda controlarlos.

Error de Lógica o procedimiento: Este tipo de error es percibido


cuando nos damos cuenta que los resultados de los procesos no son
los correctos. Todos estos tipos de errores deben examinarse antes
de dar por concluido el proceso de desarrollo del programa. De
presentarse un error de lógica se debe realizar un feedback
(Retroalimentación) y debemos volver al PASO Nro. 1

Documentación

En esta etapa debe recopilarse toda


la documentación generada en las
etapas anteriores, la cual va a servir
como base para la elaboración del
manual técnico.

Página 14 de 111
Algoritmia Unidad de Aprendizaje 1

Las estructuras condicionales comparan una variable contra otro(s) valor(es), para que, en base
al resultado de esta comparación, se siga un curso de acción dentro del programa. Cabe
mencionar que la comparación se puede hacer contra otra variable o contra una constante, según
se necesite. Existen dos tipos básicos, las simples y las múltiples.

Comenzaremos viendo el tipo más simple de


estructura de bifurcación. Debido a las
palabras reservadas que usa se suele
denominar IF-THEN (que en castellano es
SI-ENTONCES).
Esta estructura nos permitirá ejecutar un
bloque de código en función de la veracidad
o falsedad de una condición. El organigrama
que explica el funcionamiento de esta
instrucción.
Condición es una expresión que devuelve un
dato de tipo lógico y BloqueInst es un bloque
de instrucciones. La interpretación sería: “si
el resultado de evaluar Condición es
verdadero (. TRUE.) entonces ejecutamos
BloqueInst y, si es falso (.FALSE.),no lo
ejecutamos”. Es decir, planteamos como
alternativa la ejecución o no de un conjunto
de instrucciones.
La sintaxis de esta instrucción en Fortran es la siguiente:

Observa que la condición


es una expresión cuyo
resultado es un dato de
tipo lógico y que puede ser
tan compleja como
necesitemos. En este caso
la condición está
compuesta por dos
condiciones más simples.

Página 15 de 111
Unidad de Aprendizaje 1 Algoritmia

Clasificación de las Estructuras Selectivas.


La especificación formal de algoritmos tiene realmente utilidad cuando el
algoritmo requiere una descripción más complicada que una lista sencilla de
instrucciones. Este es el caso cuando existen un número de posibles alternativas
resultantes de la evaluación de una determinada condición.
Estas estructuras se identifican porque en la fase de solución del problema existe
algún punto en el cual es necesario establecer una pregunta, para decidir si
ciertas acciones deben realizarse o no.
Las condiciones se especifican usando expresiones lógicas. La representación
de una estructura selectiva se hace con palabras en pseudocódigo (if - then - else
o en español si - entonces - sino) y en flujograma con una figura geométrica en forma de rombo.
Las estructuras selectivas se utilizan para tomar decisiones lógicas; de ahí que también se conocen como
estructuras de decisión o alternativas. En las estructuras selectivas se evalúa una condición, y en función
del resultado de la misma se toma un camino u otro.

Estructura Selectiva Simple.


DEFINICIÓN: Es una estructura que evalúa una expresión lógica y dependiendo del resultado escoge
entre realizar uno u otro bloque de código.
En nuestra vida cotidiana a diario tomamos una serie de decisiones y todas tienen que ver con: evaluar
una premisa y de acuerdo a su resultado podemos tomar una u otra decisión.
Su representación en el diagrama de flujo:

Página 16 de 111
Algoritmia Unidad de Aprendizaje 1

Su representación en el pseudocódigo

Su representación en la codificación

EJEMPLOS
Construir un algoritmo tal, que dado como dato la
calificación de un alumno en un examen, escriba
DEFINICIÓN DEL PROBLEMA
El mismo enunciado.
ANÁLISIS DEL PROBLEMA
Salidas: mensaje de aprobado si se cumple la
condición.
Entradas: calificación
Datos adicionales: un alumno aprueba si la
calificación es mayor o igual que 7.

Sección de declaraciones:
Var Real: Cal
Donde Cal = calificación “Aprobado” en caso que esa
calificación fuese mayor o igual que 13

Página 17 de 111
Unidad de Aprendizaje 1 Algoritmia

Ejemplo
Las estructuras selectivas simples o condicionales están compuesta únicamente de una sola
condición si es verdadera ejecutara la acción o acciones si la condición es falsa no hará nada.
if (condición) entre paréntesis esta la condición
que se debe evaluar acción (si la condición entre
paréntesis es verdadera se ejecutara la acción o
acciones, porque, puede estar conformado por
varias acciones)
end

Antes de mostrar el ejemplo vamos a explicar algunos comandos que vamos a utilizar:
# => este comando nos permite poner un comentario, puede ser a principio de línea o en
cualquier posición de ella.
puts => este comando nos permite imprimir cualquier texto en pantalla, puede ser el resultado
de una operación o un texto escrito entre comillas.
Ejemplo
edad = 19 # asignamos un valor a la variable edad
if (edad > 17)
puts “Es mayor de edad”
end
Si ejecutamos el programa que hemos desarrollado el resultado serio: Es mayor de edad, porque,
la condición que esta entre paréntesis es verdadera, porque, edad que tiene asignado 19 es mayor
a 17, en cambio, si el valor asignado a la variable edad fuera menor a 18, no mostraría ningún
resultado.

Página 18 de 111
Algoritmia Unidad de Aprendizaje 1

Estructura Selectiva Doble.


Este tipo de estructura presenta de igual forma una condición o (expresión lógica), de ser verdadera esta
condición, se ejecuta un bloque de instrucciones y en caso contrario se ejecuta otro bloque distinto. Se
utiliza en la solución de problemas donde las alternativas se bifurcan en dos posibilidades dependiendo
de la condición evaluada.

Su representación en el diagrama de flujo:

Su representación en el pseudocódigo

Su representación en la codificación

Página 19 de 111
Unidad de Aprendizaje 1 Algoritmia

Ejemplos

Estructura Selectiva Anidada.


Este tipo de estructura está formada por una serie de
estructuras selectivas que se encuentran inmersas unas
dentro de otras. Se utiliza para establecer una serie de
condiciones jerárquicas desde las más genéricas hasta las
más específicas, sino se cumplen las primeras condiciones,
no se evalúan las siguientes.

Página 20 de 111
Algoritmia Unidad de Aprendizaje 1

Su representación en el diagrama de flujo

Su representación en el pseudocódigo

Su representación en la codificación

Página 21 de 111
Unidad de Aprendizaje 1 Algoritmia

El mismo caso anterior se puede representar de un modo más simplificado usando el elseif:
//Condición inicial
If monto>=1000
{ d=monto*0.30; }
else if (monto<1000 &&monto>=500)
//Segunda condición sino se cumple el anterior
{ d=monto*0.20; }
else If (monto<500 &&monto>=200)
//Tercera condición sino se cumplen las anteriores
{ d=monto*0.10; }
Else
//En el caso de cumplirse ninguna de las anteriores
{ d=0; }

Ejemplos

Estructura Selectiva Múltiple.


La estructura evalúa una expresión que podrá tomar n valores distintos, 1,2,3,4,…,n. Según que elija
uno de estos valores en la condición, se realizará una de las n acciones, el flujo del algoritmo seguirá un
determinado camino entre los n posibles.

Página 22 de 111
Algoritmia Unidad de Aprendizaje 1

Su representación en el diagrama de flujo

Su representación en el pseudocódigo

Su representación en la codificación

Página 23 de 111
Unidad de Aprendizaje 1 Algoritmia

Ejemplos
EJEMPLO: Realizar un algoritmo que lea el número que representa el día de la semana y diga que día
es, teniendo en cuenta lunes=1, martes 2…, domingo=7

Página 24 de 111
Algoritmia Unidad de Aprendizaje 1

Programación Modular
Uno de los métodos más conocidos para resolver un problema es dividirlo en problemas más
pequeños, llamados subproblemas. De esta manera, en lugar de resolver una tarea compleja y
tediosa, resolvemos otras más sencillas y a partir
de ellas llegamos a la solución. Esta técnica se
usa mucho en programación ya que programar no
es más que resolver problemas, y se le suele
llamar diseño descendente, metodología del
divide y vencerás o programación top-down.
Es evidente que, si esta metodología nos lleva a
tratar con subproblemas, entonces también
tengamos la necesidad de poder crear y trabajar
con subprogramas para resolverlos. A estos
subprogramas se les suele llamar módulos, de ahí viene el nombre de programación modular.
Los subproblemas o módulos se diseñan con subprogramas, que a su vez se clasifican en
procedimientos y funciones. Los procedimientos y las funciones son unidades de programas
diseñados para ejecutar una tarea específica. El proceso de
descomposición de un problema en módulos se conoce
como modulación y a la programación relativa a ellos,
programación modular.
Al aplicar la programación modular, un problema
complejo debe ser dividido en varios subproblemas más
simples, y estos a su vez en otros subproblemas más
simples. Esto debe hacerse hasta obtener subproblemas lo
suficientemente simples como para poder ser resueltos
fácilmente con algún lenguaje de programación. Esta
técnica se llama refinamiento sucesivo, divide y vencerás
o análisis descendente (Top-Down).
Un 'módulo' es cada una de las partes de un programa que resuelve uno de los subproblemas en
que se divide el problema complejo original. Cada uno de estos módulos tiene una tarea bien
definida y algunos necesitan de otros para poder operar. En caso de que un módulo necesite de
otro, puede comunicarse con éste mediante una interfaz de comunicación que también debe
estar bien definida.
Si bien un módulo puede entenderse como una parte de un programa en cualquiera de sus
formas y variados contextos, en la práctica se los suele tomar como sinónimos de
procedimientos y funciones. Pero no necesaria ni estrictamente un módulo es una función o un
procedimiento, ya que el mismo puede contener muchos de ellos. No debe confundirse el
término "módulo" (en el sentido de programación modular) con términos como "función" o
"procedimiento", propios del lenguaje que lo soporte.

Página 25 de 111
Unidad de Aprendizaje 1 Algoritmia

Un ejemplo de cómo emplear el diseño descendente para resolver un problema. Supongamos


que un profesor quiere crear un programa para gestionar las notas de sus alumnos. Quiere que
dicho programa le permita realizar tareas tales como asignar notas, cambiar notas, ver las notas
según distintas calificaciones, etc. A continuación, tienes un esquema que representa una de las
posibles divisiones del problema en módulos.
Estructura Para (for)
Es la estructura algorítmica adecuada para utilizar en un ciclo que se ejecutará un número
definido de veces. Este tipo de estructura está presente en todos los lenguajes de programación,
ya sean estructurados u orientados a objetos.
Esta sentencia incluye una expresión que especifica el valor inicial de un índice otra expresión
que determina cuando se continúa o no el bucle y una tercera expresión que permite que el
índice se modifique al final de cada pasada.
Ejecuta un bloque de instrucciones un número determinado de veces, este número de veces está
controlado por una variable contadora de tipo entero, que toma valores desde un límite inferior
hasta un límite inferior.

Esta estructura permite repetir el bucle de instrucciones,


de N a 1 veces, luego de cumplirse la n-sima vuelta, se
procederá a ejecutarse las instrucciones que se
encuentran después del bucle. La variable I va
decrementado sus valores automáticamente, siendo estos:
N,…., 3,2,1

Esta estructura permite repetir el bucle de


instrucciones, de 2 a N veces, donde en
cada vuelta el valor de I se irá
incrementando de 2 en 2. La variable I va
decreciendo sus valores
automáticamente, siendo estos: 2, 4, 6, 8,
…N

Pseudocódigo

Página 26 de 111
Algoritmia Unidad de Aprendizaje 1

El diagrama de flujo de la estructura repetir es la siguiente:

Donde:
• V es una variable de control
• VI es el valor inicial
• VF es el valor final
• INC es el incremento
Pseudocódigo
Para V<---VI hasta VF incremento 1 Hacer
Bloque de instrucciones
Fin_para
Si es verdadera, se vuelve a ejecutar el bloque de instrucciones.
Y así sucesivamente, hasta que, la condición sea falsa.
Es usado cuando se conoce de antemano, el número de veces que debe repetirse una instrucción
o conjunto de ellas
Tipos de variables:
Cuando se diseñan algoritmos que incluyen estructuras de control repetitivas, existen ciertas
variables que cumplen una función específica en cada iteración del ciclo, las más comunes son:
Variables contadoras: Se usan para contar, por lo tanto, debe ser de tipo entero. Una variable
contadora se incrementa o decrementa en un valor constante en un valor constante con cada
iteración del ciclo.
Variable acumuladora: su función es almacenar valores numéricos que generalmente se suman
o multiplican en cada iteración. Por lo tanto, esta variable debe ser de tipo real.
Variable bandera: Es utilizada dentro de la condición de un ciclo, para determinar cuándo un
círculo se sigue iterando o cuando no. De esta forma una variable bandera debe ser de tipo
booleano o entero.
Ejemplo: Escriba un algoritmo (diagrama de flujo, pseudocódigo) tal que dado como datos N
números enteros, obtenga el número de ceros que hay entre estos números.

Página 27 de 111
Unidad de Aprendizaje 1 Algoritmia

Pseudocódigo
(El programa cuenta cuántos ceros hay en un grupo de N números enteros.
Inicio
cuecer<--0, l<--1, N<--0 (Declarar variables)
Leer N
Para l desde 1 hasta N
Leer num
si num<=0 entonces
hacer cuecer<-- cueces+1
Fin_si
Fin_para
Escribir cuecer
Fin

Página 28 de 111
Algoritmia Unidad de Aprendizaje 1

Estructura Mientras (While)


La estructura repetitiva mientras (en inglés while), es aquella en que el cuerpo del bucle se repite
mientras se cumple una determinada condición. Cuando se ejecuta la instrucción mientras, la primera
cosa que sucede es evaluar la condición (una expresión booleana). Si la condición se evalúa falsa, no se
entra al ciclo y se sigue con el flujo normal del problema. Si la condición es verdadera, entonces se entra
al ciclo y se ejecuta el cuerpo del bucle (instrucciones dentro del mientras), después se evalúa de nuevo
la expresión booleana. Este proceso se repite una y otra vez mientras la condición sea verdadera.

En ocasiones requerimos utilizar ciclos en donde no sabemos de antemano la cantidad de


repeticiones que se llevaran a cabo. Para estos casos podemos utilizar la estructura mientras,
donde se utiliza un dato centinela para terminar el ciclo, el dato centinela es un dato que jamás
será un valor real dentro de mis datos esperados de entrada.
La computadora empieza revisando la condición. Si es VERDADERA, las declaraciones se
ejecutan. De otro modo, la computadora procede a la siguiente declaración y llega al fin.
Después de que las declaraciones se ejecutan, la computadora regresa y revisa la condición otra
vez. Esta secuencia de eventos se repite hasta que eventualmente la condición resulta FALSA.
Cuando eso pasa, la repetición es terminada y la ejecución continúa con las declaraciones hasta
el fin.
La condición es revisada antes de que las ejecuciones de las declaraciones tomen lugar. Por lo
tanto, las declaraciones puede que no se ejecuten para nada si la condición es FALSA cuando
la computadora llega al final de la estructura mientras. Esta es una característica distintiva de
la estructura while.
Ejemplo
Realizar un algoritmo que nos permite capturar N calificaciones, calcular e imprimir el
promedio.
Resolución
No sabemos cuántos números serán en la secuencia, pero sabemos que todos deben ser positivos
o cero. Por lo tanto, podemos usar como dato centinela un número negativo para indicar el fin
de la secuencia.

Página 29 de 111
Unidad de Aprendizaje 1 Algoritmia

Algoritmo

Diagrama de flujo

Página 30 de 111
Algoritmia Unidad de Aprendizaje 1
Este tipo de estructura es utilizada cuando se necesita la estructura repetitiva, mas no se conoce
con exactitud la cantidad de iteraciones del bucle, pues la cantidad de repeticiones dependerá
de si se sigue cumpliendo la condición principal del bucle.
Contadores y Acumuladores.
Contadores
Los procesos repetitivos son la base del uso de los
computadores. En estos procesos se necesita normalmente
contar los sucesos o acciones internas del bucle. Una
manera de controlar un bucle es mediante un contador. Un
contador es una variable cuyo valor se incrementa o
decrece en una cantidad constante en cada iteración.
La construcción de un contador es una de las técnicas más
comunes en la realización de diagramas de flujo. Es una
variable en la memoria que se incrementará en una unidad
cada vez que se ejecute el proceso. El contador se utiliza
para llevar la cuenta de determinadas acciones que se
pueden solicitar durante la resolución de un problema. En
las instrucciones de preparación se realiza la inicialización
del contador o contadores. La inicialización consiste en
poner el valor inicial de la variable que representa al
contador. Generalmente se inicializa con el valor 0.

Página 31 de 111
Unidad de Aprendizaje 1 Algoritmia

Acumulador
Un acumulador o totalizador es una variable cuya misión es almacenar cantidades variables
resultantes de sumas sucesivas. Realiza la misma función que un contador, con la diferencia de
que el incremento o decremento de cada suma es variable en lugar de constante, como en el
caso del contador. Se representa por la instrucción S=S+N, donde N es una variable y no una
constante.
Un acumulador es una variable en la memoria cuya misión es almacenar cantidades variables.
Se utiliza para efectuar sumas sucesivas. La principal diferencia con el contador es que el
incremento o decremento de cada suma es variable en lugar de constante como en el caso del
contador.

Página 32 de 111
Algoritmia Unidad de Aprendizaje 1

Definición.
Una función recursiva es una función que se llama a si misma. Esto es, dentro del cuerpo de la
función se incluyen llamadas a la propia función. Esta estrategia es una alternativa al uso de
bucles. Una solución recursiva es, normalmente, menos
eficiente que una solución basada en bucles. Esto se debe a
las operaciones auxiliares que llevan consigo las llamadas a
las funciones. Cuando un programa llama a una función que
llama a otra, la cual llama a otra y así sucesivamente, las
variables y valores de los parámetros de cada llamada a cada
función se guardan en la pila o stack, junto con la dirección
de la siguiente línea de código a ejecutar una vez finalizada
la ejecución de la función invocada. Esta pila va creciendo a
medida que se llama a más funciones y decrece cuando cada
función termina. Si una función se llama a si misma
recursivamente un número muy grande de veces existe el
riesgo de que se agote la memoria de la pila, causando la
terminación brusca del programa.
A pesar de todos estos inconvenientes, en muchas
circunstancias el uso de la recursividad permite a los programadores especificar soluciones
naturales y sencillas que sin emplear esta técnica serían mucho más complejas de resolver. Esto
convierte a la recursión en una potente herramienta de la programación. Sin embargo, por sus
inconvenientes, debe emplearse con cautela.
La mayoría de los lenguajes de programación dan soporte a la recursión permitiendo a una
función llamarse a sí misma desde el texto del programa. Los lenguajes imperativos definen las
estructuras de loops como while y for que son usadas para
realizar tareas repetitivas. Algunos lenguajes de
programación funcionales no definen estructuras de loops,
sino que posibilitan la recursión llamando código de forma
repetitiva. La teoría de la computabilidad ha demostrado que
estos dos tipos de lenguajes son matemáticamente
equivalentes, es decir que pueden resolver los mismos tipos
de problemas, aunque los lenguajes funcionales carezcan de
las típicas estructuras while y for.
Es una técnica utilizada en programación que nos permite que
un bloque de instrucciones se ejecute un cierto número de
veces (el que nosotros determinemos). A veces es algo
complicado de entender, pero no os preocupéis. Cuando
veamos los ejemplos estará clarísimo. En Java, como en otros
muchos lenguajes, los métodos pueden llamarse a sí mismos. Gracias a esto, podemos utilizar
a nuestro favor la recursividad en lugar de la iteración para resolver determinados tipos de
problemas.

Página 33 de 111
Unidad de Aprendizaje 1 Algoritmia

Ejemplo sencillo
Vamos a ver un pequeño ejemplo que no hace absolutamente nada. Es un método cuyo único
objetivo es llamarse a sí mismo:
void cuentaRegresiva () {
cuentaRegresiva();
}
Si ejecutáis esto, os va a dar un error en la pila (mítico StackOverflow Error, Biblia de los
programadores).
Como véis, he llamado al método cuentaRegresiva porque vamos a mostrar por pantalla la
cuenta atrás de un número que nosotros pasemos como parámetro a la función. Por ejemplo,
para hacer la cuenta atrás de 10 sin recursividad, haríamos:
for( int i = 10; i >= 0; i--) {
System.out.println(i);
}
Ahora, para hacerlo de manera recursiva, tendríamos que pasar como parámetro un número.
Además, tras imprimir ese número, llamaremos a la misma función con el número actual
restando uno:
void cuentaRegresiva(int numero) {
System.out.println(numero);
cuentaRegresiva(numero - 1);
}
Es lo que os he comentado arriba. Llamamos a la función con un 10. Imprimimos el 10 y
llamamos a la función con un 9. Imprimimos el 9 y llamamos a la función con un 8. Así hasta
el fin de los días. Digo hasta el fin de los días porque os va a saltar error si ejecutáis esto así
directamente:
public class Recursividad {

static void cuentaRegresiva(int numero) {


System.out.println(numero);
cuentaRegresiva(numero - 1);
}

public static void main(String[] args) {


cuentaRegresiva(10);
}

Página 34 de 111
Algoritmia Unidad de Aprendizaje 1
Problema: llamada infinita. Para ello, lo que tenemos que hacer es que cuando el número sea
0, deje de llamar a la función. Para eso, metemos una estructura condicional de toda la vida.
Gracias al condicional, dejará de ejecutarse a partir de 0:
void cuentaRegresiva(int numero) {
System.out.println(numero);
if(numero > 0) {
cuentaRegresiva(numero - 1);
}
}
Factorial
Calcular la factorial de un número con recursividad es el típico ejemplo para explicar este
método de programación. Recordad que la factorial de un número es multiplicar dicho número
por todos sus anteriores hasta llegar a 1. Se representa con una exclamación. Por ejemplo:
5! = 54321 = 120 El recorrer los números hacia atrás ya lo tenemos hecho. Ahora lo que queda
es multiplicar ese número por su anterior, y así sucesivamente:
int factorial(int n) {
if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
Si ya queréis simplificarlo al máximo y dejar a vuestros amigos con la boca abierta, haced esto:
int factorial(int n) {
return (n == 0) ? 1 : n * factorial(n - 1);
}
Espero que os haya servido el tutorial para aprender algo más sobre programación. Recordad
que esto está hecho en Java, pero vale para otros lenguajes.
Cómo funcionan los algoritmos recursivos
Un algoritmo recursivo es un algoritmo que expresa la solución de un problema en términos de
una llamada a sí mismo. La llamada a sí mismo se conoce como llamada recursiva o recurrente.
Generalmente, si la primera llamada al
subprograma se plantea sobre un problema de
tamaño u orden N, cada nueva ejecución
recurrente del mismo se planteará sobre
problemas, de igual naturaleza que el original,
pero de un tamaño menor que N. De esta
forma, al ir reduciendo progresivamente la
complejidad del problema que resolver, llegará
un momento en que su resolución sea más o
menos trivial (o, al menos, suficientemente

Página 35 de 111
Unidad de Aprendizaje 1 Algoritmia

manejable como para resolverlo de forma no recursiva). En esa situación diremos que estamos
ante un caso base de la recursividad.
Las claves para construir un subprograma recurrente son:
Cada llamada recurrente se debería definir sobre un
problema de menor complejidad (algo más fácil de
resolver).
Ha de existir al menos un caso base para evitar que la
recurrencia sea infinita.
Es frecuente que los algoritmos recurrentes sean más
ineficientes en tiempo que los iterativos, aunque suelen ser
mucho más breves en espacio.
Un método frecuente para simplificar es dividir un problema en
problemas derivados de menor tamaño del mismo tipo. Esto se
conoce como dialecting. Como técnica de programación se
denomina divide y vencerás y es pieza fundamental para el diseño de
muchos algoritmos de importancia, así como parte esencial de la
programación dinámica.
Virtualmente todos los lenguajes de programación modernos permiten la
especificación directa de funciones y subrutinas recursivas. Cuando se
llama una función de este tipo, el ordenador, para la mayoría de los lenguajes en casi todas las
arquitecturas basadas en una pila (stack) o en la implementación del lenguaje, lleva la cuenta
de las distintas instancias de la función, en numerosas arquitecturas
mediante el uso de un call stack, aunque no de forma exclusiva. A la
inversa, toda función recursiva puede transformarse en una función
iterativa usando un stack.
La mayoría (aunque no todas) de las funciones y subrutinas que
pueden ser evaluadas por un ordenador, pueden expresarse en
términos de una función recursiva (sin tener que utilizar una
iteración pura); a la inversa, cualquier función recursiva puede
expresarse en términos de una iteración pura, dado que la
recursión es, de por sí, también iterativa. Para evaluar una función
por medio de la recursión, tiene que definirse como una función
de sí misma (¡ej. el factor n! = n * (n - 1)! , donde 0! se define
como 1). Resulta evidente que no todas las evaluaciones de
funciones se prestan a un acercamiento recursivo. Por lo general,
todas las funciones finitas pueden describirse directamente de
forma recursiva; las funciones infinitas (ej. las series de e = 1/1!
+ 2/2! + 3/3!...) necesitan un criterio extra para detenerse, ej. el
número de iteraciones, o el número de dígitos significativos, en caso contrario una iteración
recursiva resultaría en un bucle infinito.
A modo de ilustración: Si se encuentra una palabra desconocida en un libro, el lector puede
anotar la página actual en un papel y ponerlo en una pila (hasta entonces vacía). El lector
consulta la palabra en otro artículo y, de nuevo, descubre otra palabra desconocida, la anota y
la pone en la pila, y así sucesivamente. Llega un momento que el lector lee un artículo que
donde todas las palabras son conocidas. El lector retorna entonces a la última página y continua
la lectura desde ahí, y así hasta que se retira la última nota de la pila retornando entonces al
libro original. Este modus operandi es recursivo.

Página 36 de 111
Algoritmia Unidad de Aprendizaje 1
Algunos lenguajes diseñados para programación lógica y programación funcional ofrecen la
recursión como el único medio de repetición directa disponible para el programador. Estos
lenguajes suelen conseguir que la recursión de cola sea tan eficiente como la iteración,
permitiendo a los programadores expresar otras estructuras repetitivas (tales como map y for
de scheme) en términos de recursión.
La recursión está profundamente anclada en la teoría de computación, con la equivalencia
teórica de función micro recursiva y máquinas de Turing en la cimentación de ideas sobre la
universalidad del ordenador moderno.
Programación recursiva
Crear una subrutina recursiva requiere principalmente la definición de un "caso base", y
entonces definir reglas para subdividir casos más complejos en el caso base. Para una subrutina
recursiva es esencial que, con cada llamada recursiva, el problema se reduzca de forma que al
final llegue al caso base.
Algunos expertos clasifican la recursión como "generativa" o bien "estructural". La distinción
se hace según de donde provengan los datos con los que trabaja la subrutina. Si los datos
proceden de una estructura de datos similar a una lista, entonces la subrutina es
"estructuralmente recursiva"; en caso contrario, es "generativamente recursiva".
Muchos algoritmos populares generan una nueva cantidad de datos a partir de los datos
aportados y recurren a partir de ahí. HTDP (How To Design Programs), al español, "Cómo
diseñar programas", se refiere a esta variante como recursión generativa. Ejemplos de recursión
generativa incluyen: máximo común divisor, quicksort, búsqueda binaria, mergesort, Método
de Newton, fractals e integración adaptiva.

Ejemplos de subrutinas definidas recursivamente (recursión generativa)


Factorial un ejemplo clásico de una subrutina recursiva es la función usada para calcular la
factorial de un entero.
Definición de la función:

Página 37 de 111
Unidad de Aprendizaje 1 Algoritmia

Una relación recurrente es una ecuación que relaciona términos posteriores en la secuencia con
términos previos.

Esta función factorial también puede describirse sin usar recursión haciendo uso de típicas
estructuras de bucle que se encuentran en lenguajes de programación imperativos:

Página 38 de 111
Algoritmia Unidad de Aprendizaje 1
El lenguaje de programación scheme es, sin embargo, un lenguaje de programación funcional
y no define estructuras de loops de cualquier tipo. Se basa únicamente en la recursión para
ejecutar todo tipo de loops. Dado que scheme es recursivo de cola, se puede definir una
subrutina recursiva que implementa la subrutina factorial como un proceso iterativo, es decir,
usa espacio constante, pero tiempo lineal.
Fibonacci
Otra popular secuencia recursiva es el Número de Fibonacci. Los primeros elementos de la
secuencia son: 0, 1, 1, 2, 3, 5, 8, 13, 21...

Este algoritmo de Fibonacci es especialmente malo pues cada vez que se ejecuta la función,
realizará dos llamadas a la función a sí misma, cada una de las cuales hará a la vez dos llamadas
más y así sucesivamente hasta que terminen en 0 o en 1. El ejemplo se denomina "recursión de
árbol", y sus requisitos de tiempo crecen de forma exponencial y los de espacio de forma lineal.

Página 39 de 111
Unidad de Aprendizaje 1 Algoritmia

Máximo común divisor


Otra famosa función recursiva es el algoritmo de Euclides, usado para computar el máximo
común divisor de dos enteros.

Relación recursiva del máximo común denominador, donde x\% y x\% y expresa el resto de la
división entera x/y

Página 40 de 111
Algoritmia Unidad de Aprendizaje 1
Nótese que el algoritmo "recursivo" mostrado arriba es, de hecho, únicamente de cola recursiva,
lo que significa que es equivalente a un algoritmo iterativo. En el ejemplo siguiente se muestra
el mismo algoritmo usando explícitamente iteración. No acumula una cadena de operaciones
deferred, sino que su estado es, más bien, mantenido completamente en las variables x e y. Su
"number of steps grows the as the logarithm of the numbers involved.", al español "número de
pasos crece a medida que lo hace el logaritmo de los números involucrados.

El algoritmo iterativo requiere una variable temporal, e incluso supuesto el conocimiento del
Algoritmo de Euclides es más difícil de entender el proceso a simple vista, aunque los dos
algoritmos son muy similares en sus pasos.
Torres de Hanói
Para una detallada discusión de la descripción de este problema, de su historia y de su solución,
consúltese el artículo principal. El problema, puesto de forma simple, es el siguiente: Dadas
pilas, una con un conjunto de N discos de tamaño creciente, determina el mínimo (óptimo)
número de pasos que lleva mover todos los discos desde su posición inicial a otra pila sin
colocar un disco de mayor tamaño sobre uno de menor tamaño.

Ejemplos de implementación:

Página 41 de 111
Unidad de Aprendizaje 1 Algoritmia

Aunque no todas las funciones recursivas tienen una solución explícita, la secuencia de la Torre
de Hanói puede reducirse a una fórmula explícita.

Búsqueda binaria
El algoritmo de búsqueda binaria es un método de búsqueda de un dato en un vector de datos
ordenado dividiendo el vector en dos tras cada pasada. El truco es escoger un punto cerca del
centro del vector, comparar en ese punto el dato con el dato buscado para responder entonces a
una de las siguientes 3 condiciones: se encuentra el dato buscado, el dato en el punto medio es
mayor que el valor buscado o el dato en el punto medio es menor que el valor buscado.
Se usa recursión en este algoritmo porque tras cada pasada se crea un nuevo vector dividiendo
en original en dos. La subrutina de búsqueda binaria se llama entonces de forma recursiva, cada
vez con un vector de menor tamaño. El tamaño del vector se ajusta normalmente cambiando el
índice inicial y final. El algoritmo muestra un orden logaritmo de crecimiento porque divide
esencialmente el dominio del problema en dos tras cada pasada.

Página 42 de 111
Algoritmia Unidad de Aprendizaje 1

Ejemplo de implementación de la búsqueda binaria:

Método de caja.
Los dos métodos más importantes para cifrar un mensaje son el de transposición y el de sustitución.
En el primero las letras del mensaje original permanecen intactas y lo que se cambia es el orden en
el que estas aparecen. En cambio, en el método de sustitución las letras son reemplazadas por
números, letras o signos cualesquiera, dejando el
orden original de las letras intacto. Este último método
también se conoce como codificación. Ambos
métodos pueden ser utilizados en un mismo sistema
de cifrado, una o varias veces, con el fin de hacer más
difícil el descifrado.
En este tema vamos a aprender a utilizar un sencillo
método de transposición conocido como el "método de las cajas” y que fue utilizado hasta finales de
la Segunda Guerra Mundial por los servicios de inteligencia de diferentes países.
Como en todo sistema de encriptación, tanto el que envía el mensaje, como el que lo recibe, deben
conocer la "clave".
En este método la clave consiste, en una palabra. Vamos a describir su funcionamiento mediante un
ejemplo práctico.

Página 43 de 111
Unidad de Aprendizaje 1 Algoritmia

Ejemplo
Supongamos que la clave secreta es "sangakoo" y que el mensaje que queremos enviar es
EL PRÓXIMO LUNES HAY EXAMEN DE MATEMÁTICAS.
Lo primero que debemos hacer es abrir una tabla y escribir en la primera fila la palabra clave.

S A N G A K O O

A continuación, debemos numerar las letras según el orden en que aparecen en el alfabeto.

S A N G A K O O
8 1 5 3 2 4 6 7

Si la letra aparece repetida le ponemos números consecutivos. Por ejemplo, la A es la primera letra,
pero como hay dos, le ponemos a la primera un 1 y a la segunda un 2. La siguiente letra del alfabeto
que aparece es la G, luego la K y así sucesivamente.

A continuación, en la siguiente fila, escribimos el mensaje que queremos enviar, todo seguido, sin
espacios.

S A N G A K O O
8 1 5 3 2 4 6 7
e l p r o x i m
o l u n e s h a
y e x a m e n d
e m a t e m a t
i c a s

Página 44 de 111
Algoritmia Unidad de Aprendizaje 1
Ahora ya tenemos construida la caja que nos va a servir para codificar el mensaje. Lo que debemos
hacer ahora es escribir todos los grupos de letras que aparecen bajo cada número. Y hacerlo
ordenadamente. Empezamos por el grupo que está debajo del 1
llemc
luego la que está debajo del 2
oeme
y así hasta completar todas las columnas. El texto cifrado completo sería pues:
llemc oeme rnats xsem puxaa ihha madt eoyei
Veamos ahora un ejemplo de descifrado.
Ejemplo
Supongamos que el mensaje que recibimos es
ELDAA AILT SDAE TOACR HVOR
y la palabra clave
PATIO
Para descifrar el mensaje primero colocamos la palabra clave en la primera fila de la tabla.

P A T I O

A continuación, numeramos las columnas tal y cómo hemos explicado antes.

P A T I O
4 1 5 2 3

Página 45 de 111
Unidad de Aprendizaje 1 Algoritmia

Luego escribimos los grupos de palabras siguiendo el orden de las columnas.

P A T I O
4 1 5 2 3
T E H A S
O L V I D
A D O L A
C A R T E
R A
Ahora ya tenemos descifrado el mensaje. Está escrito en las filas:

TE HAS OLVIDADO LA CARTERA


Y ya tenemos el mensaje descifrado.
Este tipo de cifrado admite ciertos niveles de complejidad, ya que el proceso se puede repetir varias
veces, con distintas palabras clave, para hacer más difícil el criptoanálisis.
La Criptografía es la ciencia que se dedica al estudio de las claves, concretamente a los algoritmos
de encriptación y desencriptación de mensajes.
Una vez asimilados estos contenidos puedes proponer dos tipos de ejercicios:
1. Dar un mensaje en clave y la clave para descifrarlo.
2. Cifrar un mensaje concreto dando para ello la clave.
Es aconsejable que primero se haga con papel y lápiz, de esta manera cuando generemos la tabla en
el editor de textos sabremos el número de filas y columnas que vamos a necesitar.

Página 46 de 111
Algoritmia Unidad de Aprendizaje 1

RESUMEN UNIDAD 1

Un algoritmo es un conjunto de instrucciones ordenadas, finitas y delimitadas que se crean con


el fin de describir de forma sistemática la ejecución de una tarea. Los algoritmos son de uso
común en el día a día, y se pueden encontrar en manuales de uso, instrucciones para ejecutar
un plan, o guías para ejecutar procesos. La diferencia entre un algoritmo y un programa, es que,
si bien ambos hacen referencia una serie de instrucciones, los algoritmos pueden estar escritos
en código o en lenguaje natural, mientras que los programas sólo pueden estar escritos en
lenguaje de programación. Los algoritmos son utilizados en el mundo de la ciencia para la
resolución metódica de problemas. Los algoritmos no siempre están escritos de una forma que
conduce al programa más efectivo en términos de requisitos de tiempo o almacenamiento.
Las estructuras de datos es una colección datos que pueden ser caracterizados por su
organización y las operaciones que se definen en ella. Ejemplo de Estructura de datos: Arreglos,
Archivos, Cadenas y Listas.
El diseño del algoritmo, es definidos exactamente los procesos tendremos que programar y
conocidos los datos que estarán siendo entregados y la información que debemos generar como
resultado del proceso, estamos en condiciones de diseñar nuestra solución.
Diagrama de flujo es una metodología gráfica que permite construir visualmente el recorrido
del flujo de un programa y estructurar el algoritmo para una solución determinada.
El pseudocódigo es una forma de expresar los distintos pasos que va a realizar un programa, de
la forma más parecida a un lenguaje de programación. Su principal función es la de representar
por pasos la solución a un problema o algoritmo, de la forma más detallada posible, utilizando
un lenguaje cercano al de programación. El pseudocódigo no puede ejecutarse en un
computador ya que entonces dejaría de ser pseudocódigo, como su propio nombre indica, se
trata de un código falso (pseudo = falso), es un código escrito para que lo entienda el ser humano
y no la máquina.
Las estructuras condicionales comparan una variable contra otro(s) valor(es), para que, en base
al resultado de esta comparación, se siga un curso de acción dentro del programa. Cabe
mencionar que la comparación se puede hacer contra otra variable o contra una constante, según
se necesite. Existen dos tipos básicos, las simples y las múltiples.
Las estructuras selectivas se utilizan para tomar decisiones lógicas; de ahí que también se
conocen como estructuras de decisión o alternativas. En las estructuras selectivas se evalúa una
condición, y en función del resultado de la misma se toma un camino u otro. Pueden ser simples,
dobles, compuestas y múltiples.
Uno de los métodos más conocidos para resolver un problema es dividirlo en problemas más
pequeños, llamados subproblemas. De esta manera, en lugar de resolver una tarea compleja y
tediosa, resolvemos otras más sencillas y a partir de ellas llegamos a la solución. Esta técnica
se usa mucho en programación ya que programar no es más que resolver problemas, y se le
suele llamar diseño descendente, metodología del divide y vencerás o programación top-down.
Estructura Para (for), es la estructura algorítmica adecuada para utilizar en un ciclo que se
ejecutará un número definido de veces. Este tipo de estructura está presente en todos los
lenguajes de programación, ya sean estructurados u orientados a objetos.
La estructura repetitiva mientras (en inglés while), es aquella en que el cuerpo del bucle se
repite mientras se cumple una determinada condición. Cuando se ejecuta la instrucción
mientras, la primera cosa que sucede es evaluar la condición (una expresión booleana). Si la
condición se evalúa falsa, no se entra al ciclo y se sigue con el flujo normal del problema. Si la
condición es verdadera, entonces se entra al ciclo y se ejecuta el cuerpo del bucle (instrucciones

Página 47 de 111
Unidad de Aprendizaje 1 Algoritmia

dentro del mientras), después se evalúa de nuevo la expresión booleana. Este proceso se repite
una y otra vez mientras la condición sea verdadera.
Los procesos repetitivos son la base del uso de los computadores. En estos procesos se necesita
normalmente contar los sucesos o acciones internas del bucle. Una manera de controlar un bucle
es mediante un contador. Un contador es una variable cuyo valor se incrementa o decrece en
una cantidad constante en cada iteración.
Un acumulador o totalizador es una variable cuya misión es almacenar cantidades variables
resultantes de sumas sucesivas. Realiza la misma función que un contador, con la diferencia de
que el incremento o decremento de cada suma es variable en lugar de constante, como en el
caso del contador. Se representa por la instrucción S=S+N, donde N es una variable y no una
constante.
Una función recursiva es una función que se llama a sí misma. Esto es, dentro del cuerpo de la
función se incluyen llamadas a la propia función. Esta estrategia es una alternativa al uso de
bucles. Un algoritmo recursivo es un algoritmo que expresa la solución de un problema en
términos de una llamada a sí mismo. La llamada a sí mismo se conoce como llamada recursiva
o recurrente. Crear una subrutina recursiva requiere principalmente la definición de un "caso
base", y entonces definir reglas para subdividir casos más complejos en el caso base. Para una
subrutina recursiva es esencial que, con cada llamada recursiva, el problema se reduzca de
forma que al final llegue al caso base.
El Método de caja, es un método de encriptamiento, de ahí son dos métodos más importantes
para cifrar un mensaje son el de transposición y el de sustitución. En el primero las letras del
mensaje original permanecen intactas y lo que se cambia es el orden en el que estas aparecen.
En cambio, en el método de sustitución las letras son reemplazadas por números, letras o signos
cualesquiera, dejando el orden original de las letras intacto. Este último método también se
conoce como codificación. Ambos métodos pueden ser utilizados en un mismo sistema de
cifrado, una o varias veces, con el fin de hacer más difícil el descifrado.

Lecturas Recomendadas
• http://www.edu4java.com/es/conceptos/que-es-un-algoritmo-que-es-un-programa.html
• https://es.wikibooks.org/wiki/Fundamentos_de_programaci%C3%B3n/Algoritmos_y_
programas
• http://biolab.uspceu.com/aotero/recursos/docencia/TEMA%207.pdf
• https://picandocodigo.net/2008/recursividad-en-programacion/

Actividades y/o Ejercicios

• Resolver algoritmos simples de suma, resta, multiplicación y división, utilizando el


programa DFD
• Resolver algoritmos simples con decisiones, utilizando el programa DFD
• Resolver algoritmos simples con recursiva for, utilizando el programa DFD
• Resolver algoritmos simples con recursiva While, utilizando el programa DFD

Página 48 de 111
PRESENTACIÓN Y CONTEXTUALIZACIÓN
Cuando hablamos de programación, la estructura de datos está representada por una forma
determinada que tenemos de organizar los datos de un equipo informático para que podamos
utilizarlos de la manera más efectiva posible. Dependiendo del tipo de aplicación o recurso que
vayamos a usar requeriremos una estructura de datos independiente y distinta a las demás, dado
que cada una encaja en el contexto de forma determinada y con una serie de objetivos. Con
estas estructuras tenemos la posibilidad de administrar todo tipo de datos sin ningún tipo de
obstáculo, algo que en la actualidad se usa en la red para poder llevar a cabo, por ejemplo, los
sistemas de indexado de contenidos. Y también juegan un papel clave en la creación de los
mejores algoritmos, así como en su uso con lenguajes de programación que se benefician de
ellas. Piensa en ellas como una forma de representar información. Así como usamos una
variable de tipo array para representar un número finito de elementos, podemos representar una
lista en una estructura de datos de tipo lista enlazada, esta estructura puede ser creada por
nosotros o provista por una librería. Las estructuras de datos no solo representan la información,
también tienen un comportamiento interno, y se rige por ciertas reglas/restricciones dadas por
la forma en que esta construida internamente. Las estructuras de datos nos permiten resolver un
problema de manera más sencilla gracias a que las reglas que las rigen nunca cambian, así que
puedes asumir que ciertas cosas son siempre ciertas. Adicionalmente son dinámicas, si usas
Estructura De Datos

lenguajes de programación como Java, sabrás que necesitas definir el tamaño de los arrays antes
de ser usados. Usando una estructura de datos, puedes hacer “un array” de tamaño
indeterminado.
COMPETENCIA
Construye algoritmos para resolver problemas basados en estructuras de datos estáticas.
CAPACIDADES
• Aplica las operaciones de suma, promedio, valor máximo y mínimo de los valores cargados
en el arreglo unidimensional.
• Identifica y desarrolla habilidades básicas para la recolección de datos. Insertar, Modificar,
Eliminar y Buscar elementos dentro de un arreglo.
• Identifica y desarrolla habilidades complejas para unir dos arreglos unidimensionales.
• Desarrolla soluciones utilizando recursividad y reconoce las ventajas y desventajas de su
uso.
ACTITUDES
• Desarrolla una actitud emprendedora mediante la toma de iniciativas.
• Actúa con responsabilidad personal, al cumplir con los horarios establecidos.
• Respeto a las normas de convivencia.
• Cumple con la presentación de los trabajos encomendados de manera individual y en equipo.
La Unidad de Aprendizaje 02: Estructura De Datos
Comprende el desarrollo de los siguientes temas:
Definición
Cuando hablamos de programación, la estructura de datos está representada por una forma
determinada, que tenemos de organizar los datos de un equipo informático para que podamos
utilizarlos de la manera más efectiva posible. Dependiendo del tipo de
aplicación o recurso que vayamos a usar requeriremos una estructura
de datos independiente y distinta a las demás, dado que cada una
encaja en el contexto de forma determinada y con una serie de
objetivos.
Con estas estructuras tenemos la posibilidad de administrar todo tipo
de datos sin ningún tipo de obstáculo, algo que en la actualidad
se usa en la red para poder llevar a cabo, por ejemplo, los
sistemas de indexado de contenidos. Y también juegan un
papel clave en la creación de los mejores algoritmos, así
como en su uso con lenguajes de programación que se
benefician de ellas.
Dependiendo de su finalidad hay distintos tipos de estructura de datos que pueden utilizarse y
que aportan unas características determinadas en cada uno de los casos.
Uno de los tipos más utilizados es el registro, un tipo de estructura que reúne datos que han sido
agregados. Con este tipo de estructura lo que hacemos es unificar un valor con otra serie de
valores relacionados formando una secuencia.
También existe el vector, que concentra elementos dando lugar a una estructura ordenada y
relacionada. Sus datos coinciden en formar parte de un tipo concreto y en estar colocados en un
orden determinado. Para una mejor facilidad de uso se aplican palabras de memoria que ayudan
a su organización, mientras que también hay que tener en cuenta que es factible que los arreglos
puedan sufrir cambios de tamaño. De forma derivada el vector asociativo permite que sean
eliminados pares nombre-valor dependiendo de las exigencias del programador y del contexto.
Otro tipo de estructura es la que está representada por la unión, en la cual se
unen distintos datos, pero diferenciándose del registro debido a que solo se
da cobijo a un valor determinado. En el caso de requerir un campo añadido
dentro del registro, se utiliza el término de tipo variante.
La flexibilidad de las estructuras de datos permite su aprovechamiento
de formas muy variadas. Un buen reflejo de ello lo aporta el grafo, que
se trata de una versión en la cual los datos están conectados debido a
la presencia de nodos. Y todos estos nodos no solo disponen de un
valor determinado que les ha sido asignado, sino que cuentan con
vínculos con otros de los nodos. La facilidad en la comunicación de
estos nodos abre muchas posibilidades y es por lo que se usan en la
representación de redes. También lo facilita que a la hora de crear esta estructura se puedan
definir puntos de inicio y de final mediante la elección de nodos exactos con los que se da la
oportunidad de marcar la dirección a seguir en el proceso de trabajo.
Hay una variante específica que recibe el nombre de árbol y que se representa porque el camino
parte de un punto inicial que deriva hacia todos los demás nodos. Este tipo de grafo
no da la oportunidad de crear ciclos y también se puede agrupar en conjunto, lo
que da lugar al término bosque.
Su relación con los lenguajes, comprender la utilidad de la estructura de datos
está relacionado con saber también qué tipo de relación tienen con los lenguajes
de programación. En este sentido hay que dejar totalmente de lado los
lenguajes de bajo nivel, dado que no tienen soporte para estas estructuras.
Los que sí la tienen son los lenguajes de alto nivel, además de algunas
excepciones que no resultan trascendentes. Una buena demostración de
este tipo de relación entre estructuras y lenguaje la podemos ver en dos
lenguajes muy específicos que se utilizan en la actualidad y que siguen
siendo de gran rendimiento: Pascal y C. Este tipo de soporte siempre ha ayudado a los
programadores y con el paso del tiempo las propias plataformas que acompañan a los lenguajes
modernos han ido dando soporte a estas estructuras de manera interna, permitiendo así que
sigan presentes para aprovecharlas en sus respectivos contextos.
¿Por qué son útiles las estructuras de datos?
Lo primero que debemos tener claro es la definición de “estructura de datos” y que a partir de
esta definición parten otros conceptos y tipos. Que las estructuras de datos se estudian como
conceptos sobre programación, y no sobre un lenguaje de programación en específico, por lo
que cada lenguaje puede tener diferentes implementaciones de estructuras de datos.
Una “estructura de datos” es una colección de valores, la relación que existe
entre estos valores y las operaciones que podemos hacer sobre ellos; en pocas
palabras se refiere a cómo los datos están organizados y cómo se pueden
administrar. Una estructura de datos describe el formato en que los valores
van a ser almacenados, cómo van a ser accedidos y modificados,
pudiendo así existir una gran cantidad de estructuras de datos.
Lo que hace específica a una estructura de datos es el tipo de problema
que resuelve. Algunas veces necesitaremos una estructura muy simple
que sólo permita almacenar 10 números enteros consecutivamente sin importar que se repitan
y que podamos acceder a estos números por medio de un índice, porque nuestro problema a
resolver está basado en 10 números enteros solamente. O tal vez nos interese almacenar N
cantidad de números enteros y que se puedan ordenar al momento de insertar uno nuevo, por lo
que necesitaremos una estructura más flexible.
Las estructuras de datos son útiles porque siempre manipularemos datos, y si los datos están
organizados, esta tarea será mucho más fácil.
Considera el siguiente problema:
¿De qué forma puedo saber si una palabra tiene exactamente las mismas letras que otra palabra?
o, dicho de otra forma, ¿cómo saber si una palabra es una permutación de otra palabra?
Si queremos resolver este problema con papel y
lápiz, basta con escribir las palabras y
visualmente identificar si ambas palabras tienen
las mismas letras, una forma rápida es contar
mentalmente el número de cada tipo de letra en
ambas palabras y si tienen el mismo número
entonces si es una permutación.
Pero si quisiéramos resolverlo mediante programación tenemos que decirle a la computadora
como saber que es una palabra y decirle cómo debe comparar las palabras de la misma forma
en que lo resolvimos en papel y lápiz, ¿Se te ocurre alguna forma?
Qué tal si pensamos en las palabras como una estructura de casillas de letras secuenciales (array
ó arreglo) donde cada letra representa una casilla en esta estructura. A cada casilla le damos un
índice con el cual podemos acceder a ella para saber qué letra tiene contenida, esto le indicará
a la computadora cómo reconocer una palabra.

0 1 2 3 0 1 2 3
C A S A S A C A
Ahora, para resolver el problema debemos saber cómo comparar las palabras y sus letras, lo
primero que podemos evaluar es el length ó longitud de la palabra, o
sea, cuántos caracteres tiene; si las palabras tienen diferente número
de caracteres entonces no tiene sentido comparar más, ya sabemos
que NO tendrán exactamente las mismas letras. Lo siguiente será
contar las letras por tipo de letra. En el ejemplo anterior de las
palabras “casa” y “saca” contamos las letras mentalmente, pero
las escribimos en el papel con una estructura muy específica,
cada letra que encontramos se asocia al número que
contamos; entonces, ¿habrá alguna estructura de datos que
me ayude a asociar las letras con el número que
contamos?, la respuesta es sí. Podemos pensar en esta
asociación como una estructura de tipo key-value (llave-
valor) llamada dictionary ó diccionario, donde la letra es
la llave y su valor asociado es el número contado;
además, en esta estructura no debemos repetir la llave
porque de lo contrario tendremos letras repetidas y no
podremos contar, es por eso que la estructura dictionary es distinta al array, ya que en el array
si podemos repetir letras, pero en esta otra estructura no deberíamos permitirlo ya que eso sería
lo que nos permita resolver el problema de contar.

KEY VALUE KEY VALUE


C 1 S 1
A 2 A 2
S 1 C 1
Estructura de dato map
De modo que para operar la estructura array tenemos que hacerlo recorriendo una a una todas
las casillas para obtener las letras mediante su índice; la casilla 0 contiene a la letra “c”, la
casilla 1 contiene la letra “a” y así sucesivamente. Para el caso de la estructura dictionary
podemos decir que vamos a operar en ella a través de su key (llave); la llave “c” tiene asociado
el valor 1, la llave “a” tiene asociado el valor 2, y así sucesivamente. Cada vez que encontremos
una letra la pondremos en la estructura dictionary, si no existe se agrega y se asocia con el valor
1, sí ya existe entonces “contamos” y a su valor le sumamos +1.
Hasta este punto las estructuras de datos nos han ayudado a resolver este problema con el simple
hecho de tener nuestros valores organizados y definiendo la forma en la que podemos operar
sobre ellos; aún quedan varios pasos más para resolver el problema por completo, pero eso pasa
a ser objeto de otro tema en programación que son los algoritmos. Por ahora nos basta con
identificar las estructuras de datos.
Tipos de estructuras de datos
Al hablar de estructuras de datos debemos pensar en primera instancia en cómo los datos se
representan en la memoria, ¿se trata de estructuras contiguas o enlazadas?, al partir de esta
pregunta podemos darnos la idea correcta sobre la base de nuestra estructura y cómo es que los
datos se van a almacenar. Las estructuras contiguamente asignadas están compuestas de bloques
de memoria únicos, e incluyen a los arrays,
matrices, heaps, y hash tables.
Las estructuras enlazadas están compuestas de
distintos fragmentos de memoria unidos por
pointers o punteros, e incluyen a los lists, trees, y
graphs.
Los contenedores son estructuras que permiten
almacenar y recuperar datos en un orden
determinado sin importar su contenido, en esta se
incluyen los stacks y queues.
Cuando hablamos de programación, la estructura
de datos está representada por una forma
determinada que tenemos de organizar los datos de un equipo informático para que podamos
utilizarlos de la manera más efectiva posible. Dependiendo del tipo de aplicación o recurso que
vayamos a usar requeriremos una estructura de datos independiente y distinta a las demás, dado
que cada una encaja en el contexto de forma determinada y con una serie de objetivos.
Con estas estructuras tenemos la posibilidad de administrar todo tipo de datos sin ningún tipo
de obstáculo, algo que en la actualidad se usa en la red para poder llevar a cabo, por ejemplo,
los sistemas de indexado de contenidos. Y también juegan un papel clave en la creación de los
mejores algoritmos, así como en su uso con lenguajes de programación que se benefician de
ellas.
Clasificación
Las Estructuras de datos pueden clasificarse en lineales y no lineales. Una Estructura de datos
es lineal si sus elementos forman una secuencia o, en otras palabras, una lista lineal.
Arrays
La estructura de datos más simple es el array lineal (o unidimensional). Un array lineal es una
lista de números finitos de datos similares, referenciados por medio de un conjunto de n
números consecutivos, normalmente 1,2,3, …, n.
Pila
Una pila, también denominada sistema último-dentro primero-fuera (LIFO), es una lista lineal
en la cual las inserciones y extracciones tienen lugar sólo por un extremo llamado cúspide.
Cola
Una cola, también denominada sistema primero-dentro primero-fuera (FIFO), es una lista lineal
en la cual las extracciones se realizan siempre por un extremo llamado frente y las inserciones
por el extremo contrario llamado final de la lista.
Grafos
Los datos contienen, en algunos casos, relaciones entre ellos que no es necesariamente
jerárquica. Por ejemplo, supongamos que unas líneas aéreas realizan vuelos sólo entre ciudades
conectadas por líneas. La estructura de datos que refleja esta relación recibe el nombre de grafo.
Operaciones con estructuras de datos
Recorrido
Implica el acceder a cada registro una única vez, aunque uno o más ítems del registro sean
procesados. (Este acceso o procesamiento también se denomina a veces por el término «visitar»
el registro).
Búsqueda
Implica la localización de un registro caracterizado por una determinada clave o también el
acceso a todos los registros que cumplan una o más condiciones.
Inserción
Cuando añadimos nuevos registros a la estructura.
Eliminación
Operación de borrado de un registro de la estructura.
Ordenación
Es la operación de clasificar los registros conforme a un orden lógico determinado (por ejemplo,
alfabéticamente, de acuerdo a una clave de nombre, o numérica, de acuerdo a alguna clave de
número, tal como número de Seguridad Social o de inventario).
Mezcla
Es la operación de combinar dos archivos previamente ordenados en uno único que también lo
está.
Array
Esta estructura es “la” fundamental de las estructuras contiguamente asignadas. Arrays ó
arreglos son estructuras de datos de tamaño fijo de modo que cada elemento puede ser
eficientemente ubicado por su index (índice) ó dirección. Exactamente como lo vimos en el
ejemplo anterior un array tiene un tamaño fijo y una dirección (index) con la cual podemos
localizar de forma rápida un elemento en el arreglo, de modo que podemos apuntar a un
elemento en el array de la siguiente forma: array[index], donde array es el nombre de nuestra
estructura, seguida de dos “corchetes” que abren y cierran, donde especificamos el index que
deseamos apuntar.

Los Array muestran algunas ventajas con respecto a otras estructuras, como, por ejemplo:
Al tener un espacio contiguo en memoria cada index de cada elemento del array apunta
directamente a una dirección de memoria, de esta forma podemos acceder arbitrariamente a los
datos de forma instantánea puesto que sabemos la
dirección de memoria exacta. Esto deriva en un
acceso de tiempo constante dado por los index.
Los Array son puramente datos lo que significa
que no es necesario desperdiciar espacio en
memoria almacenando información extra que
ayude a la localización de sus elementos como es
el caso de las estructuras enlazadas, los Array
tienen eficiencia de espacio.
Localidad de memoria, es común que en la
programación los datos de una estructura sean
iterados (o recorridos) y los arráis son buenos para
esto ya que exhiben excelente localización de
memoria, permitiéndonos aprovechar la alta velocidad de la caché en computadoras modernas.
La gran desventaja de los Arrays es que no podemos ajustar
su tamaño a la mitad de la ejecución de un programa, pero ¿y
qué tal si creamos uno nuevo con la nueva dimensión
deseada?; esto sería bueno si supiéramos el tamaño que
deseamos todo el tiempo, pero si no lo sabemos sólo tenemos
2 opciones: crear una array lo suficientemente grande para
almacenar nuestros datos, pero esto deriva en un desperdicio
de memoria totalmente innecesario, o podemos crear un
nuevo array, doblar el tamaño de éste cada vez que se
necesite crecer y copiar los datos del array anterior al nuevo
array, hacer esto tiene el mismo nivel de complejidad que si
tuviéramos un array único suficientemente grande, pero con
la ventaja de que sólo va a crecer cuándo sea necesario,
evitando así desperdiciar memoria. Es pues que de esta forma podemos alargar un array en
tiempo de ejecución, a este concepto se le conoce como dynamic arrays ó arreglos dinámicos.
Los Arrays son la base de muchos otros tipos de estructuras de datos
como acabamos de ver con los arreglos dinámicos.
Otra estructura muy común que nos ayuda a representar datos reales
son las llamadas matrices o tablas que no son más que Arrays de
múltiples dimensiones. Pensemos, si un arreglo es una colección
consecutiva de valores, se puede decir que son de tipo lineal en una
dimensión plana o unidimensionales, si quisiéramos formar una
matriz o tabla (matrix o table) podríamos crear un array
bidimensional, lo que quiere decir que tendremos que localizar a un
elemento por su index [i , j], o bien su ubicación lineal a la derecha y
hacia abajo como en un plano cartesiano con dimensiones xy
asemejando a una tabla, o podríamos crear un array tridimensional, agregando una dimensión
más de “fondo”, que haría que localizáramos a un elemento en su index [i, j, k], en plano
cartesiano como xyz. Además, existe una tercera clasificación conocida como jagged arrays o
matrices dentadas como se ve en la siguiente imagen, donde vemos un arreglo de tipo
bidimensional, pero con la diferencia de que una de sus dimensiones no es rectangular, sino que
puede tener distintos tamaños formando así una matriz dentada.
Puedo decir que este es un resumen sobre los Arrays, uno que sin duda nos ayudará a identificar
de forma más rápida el tipo de “array” que necesitamos usar.

Estructuras enlazadas (linked structures)


La magia de las estructuras enlazadas es dada por los pointers o punteros, que como su nombre
lo indica apuntan a una dirección de memoria donde se encuentra ubicado un valor. Los pointers
son los encargados de mantener los “enlaces” entre valores de modo que es posible tener una
secuencia de valores todos enlazados por pointers. Si ponemos atención a la definición de los
pointers podemos deducir que los valores no necesariamente tienen que estar localizados en
memoria uno después del otro como en el caso de los Arrays que son contiguamente asignados,
por lo que podemos tener los valores ubicados en distintas localidades de la memoria y aun así
representar una colección de valores consecutivos.

Como se observa en la imagen, tenemos una secuencia de valores enlazados por pointers
llamados nodos, que están representados por un cuadro vacío y una flecha. El cuadro vacío
representa el valor con la dirección de memoria a donde está apuntando el valor que contiene,
de modo que: el valor 12 está enlazado por medio de un pointer que guarda la ubicación del
siguiente valor en la secuencia que es el 19 y este a su vez guarda la ubicación al valor 37.
Cuando hablemos de estructuras enlazadas debemos siempre pensar en que además de
almacenar el valor que deseamos, debemos tener un espacio extra donde debemos almacenar la
dirección de memoria del siguiente valor.
Después de una breve introducción a los pointers podemos pasar a la estructura más común de
las estructuras enlazadas que son las linked lists o listas enlazadas.
Ejemplos
Declarar y crear un array, para declarar un array se escribe
tipo_de_dato[] nombre_del_array;
Para declarar un array de enteros escribimos
int[] numeros;
Para crear un array de 4 número enteros escribimos
numeros=new int[4];
La declaración y la creación del array se puede
hacer en una misma línea.
int[] numeros =new int[4];
Inicializar y usar los elementos del array, para inicializar el array de 4 enteros escribimos

numeros[0]=2;
numeros[1]=-4;
numeros[2]=15;
numeros[3]=-25;

Se pueden inicializar en un bucle for como resultado de alguna operación


for(int i=0; i<4; i++){
numeros[i]=i*i+4;
}
No necesitamos recordar el número de elementos del array, su miembro dato length nos
proporciona la dimensión del array. Escribimos de forma equivalente
for(int i=0; i<numeros.length; i++){
numeros[i]=i*i+4;
}
Los arrays se pueden declarar, crear e inicializar en una misma línea, del siguiente modo
int[] numeros={2, -4, 15, -25};
String[] nombres={"Juan", "José", "Miguel", "Antonio"};
Para imprimir a los elementos de array nombres se escribe
for(int i=0; i<nombres.length; i++){
System.out.println(nombres[i]);
}
Java verifica que el índice no sea mayor o igual que la dimensión del array, lo que facilita
mucho el trabajo al programador.
Para crear un array de tres objetos de la clase Rectangulo se escribe, declarar
Rectangulo[] rectangulos;
Crear el array
rectangulos=new Rectangulo[3];
Inicializar los elementos del array
rectangulos[0]=new Rectangulo(10, 20, 30, 40);
rectangulos[1]=new Rectangulo(30, 40);
rectangulos[2]=new Rectangulo(50, 80);
O bien, en una sola línea
Rectangulo[] rectangulos={new Rectangulo(10, 20, 30,
40), new Rectangulo(30, 40), new Rectangulo(50, 80)};
Usar el array
Para calcular y mostrar el área de los rectángulos escribimos
for(int i=0; i<rectangulos.length; i++){
System.out.println(rectangulos[i].calcularArea());
}
Arrays multidimensionales
Una matriz bidimensional puede tener varias filas, y en cada fila no tiene por qué haber el
mismo número de elementos o columnas. Por ejemplo, podemos declarar e inicializar la
siguiente matriz bidimensional
double[][] matriz={{1,2,3,4},{5,6},{7,8,9,10,11,12},{13}};
• La primera fila tiene cuatro elementos {1,2,3,4}
• La segunda fila tiene dos elementos {5,6}
• La tercera fila tiene seis elementos {7,8,9,10,11,12}
• La cuarta fila tiene un elemento {13}
Para mostrar los elementos de este array bidimensional escribimos el siguiente código
for (int i=0; i < matriz.length; i++) {
for (int j=0; j < matriz[i].length; j++) {
System.out.print(matriz[i][j]+"\t");
}
System.out.println("");
}
Como podemos apreciar, matriz.length nos proporciona el número de filas (cuatro), y
matriz[i].length, nos proporciona el número de elementos en cada fila.
Mostramos los elementos de una fila separados por un tabulador usando la función print. Una
vez completada una fila se pasa a la siguiente mediante println.
Los Arrays bidimensionales nos permiten guardar los elementos de una matriz. Queremos crear
y mostrar una matriz cuadrada unidad de dimensión 4. Recordaremos que una matriz unidad es
aquella cuyos elementos son ceros excepto los de la diagonal principal i==j, que son unos.
Mediante un doble bucle for recorremos los elementos de la matriz especificando su fila i y su
columna j. En el siguiente programa
Se crea una matriz cuadrada de dimensión cuatro
Se inicializa los elementos de la matriz (matriz unidad)
Se muestra la matriz una fila debajo de la otra separando los elementos de una fila por
tabuladores.
public class MatrizUnidadApp {
public static void main (String[] args) {
double[][] mUnidad= new double[4][4];

for (int i=0; i < mUnidad.length; i++) {


for (int j=0; j < mUnidad[i].length; j++) {
if (i == j) {
mUnidad[i][j]=1.0;
}else {
mUnidad[i][j] = 0.0;
}
}
}

for (int i=0; i < mUnidad.length; i++) {


for (int j=0; j < mUnidad[i].length; j++) {
System.out.print(mUnidad[i][j]+"\t");
}
System.out.println("");
}
}
}
Un ejemplo del uso de break con etiqueta y Arrays multidimensionales
int[][] matriz={ {32, 87, 3, 589},
{12, -30, 190, 0},
{622, 127, 981, -3, -5}};
int numero=12;
int i=0, j=0;
buscado:
for(i=0; i<matriz.length; i++){
for(j=0; j<matriz[i].length; j++){
if(matriz[i][j]==numero){
break buscado;
}
}
}
System.out.println("buscado: matriz("+ i+",
"+j+")="+matriz[i][j]);
Listas simples
Linked lists
Comencemos por definir una “lista”. Una lista es aquella estructura que representa un número
contable de valores ordenados donde un mismo valor puede repetirse y considerarse un valor
distinto a otro ya existente. Las listas son
consideradas secuencias de valores y cómo
ya veíamos en la explicación anterior sobre
pointers estos se pueden aplicar para
implementar una lista de tal forma que las
características principales de una lista serían:
• Cada nodo en nuestra lista contiene
uno o más campos que contienen el
valor que deseamos almacenar.
• Cada nodo contiene al menos un
campo pointer apuntando a otro nodo,
lo que significa que podemos tener 2 pointers, uno que apunta a un nodo consecuente y
otro que apunta a un nodo previo formando así una lista doblemente enlazada (double
linked list).
• Es necesario tener un pointer que apunte a la cabeza de la estructura para así saber por
dónde comenzar.

En el problema de la permutación de una palabra, específicamente cuando hablábamos de


representar una palabra en un array donde cada letra era una casilla, bien, aquí tenemos una
lista de palabras lo cual puede claramente significar que es una lista de Arrays. La mayoría de
los lenguajes de programación representan a las “cadenas” (palabras, secuencia de caracteres,
etc.) como Arrays de caracteres, es por eso que
con otra estructura como las lists podemos
tener secuencias de palabras pudiendo
representar un párrafo de un libro o de un post
de medium que habla sobre estructuras de
datos: p
Las 3 operaciones básicas soportadas por una
linked list son:
Búsqueda, inserción y eliminación de nodos.
Una comparación sobre ambos tipos de estructuras para que quede más claro.
Array vs Linked list
Una diferencia grande entre los Arrays vs linked lists es que insertar o eliminar de una linked
list es más fácil ya que no tiene tamaño fijo y lo único que debemos hacer para insertar o
eliminar un valor es simplemente apuntar al nuevo nodo creado, o apuntar al siguiente nodo de
la lista si un nodo fue eliminado. En un array no existe esta flexibilidad.
Si tenemos una gran cantidad de valores siempre será más fácil mover pointers de un valor a
otro que mover los valores en sí.
En un array podemos provocar un desbordamiento de memoria
(memory overflow) si queremos insertar un valor extra y ya
hemos excedido el tamaño del array, lo cual no
sucedería en una linked list.
Por otro lado, en una linked list necesitamos más
espacio en memoria para almacenar los pointers. Los
Arrays tienen un mejor manejo del acceso aleatorio a los
valores y son mucho mejores en la ubicación de los datos
en memoria y aprovechamiento de la caché.
¿Cuál es mejor? Ninguna es mejor que la otra, todo dependerá
de lo que necesitemos resolver. Imaginemos que estamos creando una estructura para almacenar
los usuarios de nuestra aplicación, esta estructura debe guardar el username y su password, ya
dijimos que las linked list son rápidas para insertar y eliminar, entonces ¿usaríamos una linked
list?, si podríamos. Ahora si usáramos la linked list y quisiéramos obtener un username porque
queremos saber su password para el log-in de nuestra aplicación ¿sería mejor usar un array?, el
acceso en los arrays es más rápido ya que sabiendo la ubicación obtendremos el dato
directamente, cosa que en la linked list no podríamos. Entonces, ¿cómo resolvemos este
problema si queremos insertar y eliminar de forma eficiente, y también queremos acceder a los
datos de forma eficiente?, podríamos combinar ambas estructuras, sí, hacer un array de linked
list. De esta forma podríamos asignar a cada casilla del array una linked list por cada letra del
abecedario, si deseamos agregar un username “Marcela” iríamos directamente a la casilla de la
letra M e insertaríamos un nuevo nodo con el username y su password en la linked list. Y si
queremos obtener el username podemos de nuevo ir a la casilla de la M y buscar en el linked
list. De esta forma evitaríamos buscar en todos los usernames hasta encontrar el que queremos,
y obtener y agregar nuevos datos se volvería más fácil y rápido.
Listas enlazadas
La Lista Enlazada Simple es la más fundamental estructura de datos basada en punteros, y del
concepto fundamental de ésta derivan las otras estructuras de datos.
Para solucionar un problema como el
presentado anteriormente, necesitamos
una estructura que, al contrario de los
arreglos, sea capaz de modificar su
capacidad, es decir, que maneje los
datos de forma dinámica. Para lograr
esto, nace la idea de lista enlazada.
Un arreglo asigna memoria para todos sus elementos
ordenados como un sólo bloque. En cambio, la lista enlazada
asigna espacio para cada elemento por separado, en su propio
bloque de memoria, llamado nodo. La lista conecta estos
nodos usando punteros, formando una estructura parecida a la
de una cadena.
Un nodo es un objeto como cualquier otro, y sus atributos
serán los encargados de hacer el trabajo de almacenar y
apuntar a otro nodo. Cada nodo tiene dos atributos: un atributo
“contenido”, usado para almacenar un objeto; y otro atributo “siguiente”, usado para hacer
referencia al siguiente nodo de la lista.
Los nodos serán representados por los siguientes símbolos:

La parte frontal de la lista es representada por un


puntero al primer nodo. Es decir, un atributo de la
lista enlazada es un nodo primero.

Y podríamos representar a una lista enlazada que almacena int’s por medio del siguiente
diagrama:

La lista también tendrá un atributo largo, del tipo long (o int, si se quiere), que representará la
cantidad de elementos en ella.
Implementación
Para implementar una lista simple enlazada, se define una clase Nodo, la cual indica el tipo de
los objetos guardados en los nodos de la lista. Para este caso se asume que los elementos son
String (puede ser cualquier tipo). También es posible definir nodos que puedan guardar tipos
arbitrarios de elementos.
Dada la clase Nodo, se puede definir una clase, ListaSimple, definiendo la lista enlazada actual.
Esta clase guarda una referencia al nodo cabeza y una variable va contando el número total de
nodos.
/** Nodo de una lista simple enlazada de cadenas. */
public class Nodo {
private String elemento; // Los elementos son cadenas de caracteres
private Nodo sig;

/** Crea un nodo con el elemento dado y el nodo sig . */


public Nodo ( String s, Nodo n) {
elemento = s;
sig = n;
}

/** Lista simple enlazada. */


public class ListaSimple {
protected Nodo cabeza; // nodo cabeza de la lista
protected long tam ; // cantidad de nodos en la lista

/** constructor por defecto que crea una lista vaca */


public ListaSimple () {
cabeza = null ;
tam = 0;
}

// ... métodos de actualización y búsqueda deberán ir aquí


}
Cómo acceder a un elemento de la lista
Para tener acceso a un elemento de la lista, hay que hacer uso de los punteros. Se crea un puntero
al primer nodo de la lista y se avanza por medio del atributo “siguiente”.
Por ejemplo, imaginemos que tenemos una lista enlazada llamada lista, que tiene almacenado
nombres de personas por medio de String’s. Si quisiéramos tener acceso al cuarto nombre
almacenado, tendríamos que:
Crear un puntero al primero de la lista
Avanzar con el puntero hasta el cuarto nodo
Utilizar el contenido del nodo
El código sería algo como esto:
Ahora, veamos el diagrama:

Método añadir ()
Para añadir un nodo a la lista, primero creamos un nodo, al que llamaremos nuevoNodo, que
contenga al Cliente entregados por el argumento, al que llamaremos nuevoCliente.
Luego conectamos a nuevoNodo con el último nodo de la lista. Para referirnos a éste, creamos
un puntero, al que llamaremos puntero, al primer nodo y avanzamos hasta el último por medio
de la instrucción puntero = puntero.siguiente como fue explicado anteriormente.
Ahora que puntero apunta al último nodo de la lista, y, además, ahora puntero.siguiente tiene
un valor igual a null, haremos que puntero.siguiente tome el valor nuevoNodo, es decir,
puntero.siguiente = nuevoNodo.
Gráficamente:
Método buscar ()
Para encontrar un Cliente en la lista, debemos buscarlo por medio de su atributo único: elRUT.
Es decir, crearemos un puntero con el que recorreremos la lista, comparando el RUT
especificado con el RUT de cada Client, hasta encontrar una coincidencia. Para esto, tendremos
que usar una instrucción como ésta:

Gráficamente

Método remover ()
Para eliminar un nodo de la lista, primero debemos ubicar el nodo anterior éste, es decir,
puntero.siguiente será el nodo a eliminar. Ahora, simplemente dejamos sin puntero al nodo
diciendo: puntero.siguiente = puntero.siguiente.siguiente, para que el Recolector de Basura de
Java lo recoja y sea eliminado.
Gráficamente:
Pilas y colas
Contenedores
Las estructuras de tipo contenedor se caracterizan principalmente por la forma particular de
recuperación ordenada de datos que soportan, y en los dos tipos principales de contenedores
(stack y queue) el orden de recuperación depende del orden de inserción.
Stack
Un stack o pila, soporta la
recuperación ordenada de datos
last-in, first-out (LIFO) o bien: el
último dato en entrar, el primer
dato en salir. De la misma forma en
que se hace en una pila de platos
limpios, si necesitamos un plato
limpio vamos a la pila y tomamos
el primero de la pila, que en
realidad fue el último plato que
agregamos a ella. Las pilas son
estructuras que encontramos de
muchas formas en el mundo real,
siempre que podamos apilar algún
objeto como libros, cazuelas,
películas o la forma en la que
metemos latas de refresco en el
refrigerador.
Tomando en cuenta lo anterior podemos pensar que si usamos un stack es porque
probablemente el orden de la recuperación de datos no nos importa tanto, simplemente
queremos apilarlos y desapiolarlos, por lo que las operaciones fundamentales en un stack son
push y pop para poner y obtener datos de la pila.
Con push() insertamos un elemento en el tope de la pila, y con pop() retornamos y removemos
el elemento en el tope de la pila, como se ve en la imagen.
Queue
Una queue o cola, soporta la recuperación ordenada de datos first-in, first-out (FIFO) o bien: el
primer dato en entrar, es el primer dato en salir. Justo como una cola en el banco cuando vamos
a realizar alguna operación con nuestra
cuenta bancaria, si todos los asistentes
están ocupados se genera una cola
donde el primero que llegó será el
primero en ser atendido y el resto
esperamos en la cola. Este tipo de
estructuras se utiliza mucho en el
control de tiempos de espera de servicios, tiempos de ejecución en un CPU, de conexión de red,
o en el mundo real en una cola para las tortillas, para entrar en una avenida rápida, etc.
En este tipo de estructura el orden sí importa, pues siempre el primero de la cola debe ser el
primero en ser atendido y el resto de forma ordenada esperan su turno. Las operaciones de esta
estructura son: enqueue y dequeue para encolar y desencolar (poner y obtener de la cola).
De modo que enqueue() inserta un elemento al final de la cola, y dequeue() retorna y remueve
el primer elemento de la cola.
Árboles y grafos
En este contexto árboles y grafos se refiere a estructuras de datos que permiten organizar y
mantener información en un
computador. Esta forma se inspira
una forma de organizar información
con lápiz y papel usando nodos y
flechas entre los nodos (a esas flechas
también se les llama arcos, a los
nodos también se les llama vértices).
Los grafos y árboles en papel son
apropiados por ejemplo para capturar
sólo una parte de la información de
objetos, situaciones y otros tipos de
información. En un computador
además de permitir organizar
información, resultan estructuras útiles para resolver ciertos tipos de problema (por ejemplo,
pueden emplearse árboles AVL para mantener información ordenada de forma eficiente).
ARBOLES
El árbol es una estructura de datos fundamental en la informática, muy utilizada en todos sus
campos, porque se adapta a la representación natural de informaciones homogéneas
organizadas y de una gran comodidad y
rapidez de manipulación.
Otra definición de árbol es como tipo de
grafo cíclico, conexo y no dirigido.
Las estructuras tipo árbol se usan
principalmente para representar datos
con una relación jerárquica entre sus
elementos, como son árboles
genealógicos, tablas, etc.
La definición de un árbol implica una
estructura recursiva. Esto es, la definición del árbol se refiere a otros árboles. Un árbol con
ningún nodo es un árbol nulo; no tiene raíz.
Una estructura vacía o un elemento o clave de información (nodo) más un numero finito de
estructuras tipo árbol, disjuntos, llamados subárboles. Si dicho número de estructuras es inferior
o igual a dos, se tiene un árbol binario.
Es, por tanto, una estructura no secuencial.
Terminología y representación de un árbol en general
La representación y terminología de los árboles se realiza con las típicas notaciones de las
relaciones familiares en los árboles genealógicos: padre, hijo, hermano, ascendente,
descendiente, etc.
• RAIZ: Todos los árboles que no están vacíos tienen un único nodo raíz. Todos los
demás elementos o nodos derivan o descienden de él. El nodo Raíz no tiene Padre es
decir no es hijo de ningún elemento
• PADRE: X es padre de Y sí y solo sí el nodo X apunta a Y. También se dice que X es
antecesor de Y.
• HIJO: X es hijo de Y, sí y solo sí el nodo X es apuntado por Y. También se dice que X
es descendiente directo de Y.
• HERMANO: Dos nodos serán hermanos si son descendientes directos de un mismo
nodo.
• HOJA. Se le llama hoja o Terminal a aquellos nodos que no tienen ramificaciones
(hijos).
• NODO. Son los Vértices o elementos del Árbol.
• NODO INTERIOR. Es un nodo que no es raíz ni Terminal.
• GRADO. Es el número de descendientes directos de un determinado nodo.
• GRADO DEL ARBOL Es el máximo grado de todos los nodos del árbol.
• NIVEL. Es el número de arcos que deben ser recorridos para llegar a un determinado
nodo. Por definición la raíz tiene nivel 1.
• ALTURA. Es el máximo número de niveles de todos los nodos del árbol. Equivale al
nivel más alto de los nodos más 1.
• PESO. Es el número de nodos terminales del árbol
• LONGITUD DE CAMINO. Es el número de arcos que deben ser recorridos para llegar
desde la raíz al nodo X. Por definición la raíz tiene longitud de camino 1, y sus
descendientes directos longitud de camino 2 y así sucesivamente.
También se podría decir que un árbol
binario se define como un conjunto finito
de elementos llamados nodos. En estos
casos se puede usar terminología de
relaciones familiares para descubrir las
relaciones entre los nodos de un árbol; y
que un árbol puede ser implementado
fácilmente en una computadora.
Es bueno hacer énfasis en esto ya que se
puede saber mucho sobre lo que tiene que
ver con los árboles; entre las cosas que
podemos mencionar se encuentra la raíz, los nodos de un árbol y la diferencia entre nodos
sucesores y nodos terminales, como se muestran en el contenido del trabajo.
Tipos de árboles
• Árbol binario
En ciencias de la computación, un árbol binario es una estructura de datos en la cual cada
nodo siempre tiene un hijo izquierdo y un hijo derecho. No pueden tener más de dos hijos
(de ahí el nombre "binario"). Si algún hijo tiene como referencia a null, es decir que no
almacena ningún dato, entonces este es llamado un nodo externo. En el caso contrario el hijo
es llamado un nodo interno. Usos comunes de los árboles binarios son los árboles binarios
de búsqueda, los montículos binarios y Codificación de Huffman.
Tipos de árboles binarios
• Un árbol binario es un árbol con raíz en el que cada nodo tiene como máximo dos hijos.
• Un árbol binario lleno es un árbol en el que cada nodo tiene cero o dos hijos.
• Un árbol binario perfecto es un árbol binario lleno en el que todas las hojas (vértices
con cero hijos) están a la misma profundidad (distancia desde la raíz, también llamada
altura).
A veces un árbol binario perfecto es denominado árbol binario completo. Otros definen un
árbol binario completo como un árbol binario lleno en el que todas las hojas están a
profundidad n o n-1, para alguna n.
Un árbol binario es un árbol en el que ningún nodo puede tener más de dos subárboles. En
un árbol binario cada nodo puede tener cero, uno o dos hijos (subárboles). Se conoce el
nodo de la izquierda como hijo izquierdo y el nodo de la derecha como hijo derecho.
• Árbol binario de búsqueda autobalanceable
En ciencias de la computación, un árbol binario de búsqueda autobalanceable o equilibrado
es un árbol binario de búsqueda que intenta mantener su altura, o el número de niveles de
nodos bajo la raíz, tan pequeños como sea posible
en todo momento, automáticamente. Esto es
importante, ya que muchas operaciones en un
árbol de búsqueda binaria tardan un tiempo
proporcional a la altura del árbol, y los árboles
binarios de búsqueda ordinarios pueden tomar
alturas muy grandes en situaciones normales,
como cuando las claves son insertadas en orden. Mantener baja la altura se consigue
habitualmente realizando transformaciones en el árbol, como la rotación de árboles, en
momentos clave.
Tiempos para varias operaciones en términos del número de nodos en el árbol n:
• Operación Tiempo en cota superior asintótica
• Búsqueda O(log n)
• Inserción O(log n)
• Eliminación O(log n)
• Iteración en orden O(n)
Para algunas implementaciones estos tiempos son el
peor caso, mientras que para otras están amortizados.
Estructuras de datos populares que implementan este
tipo de árbol:
• Árbol AVL
• Árbol rojo-negro
• -Árbol-B
En las ciencias de la computación, los árboles-B ó B-árboles son estructuras de datos de árbol
que se encuentran comúnmente en las implementaciones de bases de datos y sistemas de
archivos. Los árboles B mantienen los datos ordenados y las inserciones y eliminaciones se
realizan en tiempo logarítmico amortizado.
Árbol es un árbol de búsqueda
Árbol es un árbol de búsqueda que puede estar vacío o aquel cuyos nodos pueden tener varios
hijos, existiendo una relación de orden entre ellos, tal como muestra el dibujo.
Un árbol-B de orden M (el máximo número de hijos que puede tener cada nodo) es un árbol
que satisface las siguientes propiedades:
1. Cada nodo tiene como máximo M hijos.
2. Cada nodo (excepto raíz y hojas) tiene como mínimo M/2 hijos.
3. La raíz tiene al menos 2 hijos si no es un nodo hoja.
4. Todos los nodos hoja aparecen al mismo nivel.
5. Un nodo no hoja con k hijos contiene k-1 elementos almacenados.
6. Los hijos que cuelgan de la raíz (r1, ···, rm) tienen que cumplir ciertas condiciones:
• El primero tiene valor menor que r1.
• El segundo tiene valor mayor que r1 y menor que r2, etc.
• El último hijo tiene valor mayor que rm.
• Árbol multicamino
Los árboles multicamino o árboles multirrama son estructuras de datos de tipo árbol usadas en
computación.
Un árbol multicamino posee un grado g mayor a dos, donde cada nodo de información del árbol
tiene un máximo de g hijos.
GRAFOS
Un grafo en el ámbito de las ciencias de la computación es una estructura de datos, en concreto
un tipo abstracto de datos (TAD), que consiste en un conjunto de nodo (también llamados
vértices) y un conjunto de arcos
(aristas) que establecen relaciones
entre los nodos. El concepto de grafo
TAD desciende directamente del
concepto matemático de grafo.
Informalmente se define como G =
(V, E), siendo los elementos de V los
vértices, y los elementos de E, las
aristas (edges en inglés).
Formalmente, un grafo, G, se define
como un par ordenado, G = (V, E),
donde V es un conjunto finito y E es
un conjunto que consta de dos
elementos de V.
De aquí se podría deducir que un
grafo es básicamente un objeto
geométrico, aunque en realidad sea
un objeto combinatorio, es decir, un
conjunto de puntos y un conjunto de líneas tomado de entre el conjunto de líneas que une cada
par de vértices. Por otro lado, y debido a su generalidad y a la gran diversidad
de formas que pueden usarse, resulta complejo tratar con todas las ideas
relacionadas con un grafo.
Para facilitar el estudio de este tipo de dato, a continuación, se
realizará un estudio de la teoría de grafos desde el
punto de vista de las ciencias de la computación.
Considerando que dicha teoría es compleja y amplia,
aquí sólo se realizará una introducción a la misma,
describiéndose el grafo como un tipo de dato y
mostrándose los problemas típicos y los algoritmos que
permiten solucionarlos usando un ordenador.
Los grafos son estructuras de datos no lineales que tienen una
naturaleza generalmente dinámica. Su estudio podría dividirse en
dos grandes bloques:
Grafos Dirigidos.
Grafos no Dirigidos (pueden
ser considerados un caso
particular de los anteriores).
Un ejemplo de grafo dirigido
lo constituye la red de aguas
de una ciudad ya que cada
tubería sólo admite que el
agua la recorra en un único
sentido. Por el contrario, la
red de carreteras de un país
representa en general un
grafo no dirigido, puesto que
una misma carretera puede
ser recorrida en ambos sentidos.
Algunos de los principales tipos de grafos son los que se muestran a continuación:
• Grafo regular: Aquel con el mismo grado en todos los vértices. Si ese grado es k lo
llamaremos k-regular.
• Grafo bipartito: Es aquel con cuyos vértices pueden formarse dos conjuntos disjuntos
de modo que no haya adyacencias entre vértices pertenecientes al mismo conjunto.
• Grafo completo: Aquel con una arista entre cada par de vértices. Un grafo completo con
n vértices se denota Kn.
• Un grafo bipartito regular: se denota Km,n donde m, n es el grado de cada conjunto
disjunto de vértices.
• Grafo nulo: Se dice que un grafo es nulo cuando los vértices que lo componen no están
conectados, esto es, que son vértices aislados.
• Grafos Isomorfos: Dos grafos son isomorfos cuando existe una correspondencia
biunívoca (uno a uno), entre sus vértices de tal forma que dos de estos quedan unidos
por una arista en común.
• Grafos Platónicos: Son los Grafos formados por los vértices y aristas de los cinco sólidos
regulares (Sólidos Platónicos), a saber, el tetraedro, el cubo, el octaedro, el dodecaedro
y el icosaedro.
Grafos Eulerianos.
Para definir un camino euleriano es importante definir un camino
euleriano primero. Un camino euleriano se define de la manera
más sencilla como un camino que contiene todos los arcos del
grafo. Teniendo esto definido podemos hablar de los grafos
eulerianos describiéndolos simplemente como aquel grafo que
contiene un camino euleriano.
Grafos Conexos.
Un grafo se puede definir como conexo si cualquier vértice V
pertenece al conjunto de vértices y es alcanzable por algún otro.
Otra definición que dejaría esto más claro sería: "un grafo conexo
es un grafo no dirigido de modo que para cualquier par de nodos
existe al menos un camino que los une".
Recorrido de un grafo.
Recorrer un grafo significa tratar de alcanzar todos los nodos que
estén relacionados con uno que llamaremos nodo de salida. Existen
básicamente dos técnicas para recorrer un grafo: el recorrido en
anchura; y el recorrido en profundidad.
• Recorrido en anchura: El recorrido en anchura supone
recorrer el grafo, a partir de un nodo dado, en niveles, es
decir, primero los que están a una distancia de un arco del
nodo de salida, después los que están a dos arcos de distancia,
y así sucesivamente hasta alcanzar todos los nodos a los que
se pudiese llegar desde el nodo salida.
• Recorrido en profundidad: el recorrido en profundidad trata
de buscar los caminos que parten desde el nodo de salida
hasta que ya no es posible avanzar más. Cuando ya no puede
avanzarse más sobre el camino elegido, se vuelve atrás en
busca de caminos alternativos, que no se estudiaron
previamente.
Representación de grafos en programas.
Hay tres maneras de representar un grafo en un programa: mediante matrices, mediante listas y
mediante matrices dispersas.
• Representación mediante matrices: La forma más fácil de guardar la información de los
nodos es mediante la utilización de un vector que indexe los nodos, de manera que los
arcos entre los nodos se pueden ver como relaciones entre los índices. Esta relación
entre índices se puede guardar en una matriz, que llamaremos de adyacencia.
• Representación mediante listas: En las listas de adyacencia lo que haremos será guardar
por cada nodo, además de la información que pueda contener el propio nodo, una lista
dinámica con los nodos a los que se puede acceder desde él. La información de los nodos
se puede guardar en un vector, al igual que antes, o en otra lista dinámica.
• Representación mediante matrices dispersas: Para evitar uno de los problemas que
teníamos con las listas de adyacencia, que era la dificultad de obtener las relaciones
inversas, podemos utilizar las matrices dispersas, que contienen tanta información como
las matrices de adyacencia, pero, en principio, no ocupan tanta memoria como las
matrices, ya que al igual que en las listas de adyacencia, sólo representaremos aquellos
enlaces que existen en el grafo.
Dígrafo (grafo dirigido).
A un grafo dirigido se le puede definir como un grafo que contiene
aristas dirigidas, como en el siguiente caso.
Aplicaciones de los dígrafos
Una de las aplicaciones más importantes es de hallar el camino más
corto hacia un destino, ya sea de una ciudad a otra, de unos
departamentos a otros, para el recorrido de árboles, sirve para la
representación de algoritmos, etc. Un ejemplo de esto es la tarea de freír
un huevo.
Grado de un grafo.
• Grado de incidencia positivo: El grado de incidencia positivo de
un nodonjes el número de arcos que tienen como nodo inicial
anj. Ejemplo: El grado de incidencia de 1 es igual a 3.
• Grado de incidencia negativo: El grado de incidencia negativo
de un nodonjes el número de arcos que terminan ennj. Ejemplo:
El grado de incidencia negativo de 1 es igual a 1.
• Grado de un nodo: Paradigrafoses el grado de incidencia
positivo menos el grado de incidencia negativo del nodo.
Ejemplo: El grado de 1 es igual a 3 –1 = 2, el grado del nodo 4
es 2 –2 = 0. Para grafos no dirigidos es el número de líneas
asociadas al nodo.
Ciclo de un grafo.
Ciclo: Es una cadena finita donde el nodo inicial de la cadena coincide con el nodo terminal de
la misma.
• Ciclo simple: Es el ciclo que a su vez es una cadena simple.
Estructuras no lineales: Grafos
Las estructuras de datos no lineales se caracterizan por no existir una relación de adyacencia,
entre sus elementos, es decir, un elemento puede estar relacionado con cero, uno o más
elementos.
La estructura no lineal de datos más general es el grafo donde sus nodos pueden relacionarse
de cualquier manera sin una relación de orden predefinida.
Estructuras no lineales: Grafos Entre las múltiples aplicaciones que tienen estas estructuras
podemos mencionar:
• Para modelar diversas situaciones tales como: sistemas de aeropuertos, flujo de tráfico,
y responder a preguntas como: ¿Qué tiempo es más corto?, ¿Cómo es más barato?, o
¿Qué camino es más corto?
• Los grafos también son utilizados para realizar planificación de actividades, tareas del
computador, planificar operaciones en lenguaje de máquinas para minimizar tiempo de
ejecución. ¿Qué tarea debo hacer primero?
• Para representar circuitos eléctricos, de aguas etc. y preguntar, están todas las
componentes conectadas.
CLASIFICACIÓN DE LA ESTRUCTURA DE DATOS
Introducción
Estructura de datos, pueden
organizarse en muchas formas
diferentes; el modelo matemático o
lógico de una organización
particular de datos recibe el nombre
de estructura de datos.
La elección de un modelo de datos
depende de dos cuestiones. Primero,
debe ser lo suficientemente
complejo para mostrarnos la
relación entre los datos y lo que
representan. Por el contrario, la
estructura debe ser lo suficiente
mente simple para que los datos
puedan ser procesados de forma
eficiente cuando sean necesario.
Clasificación de los datos simples
Todas las variables que se han considerado hasta el momento son de tipo simple. Una variable
de tipo simple consiste de una sola caja de memoria y sólo puede contener un valor cada vez.
Una variable de tipo estructurado consiste en toda una colección de casillas de memoria.
Los tipos de datos estudiados: entero, real, alfabético son considerados como datos de tipo
simple, puesto que una variable que se define con alguno de estos tipos sólo puede almacenar
un valor a la vez, es decir existe una relación de uno a uno entre la variable y el número de
elementos (valores) que es capaz de almacenar.
En cambio, un dato de tipo estructurado, como el arreglo, puede almacenar más de un elemento
(valor) a la vez, con la condición de que todos los elementos deben ser del mismo tipo, es decir,
que se puede tener un conjunto de datos enteros, reales, etc.
• Datos Numéricos: Permiten representar
valores escalares de forma numérica, esto
incluye a los números enteros y reales.
Estos tipos de datos permiten realizar
operaciones aritméticas comunes.
• Datos Lógicos: Son aquellos que solo
pueden tener dos valores (cierto o falso)
ya que representan el resultado de una
comparación entre otros datos
(numéricos o alfanuméricos).
Datos Alfanuméricos (String): Son una secuencia de caracteres alfanuméricos que permiten
representar valores identificables de forma descriptiva, esto incluye nombres de personas,
direcciones, etc. Es posible representar números como alfanuméricos, pero estos pierden su
propiedad matemática, es decir no es posible hacer operaciones con ellos. Este tipo de datos se
representan encerrados entre comillas.
• DEFINICION DE EXPRESIÓN: Las
expresiones son combinaciones de constantes,
variables, símbolos de operación, paréntesis y
nombres de funciones especiales.
• DEFINICION DE OPERADOR Y SUS TIPOS:
Operadores: Son elementos que relacionan de forma diferente, los valores de una o más
variables y/o constantes. Los operadores permiten manipular valores.
DEFINICIÓN DE CONSTANTE
Una constante es un dato numérico o alfanumérico que no cambia durante la ejecución del
programa.
DATOS ESTRUCTURADOS
Estructura de Datos es una colección de datos que se caracteriza por su organización y las
operaciones que se definen en ella. Los datos de tipo estándar pueden ser organizados en
diferentes estructuras de datos: estáticas y dinámicas.
Definición de operadores y sus tipos
• Relaciónales: Compara estos valores entre sí y esta
comparación produce un resultado de certeza o falsedad
(verdadero o falso).
• Lógicos: Operadores que se utiliza para establecer relaciones
entre valores lógicos.
Los operadores relaciónales comparan valores del mismo tipo
(numéricos o cadenas).
Operadores
Los operadores son símbolos que indican cómo se deben
manipular los operandos. Los operadores junto con los
operandos forman una expresión, que es una fórmula que
define el cálculo de un valor. Los operandos pueden ser
constantes, variables o llamadas a funciones, siempre que
éstas devuelvan algún valor. El compilador evalúa los
operadores, algunos de izquierda a derecha, otros de
derecha a izquierda, siguiendo un orden de precedencia.
Este orden se puede alterar utilizando paréntesis para forzar
al compilador a evaluar primero las partes que se deseen.
Operadores Aritméticos
OPERADOR PROPÓSITO
+ adición
- sustracción
* multiplicación
/ división
% resto de división entera
Otros dos operadores monarios interesantes son:
++ Que es el operador incremento, hace que su operando se incremente en uno.
-- Que es el operador decremento, hace que el operando se decremente en uno.
Operadores Lógicos y Relacionales:
OPERADOR PROPÓSITO
> mayor que
>= mayor o igual que
< menor que
<= menor o igual que
== igual
!= distinto
&& AND lógico
|| OR lógico
! NOT lógico
Operadores de asignación
Se utiliza el operador =, dentro de una asignación, para asignar valores a una variable. Tiene el
siguiente formato:

Operadores de Punteros
En temas posteriores entraremos más a fondo sobre ellos, ya que son una parte imprescindible
del lenguaje. De ellos sólo diremos que los operadores unitarios * y & se utilizan con punteros.
Cuando se evalúa el operador unitario &, se obtiene la dirección del operando. El operador *,
al evaluarse, devuelve el valor de la variable a la que apunta el operando; por lo tanto, el
operando debe ser un puntero.
Definición y clasificación de Variables.
Una variable estudia características de la población, objetos e instituciones, estas características
pueden ser sometidas a cambios y clasificarse en
diversos criterios.
Clasificaciones de las variables
• Cualitativas
• Cuantitativas
• Clasificaciones de las variables
• Clasificaciones de las variables
• Grafica de cómo se clasifican las variables
De acuerdo a su comportamiento en una investigación:
Clasificaciones de las variables
• Cualitativas
• Cuantitativas
Cualitativas
Se determinan por las cualidades o
atributos, verbigracia, colores (azul, verde,
naranja, rojo, amarillo, marrón, etc.),
tamaños (grandes, pequeños, altos, bajos).
Estas cualidades pueden distinguir a una
persona o cosa, las características podrán
distinguirse uno de otro, por lo que no
pueden ser medidas por cifras. Por lo
tanto, su modalidad no es numérica,
verbigracia, no pueden ser usadas en el
campo de las finanzas o en la economía.
Sin embargo, a estas cualidades se le
pueden atribuir números, o letras para
diferenciarlos o categorizarlos, como
prototipo tenemos, al sexo masculino el
número 1 y al femenino el número 2, de tal manera poder hacer algún análisis matemático.
Ejemplo:
• Sensibilidad de una persona.
• Color de piel
• Color de cabello
• Oficio u profesión de una persona.
• Carácter de una persona.
Las cualitativas se determinan por ser:
Nominales
No corresponden a ningún orden. Estas no son numéricas, ejemplo; amarillo, dorado, negro,
fucsia, además de, casado o soltero.
Ordinales: En están tampoco son representadas por números sin por orden. Por ejemplo, las
calificaciones de un examen si es aprobado o reprobado, sobresaliente, o no. Sonidos, débiles
o fuertes.
Cuantitativas
Son de carácter numérico, cifras finitas, como las fórmulas o algoritmos, además, son:
Discretas: Manejan los valores precisos, enteros; no obstante, para que así lo sea deben ser
valores infinitos. Ahora bien, estas variables son usadas comúnmente en experimentos.
Ejemplo:
• Edad
• Centímetros
• Talla
• Peso
• Número de quejas en una sala de
espera
• Numero de perritos en una cesta
• Cantidad de pollitos
Sería imposible decir que tiene 2,3 vasos, o 5,6 hijos, numero de hermanos 3,7, número de
personas en un aéreo puerto, 15076,9, o cantidad de alumnos en un aula, 20,6, por ejemplo.
Continuas
Cualquier valor real dentro de un intervalo. Su función es continua. En este caso si es posible
usar un valor medio como los decimales.
Ejemplo
• María mide 1.55 cm de alto.
• La velocidad de un motociclista.
• Las temperaturas.
• La hora
• Fecha de entrada o de salida.
Las medidas no son exactas, por lo que en las variables continuas se puede dar uso a los
decimales.
Una variable es una propiedad característica de la población en estudio, susceptible de tomar
diferentes valores, los cuales se pueden observar y medir.
Nominales: Son datos que corresponden a categorías que por su naturaleza no admiten un
orden. Por ejemplo: sexo
(masculino y femenino);
carrera de estudio:
economía, contabilidad,
administración, etc.
Ordinales: Son aquellos que
corresponden a
evaluaciones subjetivas que
se pueden ordenar o
jerarquizar. Por ejemplo: en
una competencia artística las posiciones de los ganadores se ordenan o
jerarquizan en primer lugar, segundo lugar, tercer lugar, cuarto lugar, etc.
Variables cuantitativas: son aquellas que tienen valor numérico como la edad, el precio de un
producto, ingresos anuales de un consumidor, etc.
Clasificación de la estructura de datos.
Una estructura de datos es una clase de datos
que se puede caracterizar por su organización
y operaciones definidas sobre ella. Algunas
veces a estas estructuras se les llama tipos de
datos.
En ellas encontramos las siguientes:
Estructuras lógicas de datos:
En un programa, cada variable pertenece a
alguna estructura de datos explícita o
implícitamente definida, la cual determina el
conjunto de operaciones válidas para ella. Las
estructuras de datos que se discuten aquí son
estructuras de datos lógicas. Cada estructura
de datos lógica puede tener varias
representaciones físicas diferentes para sus
almacenamientos
Estructuras primitivas y simples:
Son primitivas aquellas que no están compuestas por otras estructuras de datos, por ejemplo,
enteros, booleanos y caracteres. Otras estructuras de datos se pueden construir de una o más
primitivas. Las estructuras de datos simples que consideramos se construyen a partir de
estructuras primitivas y son: cadenas, arreglos y registros. A estas estructuras de datos las
respaldan muchos lenguajes de programación.

Estructuras lineales y no lineales


Las estructuras de datos simples se pueden combinar de varias maneras para formar estructuras
más complejas. Los dos cases principales de estructuras de datos son las lineales y las no
lineales, dependiendo de la complejidad de las relaciones lógicas que representan. Las
estructuras de datos lineales incluyen pilas, colas y listas ligadas lineales. Las estructuras de
datos no lineales incluyen grafos y árboles.
Datos estáticos: Su tamaño y forma es constante durante la ejecución de un programa y
por tanto se determinan en tiempo de compilación. El ejemplo típico son
los Arrays. Tienen el problema de que hay que dimensionar la estructura
de antemano, lo que puede conllevar desperdicio o falta de memoria.
Datos dinámicos: Su tamaño y forma es variable (o puede serlo) a lo largo de un programa,
por lo que se crean y destruyen en tiempo de ejecución. Esto permite
dimensionar la estructura de datos de una forma precisa: se va asignando
memoria en tiempo de ejecución según se va necesitando
Operaciones algorítmicas con arreglos unidimensionales
Arreglos Unidimensionales
Un arreglo unidimensional es un tipo de datos estructurado
que está formado de una colección finita y ordenada de datos
del mismo tipo. Es la estructura natural para modelar listas
de elementos iguales.
El tipo de acceso a los arreglos unidimensionales es el acceso
directo, es decir, podemos acceder a cualquier elemento del
arreglo sin tener que consultar a elementos anteriores o
posteriores, esto mediante el uso de un índice para cada
elemento del arreglo que nos da su posición relativa.
Para implementar arreglos unidimensionales se debe reservar espacio en memoria, y se debe
proporcionar la dirección base del arreglo, la cota superior y la inferior.
Representación en Memoria
Los arreglos se representan en memoria de la forma
siguiente:
x : array[1..5] of integer

Para establecer el rango del arreglo (número total de


elementos) que componen el arreglo se utiliza la
siguiente formula:
RANGO = Ls - (Li+1)
donde:
ls = Límite superior del arreglo
li = Límite inferior del arreglo
Para calcular la dirección de memoria de un elemento dentro de un arreglo se usa la siguiente
formula:
A[i] = base(A) + [(i-li) * w]
Donde:
A = Identificador único del arreglo
i = Indice del elemento
li = Límite inferior
w = Número de bytes tipo componente
Si el arreglo en el cual estamos trabajando tiene un índice numerativo utilizaremos las siguientes
fórmulas:
RANGO = ord (ls) - (ord (li)+1)
A[i] = base (A) + [ord (i) - ord (li) * w]
Operaciones:
Las operaciones en arreglos pueden clasificarse de la siguiente forma:
• Lectura
• Escritura
• Asignación
• Actualización
• Ordenación
• Búsqueda
Lectura
Este proceso consiste en leer un dato de un arreglo y asignar un
valor a cada uno de sus componentes.
La lectura se realiza de la siguiente manera:
para i desde 1 hasta N haz
x<--arreglo[i]
Escritura
Consiste en asignarle un valor a cada elemento del arreglo.
La escritura se realiza de la siguiente manera:
para i desde 1 hasta N haz
arreglo[i]<--x
Asignación
No es posible asignar directamente un valor a
todo el arreglo, por lo que se realiza de la
manera siguiente:
para i desde 1 hasta N haz
arreglo[i]<--algún_valor

Actualización
Dentro de esta operación se encuentran las operaciones de eliminar, insertar y modificar datos.
Para realizar este tipo de operaciones se debe tomar en cuenta si el arreglo está o no ordenado.
Para arreglos ordenados los algoritmos de inserción, borrado y modificación son los siguientes:
1.- Insertar.
Si i< mensaje (arreglo contrario caso En arreglo[i]<--valor i<--i+1 entonces>
2.- Borrar.
Si N>=1 entonces
inicio
i<--1
encontrado<--falso
mientras i<=n y encontrado=falso
inicio
si arreglo[i]=valor_a_borrar entonces
inicio
encontrado<--verdadero
N<--N-1
para k desde i hasta N haz
arreglo[k]<--arreglo[k-1]
fin
en caso contrario
i<--i+1
fin
fin
Si encontrado=falso entonces
mensaje (valor no encontrado)
3.- Modificar.
Si N>=1 entonces
inicio
i<--1
encontrado<--falso
mientras i<=N y encontrado=false haz
inicio
Si arreglo[i]=valor entonces
arreglo[i]<--valor_nuevo
encontrado<--verdadero
En caso contrario
i<--i+1
fin
fin
Ordenaciones en arreglos
La importancia de mantener nuestros arreglos ordenados radica en que es mucho más rápido
tener acceso a un dato en un arreglo ordenado que en uno desordenado.
Existen muchos algoritmos para la ordenación de elementos en arreglos, enseguida veremos
algunos de ellos.
a) Selección Directa
Este método consiste en seleccionar el elemento más pequeño de nuestra lista para
colocarlo al inicio y así excluirlo de la lista.
Para ahorrar espacio, siempre que vayamos a colocar un elemento en su posición
correcta lo intercambiaremos por aquel que la esté ocupando en ese momento.
El algoritmo de selección directa es el siguiente:
i <- 1
mientras i<= N haz
min <-i
j <- i + 1
mientras j <= N haz
si arreglo[j] < [min] entonces
min <-j
j <- j + 1
intercambia(arreglo[min],arreglo[i])
i <- i +1
b) Ordenación por Burbuja
Es el método de ordenación más utilizado por su fácil comprensión y programación,
pero es importante señalar que es el más ineficiente de todos los métodos.
Este método consiste en llevar los elementos menores a la izquierda del arreglo ó los
mayores a la derecha del mismo. La idea básica del algoritmo es comparar pares de
elementos adyacentes e intercambiarlos entre sí hasta que todos se encuentren
ordenados.
i <- 1
mientras i < N haz
j <- N
mientras j > i haz
si arreglo[j] < arreglo[j-1] entonces
intercambia(arreglo[j],arreglo[j-1])
j<j-1
i <- i +1
c) Ordenación por Mezcla
Este algoritmo consiste en partir el arreglo por la mitad, ordenar la mitad izquierda,
ordenar la mitad derecha y mezclar las dos mitades ordenadas en un array ordenado.
Este último paso consiste en ir comparando pares sucesivos de elementos (uno de cada
mitad) y poniendo el valor más pequeño en el siguiente hueco.
Procedimiento mezclar(dat,izqp,izqu,derp,deru)
inicio
izqa <- izqp
dera <- derp
ind <- izqp
mientras (izqa <= izqu) y (dera <= deru) haz
si arreglo[izqa] < dat[dera] entonces
temporal[ind] <- arreglo[izqa]
izqa <- izqa + 1
en caso contrario
temporal[ind] <- arreglo[dera]
dera <- dera + 1
ind <- ind +1
mientras izqa <= izqu haz
temporal[ind] <- arreglo[izqa]
izqa <- izqa + 1
ind <- ind +1
mientras dera <= deru haz
temporal[ind] <=dat[dera]
dera <- dera + 1
ind <- ind + 1
para ind <- izqp hasta deru haz
arreglo[ind] <- temporal[ind]
fin
Búsquedas en arreglos
Una búsqueda es el proceso mediante el cual podemos localizar un elemento con un valor
específico dentro de un conjunto de datos. Terminamos con éxito la búsqueda cuando el
elemento es encontrado.
A continuación, veremos algunos de los algoritmos de búsqueda que existen.
a) Búsqueda Secuencial
A este método también se le conoce como búsqueda lineal y consiste en empezar al
inicio del conjunto de elementos, e ir atreves de ellos hasta encontrar el elemento
indicado o hasta llegar al final de arreglo.
Este es el método de búsqueda más lento, pero si nuestro arreglo se encuentra
completamente desordenado es el único que nos podrá ayudar a encontrar el dato que
buscamos.
ind <- 1
encontrado <- falso
mientras no encontrado y ind < N haz
si arreglo[ind] = valor_buscado entonces
encontrado <- verdadero
en caso contrario
ind <- ind +1
b) Búsqueda Binaria
Las condiciones que debe cumplir el arreglo para poder usar búsqueda binaria son que
el arreglo este ordenado y que se conozca el número de elementos.
Este método consiste en lo siguiente: comparar el elemento buscado con el elemento
situado en la mitad del arreglo, si tenemos suerte y los dos valores coinciden, en ese
momento la búsqueda termina. Pero como existe un alto porcentaje de que esto no
ocurra, repetiremos los pasos anteriores en la mitad inferior del arreglo si el elemento
que buscamos resulto menor que el de la mitad del arreglo, o en la mitad superior si el
elemento buscado fue mayor.
La búsqueda termina cuando encontramos el elemento o cuando el tamaño del arreglo a
examinar sea cero.
encontrado <- falso
primero <- 1
ultimo <- N
mientras primero <= ultimo y no encontrado haz
mitad <- (primero + ultimo)/2
si arreglo[mitad] = valor_buscado entonces
encntrado <- verdadero
en caso contrario
si arreglo[mitad] > valor_buscado entonces
ultimo <- mitad - 1
en caso contrario
primero <- mitad + 1
c) Búsqueda por Hash
La idea principal de este método consiste en aplicar una función que traduce el valor del
elemento buscado en un rango de direcciones relativas. Una desventaja importante de
este método es que puede ocasionar colisiones.
funcion hash (valor_buscado)
inicio
hash <- valor_buscado mod numero_primo
fin
inicio <- hash (valor)
il <- inicio
encontrado <- falso
repite
si arreglo[il] = valor entonces
encontrado <- verdadero
en caso contrario
il <- (il +1) mod N
hasta encontrado o il = inicio
Declaración de un arreglo
Funcionamiento
La declaración de un arreglo bidimensional es similar al de un arreglo unidimensional, así:

SINTAXIS EJEMPLO
tipoDato [ ][ ] nombre_variable; int [ ][ ] registroNotas;
tipoDato nombre_variable [ ][ ]; int registroNotas [ ][ ];
De esta forma lo que se logra es disponer de una variable, en los ejemplos registro Notas, en el
que se podrá almacenar un conjunto de valores. Para establecer la cantidad máxima de valores
que puede contener la estructura creada es necesario realizar la reserva de memoria o
instanciación utilizando la palabra reservada new. Esto puede hacerse a continuación de la
declaración, o en dos instrucciones diferentes, así:

SINTAXIS EJEMPLO
tipoDato [ ][ ] nombreVariable = new int [ ][ ] registroNotas = new int [4][5];
tipoDato [nFilas][nCol];
tipoDato [ ][ ] nombreVariable; nombreVariable = new tipoDato
[nFilas][nCol];
int registroNotas [4][5]; registroNotas = new int [4][5];
Esto último puede aplicarse a cualquiera de las declaraciones revisadas. Donde:
• tipoDato puede ser cualquiera de tipos estándares de datos: int, double, String, entre
otros. Esto implica que todos los datos serán únicamente del tipo especificado en la
declaración.
• nombreVariable es el nombre que se asigna al arreglo, en este ejemplo: registroNotas.
• nFilas es el número máximo de filas que tendrá la matriz.
• nCol es el número máximo de columnas que tendrá la matriz.
Sin embargo y únicamente para fines didácticos en los ejercicios, utilizaremos otro tipo de
declaración, que es muy estática, pero válida para evitar el continuo ingreso datos.

SINTAXIS EJEMPLO
tipoDato [][] nombreVariable = {{v1, v2, int [][] RegistroNotas =
v3},{v4, v5, v6},{v7,v8,v9}}; {{6,8,10,2,9},{1,2,3,4,5},{6,7,8,9,10}};
De esta manera dispondremos de un arreglo bidimensional de
3 filas por 5 columnas con los valores asignados entre llaves.

Gráficamente se vería así:

Operaciones con arreglos unidimensionales o vectores


Cargar los datos en un arreglo unidimensional
• Se solicitará la posición en donde desee insertar.
• Incrementar la longitud del arreglo en una posición.
• Solicitar el nuevo dato y almacenarlo en la posición señalada por el usuario.
Argumentos que recibe: Posición, dato y longitud.
Procedimiento Cargar (VAR Vec:array, pos:entero)
dato:entero, i:entero Declaración de variables
Para i=1 a pos hacer
Vec[i] = dato Transferir el elemento a la posición correcta
Fin Para
Fin Procedimiento
Insertar los datos en un arreglo unidimensional
• Se solicitará la posición en donde desee insertar.
• Incrementar la longitud del arreglo en una posición.
• Luego se procederá a transferir los elementos una posición hacia la derecha,
• comenzando desde el último elemento hasta la posición donde se desea
• insertar.
• Solicitar el nuevo dato y almacenarlo en la posición señalada por el usuario.
Procedimiento Insertar (VAR Vec:array, pos:entero, dato:entero, i:entero)
Mientras ( i>=pos) hacer
Vec[i+1] =Vec[i]
Transferir el elemento a la posición siguiente
i=i-1
Fin Mientras
Vec[pos] = dato Posicionar el nuevo dato
Fin Procedimiento
Buscar datos en un arreglo unidimensional
• Ingresar el dato que se desea buscar
• Comparar el dato ingresado con cada uno de los datos del arreglo.
• Si el dato ha sido hallado mostrar la posición donde se encontró a dicho dato.
Argumentos que recibe: dato, longitud
Procedimiento Buscar (Vec:array, dato:integer, n:entero)
Var i,a: entero
a=0
i=0
Mientras (i<=n) hacer
Si (vec[i]=dato) entonces
a=i
• Para la posición del valor encontrado
i=n
• Para salir de la condición mientras
Fin Si
i=i+1
Fin Mientras
Si (a=0) entonces
Escribir (“Lo sentimos, el dato no ha sido encontrado...”)
Sino
Escribir (“El Dato se encontró en la posición: “,a)
Fin Si
Fin procedimiento
Imprimir datos en un arreglo unidimensional
Mostramos el dato de cada posición del vector unidimensional.
Argumentos que recibe: dato, longitud.
Procedimiento Insertar (VAR Vec:array, pos:entero)
dato:entero, i:entero Declaración de variables
Para i=1 a pos hacer
Imprimir Vec[i]
• Imprime los elementos del vector.
Fin Para
Fin Procedimiento
Sumar los datos de un arreglo unidimensional
Suma de elementos de Array
Sintaxis
• S = sum(A)
• S = sum(A,dim)
• S = sum(___,outtype)
• S = sum(___,nanflag)
Descripción
S = sum(A) devuelve la suma de los elementos de A a lo largo de la primera dimensión del
array cuyo tamaño no es igual a 1.
• Si A es un vector, sum(A) devuelve la suma de los elementos.
• Si A es una matriz, sum(A) devuelve un vector de fila que contiene la suma de cada
columna.
• Si A es un array multidimensional, sum(A) opera a lo largo de la primera dimensión del
array cuyo tamaño no sea igual a 1, tratando los elementos como vectores. Esta
dimensión se convierte en 1, mientras que los tamaños de todas las demás dimensiones
permanecen iguales.
S = sum(A,dim) devuelve la suma a lo largo de la dimensión dim. Por ejemplo, si A es una
matriz, sum(A,2) es un vector de columna que contiene la suma de cada fila.
S = sum(___,outtype) devuelve la suma con un tipo de datos especificado, utilizando cualquiera
de los argumentos de entrada de las sintaxis anteriores. outtype puede ser 'default', 'double'
o 'native'.
S = sum(___,nanflag) especifica si se deben incluir u omitir los valores NaN en el cálculo en
cualquiera de las sintaxis anteriores. sum(A,'includenan') incluye todos los valores NaN en
el cálculo, mientras que sum(A,'omitnan') los ignora.
Ejemplos
Suma de elementos vectoriales
Cree un vector y calcule la suma de sus elementos.
A = 1:10;
S = sum(A)
S = 55
Suma de columnas matriciales
Cree una matriz y calcule la suma de los elementos de cada columna.
A = [1 3 2; 4 2 5; 6 1 4]
A = 3×3
1 3 2
4 2 5
6 1 4
S = sum(A)
S = 1×3
11 6 11
Suma de filas matriciales
Cree una matriz y calcule la suma de los elementos de cada fila.
A = [1 3 2; 4 2 5; 6 1 4]
A = 3×3
1 3 2
4 2 5
6 1 4
S = sum(A,2)
S = 3×1
6
11
11
Suma de un array tridimensional
Cree un array de unos de 4 por 2 por 3 y calcule la suma a lo largo de la tercera dimensión.
A = ones(4,2,3);
S = sum(A,3)
S = 4×2

3 3
3 3
3 3
3 3

Suma de enteros de 32 bits


Cree un vector de enteros de 32 bits y calcule la
suma int32 de sus elementos especificando el tipo
de salida como native.
A = int32(1:10);
S = sum(A,'native')
S = int32
55
Suma excluyendo NaN
Cree un vector y calcule su suma, excluyendo los valores NaN.
A = [1.77 -0.005 3.98 -2.95 NaN 0.34 NaN 0.19];
S = sum(A,'omitnan')
S = 3.3250
Si no especifica 'omitnan', sum(A) devuelve NaN.
Argumentos de entrada
A — Array de entrada
Array de entrada, especificado como vector, matriz o array
multidimensional.
Si A es un escalar, sum(A) devuelve A.
Si A es una matriz vacía de 0 por 0, sum(A) devuelve 0.
dim — Dimensión en la que operar
escalar entero positivo
Dimensión en la que operar, especificada como un escalar entero positivo. Si no se especifica
ningún valor, la opción predeterminada es la primera dimensión del array cuyo tamaño no sea
igual a 1.
La dimensión dim indica la dimensión cuya longitud se reduce a 1. size(S,dim) es 1, mientras
que los tamaños de todas las demás dimensiones permanecen iguales.
Considere un array de entrada de dos dimensiones, A:
sum(A,1) opera con los elementos sucesivos en las columnas de A y devuelve un vector de fila
de las sumas de cada columna.

sum(A,2) opera con los elementos sucesivos en las filas de A y devuelve un vector de columna
de las sumas de cada fila.

sum devuelve A cuando dim es mayor que ndims(A) o cuando size(A,dim) es 1.

outtype — Tipo de datos de salida


'default' (predeterminado) | 'double' | 'native'
Tipo de datos de salida, especificado como 'default', 'double' o 'native'. Estas opciones también
especifican el tipo de datos en el que se realiza la operación.
outtype Tipo de datos de salida
double, a menos que el tipo de datos de entrada sea single o duration, en
'default'
cuyo caso la salida es 'native'
double, a menos que el tipo de datos sea duration, en cuyo caso no se
'double'
admite 'double'
el mismo tipo de datos que la entrada, a menos que el tipo de datos de
'native'
entrada sea char, en cuyo caso 'native' no se admite
Tipos de datos: char
nanflag — Condición NaN
‘includenan’ (predeterminado) | ‘omitnan’
Condición NaN, especificada como uno de estos valores:
• 'includenan': incluir los valores NaN al calcular la suma, resultando en NaN.
• 'omitnan': ignorar todos los valores NaN de la entrada.
Tipos de datos: char
Valores de los datos contenidos en un arreglo unidimensional
Estructuras de datos
Hasta ahora se han usado datos que representan valores de
tipo simple como un número entero, real o carácter.
Sin embargo, en muchas situaciones se necesita procesar
un conjunto de valores que están relacionados entre sí
por algún método, por ejemplo, una lista de
calificaciones, una serie de temperaturas.
En este caso, el procesamiento con datos simples se hace
muy difícil, por lo que la mayoría de los lenguajes de
programación incluyen
características de estructuras de
datos
En computación, una estructura de
datos es una manera de almacenar
información (datos) en un computador
de manera que puedan ser usados de una
manera eficiente. Una selección
cuidadosa de la estructura permitirá
usar un algoritmo más eficiente. Una
estructura bien diseñada permitirá
efectuar una variedad de operaciones, usando un mínimo de tiempo de ejecución y espacio de
memoria.
Los tipos de datos más utilizados son:
• Entero
• Real
Datos SIMPLES
• Carácter
• Lógico
• Arreglos
Datos ESTRUCTURADOS • Registros
• Archivos
Las estructuras de datos estáticas son aquellas en las que el tamaño ocupado en memoria se
define antes de que el programa se ejecute y no puede modificarse dicho tamaño durante la
ejecución del programa.
Las estructuras dinámicas pueden ser definidas en tiempo de ejecución y la limitación sería el
tamaño de la memoria disponible.
Arreglos Unidimensionales - NDimensionales.
Un arreglo es una secuencia de posiciones consecutivas de memoria que almacenan datos del
mismo tipo. Estos datos comparten un nombre común.
En cuanto a su dimensión, los arreglos se pueden clasificar como:
Unidimensionales:
Vector, lista, matriz de una dimensión.
Ej: Un vector denominado ventas, de 10 elementos, se puede representar como:

ventas [1] ventas [2] ventas [3] ……… ventas [10]

El subíndice de cada elemento designa su posición en la ordenación del vector. Se observa que
todos los elementos comparten el nombre y que cada elemento se referencia por su subíndice o
sea su posición relativa en el vector.
Declaración de un arreglo Unidimensional:
Estático:

tipo nombre [dimensión]


Ej: la instrucción entero x [8] declara un arreglo de nombre x, de 8
elementos de tipo entero.
Bidimensionales (Tabla, Matriz):
Se pueden considerar como un vector de
vectores. En este caso se necesita especificar dos
subíndices para identificar cada elemento del
arreglo: El primer subíndice se refiere a las filas ( i ) y
el segundo a las columnas ( j ) .
Declaración de un arreglo de dos dimensiones:

Estático:
tipo nombre [filas, columnas]
Ej: entero A [3,3] declara un arreglo de datos tipo entero de 3 filas y tres columnas.
Almacenamiento de arreglos en memoria
Arreglos de Una y dos dimensiones se representan como se muestra:
La memoria de la computadora es unidimensional, por lo que el almacenamiento de los arreglos
de más de una dimensión requiere que la posición de los elementos del arreglo sea "linealizada".
La forma más común de almacenamiento de vectores de dos dimensiones es por filas, así un
vector A [3,4] se almacenaría de la manera siguiente:

1 2 3 4 5 6 7 8 9

A[1,1] A[1,2] A[1,3] A[1,4] A[2,1] A[2,2] A[2,3] A[2,4] A[3,1] ……

La posición de un elemento A[i,j] del arreglo A[3,4] de dimensiones [m,n] con relación al
primer elemento es: Posición = n*(i -1) + j
Así la posición dentro del arreglo del elemento A [2,3] del ejemplo anterior sería:
m = 3, n = 4, i = 2, j = 3 Posición = 4 * (2 - 1) + 3 = 7
Operaciones sobre arreglos
Las operaciones que se pueden realizar con arreglos durante el proceso de resolución de un
problema son:
• Asignación
• Lectura / Escritura
• Recorrido
• Búsqueda
• Ordenamiento.
Asignación:
La asignación de valores a un elemento de un arreglo se representa con la instrucción:
A [10] = 3 / asigna el valor 3 al elemento 10 del vector A
Ventas [2,2] = 1500
Si se desea asignar valores a todos los elementos de un vector, se debe usar estructuras de
repetición.
• Caso Unidimensional: Asignar el valor 6 a todos los elementos de un vector A[5]
repetir_desde ( i = 1; i <= 5 ; i=i+1)
A[i] = 6
fin_repetir_desde
• Caso Bidimensional: Inicializar un vector B[2,3] con el valor cero.
repetir_desde ( i = 1; i <= 2 ; i=i+1)
repetir_desde ( j = 1; j <= 3 ; j=j+1)
B[i,j] = 0
fin_repetir_desde
fin_repetir_desde
Lectura / Escritura:
La lectura/escritura de datos en arreglos u operaciones de entrada/salida, normalmente se
realizan con estructuras repetitivas o selectivas. Las instrucciones simples de lectura/escritura
se representan como:
leer(Nombre_del_arreglo[Indice])
mostrar(Nombre_del_arreglo[Indice])
Ej : leer(X[3]) / Lee el elemento 3 del vector X
Recorrido:
A la operación de efectuar alguna acción sobre todos los elementos del vector se le llama
recorrido. Estas operaciones se realizan usando estructuras de repetición, cuyas variables de
control se usan como índices del vector. Se puede realizar esta operación para introducir datos
al vector (leer) o para ver su contenido (mostrar).
Ejemplo 1: Lectura de los 10 valores de un vector P.

Ejemplo 2: El siguiente algoritmo lee las notas del primer examen de Computación de una
sección de 40 alumnos, a fin de calcular el promedio.

Si se deseara mostrar la cantidad de alumnos con notas superiores al promedio se agregan las
siguientes líneas al algoritmo anterior:
Ejemplo 3: Leer una matriz de dos dimensiones A [5,4].
Dado que es una matriz de dos dimensiones, se necesitan dos bucles anidados para recorrer
todos sus elementos.

Ejemplo 4: Inicializar una matriz de dos dimensiones con valor constante 0.


El algoritmo debe asignar la constante 0 a todos los elementos de la matriz A[5,4].
Dado que es una matriz de dos dimensiones, se necesitan dos bucles anidados para recorrer
todos sus elementos:

Ejemplo 5: Realizar la suma de dos matrices bidimensionales.


Para sumar dos matrices es preciso que las dos matrices tengan las mismas dimensiones. La
matriz suma [I, J] tendrá las mismas dimensiones de las matrices sumandos y cada elemento
será la suma de los mismos elementos correspondientes en las matrices sumandos: suma[I,J] =
A[I,J] + B[I,J].
Dado que las matrices son de dos dimensiones se requieren dos bucles anidados:

Ejemplo 6: Diseñar un algoritmo que genere una matriz identidad de orden n.


• La matriz identidad tiene todos los elementos de la diagonal principal igual a uno (1), todos
los demás elementos son igual a cero (0).
• En los elementos de la diagonal principal I = J.
Algoritmos Básicos de Búsqueda y Ordenamiento
Búsqueda:
La operación de búsqueda es una de las tareas más comunes en computación y básicamente
consiste en encontrar la posición de un elemento específico en un conjunto de elementos dados.
Búsqueda secuencial:
Suponemos una lista (vector) de elementos, donde no hay elementos repetidos; la forma más
sencilla de buscar un elemento específico es recorriendo la lista y verificando si existe alguna
coincidencia entre los elementos de la lista y el elemento buscado.
Ejemplo: Suponemos se desea buscar un nombre en una lista de n elementos. El algoritmo debe
mostrar los mensajes:
• nombre encontrado, si el nombre está en la lista.
• nombre no existe, si no se encuentra el nombre.
Se supone que no hay elementos repetidos.
Análisis: Se requiere leer el número de elementos ( n ) y el elemento a buscar. Se debe recorrer
toda la lista y preguntar si cada elemento de la lista es el que estamos buscando, para lo cual se
requiere un ciclo con contador (i - desde 1 hasta n) y una estructura de decisión para confirmar
la condición del elemento buscado.
Se usa además una variable tipo interruptor (sw) , la cual se incializa en cero antes de comenzar
el ciclo de búsqueda y se cambia a uno cuando se encuentra el nombre buscado.
Diseño del algoritmo:
Búsqueda Menor / Mayor:
El problema consiste en buscar el elemento menor/mayor de un conjunto de elementos
almacenados en un arreglo. Por ejemplo, buscar el elemento mayor del vector A mostrado:

Análisis:
La estrategia a seguir consiste en asignar la condición deseada (MAYOR) al primer elemento
de la lista (A[1]) y se empieza a comparar con todos los elementos de la lista. Si alguno de los
elementos resulta mayor que el elemento al cual se le ha asignado la condición, se cambia la
condición al nuevo elemento. Al terminar de recorrer todo el vector, el valor que mantiene la
condición deseada es el mayor.
Los resultados sobre el ejemplo se podrían ver como sigue:

Diseño del algoritmo


Métodos de ordenación
La ordenación es una aplicación fundamental en computación. La mayoría de los datos
producidos por un programa están ordenados de alguna manera, y muchos de los cómputos que
tiene que realizar un programa son más eficientes
si los datos sobre los que operan están ordenados.
Uno de los tipos de cómputo que más se
benefician de operar sobre un conjunto de datos
ordenados es la búsqueda de un dato: encontrar el
número de teléfono de una persona en un listín
telefónico es una tarea muy simple y rápida si
conocemos su nombre, ya que los listines
telefónicos se encuentran ordenados
alfabéticamente.
Las operaciones de ordenación y búsqueda suelen clasificarse en dos tipos:
• Internas: todos los datos a
procesar residen en la
memoria principal.
• Externas: los otros
procesos residen en un
dispositivo de
almacenamiento masivo,
de acceso lento. Su
volumen es demasiado
elevado para trasladarlos todos a la memoria principal, lo cual fuerza realizar numerosos
accesos al dispositivo de almacenamiento masivo.
En este tema nos centraremos en las operaciones internas. Un factor clave en cualquier
algoritmo de búsqueda u ordenación es su complejidad computacional, y el cómo esta depende
del número de datos a procesar. Habitualmente, cuando nos enfrentamos una tarea de
ordenación o búsqueda con un ordenador el volumen de datos de entrada será enorme y es
importante contar con algoritmos que no degraden considerablemente su rendimiento con el
tamaño del conjunto de datos. Por ello, antes de
abordar algoritmos de ordenación y búsqueda
realizaremos una pequeña introducción al cálculo de
la complejidad computacional de un algoritmo.
Habitualmente, un mismo problema puede tener
numerosas soluciones que tienen distinta eficiencia
(rapidez de ejecución). Así, por ejemplo, un problema
tan fácil como el multiplicar dos números enteros es
resuelto mediante dos algoritmos diferentes en
Inglaterra y el resto de Europa. Otra forma mucho
más curiosa de realizar la multiplicación de los números enteros es el método ruso; un método
más complejo que los anteriores y que requiere realizar un número de operaciones matemáticas
bastante superior, aunque obtiene el mismo resultado:

Cuando seleccionamos un algoritmo para resolver un determinado problema es importante


determinar los recursos computacionales necesarios (tiempo de computación y espacio de
almacenamiento en memoria) en función del volumen de datos a procesar. La eficiencia de un
algoritmo no puede medirse en unidades de tiempo, ya que esto obligaría a realizar medidas
empíricas sobre una implementación concreta, un compilador concreto y una máquina concreta.
¿Qué es ordenamiento?
Es la operación de arreglar los registros de una tabla en
algún orden secuencial de acuerdo a un criterio de
ordenamiento. El ordenamiento se efectúa con base en el
valor de algún campo en un registro. El propósito
principal de un ordenamiento es el de facilitar las
búsquedas de los miembros del conjunto ordenado.
Los 2 tipos de ordenamientos que se pueden realizar son:
los internos y los externos.
Los internos:
Son aquellos en los que los valores a ordenar están en la
memoria principal, por lo que se asume que el tiempo que
se requiere para acceder cualquier elemento sea el mismo
(vec[1], vec[500], etc.).
Los externos:
Son aquellos en los que los valores a ordenar están en
memoria secundaria (disco, cinta, cilindro magnético,
etc.), por lo que se asume que el tiempo que se requiere
para acceder a cualquier elemento depende de la última
posición accedida (posición 1, posición 500, etc.).
Clasificación de los algoritmos de ordenamiento de Información:
El hecho de que la información está ordenada, nos sirve para poder encontrarla y accederla de
manera más eficiente ya que de lo contrario se tendría que hacer de manera secuencial.
A continuación, se describirán 4 grupos de algoritmos para ordenar información:
Algoritmos de inserción:
En este tipo de algoritmo los elementos que van a ser ordenados son considerados uno a la vez.
Cada elemento es INSERTADO en la posición apropiada con respecto al resto
de los elementos ya ordenados. Entre estos algoritmos se encuentran el
de INSERCION DIRECTA, SHELL SORT, INSERCION
BINARIA y HASHING.
Algoritmos de intercambio:
En este tipo de algoritmos se toman los elementos de dos en dos,
se comparan y se INTERCAMBIAN si no están en el orden
adecuado. Este proceso se repite hasta que se ha analizado todo el
conjunto de elementos y ya no hay intercambios.
Entre estos algoritmos se encuentran el BURBUJA y QUICK
SORT.
Algoritmos de selección
En este tipo de algoritmos se SELECCIONA o se busca el elemento más pequeño (o más
grande) de todo el conjunto de elementos y se coloca en su posición adecuada. Este proceso se
repite para el resto de los elementos hasta que todos son analizados.
Entre estos algoritmos se encuentra el de SELECCION DIRECTA.
Algoritmos de enumeración:
En este tipo de algoritmos cada elemento es comparado contra los demás. En la comparación
se cuenta cuántos elementos son más pequeños que el elemento que se está analizando,
generando así una ENUMERACION. El número generado para cada elemento indicará su
posición.
Los métodos simples son: Inserción (o por inserción directa), selección, burbuja y shell, en
dónde el último es una extensión del método de inserción, siendo más rápido. Los métodos más
complejos son el quick-sort (ordenación rápida) y el heapsort.
Método de la Burbuja
Este método consiste en ordenar el arreglo moviendo dato mayor hasta la última posición,
comenzando desde la casilla cero del arreglo hasta haber enviado el numeral grande a la última
posición, una vez acomodado demás grande, prosigue a encontrar y acomodar siguiente más
grande comparando de nuevo los números desde el inicio del arreglo, y así se sigue hasta
ordenar todos los elementos del arreglo.
Método Directo (Método burbuja1)
Argumentos que recibe: vector a ordenar y longitud del arreglo.

Método Burbuja
Argumentos que recibe: vector a ordenar y longitud del arreglo.
Métodos de ordenación por selección
El ordenamiento por selección (Selection Sort en inglés) es un algoritmo de ordenamiento que
requiere n2 operaciones para ordenar una lista de n elementos.
Su funcionamiento es el siguiente:
• Buscar el mínimo elemento de la lista.
• Intercambiarlo con el primero.
• Luego, buscar el mínimo en el resto de la lista.
• Intercambiarlo con el segundo y así sucesivamente.
En general:
• Buscar el mínimo elemento entre una posición i y el final de la lista
• Intercambiar el mínimo con el elemento de la posición i
Ventajas:
Este algoritmo mejora ligeramente el algoritmo de la burbuja. En el caso de tener que ordenar
un vector de enteros, esta mejora no es muy sustancial, pero cuando hay que ordenar un vector
de estructuras más complejas, la operación intercambiar () sería más costosa en este caso. Este
algoritmo realiza menos operaciones intercambiar () que el de la burbuja, por lo que lo mejora
en algo.
Desventajas:
• Realiza numerosas comparaciones.
• Este es un algoritmo lento. No obstante, ya que sólo realiza un intercambio en cada
ejecución del ciclo externo, puede ser una buena opción para listas con registros grandes
y claves pequeñas.
Método Selección
Argumentos que recibe: Vector a ordenar y longitud del arreglo.

Ejemplo:
El arreglo a ordenar es A = ['a','s','o','r','t','i','n','g','e','x','a','m','p','l','e'].
Se empieza por recorrer el arreglo hasta encontrar el menor elemento. En este caso el
menor elemento es la primera 'a'. De manera que no ocurre ningún cambio. Luego se
procede a buscar el siguiente elemento y se encuentra la segunda 'a'.
Esta se intercambia con el dato que está en la segunda posición, la 's', quedando el
arreglo así después de dos recorridos:
a = ['a','a','o','r','t','i','n','g','e','x','s','m','p','l','e'].
El siguiente elemento, el tercero en orden de menor mayor es la primera 'e', la cual se
intercambia con lo que está en la tercera posición, o sea, la 'o'. Le sigue la segunda 's',
la cual es intercambiada con la 'r', quedando el arreglo así
a = ['a','a','e','e','t','i','n','g','o','x','s','m','p','l','r'].
De esta manera se va buscando el elemento que debe ir en la siguiente posición hasta
ordenar todo el arreglo.
Método de Ordenamiento Shell
El ordenamiento Shell (Shell sort en inglés) es un algoritmo de ordenamiento.

El método se denomina Shell en honor de su inventor Donald Shell. Su implementación


original, requiere (n2) comparaciones e intercambios en el peor caso. Un cambio menor
presentado en el libro de V. Pratt produce una implementación con un rendimiento de O(nlog2n)
en el peor caso. Esto es mejor que las O(n2) comparaciones requeridas por algoritmos simples
pero peor que el óptimo O(n log n). Aunque es fácil desarrollar un sentido intuitivo de cómo
funciona este algoritmo, es muy difícil analizar su tiempo de ejecución.
El Shell sort es una generalización del ordenamiento por inserción, teniendo en cuenta dos
observaciones:
1. El ordenamiento por inserción es eficiente si la entrada está "casi ordenada".
1. El ordenamiento por inserción es ineficiente, en general, porque mueve los valores sólo
una posición cada vez.
El algoritmo Shell sort mejora el ordenamiento por inserción comparando elementos separados
por un espacio de varias posiciones. Esto permite que un elemento haga "pasos más grandes"
hacia su posición esperada. Los pasos múltiples sobre los datos se hacen con tamaños de espacio
cada vez más pequeños. El último paso del Shell sort es un simple ordenamiento por inserción,
pero para entonces, ya está garantizado que los datos del vector están casi ordenados.
Ventajas:
• No requiere memoria adicional.
• Mejor rendimiento que el método de Inserción
clásico
• Es inestable no mantiene el orden relativo de los
registros.
Desventajas:
• Los algoritmos eficientes tienden a ser más
complejos que los ineficientes por lo que son más
difíciles de expresar con palabras.
• Aun siendo la ordenación Shell tan eficiente como
es, la ordenación rápida (Quick Sort) es 2 o 3 veces
más eficiente.
Método Shell
Argumentos que recibe: Vector a ordenar y longitud del arreglo.

Métodos de búsqueda.
Método de Búsqueda Secuencial:
La búsqueda secuencial, también se le conoce como
búsqueda lineal. Este método consiste en recorrer el
arreglo o vector elemento a elemento e ir comparando con
el valor buscado (clave). Se empieza con la primera casilla
del vector y se observa una casilla tras otra hasta que se
encuentre el elemento buscado o se hayan visto todas las
casillas. El resultado de la búsqueda es un solo valor, y
será la posición del elemento buscado o cero.
Dado que el vector o arreglo no está en ningún orden en
particular, existe la misma probabilidad de que el valor se
encuentra ya sea en el primer elemento, como en el último.
Por lo tanto, en promedio, el programa tendrá que
comparar el valor buscado con la mitad de los elementos
del vector. El método de búsqueda lineal funciona bien con
arreglos pequeños o para arreglos no ordenados.
Ventajas:
Es un método sumamente simple que resulta útil cuando se tiene un conjunto de datos pequeños
(Hasta aproximadamente 500 elementos). Es fácil adaptar la búsqueda secuencial para que
utilice una lista enlazada ordenada, lo que hace la búsqueda más eficaz. Si los datos buscados
no están en orden es el único método que puede emplearse para hacer dichas búsquedas.
Desventajas:
• El método tiende hacer muy lento.
• Si los valores de la clave no son únicos, para encontrar todos los elementos con una
clave particular, se requiere buscar en todo el arreglo, lo que hace el proceso muy largo.
Método búsqueda Secuencial
Argumentos que recibe: Vector de búsqueda y longitud del arreglo.

Método de Búsqueda Binaria:


La operación de búsqueda puede ser mucho más eficiente si se sabe que los datos están
previamente ordenados. Un ejemplo de ordenación es un diccionario, en el que hacemos una
búsqueda gracias a la ordenación alfabética de las palabras. En otro caso sería completamente
inutilizable.
La idea consiste en comparar un elemento cualquiera, de índice m (normalmente a la mitad) e
ir comprando con los demás argumentos de la izquierda o derecha según sea el caso. El
resultado de la búsqueda es un solo valor, y será la posición del elemento buscado o cero.
Los posibles casos son:
• Vec(m)=X, ha terminado la búsqueda y el índice buscado es m.
• Vec(m)<X, sabemos que todos los elementos a la izquierda de m son menores que X, y
por lo tanto puede ser eliminada esta región de la zona de búsqueda y considerar solo la
zona derecha (desde m+1 a N).
• Vec(m)>X, sabemos que todos los elementos de la derecha de m son mayores que X, y
por lo tanto puede ser eliminada esta región de la zona de búsqueda y considerar solo la
zona izquierda (desde1 a m-1).
• La repetición de este proceso de forma iterativa constituye este algoritmo. Para ello, se
utilizan dos variables de índice VP y VS que marcan los extremos izquierdo y derecho
de la zona de búsqueda considerada.
Método Búsqueda Dicotómica
Argumentos que recibe: Vector de búsqueda y longitud del arreglo.

Algoritmos de búsqueda: Un problema de búsqueda puede enunciarse del siguiente modo:


dado un conjunto de elementos CB (Conjunto Búsqueda) de un cierto tipo determinar si un
elemento ("dato") se encuentra en el conjunto o no.
Existen diferentes algoritmos de búsqueda y la elección depende de la forma en que se
encuentren organizados los datos: si se encuentran ordenados o si se ignora su disposición o se
sabe que están al azar. También depende de si los datos a ordenar pueden ser accedidos de modo
aleatorio o deben ser accedidos de modo secuencial.
Búsqueda secuencial
Es el algoritmo de búsqueda más simple,
menos eficiente y que menos
precondiciones requiere: no requiere
conocimientos sobre el conjunto de
búsqueda ni acceso aleatorio.
Consiste en comparar cada elemento del
conjunto de búsqueda con el valor
deseado hasta que éste sea encontrado o
hasta que se termine de leer el conjunto.
Supondremos que los datos están
almacenados en un array y se asumirá
acceso secuencial.
Se pueden considerar dos variantes del
método: con y sin centinela.
Búsqueda sin centinela
El algoritmo simplemente recorre el array comparando cada elemento con el dato que se está
buscando:
Operaciones con arreglos multidimensionales
Arreglos bidimensionales (tablas/matrices)
El array bidimensional se puede considerar como un vector de vectores. Es, por consiguiente,
un conjunto de elementos, todos del mismo tipo, en el cual el orden de los componentes es
significativo y en el que se necesita especificar dos subíndices para poder identificar cada
elemento del array. Si se visualiza un array unidimensional, se puede considerar como una
columna de datos: un array bidimensional es un grupo de columnas. El diagrama representa
una tabla o matriz de treinta elementos (5 x 6) con 5 filas y 6 columnas.

Declaración de una matriz:


Los arreglos dimensionales se declaran de la siguiente manera:
TIPO identificador=ARRAY [indice1, indice2]OFtipo;
Donde:
• Identificador: es el nombre del arreglo
• Indice1: tipo enumerado o tipo sub rango, representa la cantidad de filas.
• Indice2: tipo enumerado o tipo sub rango, representa la cantidad de columnas. tipo: se
refiere al tipo de los elementos y puede ser de cualquiera de los tipos estándar o definido
por el usuario.
Ejemplo:
TYPE
Estudiantes = array[1..37, 1..5] of real
Cuenta = array[1..10,’a’..’z’] of integer
Consola = array[0..4,0..8] of char
VAR
Cálculos: Cuenta
Códigos: Consola
Clase: Estudiantes
Operaciones con arreglos dimensionales o matrices:
LECTURA: Tienen que utilizarse estructuras de repetición para leer los elementos del arreglo.
Procedimiento Leer Matriz (varMat:matriz, indfil:entero,indcol:entero)
Variables i, j: entero
Para i=1 a indfil hacer
Para j = 1 a indcol hacer
Leer Mat[i,j]
Fin Para
Fin Procedimiento
Escritura:
Procedimiento CargaMatriz (varMat:matriz, indfil:entero, indcol:entero)
Variables i, j, dato: entero
Para i=1 a indfil hacer
Para j = 1 a indcol hacer
Leer dato
Mat[i,j]=dato
Fin ParaFin Procedimiento
Sumar los datos de un arreglo dimensional
Para sumar los datos de un arreglo dimensional:
• Se deberá recorrer todos los datos del arreglo dimensional (Usar la estructura FOR)
• Dentro de la estructura repetitiva, acumular cada uno de los datos del arreglo
dimensional en un variable acumulador numérico.
• Retornar el valor acumulado en la variable numérica.
Método Sumar
Tipo de dato que retorna como respuesta: doublé.
FuncionSumar(Mat:matriz, indfil:entero, indcol:entero): real
acum:real *Declaración de variables
i: entero
acum=0 *Inicio del acumulador
Para i=1 a indfil hacer *Recorre el arreglo
Para j =1 a indcol hacer
acum= acum + Mat[i,j] *Acumulador de suma de los valores
Fin Para
Fin Para
SUMAR=acum *Asignación a la función
Fin función
Resumes de la Unidad 2

Cuando hablamos de programación, la estructura de datos está representada por una forma
determinada que tenemos de organizar los datos de un equipo informático para que podamos
utilizarlos de la manera más efectiva posible. Dependiendo del tipo de aplicación o recurso que
vayamos a usar requeriremos una estructura de datos independiente y distinta a las demás, dado
que cada una encaja en el contexto de forma determinada y con una serie de objetivos.
Con estas estructuras tenemos la posibilidad de administrar todo tipo de datos sin ningún tipo
de obstáculo, algo que en la actualidad se usa en la red para poder llevar a cabo, por ejemplo,
los sistemas de indexado de contenidos. Y también juegan un papel clave en la creación de los
mejores algoritmos, así como en su uso con lenguajes de programación que se benefician de
ellas. Lo primero que debemos tener claro es la definición de “estructura de datos” y que a
partir de esta definición parten otros conceptos y tipos. Que las estructuras de datos se estudian
como conceptos sobre programación, y no sobre un lenguaje de programación en específico,
por lo que cada lenguaje puede tener diferentes implementaciones de estructuras de datos.
Una “estructura de datos” es una colección de valores, la relación que existe entre estos valores
y las operaciones que podemos hacer sobre ellos; en pocas palabras se refiere a cómo los datos
están organizados y cómo se pueden administrar. Una estructura de datos describe el formato
en que los valores van a ser almacenados, cómo van a ser accedidos y modificados, pudiendo
así existir una gran cantidad de estructuras de datos.
Los datos se representan en la memoria, las estructuras contiguamente asignadas están
compuestas de bloques de memoria únicos, e incluyen a los arrays, matrices, heaps, y hash
tables. las estructuras enlazadas están compuestas de distintos fragmentos de memoria unidos
por pointers o punteros, e incluyen a los lists, trees, y graphs y los contenedores son estructuras
que permiten almacenar y recuperar datos en un orden determinado sin importar su contenido,
en esta se incluyen los stacks y queues. Esta estructura es “la” fundamental de las estructuras
contiguamente asignadas. Arrays ó arreglos son estructuras de datos de tamaño fijo de modo
que cada elemento puede ser eficientemente ubicado por su index (índice) o dirección.
La magia de las estructuras enlazadas es dada por los pointers o punteros, que como su nombre
lo indica apuntan a una dirección de memoria donde se encuentra ubicado un valor
Una diferencia grande entre los Arrays vs linked lists es que insertar o eliminar de una linked
list es más fácil ya que no tiene tamaño fijo y lo único que debemos hacer para insertar o
eliminar un valor es simplemente apuntar al nuevo nodo creado, o apuntar al siguiente nodo de
la lista si un nodo fue eliminado. En un array no existe esta flexibilidad.
En este contexto árboles y grafos se refiere a estructuras de datos que permiten organizar y
mantener información en un computador. Esta forma se inspira una forma de organizar
información con lápiz y papel usando nodos y flechas entre los nodos (a esas flechas también
se les llama arcos, a los nodos también se les llama vértices). Los grafos y árboles en papel son
apropiados por ejemplo para capturar sólo una parte de la información de objetos, situaciones
y otros tipos de información.
El árbol es una estructura de datos fundamental en la informática, muy utilizada en todos sus
campos, porque se adapta a la representación natural de informaciones homogéneas
organizadas y de una gran comodidad y rapidez de manipulación.
En ciencias de la computación, un árbol binario es una estructura de datos en la cual cada nodo
siempre tiene un hijo izquierdo y un hijo derecho. No pueden tener más de dos hijos (de ahí el
nombre "binario"). Si algún hijo tiene como referencia a null, es decir que no almacena ningún
dato, entonces este es llamado un nodo externo. En el caso contrario el hijo es llamado un nodo
interno. Usos comunes de los árboles binarios son los árboles binarios de búsqueda, los
montículos binarios y Codificación de Huffman.
Un grafo en el ámbito de las ciencias de la computación es una estructura de datos, en concreto
un tipo abstracto de datos (TAD), que consiste en un conjunto de nodo (también llamados
vértices) y un conjunto de arcos (aristas) que establecen relaciones entre los nodos. El concepto
de grafo TAD desciende directamente del concepto matemático de grafo.
Un arreglo unidimensional es un tipo de datos estructurado que está formado de una colección
finita y ordenada de datos del mismo tipo. Es la estructura natural para modelar listas de
elementos iguales.
La ordenación es una aplicación fundamental en computación. La mayoría de los datos
producidos por un programa están ordenados de alguna manera, y muchos de los cómputos que
tiene que realizar un programa son más eficientes si los datos sobre los que operan están
ordenados. Uno de los tipos de cómputo que más se benefician de operar sobre un conjunto de
datos ordenados es la búsqueda de un dato: encontrar el número de teléfono de una persona en
un listín telefónico es una tarea muy simple y rápida si conocemos su nombre, ya que los listines
telefónicos se encuentran ordenados alfabéticamente.
La búsqueda secuencial, también se le conoce como búsqueda lineal. Este método consiste en
recorrer el arreglo o vector elemento a elemento e ir comparando con el valor buscado (clave).
Se empieza con la primera casilla del vector y se observa una casilla tras otra hasta que se
encuentre el elemento buscado o se hayan visto todas las casillas. El resultado de la búsqueda
es un solo valor, y será la posición del elemento buscado o cero.

Lecturas Recomendadas
• http://www.algorist.com/
• http://sistemas.itlp.edu.mx/tutoriales/progorientobjetos/t11.htm
• http://www.ceidis.ula.ve/cursos/ingenieria/pd_10/clases/Apunt_.pdf
• http://sistemas.itlp.edu.mx/tutoriales/algoritmos/tema_71.htm

Actividades y/o Ejercicios


• Resolver mediante un pseudocódigo, un programa donde solicite las 5 notas de un
alumno (en un arreglo). Luego ordénelas de mayor a menor utilizando el método de
burbuja e indica en cuántos pasos lo realizó.
• Envía esta actividad a través de “Burbuja”.
• El ejercicio anterior (arreglo original), resolverlo mediante el método de inserción.
Compare la cantidad de pasos realizados por ambos e indique cuál es el más rápido.
• Envía esta actividad a través de “Inserción”.

También podría gustarte