Escolar Documentos
Profissional Documentos
Cultura Documentos
Contenido
Conceptos de programación Teoría y práctica
Ejercicios y problemas propuestos y resueltos
Evaluación
Recomendaciones
Si Usted esta cursando por primera vez la materia y quiere abordar las definiciones
específicas de términos y frases de la programación debería acceder a Conceptos de
programación, en particular el capitulo primero es allí donde se abordan estos temas.
Si ya conoce la terminología de la disciplina y necesita profundizar conocimientos sobre
técnicas de desarrollo de algoritmos debe acceder Conceptos de programación en los
capítulos segundo a cuarto. Analice con mucha atención todo lo desarrollado en el
capitulo referido a acciones y funciones y al capitulo referido a estructuras enlazadas si
su propósito es ver los temas finales y mas complejos de la materia. Para profundizar
sus conocimientos de patrones de algoritmia debe acceder al capitulo de algoritmos
puntuales donde encontrara un desarrollo detallado y con explicación paso a paso de la
secuenciación de las acciones para la solución de situaciones problemáticas particulares
Si lo que necesita es ejercitar para acompañar los conceptos teóricos de sus clases puede
acceder a Ejercicios , allí encontrara ejercitación y problemas, separados por nivel de
complejidad desde los mas simples hasta los exámenes finales mas recientes, alguno de
los cuales se presentan con estrategias de resolución, para orientarlo sobre los mismos.
Todo el Material esta debidamente enlazado para que Usted pueda interactuar con ellos.
Por ultimo puede acceder a su propia evaluación, diagnostica al principio y de
comprensión de cada uno de los módulos y de resolución de problemas. Para mayor
diversidad de problemas puede acceder al grupo o blog que se indica mas adelante, allí
se actualizan semanalmente las situaciones problemáticas distintas agrupadas por nivel
de complejidad para que Usted acceda según su propia necesidad y evolución.
El modulo de Evaluación le permite acceder a su propia evaluación, ya sea diagnostica
o según su evolución, sugiriéndole según una matriz de recomendaciones como
planificar su estudio en función del conocimiento demostrado.
Por último, usted puede contactarse conmigo vía correo electrónico a
oscarrbruno@yahoo.com, o suscribirse al grupo de apoyo a través un correo electrónico
a oscarrbruno-subscribe@gruposyahoo.com.ar Suerte en el esfuerzo, espero su consulta o
su crítica al material para la mejora del mismo Cordialmente, Oscar Ricardo Bruno
Agradezco a Karina Lion, Alejandra Tolcachier, Karina Cuzzani, Marcelo Giura y UTN
por orientarnos en la elaboración de material Didáctico
Prologo
De la experiencia en las aulas al libro de programación
He procurado volcar en esta publicación toda mi experiencia docente a lo largo de
muchos años en la UTN FRBA, E.T. Otto Krause, USAL y otros institutos terciarios y
universitarios.
El propósito es que no sea este otra publicación de programación, se propone un estilo
riguroso, científico y disciplinado pero al mismo tiempo con un enfoque practico y
realista para quien desea aprender las técnicas y el arte de la programación desde el
inicio y solidamente.
El desafió, uno de los más atractivos desde el punto de vista de la actividad académica,
es introducir al lector en los conceptos básicos de la disciplina para su formación
integral. Se busca profundizar sobre los aspectos conceptuales superando los límites de
la simple transmisión de los conocimientos para que pueda transformarse en una
enseñanza basada en la comprensión.
Se abordan los temas de especificación y representación de algoritmos combinándolo
con la utilización de las estructuras de datos mas adecuadas para cada situación
problemática planteada para extender los conceptos a tipos de datos derivados, definidos
por el usuario y tipos abstractos de datos.
En definitiva, el objetivo principal de esta publicación esta centrado en especificar y
expresar correctamente los algoritmos, analizando su eficiencia y corrección; el
tratamiento adecuado de las estructuras de datos hasta alcanzar el concepto de tipo
abstracto de dato y la contextualización de los algoritmos considerando los aspectos de
reusabilidad y documentación.
Las técnicas que se desarrollan son validas con independencia del lenguaje de
programación que pueda escoger. De ninguna manera se ve esta publicación como un
libro definitivo sino como un planteo ordenado de la experiencia docente en la
formación básica en técnicas de programación.
Por todo esto espero disfrute la lectura y que la misma sea de utilidad para el
aprendizaje comprensivo de algoritmia.
Oscar Ricardo Bruno
Contenido
INTRODUCCION _____________________________________________________ 8
CAPITULO 1 Conceptos básicos ________________________________________ 11
Introducción:______________________________________________________ 11
Informática _______________________________________________________ 11
Programación _____________________________________________________ 11
Partes de un programa______________________________________________ 12
Dato _____________________________________________________________ 13
Abstracción _______________________________________________________ 13
Modelizacion ______________________________________________________ 13
Precondición ______________________________________________________ 13
Poscondición ______________________________________________________ 13
Especificación _____________________________________________________ 13
Lenguaje de programación __________________________________________ 13
Del problema real a su solución por computadoras ______________________ 13
Características de un algoritmo ______________________________________ 16
Propiedades de los algoritmos ________________________________________ 17
Eficiencia de un algoritmo ___________________________________________ 18
Complejidades más comunes_________________________________________ 18
Léxico y algoritmo _________________________________________________ 18
Estructura de un algoritmo __________________________________________ 19
Proceso Computacional _____________________________________________ 19
CAPITULO 2 Asignación - Control – Iteración ____________________________ 23
Introducción:______________________________________________________ 23
Asignación, secuenciación y análisis de casos ___________________________ 23
Asignación. _______________________________________________________ 23
Análisis de casos ___________________________________________________ 24
La necesidad de iterar ______________________________________________ 28
Composiciones iterativas ____________________________________________ 29
Composiciones iterativas No Exactas __________________________________ 30
Composiciones iterativas Exactas _____________________________________ 30
Recursividad ______________________________________________________ 32
Iteración vs. Recursion______________________________________________ 33
Algoritmos que utilizan secuencias ____________________________________ 34
CAPITULO 3 Acciones y Funciones _____________________________________ 40
Introducción ______________________________________________________ 40
Modularizacion ____________________________________________________ 40
Módulos __________________________________________________________ 40
Alcance de los datos ________________________________________________ 41
Datos locales y globales _____________________________________________ 41
Ocultamiento y protección de datos ___________________________________ 41
Parámetros _______________________________________________________ 41
Integridad de los datos ______________________________________________ 41
Protección de datos_________________________________________________ 42
Uso de parámetros para retornar valores ______________________________ 42
Utilidad del uso de parámetros _______________________________________ 42
Reusabilidad ______________________________________________________ 42
Acciones __________________________________________________________ 42
Utilización de acciones ______________________________________________ 42
Acciones con parámetros ____________________________________________ 43
Abstracción y acciones ______________________________________________ 43
Tipos de Parámetros________________________________________________ 44
Beneficios del uso de acciones ________________________________________ 45
Funciones_________________________________________________________ 45
CAPITULO 4 Tipos de datos y Operadores ________________________________ 47
Introducción:______________________________________________________ 47
Variables _________________________________________________________ 47
Constante_________________________________________________________ 47
Tipos de Datos_____________________________________________________ 47
análisis Comparativo de estructuras __________________________________ 50
Criterio de selección de estructuras ___________________________________ 51
Comparación entre estructuras enlazadas lineales _______________________ 52
Operadores _______________________________________________________ 53
CAPITULO 5 Estructuras Enlazadas_____________________________________ 57
Introducción ______________________________________________________ 57
Estructuras enlazadas vs. estructuras indexadas ________________________ 57
Estructuras enlazadas con asignación dinámica en memoria ______________ 57
El tipo de dato Puntero______________________________________________ 59
Acceso a datos mediante apuntadores _________________________________ 61
Tipos de datos autorreferenciados o recursivos _________________________ 62
Estructuras de datos dinámicas lineales________________________________ 63
El tipo pila ________________________________________________________ 63
Insertar elemento en una pila:________________________________________ 65
Desapilar: leer y eliminar un elemento ________________________________ 65
El tipo cola________________________________________________________ 66
Añadir un elemento Encolar: ________________________________________ 66
Leer un elemento de una cola Eliminar primero: ________________________ 67
El tipo lista________________________________________________________ 67
Listas simplemente enlazadas ________________________________________ 67
Eliminar elementos en una lista ______________________________________ 68
Algoritmo de inserción ______________________________________________ 70
Listas circulares ___________________________________________________ 72
Operaciones básicas con listas circulares _______________________________ 73
Añadir un elemento ________________________________________________ 73
Eliminar un elemento de una lista circular _____________________________ 74
Listas doblemente enlazadas _________________________________________ 78
Operaciones básicas con listas doblemente enlazadas ____________________ 78
Añadir un elemento ________________________________________________ 78
Eliminar un elemento de una lista doblemente enlazada __________________ 81
A modo de síntesis con estructuras enlazadas ___________________________ 83
CAPITULO 6 Árboles _________________________________________________ 85
Introducción ______________________________________________________ 85
Árboles___________________________________________________________ 85
Definiciones comunes _______________________________________________ 86
árbol AVL ________________________________________________________ 93
Inserciones en árboles AVL __________________________________________ 94
Los árboles-B _____________________________________________________ 94
TABLAS HASHING (DISPERSION) _________________________________ 95
CAPITULO 7 ALGORITMOS _________________________________________ 108
Acciones y funciones para vectores___________________________________ 108
Acciones y funciones para archivos __________________________________ 117
Acciones y funciones para pilas______________________________________ 121
Acciones y funciones para Colas _____________________________________ 122
Acciones y funciones para Listas Ordenadas enlazadas __________________ 123
Acciones y funciones para arboles ___________________________________ 132
Inserciones en arboles AVL _________________________________________ 137
Funciones recursivas ______________________________________________ 138
Archivos en pascal ________________________________________________ 140
Ejemplo en C con aplicaciones de estructuras enlzadas __________________ 145
ANEXO 1 Representaciones gráficas de algoritmos________________________ 147
Diagrama de Nassi-Sneiderman _____________________________________ 147
Diagramas de Jackson _____________________________________________ 147
Diagramas de Lindsay._____________________________________________ 148
Llaves de Warniel _________________________________________________ 149
Notación Algoritmica ______________________________________________ 149
Equivalencias entre notación algorítmica y lenguajes de programación ____ 150
Estilos de Indentación _____________________________________________ 150
BIBLIOGRAFIA ____________________________________________________ 152
Objetivos específicos
1. Abordar los temas propios de la programación sin centrarse en un lenguaje
concreto.
2. Ir de la observación de la realidad a la especificación de los problemas y de allí a
los programas con técnicas creativas.
3. Presentar con claridad el tipo de razonamiento que lleva a la estrategia de
solución.
4. Conocer las estructuras de datos y tener capacidad para seleccionar la más
adecuada según el problema que se intenta resolver.
5. Agrupar y sistematizar los algoritmos puntuales de resolución de problemas.
Sugerencias
No es suficiente con los conocimientos teóricos o con el análisis de lo que otro escribió,
como toda actividad constructiva REQUIERE DE PRACTICA.
Pensar primero en soluciones algorítmicas no ejecutables para el análisis y comprensión
como paso previo a escribir código. Se debe procurar pensar sobre la solución antes de
comenzar a escribir programas evitando transformarse en un programador compulsivo.
Objetivos de aprendizaje
Dominando los temas del presente capitulo Usted podrá.
1. Conocer la terminología propia de la disciplina.
2. Definir y comprender claramente conceptos específicos muchas veces
mal definidos
3. Comprender el valor de la abstracción.
4. Dar valor a la eficiencia en las soluciones
5. Introducirse en la notación algorítmica y a la forma e encarar los
problemas de programación
Introducción:
Se presenta el alcance del presente trabajo y se introducen conceptos fundamentales de
algoritmia y programación, los que servirán de base para el desarrollo de los temas a
tratar.
Informática
Disciplina del estudio sistematizado de los procesos algorítmicos que describen y
transforman información, su teoría, análisis, diseño, eficiencia, implementación y
aplicación.
La informática es una disciplina científica, matemática y una ingeniería; tiene tres
formas de pensar propias: Teoría, abstracción y diseño.
Las tres se complementan para la resolución de la mayoría de los problemas.
Teoría: Con el pensamiento teórico se describen y prueban relaciones.
Abstracción: Recolección de datos y formulación de un modelo, se eliminan los
detalles irrelevantes.
Diseño: se tienen en cuenta requisitos, especificaciones y se diseñan o analizan
mecanismos para resolver problemas. Supone llevar a la práctica los resultados
teóricos.
Programación
La programación es una actividad transversal asociada a cualquier área de la
informática, aunque es la ingeniería del software el área específica que se ocupa de la
creación del software.
En principio la programación se veía como un arte, solo era cuestión de dominar un
lenguaje de programación y aplicar habilidades personales a la resolución de problemas,
casi en forma artesanal. El software era visto como algo desarrollado a través de la
intuición sin la utilización de métodos de diseño con técnicas para proceder en forma
sistemática y sin ningún control de su desarrollo. Con el reconocimiento de la
complejidad del desarrollo del software nació la ingeniería del software.
Se considero que al igual que cualquier otra disciplina la creación de software debía ser
reconocida como una actividad de ingeniería que requería la aplicación de sólidos
principios científicos.
La ingeniería del software es la disciplina que se ocupa de la aplicación del
conocimiento científico al diseño y construcción de programas de computación y a
todas las actividades asociadas de documentación, operación y mantenimiento, lo que
proporciona un enfoque sistemático.
La programación es una actividad en la que la creatividad juega un rol primordial
Programa:
Conjunto de instrucciones, ejecutables sobre una computadora, que permite cumplir una
función especifica. Se asocia al programa con una determinada función o requerimiento
a satisfacer por la ejecución del conjunto de instrucciones que lo forman. En general
alcanzan su objetivo en tiempo finito, aunque hay excepciones, por ejemplo los
programas de control de un sistema de alarma poseen requerimiento de tiempo infinito.
Un programa sin errores que se ejecuta puede no ser correcto si no cumple con los
requerimientos.
Definición
Programa: conjunto de instrucciones no activas almacenadas en un computador, se
vuelve tarea a partir de que se selecciona para su ejecución y permite cumplir una
función específica. Un proceso es un programa en ejecución.
En principio las tareas más importantes a la que se enfrenta quien debe escribir
programas en computadoras son:
1. Definir el conjunto de instrucciones cuya ejecución ordenada conduce a la
solución.
2. Elegir la representación adecuada de los datos del problema.
La función esencial del especialista informático es explotar el potencial de las
computadoras para resolver situaciones del mundo real. Para esto debe analizar los
problemas del mundo real, ser capaz de sintetizar sus aspectos principales y poder
especificar la función objetivo que se desee. Posteriormente debe expresar la solución
en forma de programa, manejando los datos del mundo real mediante una
representación valida para una computadora.
Partes de un programa
Los componentes básicos son las instrucciones y los datos. Las instrucciones o
sentencias representan las operaciones que se ejecutaran al interpretar el programa.
Todos los lenguajes de programación tienen un conjunto mínimo de operaciones que
son las de asignación, selección e iteración. Un lenguaje con solo estas tres
instrucciones permite escribir cualquier algoritmo.
Los datos son valores de información de los que se necesita disponer, en ocasiones
transformar para ejecutar la función del programa.
Los datos están representados simbólicamente por un nombre que se asocia con una
dirección única de memoria.
El contenido de la dirección de memoria correspondiente a un dato constante se asigna
solo una vez y solo puede ser modificado en una nueva compilación. En cambio el
contenido o valor de la dirección de memoria correspondiente a un dato variable puede
ser asignado o modificado en tiempo de ejecución.
Un programa se corresponde con una transformación de datos. A partir de un contexto
determinado por las precondiciones.
El programa transforma la información debiendo llegar al resultado esperado
produciendo el nuevo contexto caracterizado por las poscondiciones.
Dato
Representación de un objeto el mundo real mediante el cual se pueden modelizar
aspectos de un problema que se desea resolver con un programa en una computadora.
Definición
Dato representación de un objeto el mundo real mediante el cual se pueden modelizar
aspectos de un problema que se desea resolver con un programa en una computadora.
<dato> -> <objeto><atributo><valor>
Abstracción
Proceso de análisis del mundo real con el propósito de interpretar los aspectos
esenciales de un problema y expresarlo en términos precisos.
Modelización
Abstraer un problema del mundo real y simplificar su expresión, tratando de encontrar
los aspectos principales que se pueden resolver, requerimientos, los datos que se han de
procesar y el contexto del problema.
Precondición
Información conocida como verdadera antes de iniciar el programa.
Poscondición
Información que debiera ser verdadera al cumplir un programa, si se cumple
adecuadamente el requerimiento pedido.
Especificación
Proceso de analizar problemas del mundo real y determinar en forma clara y concreta el
objetivo que se desea. Especificar un problema significa establecer en forma univoca el
contexto, las precondiciones el resultado esperado, del cual se derivan las
poscondiciones.
Lenguaje de programación
Conjunto de instrucciones permitidas y definidas por sus reglas sintácticas y su valor
semántico para la expresión de soluciones de problemas.
Algoritmo
El termino algoritmo es en honor del matemático árabe del siglo IX, Abu Jafar
Mohamed ibn Musa Al Khowârizmî. Refiere conjunto de reglas, ordenadas de forma
lógica, finito y preciso para la solución de un problema, con utilización o no de un
computador.
En la actualidad al término se lo vincula fuertemente con la programación, como paso
previo a la realización de un programa de computación aunque en realidad es una
metodología de resolución presente en muchas de las actividades que se desarrolla a lo
largo de la vida.
Desde los primeros años de escuela se trabaja con algoritmos, en especial en el campo
de las matemáticas. Los métodos utilizados para sumar, restar, multiplicar y dividir son
algoritmos que cumplen perfectamente las características de precisión, finitud,
definición y eficiencia.
Para que el algoritmo pueda ser fácilmente traducido a un lenguaje de programación y
luego ser ejecutado la especificación debe ser clara, precisa, que pueda ser interpretada
con precisión y corresponda a pocas acciones, si esto no ocurre será necesario acudir a
desarrollar un mayor nivel de refinamiento.
La utilización de refinamientos sucesivos es lo que permite alcanzar la solución modular
que se propone.
Diseño modular, entonces, es la aplicación del criterio de refinamientos sucesivos,
partiendo de un plan de acción, determinando que hacer, por aplicación de los
conocimientos estratégicos de resolución pasando luego al como hacerlo con los
conocimientos tácticos para la realización del algoritmo.
La programación de algoritmos representa un caso de resolución de problemas que
requiere representación mental del mundo real, adaptación para tener una solución
computable y criterio para elegir una alternativa eficiente de implementación.
Cuando se analiza un problema, particularmente de programación, y éste es difícil de
describir, el plan de acción recomendable para alcanzar la solución es comenzar
trazando un esbozo de las formas más gruesas, para que sirvan de andamio a las demás;
aunque algunas de ellas se deban cambiar posteriormente. Después, se agregan los
detalles, (obteniéndose el algoritmo refinado), para dotar a estos esqueletos de una
estructura más realista.
Durante la tarea de integración final, se descartan aquellas primeras ideas provisionales
que ya no encajan en la solución. Por lo que, hasta que no se haya visto el conjunto
global es imposible encontrarle sentido a ninguna de las partes por sí solas.
Siempre es mejor explicar un misterio en términos de lo que se conoce, pero cuando
esto resulta difícil de hacer, se debe elegir entre seguir tratando de aplicar las antiguas
teorías, o de descartarlas y probar con otras nuevas. Siguiendo este análisis, se define
como reduccionistas a aquellas personas que prefieren trabajar sobre la base de ideas
existentes, y como renovadores a los que les gusta impulsar nuevas hipótesis. En
programación debe encontrarse un equilibrio entre ambas posturas.
La programación como toda actividad que requiere creatividad necesita que se produzca
un salto mental que se puede sintetizar como señala David Perkins en:
1. Larga búsqueda, se requiere esfuerzo en analizar y buscar.
2. Escaso avance aparente: el salto mental sobreviene tras un avance que parece
escaso o no muy evidente, pero sobreviene.
3. Acontecimiento desencadenante: El típico proceso de hacer clic comienza con
un acontecimiento que lo desencadena.
4. Chasquido cognitivo: De pronto aparece la solución la que sobreviene con
rapidez que hace que las piezas encajen con precisión, aun cuando sea necesario
todavía ajustar algunos detalles. Pero la idea generadora apareció.
5. Transformación. Este avance nos va modificando nuestro mundo mental.
En síntesis, la practica del salto de pensamiento requiere en primer lugar de buscar
analogías, en segundo lugar juegan un papel importante las conexiones lógicas,
formulación de una pregunta crucial ocupa un papel decisivo. El repertorio de acciones
tras el salto del pensamiento se expande para incluir no solo la analogía sino una
extrapolación lógica y la formulación de la pregunta adecuada
Para encontrar una solución muchas veces se necesita desplazarse bastante por el
entorno adecuado. Conforme a esto Thomas Edison declaro que la invención significa
99% de transpiración y 1% de inspiración, en contraposición con Platón que sostenía
que las soluciones aparecen por inspiración divina.
Muchos problemas son razonables, cabe razonarlos paso a paso para alcanzar la
solución. Otros son irrazonables no se prestan a un a reflexión por etapas.
Definición
Algoritmo
Especificación rigurosa (debe expresarse en forma univoca) de la secuencia de pasos,
instrucciones, a realizar sobre un autómata para alcanzar un resultado deseado en un
tiempo finito. Esto último supone que el algoritmo empieza y termina, en el caso de los
que no son de tiempo finito (ej. Sistemas en tiempo real) deben ser de número finito de
instrucciones.
Definición
Algoritmo:
Secuencia finita de instrucciones, reglas o pasos que describen en forma precisa las
operaciones que una computadora debe realizar para llevar a cabo una tarea en tiempo
finito [Knuth, 1968].
Descripción de un esquema de comportamiento expresado mediante un repertorio finito
de acciones y de informaciones elementales, identificadas, bien comprendidas y
realizables a priori. Este repertorio se denomina léxico[Scholl, 1988].
Esta formado por reglas, pasos e instrucciones.
Las reglas especifican operaciones.
La computadora es el agente ejecutor.
La secuencia de reglas y la duración de la ejecución son finitas.
Características de un algoritmo
Un algoritmo debe tener al menos las siguientes características:
1. Ser preciso: esto significa que las operaciones o pasos del algoritmo deben
desarrollarse en un orden estricto, ya que el desarrollo de cada paso debe
obedecer a un orden lógico.
2. Ser definido. Ya que en el área de programación, el algoritmo es el paso
previo fundamental para desarrollar un programa, es necesario tener en cuenta
que el computador solo desarrollará las tareas programadas y con los datos
suministrados; es decir, no puede improvisar y tampoco inventará o adivinará el
dato que necesite para realizar un proceso. Por eso, el algoritmo debe estar
plenamente definido; esto es, que cuantas veces se ejecute, el resultado depende
estrictamente de los datos suministrados. Si se ejecuta con un mismo conjunto
de datos de entrada, el resultado deberá ser siempre el mismo.
3. Ser finito: esta característica implica que el número de pasos de un algoritmo,
por grande y complicado que sea el problema que soluciona, debe ser limitado.
Todo algoritmo, sin importar el número de pasos que incluya, debe llegar a un
final. Para hacer evidente esta característica, en la representación de un
algoritmo siempre se incluyen los pasos inicio y fin.
4. Presentación formal: para que el algoritmo sea entendido por cualquier persona
interesada es necesario que se exprese en alguna de las formas comúnmente
aceptadas; pues, si se describe de cualquier forma puede no ser muy útil ya que
solo lo entenderá quien lo diseñó. Las formas de presentación de algoritmos
son: el pseudo código, diagrama de flujo y diagramas de Nassi/Schneiderman,
entre otras. En esta publicación se propondrá una notación algorítmica y se
darán las equivalencias entre la propuesta y las existentes y también con las
sentencias de los lenguajes de programación, en particular Pascal y C.
5. Corrección: el algoritmo debe ser correcto, es decir debe satisfacer la necesidad
o solucionar el problema para el cual fue diseñado. Para garantizar que el
algoritmo logre el objetivo, es necesario ponerlo a prueba; a esto se le llama
verificación o prueba de escritorio.
6. Eficiencia: hablar de eficiencia o complejidad de un algoritmo es evaluar los
recursos de cómputo que requiere para almacenar datos y para ejecutar
operaciones frente al beneficio que ofrece. En cuanto menos recursos requiere
será más eficiente el algoritmo.
La vida cotidiana está llena de soluciones algorítmicas, algunas de ellas son tan
comunes que no se requiere pensar en los pasos que incluye la solución. La mayoría de
las actividades que se realizan diariamente están compuestas por tareas más simples que
se ejecutan en un orden determinado, lo cual genera un algoritmo. Muchos de los
procedimientos utilizados para desarrollar tareas cotidianas son algorítmicos, sin
embargo, esto no significa que todo lo que se hace está determinado por un algoritmo.
El primer paso en el diseño de un algoritmo es conocer la temática a tratar, el segundo
será pensar en las actividades a realizar y el orden en que deben ejecutarse para lograr el
objetivo, el tercero y no menos importante es la presentación formal.
Léxico y algoritmo
Para escribir un algoritmo deben seguirse un conjunto de pasos básicos
1. Comprender el problema
2. Identificar los elementos a incluir en el léxico: constantes, tipos, variables y
acciones.
3. Encontrar la forma de secuenciar las acciones para obtener el resultado, esto es,
alcanzar las poscondiciones a partir de un estado inicial que cumple con la
precondición. Para establecer el orden de las acciones los lenguajes de
programación proporcionan mecanismos de composición: Secuenciación,
análisis de casos, iteración y reexcursión.
4. Al organizar las acciones en el tercer paso puede ocurrir que se detecte que
faltan elementos en el léxico o que algún aspecto del problema no ha sido bien
comprendido lo cual requeriría volver a los pasos anteriores.
5. Nunca la solución aparece en el primer intento, en general aparece en un proceso
cíclico, entonces se debe:
6. Escribir el léxico,
a. escribir la primera versión,
b. incluir en el léxico nuevos elementos que faltaban,
c. escribir la nueva versión del algoritmo y así sucesivamente
Estructura de un algoritmo
LEXICO {Léxico Global del algoritmo}
{Declaración de tipos, constantes, variables y acciones}
Acción 1
PRE {Precondición de la acción 1}
POS {Poscondición de la acción 1}
LEXICO {Léxico local, propio de la acción 1}
Declaraciones locales
ALGORITMO {Implementación de la acción 1}
{Secuencia de instrucciones de la acción 1}
FIN {Fin implementación algoritmo de la acción 1}
ALGORITMO
PRE {Precondición del algoritmo principal}
POS {Poscondición del algoritmo principal}
{Secuencia de instrucciones del algoritmo principal}
FIN {Fin del algoritmo principal}
Proceso Computacional
Se refiere a un algoritmo en ejecución. La ejecución de las instrucciones origina una
serie de acciones sobre elementos de memoria que representan información manejada
por el algoritmo. A nivel algorítmico se asigna un nombre a cada información de modo
de manejar un par nombre-valor para cada información.
Una variable representa alguna entidad del mundo real, relevante para el problema que
se quiere resolver. El efecto que producen las acciones del proceso sobre las variables
produce cambio de estados o de sus valores.
Definiciones
Estructura de un algoritmo
LEXICO {Léxico Global del algoritmo}
{Declaración de tipos, constantes, variables y acciones}
Acción 1
PRE {Precondición de la acción 1}
POS {Poscondición de la acción 1}
LEXICO {Léxico local, propio de la acción 1}
Declaraciones locales
ALGORITMO {Implementación de la acción 1}
{Secuencia de instrucciones de la acción 1}
FIN {Fin implementación algoritmo de la acción 1}
ALGORITMO
PRE {Precondición del algoritmo principal}
POS {Poscondición del algoritmo principal}
{Secuencia de instrucciones del algoritmo principal}
FIN {Fin del algoritmo principal}
Resumen:
En el presente capitulo se introdujeron términos y frases de la disciplina en estudio.
Se abordo el problema desde un punto de vista conceptual definiendo con precisión
términos y frases para evitar ambigüedades. Se puso especial énfasis en la necesidad de
pensar las soluciones antes de escribir código. Se establecieron cuales son los pasos que
deben seguirse para una solución correcta de problemas y cuales son los problemas que
pueden tratarse en forma algorítmica. Se definió cuales son las características deseables
de los algoritmos y se introdujo el concepto de eficiencia de modo de hacer, tal como
recomendaba Albert Einstein, las cosas tan simple como se pueda.
Se introdujo, además una representación sem. formal para la descripción de los
algoritmos
CAPITULO 2 Asignación - Control – Iteración
Objetivos de aprendizaje
Dominando los temas del presente capitulo Usted podrá.
1. Conocer las acciones simples que permiten capturar o mostrar los datos.
2. Encontrar en la secuenciación el camino simple para la resolución de
problemas
3. Dividir el dominio de los datos en subdominios excluyentes y
abarcativos.
4. Descubrir la necesidad de la repetición.
5. Verificar que es posible repetir acciones en formas diferentes y para
distintos conjunto de secuencias.
Introducción:
Se presenta en este capitulo un primer criterio de descomposición del problema para
poder alcanzar la solución del mismo. Se establece la secuenciación y el análisis de
casos como primer mecanismo correcto para la solución de problemas.
Habitualmente se observa que aquellos jóvenes que se acercan por primera vez a la
resolución de problemas de programación encuentran muchas dificultades para
organizar su pensamiento y encontrar una solución algorítmica.
No es simple comprender como se razona cuando se diseña un algoritmo o cual es el
mecanismo que permite extraer de nuestro conocimiento la técnica que nos guíe a la
solución de los mismos.
Una de las técnicas que se dispone para dominar la complejidad de los problemas es la
descomposición en subproblemas que pueden abordarse por separado, utilizando una de
las premisas básicas en la programación que es “dividir para vencer”. Cada problema
puede entonces verse como un conjunto de módulos, vinculados y relacionados
adecuadamente. Estos módulos utilizan mecanismos como la secuenciación, análisis de
casos, composiciones iterativas exactas, condicionales y recursivas para alcanzar la
solución.
Asignación.
Asignar significa almacenar valores, constantes o variables, en una determinada
dirección de memoria, este valor puede ser almacenado desde una instrucción del
programa u obtenido o derivado hacia algún dispositivo externo.
Análisis de casos
Al no ser posible resolver problemas con instrucciones secuenciales puras, muchas
veces es necesario tener que tomar decisiones en función de los datos del problema,
para ello se cuentan con instrucciones que permiten analizar alternativas y decidir que
camino seguir en cada caso.
Formato Efecto
SI Condición Si la expresión toma el valor verdadero entonces se ejecuta
ENTONCES la secuencia S si es falso ejecuta la acción siguiente a la
S instrucción en caso de existir.
FIN_SI
Formato Efecto
SI Condición Si la expresión toma el valor verdadero entonces se ejecuta
ENTONCES la secuencia S si es falso se ejecuta la secuencia R.
S
SI_NO
R
FIN_SI
Formato Efecto
SEGÚN expr Según el valor Vi de la expresión ordinal expr, se ejecuta la
V1 : S1 secuencia Si(i = 1 ..n). Cualquier valor no especificado no
V2 : S2 tiene acción asociada
FIN_SEGÚN
Estructura de decisión múltiple Completa. Agrega una cláusula para realizar acciones
particulares en caso de que el ordinal no se corresponda con ninguno de los valores
determinados.
Formato Efecto
SEGÚN expr Según el valor Vi de la expresión ordinal expr, se ejecuta la
V1 : S1 secuencia Si(i = 1 ..n). Cualquier valor no especificado
V2 : S2 realiza la secuencia Sn.
EN_OTRO_CASO : Sn
FIN_SEGÚN
LEXICO
dia, mes, año : Entero >0 ;
EsBisiesto, FechaValida : Booleano ;
AñoBisiesto : una acción
POST {AñoBisiesto V si año es bisiesto F en caso contrario}
ALGORITMO
EsBisiesto (año Mod 400 = 0) o ((año Mod = 0)y(año mod 100<>0))
FIN AñoBisiesto
ALGORITMO
Leer (dia, mes, año);
SI (dia < 1 )
ENTONCES FechaValida Falso
SI_NO
SEGÚN mes
mes = 1,3,5,7,8,10,12 : FechaValida dia <= 31
// si los meses son los que se enumeran y el dia menor o igual a 31 es valida//
mes = 4,6,9,11 : FechaValida dia <= 30
//en estos meses para ser valida el dia debe ser menor o igual a 30//
mes = 2 : AñoBisiesto; FechaValida dia <=28 o (AñoBisiesto y
dia<=29)
// en febrero debe verificar si el año es o no bisiesto//
EN_OTRO_CASO : FechaValida Falso
// de no ocurrir nada de lo anterior, la fecha es falsa, no es una fecha valida//
FIN_SEGUN
FIN_SI
Escribir (´La fecha ´, dia, ´/´, mes,´/´.año, ´ : ´);
SI FechaValida
ENTONCES Escribir (“Es Valida”)
SI_NO Escribir(“No es valida”)
FIN_SI
FIN
A los efectos de simplificar la notación de los análisis de casos en una única forma de
escritura se podría utilizar la notación SI, con las variantes que correspondan y con la
sentencia equivalente según el lenguaje de programación.[J.D. Muchnik]
La necesidad de iterar
Supongamos que de un determinado curso se dispone de cada uno de los alumnos de su
nota y se requiere conocer la nota promedio del curso.
Para ello será necesario conocer la nota de cada estudiante, se debe saber además la
suma de todas las notas de los estudiantes y, para calcular el promedio, es
imprescindible conocer la cantidad de estudiantes.
Podríamos pensar en un algoritmo con una estructura como la siguiente
LÉXICO
V : Real //Contiene la nota leída
S : Real //Contiene la sumatoria de las notas
N : Entero //Contiene el numero de alumnos
ALGORITMO
S <- 0 ;
N <- 0 ;
Leer (V) ;
S <- S + V ; //Suma las notas
N <- N + 1 ; // Cuenta los alumnos
Este bloque debe repetirse una cantidad de veces que no se puede determinar con
precisión, seria necesario escribir un texto diferente para cada caso particular, hasta
podría llegarse al extremo de tener que escribir un texto infinito, cosa imposible en la
implementación. El infinito se lo puede pensar desde un punto de vista teórico pero no
es posible implementarlo. Habíamos mencionado entre las características de los
algoritmos su finitud en tiempo y en instrucciones.
La necesidad de un mecanismo de iteración es clara si pensamos en computadoras como
maquinas de gran utilidad porque automatizan tareas, si solo se dispusiera de
composición secuencial o análisis de casos estaríamos realmente limitados y los
programas deberían tener una gran extensión para ejecutar muy pocas acciones si estas
se repiten.
Secuencias
Algunas operaciones tienen que ver con secuencia de datos. Una secuencia es una
colección de elementos del mismo tipo. En ella se puede aplicar algún tratamiento a
cada uno de los elementos que componen la secuencia o buscar alguno que cumpla con
cierta propiedad.
Las secuencias pueden representarse de diferentes formas y se caracteriza por el acceso
secuencial a cada elemento. Esto es, para acceder al elemento de la posición p, hay que
recorrer los p-1 elementos anteriores.
La secuencia debe contar con un elemento característico que indique que se ha
alcanzado el final de la misma en forma correcta. Esta marca puede determinarse según
una especificación precisa de la cantidad de elementos o por un valor particular que
indique la finalización. Las secuencias están vinculadas con las composiciones iterativas
y la reursion
Composiciones iterativas
Los componentes de una iteración son
1. Inicialización: instrucciones que se ejecutan para inicializar las variables que
participan de la iteración.
2. Condición de terminación: expresión booleana que determina cuando acaba la
iteración.
3. Cuerpo: conjunto de instrucciones que se ejecutan mientras no se cumple la
condición de terminación.
4. Finalización: conjunto de instrucciones que deben ejecutarse cuando la iteración
termina.
Se presentan dos composiciones iterativas en las que la finalización viene determinada
por una condición. Estas son la composición MIENTRAS, REPETIR. Lo que las
diferencia es el lugar donde se comprueba la condición: al principio o al final. Y
también si es una condición de terminación o de continuación. Existe además
composiciones iterativas en la que la finalización esta dada por la cantidad de elementos
que se deben evaluar
Formato Efecto
Mientras Cond. Hacer Ejecutar la secuencia S mientras la condición Cond tenga
S el valor de verdadero
FIN_MIENTRAS
Poscondicionales
Formato Efecto
REPETIR Ejecutar la secuencia S hasta que la expresión Cond tome
S el valor de verdadero
HASTA Cond
Formato Efecto
PARA i [Vi..Vf] HACER Ejecutar la secuencia S (Vf – Vi + 1) veces.
S
FIN_PARA
Exacta. Una forma natural de esta estructura consiste en repetir un bloque de acciones
una cantidad fija de veces o conocido de antemano.
Tiene definido un contador o índice como una variable de tipo ordinal que se
incrementa en una cantidad constante con cada iteración y permite controlar el numero
de veces que se ejecutara el ciclo.
En cada iteración el índice toma el valor siguiente de acuerdo al tipo de valor asociado
al ordinal. Hay lenguajes que solo permiten que la variación (que puede ser positiva o
negativa) sea solamente 1, y otros lenguajes que pueden modificar ese incremento. De
todas formas la variable índice se actualiza con cada iteración y se testea en cada ciclo
respecto del valor final deseado.
Si el incremento es uno la cantidad de repeticiones esta dada por:
Cantidad de repeticiones = Valor final del Índice – Valor inicial del Índice + 1.
En el caso de que valor inicial y valor final coincidan se ejecuta 1 vez, en el caso de que
el valor inicial es mayor que el final y por cada iteración se incrementa el índice el ciclo
no se ejecutara y no producirá errores en tiempo de ejecución.
La composición iterativa mientras puede no ejecutarse por lo que es necesario al final del
proceso verificar si hubieron valores, el valor de n se puede utilizar para verificar esa condición.
ALGORITMO // Composición iterativa repetir hasta
S0;
N0;
Leer (v) // El primer valor leído debe ser valido ya que se evalúa
REPETIR
S S + V ; //Suma las notas
‘ N N + 1 ; // Cuenta los alumnos
Leer (V) ;
HASTA_QUE V = 0 //Cundo esto es verdadero finaliza la iteración
Escribir (¨Promedio de las notas ¨, S/N)
No requiere validar ya que al menos hay un valor
ALGORITMO // Composición iterativa para
S0;
N0;
Leer (N) // Lectura de la cantidad de elementos que contiene el lote de datos
PARA I [1..N] HACER
Leer (V)
S S + V ; //Suma las notas
‘ N N + 1 ; // Cuenta los alumnos
FIN_PARA
SI N > 0
ENTONCES
Escribir (¨Promedio de las notas ¨, S/N)
SI_NO
Escribir (¨No hay valores para procesar¨)
La composición iterativa para puede no ejecutarse ya que el valor leído de N puede ser
cero, en ese caso no corresponde la iteración por lo que es necesario al final del proceso
verificar si hubieron valores, el valor de n se puede utilizar para verificar esa condición.
Recursividad
La repetición puede relacionarse con el concepto de inducción matemática, este tipo de
razonamiento permite demostrar una proposición que depende de cierto parámetro N,
normalmente en el rango de los números naturales, una forma de demostrar si una
proposición P es cierta para todo natural N es:
Se demuestra que es cierta para N=0, inicio de la inducción o caso base
Se asume que P es cierto para n-1, hipótesis de inducción
Entonces se demuestra que también lo es para n, relación de inducción
También se puede utilizar la inducción para realizar definiciones matemáticas de
funciones y sucesiones.
Por ejemplo se puede definir el factorial introduciendo el valor cuando n = 0 e
introduciendo una relación para n > 0 a partir del factorial de n-1
0! = 1
N! = N * (N – 1)!
La primera igualdad corresponde al caso base, la siguiente es la relación d inducción
Otro ejemplo Es la conocida sucesión de Fibonacci
Fibo(0) = 1
Fibo(1) = 1
Fibo(N) = Fibo(n-1) + Fibo(N-2) para N>1
Los primeros valores de la serie son 1, 1, 2, 3, 5, 8, 11, 19, 21,......
Una función con sentencias entre las cuales se encuentra una llamada a si misma se dice
que es recursiva. Existen muchas aplicaciones matemáticas y computacionales de la
recursividad. Numerosas circunstancias de la vida común tiene naturaleza recursiva
Se debe tener en cuenta
1. Define el problema en términos de un problema más simple de la misma
naturaleza.
2. Debe disminuir el espacio del problema en cada llamada recursiva
Hay una instancia particular que se conoce como caso base o caso degenerado
3. Divide el problema original en subproblemas más pequeños. Cuando es lo
suficientemente chico se resuelve directamente y se combinan soluciones del
subproblema hasta que queda resuelto el problema
HANOI
Este es un juego que tiene su origen en la cultura oriental y en una leyenda sobre el
templo de Brama, cuya estructura simulaba una plataforma metálica con tres varillas y
discos en su interior. El problema suponía tres varillas Inicial, Central y Final en la que
se alojaban discos que podían trasladarse de una varilla a otra con la condición que cada
disco era ligeramente inferior en diámetro al que estaba justo debajo de el. Los discos
originalmente están en la varilla Inicial y se los desea trasladar a la final cumpliendo en
todos los pasos la condición del tamaño de los discos, el superior ligeramente menor al
inferior.
El analisis es simple desde el punto de vista
recursuivo es simple, si solo queda un disco en
Si n = 1 ENTONCES el poste inicial este puede ser movido
directamente al disco final. Si quedaran mas
de uno habria que mover los n-1 anteriores
del inicio al medio para poder mover el ultimo
al fina. Después habra que colocar todos los
del medio en el fina, esto recursivamentel
Mover n de I a F
SI_NO
Mov n-1 de I a C usando F
Mover n de I a F
Mover n-1 de C a F usando I
FIN_SI
El flujo de control de una función recursiva requiere tres condiciones para una
terminación normal:
1. Un test para detener o continuar con la recursion.
2. una llamada recursiva para continuar la recursion, con un problema de la misma
naturaleza pero de menor complejidad.
3. un caso base para terminar la recursion
ALGORITMO
SI Es el caso base
ENTONCES
Ejecutar la acción final
Terminar con la recursion
SI_NO
Disminuir el espacio del problema
Volver a invocar a la función
FIN_SI
acción EnsayarSolucion
ALGORITMO
<Inicializar cantidad de posibles soluciones>
REPETIR
<tomar la siguiente selección>
<determinar si es selección valida>
SI Valido
ENTONCES
<anotar selección>
SI Problema Solucionado
ENTONCES
Éxito = Verdadero
SINO
EnsayarSolucion llamada para realizar otra tarea
SINO Éxito
ENTONCES
<Borrar anotación y probar otra solución>
FINSI
FINSI
FINSI
HASTA Éxito O <No mas posibilidades>
Si sale por éxito habrá encontrado la solución, si agota las posibilidades sin éxito, el
problema no tiene solución
FIN.
Máximos y mínimos
En lo que hace a la información relativa a un conjunto de datos puede ser necesario
encontrar cual o cuales de los elementos de ese conjunto cumplen con ciertas
características. Por ejemplo si se tiene información de los nombres de los estudiantes y
sus calificaciones puede ser posible conocer el nombre del mejor estudiante, según el
valor de sus notas, o conociendo el nombre de los atletas y el tiempo empleado en una
carrera de velocidad puede requerirse conocer el ganador. En el primer caso se busca al
estudiante cuyo promedio es el mayor, en el otro caso se busca el atleta cuyo tiempo es
el menor. Existen algoritmos puntuales que permiten buscar máximos y mínimos.
Buscar un máximo
Datos e entrada L1 ... L n Lista de n elementos
Datos de salida M identificador que contendrá al valor máximo
N pertenece a los números Naturales
N > 0 Por lo menos hay un elemento en la lista
Li pertenece a los números racionales para todo 1<= i <= n
Poscondición
M pertenece a los números racionales
m>=li para todo 1<=i<=n
LÉXICO
Valor : Entero // Identificador que contendrá las lecturas
Máximo : Entero // Identificador que contendrá el máximo del conjunto
Esta acción le asigna el primer valor al máximo que puede ser el primer valor
leído, si es que se tiene o un valor arbitrario. El valor arbitrario puede ser un valor
razonablemente bajo para que cualquier dato valido lo pueda desplazar o un valor
utilizado como valor centinela para indicar que corresponde a la primer lectura.
FIN Para inicializar el identificador que contendra el maximo se
ALGORITMO puede hacer con el primer valor leido o con un valor
Leer(Valor) ; razonablemente alto dentro del contexto delprpblema
Máximo,valor ;
MIENTRAS haya datos HACER
SI Valor > Máximo
ENTONCES
Máximo valor
FIN_SI
FIN_MIENTRAS
SI hubodatos
ENTONCES
Escribir(¨El máximo del conjunto es ¨, máximo)
SI_NO
Escribir(¨No hubo datos para procesar¨)
FIN_SI
FIN
La búsqueda de un máximo requiere:
1. Determinar el conjunto de datos para poder definir que tipo de composición
iterativa es la mas adecuada para el problema planteado
2. Definir al menos dos identificadores del mismo tipo de dato, uno para las
sucesivas lecturas y el otro para contener el máximo del conjunto.
3. Se debe inicializar el máximo esto puede ser con un valor arbitrario,
razonablemente bajo o un valor particular que pueda ser utilizado como valor
centinela o con el valor de la primer lectura.
4. A continuación se compara la nueva entrada con el valor máximo, cada vez que
la nueva entrada lo supera, se conserva ese valor como el nuevo máximo.
La búsqueda de un mínimo utiliza el mismo criterio solamente que requiere :
1. La inicialización del mínimo si se hace con un valor arbitrario este debe ser lo
suficientemente alto coma para que cualquier dato del conjunto lo reemplace
2. Las comparaciones posteriores requieren simplemente cambia el operador de
relación. Si en el máximo se compra por mayor, en el mínimo debe hacerse por
menor.
Dado 100 valores buscar el máximo del conjunto y su posición relativa dentro del
mismo
//Búsqueda con una cantidad conocida de datos
LÉXICO
TAMAÑO_LOTE = 100;
I : Entero // variable para la iteración
V : Entero // para las distintas lecturas
Máximo : Entero // Contiene el máximo del conjunto
PosRel : Entero // Contiene la pos del máximo dentro del conjunto
ALGORITMO
Leer(V);
Máximo = V ;//Asigna al máximo la primer lectura
PosRel = 1
PARA i [2,TAMAÑO_LOTE] HACER
Leer(V);
SI (V > Máximo)
ENTONCES
Máximo v;
PosRel i
FIN_SI;
FIN_PARA;
Escribir(“Máximo es : “,Máximo. “ y su posición relativa “,PosRel);
FIN.
Dado 100 valores buscar el máximo del conjunto y su posición relativa dentro del
mismo
//Búsqueda con una cantidad conocida de datos con valor centinela
LÉXICO
TAMAÑO_LOTE = 100;
I : Entero // variable para la iteración
V : Entero // para las distintas lecturas
Máximo : Entero // Contiene el máximo del conjunto
PosRel : Entero // Contiene la pos del máximo dentro del conjunto
ALGORITMO
PARA i [1,TAMAÑO_LOTE] HACER
Leer(V); El maximo lo asigna cuando es la primer lectura, es
SI ( i = 1 o V > Máximo) decir cuando i es igual a uno, cosa que ocurre solo
ENTONCES una vez o cuando el valor leido reemplaza al
maximo. Los compiladores evaluan las expresiones
Máximo v; logicas de izquierda a derecha, al ser verdadera la
PosRel i primera proposicion por ser una disyuncion no
FIN_SI; necesita evaluar la segunda para saber su valor de
verdadero
FIN_PARA;
Escribir(“Máximo es : “,Máximo. “ y su posición relativa “,PosRel);
FIN.
Dado un conjunto de valores, que finalizan con un valor igual a 0 buscar el máximo del
conjunto y su posición relativa dentro del mismo
//Búsqueda con una cantidad indefinida de datos
LÉXICO
I : Entero // variable para la iteración
V : Entero // para las distintas lecturas
Máximo : Entero // Contiene el máximo del conjunto
PosRel : Entero // Contiene la pos del máximo dentro del conjunto
ALGORITMO
Leer(V);
Máximo = V ;//Asigna al máximo la primer lectura
PosRel = 1
MIENTRAS (V <> 0) HACER
SI (V > Máximo)
ENTONCES
Máximo v;
PosRel i
FIN_SI;
Leer(v);
FIN_MIENTRAS;
SI (Máximo <> 0)
ENTONCES
Escribir(“Máximo es : “,Máximo. “ y su posición relativa “,PosRel);
SI_NO
Escribir(“No se ingresaron valores”);
FN_SI;
FIN.
Dado un conjunto de valores, que finalizan con un valor igual a 0 buscar el máximo del
conjunto y su posición relativa dentro del mismo
//Búsqueda con una cantidad indefinida de datos
LÉXICO
I : Entero // variable para la iteración
V : Entero // para las distintas lecturas
Máximo : Entero // Contiene el máximo del conjunto
PosRel : Entero // Contiene la pos del máximo dentro del conjunto
ALGORITMO
Leer(V);
Máximo = 0; PosRel = 0;
MIENTRAS (PosRel = 0 o V <> 0) HACER
Leer(V);
SI (V > Máximo)
ENTONCES
Máximo v;
PosRel i
FIN_SI;
Leer(v);
FIN_MIENTRAS;
SI (Máximo <> 0)
ENTONCES
Escribir(“Máximo es : “,Máximo. “ y su posición relativa “,PosRel);
SI_NO
Escribir(“No se ingresaron valores”);
FIN.
Alternativa de solución reemplazando el análisis de casos con la notación [JDM]
ALGORITMO
Leer(V);
Máximo = 0; PosRel = 0;
MIENTRAS (PosRel = 0 o V <> 0) HACER
Leer(V);
SI
(V > Máximo)
Máximo v;
PosRel i
FIN_SI;
Leer(v);
FIN_MIENTRAS;
SI
(Máximo <> 0)
Escribir(“Máximo es : “,Máximo. “ y su posición relativa “,PosRel);
(Máximo = 0) Escribir(“No se ingresaron valores”);
FN_SI;
FIN.
Corte de control
La utilización de este algoritmo puntual permite resolver el análisis de una secuencia de
datos que cumplen con la precondición de tener una clave que se repite, están ordenados
o agrupados por esta clave y se requiere información de cada subconjunto formado por
todos los valores de la misma clave y además información sobre la totalidad de los
datos. Debe garantizarse que se evaluaran todos los datos y que los que corresponden al
mismo subgrupo serán evaluados agrupados.
LÉXICO
Importe,Suma : Entero;
NumeroCliente, Anterior : Entero;
AGORITMO
Leer(NumeroCliente)
// hace una lectura anticipada del dato de la expresión lógica//
MIENTRAS (NumeroCliente > 0) HACER
//garantiza la secuencia de lectura de todos los datos//
Suma = 0; //inicializa acumuladores//
Anterior = NumeroCliente; //guarda e valor a controlar//
MIENTRAS (NumeroCliente > 0 y Anterior = NumeroCliente;) HACER
//ciclo que garantiza estar en el mismo subgrupo y que aun haya datos//
Leer(Importe); // lectura del resto de los datos//
Suma = Suma + Importe;
Leer(NumeroCliente)//lectura del nuevo s es igual al anterior
Continua, sino sale del ciclo.
FIN_MIENTRAS
Escribir(“El Cliente : “,Anterior, “ Compro : “,Suma);
FIN_MIENTRAS
FIN.
Resumen:
En el presente se abordaron temas de solución de problemas. Para resolver problemas de
programación se requiere conocer las estructuras de datos que contienen la información
a procesar, las que se utilizarán como estructuras intermedias y las que servirán como
estructuras finales para mostrar los resultados obtenidos. además de las estructuras de
datos se requiere saber cuales son las acciones que deben seguirse para alcanzar la
solución propuesta.
En este capitulo el objeto de estudio fue justamente el conocimiento de estas
herramientas, allí se vio como tomar valores o mostrarlos, como hacer distintos análisis
de casos, como secuenciar las acciones de modo de encontrar la solución de problemas
que no se resuelven con una única acción, la necesidad de repetir acciones.
Se pudo observar como se puede repetir con repeticiones explicitas, con composiciones
de iteración, o repetir con invocaciones múltiples a través de recursividad. Por ultimo se
introdujo el concepto de secuencia y su utilidad
CAPITULO 3 Acciones y Funciones
Objetivos de aprendizaje
Dominando los temas del presente capitulo Usted podrá.
1. Entender la descomposición como forma de resolución de problemas.
2. Dar valor a la reusabilidad en búsqueda de la eficiencia en la escritura el
código.
3. Establecer comunicación entre módulos.
4. Comprender las ventajas de la descomposición
5. Diferenciar acciones de funciones y los distintos tipos de parámetros
Introducción
En este capitulo se analiza la descomposición como forma de alcanzar la solución de
problemas. Una regla básica de la programación indica que si existe un programa de
longitud L tal que L = L1 + L2, se dice que el esfuerzo de resolver L es mayor que la
suma de los esfuerzos de resolución de L1 y L2, aun cuando haya que derivar esfuerzo
para la integración de los módulos.
SI L = L 1 + L 2
Entonces
Esfuerzo(L) > Esfuerzo(L1) + Esfuerzo (L2)
Antes de analizar las particularidades de las acciones y funciones es necesaria la
definición de los términos que se utilizan.
Modularizacion
En general los problemas a resolver son complejos y extensos, puede incluso
presentarse situaciones en que una parte del problema deba ser modificada para
adaptarse a nuevos requerimientos. Se hace necesario conocer algunas herramientas que
permitan facilitar la solución de estos problemas, la abstracción y la descomposición
pueden ayudar a ello. La abstracción permitirá encontrar y representar lo relevante del
problema y la descomposición se basa en el paradigma de ¨ dividir para vencer ¨. La
descomposición tiene como objetivo dividir cada problema en subproblemas cada uno
de los cuales será de más simple solución.
Es conveniente, e importante descomponer por varias razones:
Una persona entiende un problema de características complejas partiendo la
información. Por esto para comprender un problema complejo del mundo real es
necesario dividirlo o modularizar.
Favorece el trabajo en equipo, cada programador recibe las especificaciones la tarea a
realizar y las restricciones con las que debe manejarse.
Favorece el mantenimiento. Las tareas involucradas en este mantenimiento, corregir
errores y modificar código, se hace mucho mas simple el análisis y control de una
porción o modulo que de la totalidad del problema.
Permite la reusabilidad del código. Siempre es deseable, de ser posible, hacer uso de
código ya escrito.
Módulos
Un problema debe ser descompuesto en subproblemas que se denominan módulos en
los que cada uno tendrá una tarea especifica, bien definida y se comunicaran entre si
adecuadamente para conseguir un objetivo común. Un modulo es un conjunto de
instrucciones mas un conjunto de datos que realizan una tarea lógica.
Parámetros
Son variables cuya característica principal es que se utilizan para transferir información
entre módulos. En programas bien organizados toda información que viaja hacia o
desde módulos se hace a través de parámetros.
Hay dos tipos de parámetros, los pasados por valor y los pasados por referencia o
dirección.
Cuando existen datos compartidos entre módulos una solución es que un modulo pase
una copia de esos datos al otro. En este caso el pasaje se denomina pasaje por valor. El
modulo que recibe esta copia no puede efectuar ningún cambio sobre el dato original; el
dato original se encuentra de este modo protegido de modificación.
Los parámetros pasados por referencia o dirección no envían una copia del dato sino
envían la dirección de memoria donde el dato se encuentra por lo que tanto el proceso
que lo llama como el proceso llamado pueden acceder a dicho dato para modificarlo.
Razones por la que es conveniente la utilización de parámetros sobre las variables
globales.
Reusabilidad
El uso de parámetros permite separar el nombre del dato, del dato en si mismo, lo que
permite que el mismo código sea utilizado en distintas partes del programa simplemente
cambiando la lista de parámetros actuales.
Acciones
El léxico establece el nivel de abstracción de un algoritmo. Es decir, introduce las
variables, las constantes, los tipos de datos y las acciones con que se construye el
algoritmo. Ahora se profundizara sobre el estudio de las acciones.
El concepto de acción está muy ligado al concepto de abstracción. Se analiza como
abstracción por parametrización y abstracción por especificación.
Utilización de acciones
Una acción es una secuencia de instrucciones que se identifica por un nombre y que
puede ser invocada desde un algoritmo principal o desde otra acción. Cuando una
acción es invocada desde algún punto de un algoritmo, el flujo de ejecución se traslada a
la primera instrucción de la acción, entonces la acción se ejecuta hasta el final y cuando
acaba, el flujo se traslada de nuevo a la instrucción del algoritmo que sigue a aquella
que origino la invocación.
Una acción debe tener un efecto bien definido, lo que significa que debe ser cohesiva.
El nombre de la acción es conveniente que evoque la tarea que realiza. Hay que definir
acciones que sean aplicables a cualquier posible conjunto de valores de entrada y no a
un valor concreto.
Entre una acción y el algoritmo que la invoca se debe producir una comunicación de
valores: el algoritmo debe proporcionar los valores de entrada y la acción puede retornar
el resultado, o puede modificar el estado de alguno de ellos.
Puede haber acciones en las que la comunicación se realice mediante variables globales
definidas en el ámbito del algoritmo principal, que pueden ser manejadas por la acción.
Pero esta no es la forma mas apropiada. Una acción, en principio, nunca debería acceder
a variables globales.
Los parámetros son el mecanismo que posibilita escribir acciones generales, aplicables
a cualquier valor de la entrada, e independientes del léxico del algoritmo.
Abstracción y acciones
El término abstracción se refiere al proceso de eliminar detalles innecesarios en el
dominio del problema y quedarse con aquello que es esencial para encontrar la solución.
Como resultado de aplicar la abstracción, se obtiene un modelo que representa la
realidad que nos ocupa. Este modelo no es la realidad, es una simplificación de esa
realidad que establece un nivel de abstracción mas apropiado para razonar sobre el
problema y encontrar la solución.
La abstracción también está relacionada con los lenguajes de programación. Estos
ofrecen mecanismos de abstracción que determinan la forma de razonar de un
programador.
El concepto de acción conjuga dos técnicas de abstracción que son la parametrización y
la especificación. La parametrización es un mecanismo por el cual se generaliza una
declaración para que no sea aplicado a un único caso, sino que sirva para cualquier valor
que pueda tomar cierto parámetro.
La abstracción por especificación es la separación entre la especificación (el Qué) y la
implementación (el cómo). En el caso de acciones, se refiere a distinguir entre la
descripción de qué hace la acción y el cómo se la implementa. Una vez que se define
una nueva acción, se las utiliza del mismo modo que si se tratase de una acción
primitiva.
Del mismo modo que el algoritmo, las acciones se especifican mediante una
precondición y una poscondición. La precondición establece las restricciones que
satisfacen los parámetros (datos de entrada) para que se pueda ejecutar la acción, y la
postcondición describe el resultado.
Cada acción debe ir siempre acompañada de una descripción textual de su efecto y de su
precondición y postcondición. De esta forma, cualquier programador podría conocer
qué hace y podría utilizarla sin conocer cómo lo hace.
El programador solo debe preocuparse por que se cumpla la precondición al invocar la
acción y tendrá la certeza de que la acción cumplirá su objetivo.
Tipos de Parámetros
Una acción se comunica con el algoritmo que lo invoca a través de los parámetros. Es
necesaria una comunicación en dos sentidos. El algoritmo debe proporcionar los datos
de entrada que manipulará durante su ejecución y la acción debe retornar los resultados
que obtiene.
El tipo de parámetro indica cómo los valores de los argumentos son ligados a los
parámetros.
El tipo de parámetro dato solo permite que el parámetro pueda recibir el valor de un
argumento mientras que el tipo de parámetro dato-resultado permite que el parámetro
pueda recibir un valor y pueda retornar un resultado. En la declaración de una acción
hay que indicar el tipo de cada parámetro. Si hay varios parámetros del mismo tipo y del
mismo tipo de dato, se pueden poner en la misma declaración, separados por comas. La
declaración de una acción se la denomina cabecera.
Se denomina paso por valor al paso de parámetros que corresponda al tipo de parámetro
dato y paso por referencia al que corresponda al tipo de parámetro dato-resultado.
Acción para el intercambio de dos variables: La acción recibe dos identificadores con
dos valores y debe retornar los identificadores con los valores cambiados
Funciones
Si el propósito es calcular un valor a partir de otros que se pasan con argumentos y se
utilizan acciones, habrá que definir un parámetro dato-resultado para que retorne el
valor calculado. Las acciones que retornan un único valor no pueden ser utilizadas en
una expresión.
Para resolver este problema, los lenguajes de programación incorporan el concepto de
función. Las funciones devuelven un único valor. La función supone extender el
conjunto de operadores primitivos.
En cada declaración se especifica el nombre de la función, la lista de los parámetros y
finalmente el tipo de valor que retorna la función.
Nombre_funcion (par1 : td1 ; ... ; parn : tdn) : tr : una función
Una función no debe tener efectos laterales. Es decir, debe limitarse a calcular un valor
y no modificar ninguno de los que se describen en el momento de la invocación.
Las acciones se utilizan para extender el conjunto de acciones primitivas. Las funciones
permiten extender el conjunto de funciones u operadores primitivos. Siempre deben
utilizarse dentro de una expresión.
Resumen:
En este capitulo se avanza sobre la necesidad de mayor abstracción procedural
introduciendo conceptos claves de programación como acciones y funciones como la
forma mas adecuada de estructurar problemas en el paradigma procedural. Es una
introducción a la abstracción procedural y de datos que servirá de base para sustentar
futuros conocimientos de estructuración de programas en clases cuando se aborde, en
otra instancia, la programación orientada a objetos.
Se dio valor a términos como reusabilidad, ocultamiento de datos y cohesión
CAPITULO 4 Tipos de datos y Operadores
Objetivos de aprendizaje
Dominando los temas del presente capitulo Usted podrá.
1. Conocer los valores y las operaciones aplicables a los tipos de datos.
2. Conocer los datos simples y las estructuras
3. Hacer análisis comparativos de las estructuras según su utilizacion
4. Definir colecciones de datos del mismo tipo o tipo diferente
5. Determinar como acceder a cada miembro de una estructura según su
tipo
Introducción:
Se introducen los conceptos de tipos de datos primitivos, derivados y abstractos. Se
definen los operadores aritméticos, lógicos, de relación y los de acceso a los miembros
particulares de las estructuras de datos con análisis comparativo de las distintas
estructuras de datos y una sugerencia para el modo correcto de selección de las mismas
en función de los resultados a obtener.
Variables
Denotan una magnitud o indicador del proceso. Se caracteriza por un nombre, un tipo de
dato y el valor que tiene en un momento determinado. Utilizadas para los datos de
entrada, intermedios y de salida.
Constante
Nombre asociado a un valor que permanece sin modificación durante todo el proceso
Tipos de Datos
Identifica o determina un dominio de valores y el conjunto de operaciones aplicables
sobre esos valores.
1. Primitivos.
2. Derivados.
3. Abstractos.
Los algoritmos operan sobre datos de distinta naturaleza, por lo tanto los programas que
implementan dichos algoritmos necesitan una forma de representarlos.
Tipo de dato es una clase de objeto ligado a un conjunto de operaciones para crearlos y
manipularlos, un tipo de dato se caracteriza por
1. Un rango de valores posibles.
2. Un conjunto de operaciones realizadas sobre ese tipo.
3. Su representación interna.
Al definir un tipo de dato se esta indicando los valores que pueden tomar sus elementos
y las operaciones que pueden hacerse sobre ellos.
Al definir un identificador de un determinado tipo el nombre del identificador indica la
localización en memoria, el tipo los valores y operaciones permitidas, y como cada tipo
se representa de forma distinta en la computadora los lenguajes de alto nivel hacen
abstracción de la representación interna e ignoran los detalles pero interpretan la
representación según el tipo.
Los tipos de datos pueden ser.
1. Estáticos: Ocupan una posición de memoria en el momento de la definición, no
la liberan durante el proceso solamente la liberan al finalizar la aplicación.
a. Simples: Son indivisibles en datos mas elementales, ocupan una única
posición para un único dato de un único tipo por vez.
i. Ordinales: Un tipo de dato es ordinal o esta ordenado
discretamente si cada elemento que es parte del tipo tiene un
único elemento anterior (salvo el primero) y un único elemento
siguiente (salvo el ultimo).
1. Enteros: Es el tipo de dato numérico mas simple.
2. Lógico o booleano: puede tomar valores entre dos
posibles: verdadero o falso.
3. Carácter: Proporcionan objetos de la clase de datos que
contienen un solo elemento como valor. Este conjunto de
elementos esta establecido y normatizado por el estándar
ASCII.
ii. No ordinales: No están ordenados discretamente, la
implementación es por aproximación
1. Reales: Es una clase de dato numérico que permite
representar números decimales.
b. Cadenas: Contienen N caracteres tratados como una única variable.
c. Estructuras: Tienen un único nombre para mas de un dato que puede ser
del mismo tipo o de tipo distinto. Permiten acceso a cada dato particular
y son divisibles en datos mas elementales.
Una estructura es, en definitiva, un conjunto de variables no
necesariamente del mismo tipo relacionadas entre si de diversas formas.
Si los datos que la componen son todas del mismo tipo son homogéneas,
heterogéneas en caso contrario.
Una estructura es estática si la cantidad de elementos que contiene es fija,
es decir no cambia durante la ejecución del programa
i. Registro: Es un conjunto de valores que tiene las siguientes
características:
Los valores pueden ser de tipo distinto. Es una estructura
heterogénea.
Los valores almacenados se llaman campos, cada uno de ellos
tiene un identificador y pueden ser accedidos individualmente.
El operador de acceso a cada miembro de un registro es l
operador punto ( . )
El almacenamiento es fijo.
ii. Arreglo: Colección ordenada e indexada de elementos con las
siguientes características:
Todos los elementos son del mismo tipo, un arreglo es una
estructura homogénea.
Los elementos pueden recuperarse en cualquier orden,
simplemente indicando la posición que ocupa dentro de la
estructura, esto indica que el arreglo es una estructura indexada.
El operador de acceso es el operador []
La memoria ocupada a lo largo de la ejecución del programa es
fija, por esto es una estructura estática.
El nombre del arreglo se socia a un área de memoria fija y
consecutiva del tamaño especificado en la declaración.
El índice debe ser de tipo ordinal. El valor del índice puede verse
como el desplazamiento respecto de la posición inicial del
arreglo.
Los arreglos pueden ser de varias dimensiones. Esta dimensión
indica la cantidad de índices necesarias para acceder a un
elemento del arreglo.
El arreglo lineal, con un índice, o una dimensión se llama vector.
El arreglo con 2 o mas índices o dimensiones es una matriz. Un
grupo de elementos homogéneo con un orden interno en el que se
necesitan 2 o mas índices para referenciar a un elemento de la
estructura.
iii. Archivos: Estructura de datos con almacenamiento físico en
memoria secundaria o disco.
Las acciones generales vinculadas con archivos son
Asignar, abrir, crear, cerrar, leer, grabar, Cantidad de elementos,
Posición del puntero, Acceder a una posición determinada, marca
de final del archivo, definiciones y declaraciones de variables.
Según su organización pueden ser secuenciales, indexados.
1. Archivos de texto: Secuencia de líneas compuestas por
cero uno o mas caracteres que finalizan con un carácter
especial que indica el final de la línea. Los datos internos
son representados en caracteres, son mas portables y en
general mas extensos.
2. Archivos de tipo o binarios: secuencia de bytes en su
representación interna sin interpretar. Son reconocidos
como iguales si son leídos de la forma en que fueron
escritos. Son menos portables y menos extensos.
2. Dinámicos: Ocupan direcciones de memoria en tiempo de ejecución y se
instancian a través de punteros. Esta s instancias pueden también liberarse en
tiempo de ejecución. El tema de puntadores y estructuras enlazadas (estructuras
relacionadas con este tipo de dato se analizan en detalle en capítulos siguentes)
a. Listas simplemente enlazadas: cada elemento sólo dispone de un
puntero, que apuntará al siguiente elemento de la lista o valdrá NULL si
es el último elemento.
b. Pilas: son un tipo especial de lista, conocidas como listas LIFO (Last In,
First Out: el último en entrar es el primero en salir). Los elementos se
"amontonan" o apilan, de modo que sólo el elemento que está encima de
la pila puede ser leído, y sólo pueden añadirse elementos encima de la
pila.
c. Colas: otro tipo de listas, conocidas como listas FIFO (First In, First Out:
El primero en entrar es el primero en salir). Los elementos se almacenan
en fila, pero sólo pueden añadirse por un extremo y leerse por el otro.
d. Listas circulares: o listas cerradas, son parecidas a las listas abiertas,
pero el último elemento apunta al primero. De hecho, en las listas
circulares no puede hablarse de "primero" ni de "último". Cualquier nodo
puede ser el nodo de entrada y salida.
e. Listas doblemente enlazadas: cada elemento dispone de dos punteros,
uno a punta al siguiente elemento y el otro al elemento anterior. Al
contrario que las listas abiertas anteriores, estas listas pueden recorrerse
en los dos sentidos.
f. Árboles: cada elemento dispone de dos o más punteros, pero las
referencias nunca son a elementos anteriores, de modo que la estructura
se ramifica y crece igual que un árbol.
g. Árboles binarios: son árboles donde cada nodo sólo puede apuntar a dos
nodos.
h. Árboles binarios de búsqueda (ABB): son árboles binarios ordenados.
Desde cada nodo todos los nodos de una rama serán mayores, según la
norma que se haya seguido para ordenar el árbol, y los de la otra rama
serán menores.
i. Árboles AVL: son también árboles de búsqueda, pero su estructura está
más optimizada para reducir los tiempos de búsqueda.
j. Árboles B: son estructuras más complejas, aunque también se trata de
árboles de búsqueda, están mucho más optimizados que los anteriores.
k. Tablas HASH: son estructuras auxiliares para ordenar listas.
l. Grafos: es el siguiente nivel de complejidad, podemos considerar estas
estructuras como árboles no jerarquizados.
m. Diccionarios.
acción Efecto
Asignar(a,s) Asigna al identificador a la cadena s que representa un archivo
en disco
Abrir(a) Prepara el archivo asignado a la variable a para su utilización
Crear(a) Crea el archivo asignado al identificador a y lo prepara para su
acceso
Cerrar(a) Cierra el archivo apuntado por a, actualiza la marca de fin si
corresponde
LeerCaracter(a,c) Lee el siguiente carácter del flujo apuntado por a y lo almacena
en c
LeerLinea(a,s) Lee la siguiente línea del flujo apuntado por a y la almacena en
s
GrabarCaracter(a,c) Escribe secuencialmente en el flujo a el caracter c
GrabarCadena(a,s) Escribe en el flujo a la cadena s
LeerArchivo(a,r) Lee el siguiente tipo de dato (ejemplo registro) del flujo a y lo
almacena en r
GrabarArchivo(a,r) Graba el siguiente tipo de dato, r, en el flujo a
LeerPosicion(a,p,r) Lee del flujo a el valor contenido en l posición p y lo almacena
en r
GrabarPosicion(a,p,r) Graba en la posicion p del flujo a el valor contenido en r
NumeroElementos(a) Retorna el numero de elementos almacenados en el archivo
PosicionActual(a) Retorna la posición en la que se encuentra posicionado el
puntero actual del archivo.
ApuntarA(a,p) Accede a la posición indicada por p en el archivo a.
FinArchivo(a) Retorna verdadero si se ha alcanzado la marca de fin de archivo
Tipos de datos
Tipos de datos primitivos
notación Pascal C
Entero Byte unsigned short int
Shortint short int
Integer int
Word unsigned int
Longint long int
unsigneg long int
Booleano boolean
char[]
Cadena string char *
Tipo Registro
Tr = TIPO<c1:t1;c2:t2> Tr = Record typedef struct {
c1: t1; t1 c1;
c2: t2; t2 c2;
end; };
.
Operador acceso “ “
Tipo archivo
Ta=TIPO Archivo de texto Text Se define en apertura
colocando t o b en modo.
Ta=TIPO Archivo de tdato Ta = FILE of Tipo de
dato
Asignar (Ni, NExterno) Assign(NInt, NExt) FILE *f
2 o mas dimensiones
Ta=TIPO TABLA [i..f; i..f] de Ta= Array [i..f, i..f] of tipo
Tipo de dato Tipo de Dato Nombre[cant1][cant2]
Acceso[ind1, ind2]
Acceso[ind1][ind2]
Punteros
Definición
TP = Apuntador a TipoDato TP = ^TipoDato tipoDato * identificador
Instancia
Nuevo(puntero) New(puntero) pro = malloc(sizeof(tipo))
Destruir(puntero) Dispose(puntero) free(pro)
Acceso
Punteros, o *punter Punteros *pro
Est. Autoreferenciadas
TipoNodo = TIPO
<info:TipoInfo,sgte:TPuntero> TipoNodo = Record typedef struct TN {
info:TipoInfo; TipoInfo info;
sgte:TPuntero struct TN sgte;
end; }TipoNodo;
Typedef TipoNodo *
Tpuntero;
Operador de dirección
DireccionDe(Puntero) &Puntero
ARCHIVOS
CLASIFICACION
Según la dirección del flujo de datos:
• De entrada:
• De salida:
• De entrada/salida:
Archivos secuenciales
Los archivos secuenciales tienen algunas características que hay que tener en cuenta:
1. La escritura de nuevos datos siempre se hace al final del archivo.
2. Para leer una zona concreta del archivo hay que avanzar siempre, si la zona está
antes de la zona actual de lectura, será necesario "rebobinar" el archivo.
3. Los ficheros sólo se pueden abrir para lectura o para escritura, nunca de los dos
modos a la vez.
C no distingue si los archivos que usamos son secuenciales o no, es el tratamiento que
hagamos de ellos lo que los clasifica como de uno u otro tipo. Hay archivos que se
comportan siempre como secuenciales de entrada y salida estándar: stdin, stdout, stderr.
En stdin, que suele ser el teclado. El programa sólo podrá abrir ese fichero como de
lectura, y sólo podrá leer los caracteres a medida que estén disponibles, y en el mismo
orden en que fueron tecleados.
Resumen:
En este capitulo el objeto de estudio el conocimiento de las estructuras de datos, sus
particularidades y ventajas y desventajas de cada una de ellas. Ya se ha visto como
seleccionar acciones y como dividir el problema para alcanzar la solución, aquí se
estudia en profundidad en que estructura de datos contener esa información.
SE estudiaron en profundidad estructuras estáticas con almacenamiento electrónico o
físico. Se estableció un criterio de selección que puede resumirse simplemente de este
modo, si se requiere que el dato persista el almacenamiento debe ser físico, es decir en
disco. Si esto no se requiere y lo que se prioriza es la velocidad de procesamiento el
almacenamiento debe ser electrónico, en estructuras de tipo tabla. Siempre priorizar
acceso o búsqueda directa, binaria en caso de no poder y secuencial solo con estructuras
en memoria.
CAPITULO 5 Estructuras Enlazadas
Objetivos de aprendizaje
Dominando los temas del presente capitulo Usted podrá.
1. Manejar estructuras complejas.
2. Introducirse a la semántica de direcciones
3. Comprender como se asigna memoria dinámicamente en tiempo de
ejecución.
4. Conocer estructuras enlazadas lineales
5. Diferenciar entre estructuras indexadas y estructuras enlazadas
Introducción:
En el presente capitulo se incorpora el concepto de asignación dinámica en memoria.
Esto permite romper con la limitación de tamaño fijo que proporcionan las tablas
cuando es necesario trabajar con colección de datos el mismo tipo en memoria.
Para esto se incorporan los conceptos de estructuras enlazadas, punteros y asignación
dinámica en memoria.
LÉXICO
TipoRegistro=TIPO <Campo1 : Entero ; Campo2 : Entero>
TipoPunteroRegistro = TIPO Apuntador a TipoRegistro;
Registro : TipoRegistro
PunteroRegistro : TipoPunteroRegistro
ALGORITMO
Registro.Campo1 ← 5; //asigna valores al registro
Registro.Campo2 10;
PunteroRegistro ← dirección_de(Registro);//
PunteroRegistro↑.Campo1 ← 35;
Escribir(Registro.Campo1); //imprime el valor 35
FIN
Obviamente, cuando una variable de tipo apuntador haga referencia a una variable
declarada en el léxico, como en el caso del ejemplo anterior, no será posible destruir su
contenido mediante la acción Destruir, ya que solo pueden destruirse las instancias que
se crean en tiempo de ejecución con la acción Nuevo.
El tipo pila
Una pila es una colección de elementos de un mismo tipo, posiblemente vacía, sobre la
que podemos hacer operaciones de inserción de un nuevo elemento, eliminación de un
elemento. Una pila es una estructura de tipo LIFO (del inglés, Last-Input, First-
Output), lo cual significa que los elementos siempre serán eliminados de ella en orden
inverso al que fueron colocados, de modo que el último elemento en entrar será el
primero en salir. A este tipo de colección de datos se la conoce por el nombre de “pila”
precisamente por esta característica. A la hora de retirar elementos, únicamente
podremos tomar el que está en la cima de la pila, que será el último que fue apilado.
Para definir el tipo de pila debemos, por tanto, diseñar las siguientes operaciones:
PilaVacia: Pila
EsPilaVacia: Pila Booleano
Apilar : Pila X TipoBase Pila
Cima : Pila TipoBase
Desapilar : Pila Pila
La lista anterior de operaciones disponibles para un tipo se conoce con el nombre de
signatura, y en ella se establecen los nombres de las operaciones y el número y tipo de
parámetros de cada una de ellas. En la signatura de un cierto tipo T se distinguen tres
tipos de operaciones:
1. Operaciones constructoras: son aquellas en las que el tipo T aparece como
resultado devuelto, pero no como parámetro de la operación.
2. Operaciones modificadoras: son aquellas en las que el tipo T aparece tanto en la
lista de parámetros como en el resultado devuelto.
3. Operaciones de consulta: son aquellas en las que el tipo T aparece únicamente
en la lista de parámetros.
En el caso del tipo pila anterior, la única operación constructora sería PilaVacia; las
operaciones Apilar y Desapilar serían modificadoras, y las operaciones EsPilaVacia y
Cima serían de consulta. Las operaciones Cima y Desapilar tendrán como precondición
que la pila que reciben como parámetro no sea vacía, pues para una pila vacía no estaría
definida su cima ni se podría desapilar.
Se puede definir una pila de caracteres mediante las siguientes declaraciones:
TPNodoPilaCars = TIPO Apuntador a NodoPilaCars;
NodoPilaCars=Tipo < dato: Carácter; sig : TPNodoPilaCars >;
PilaCars = TPNodoPilaCars;
PilaVacia PilaCars : una función
ALGORITMO
PilaVacia ← NULO
FIN
Es importante que se sepa que cuando se definen tipos de datos basados en estructuras
de datos complejas, las operaciones usuales de igualdad o desigualdad, que se realizan
por defecto mediante los operadores booleanos = y ≠, producirían resultados que no se
corresponden con la semántica del tipo implementado. Por ejemplo, suponga que
declaramos dos variables, p1 y p2 de tipo PilaCars, y efectuamos una comparación
como la siguiente:
SI p1 = p2 ENTONCES
...
FIN_SI;
Lo que estamos comparando en realidad no son las pilas, es decir, si constan de los
mismos elementos y están en idéntico orden, sino los apuntadores p1 y p2, puesto que
ambas variables son de tipo PilaCars el cual, a su vez, es un tipo apuntador. La
comparación p1 = p2 devolverá Verdadero si y sólo si los apuntadores p1 y p2
apuntan a la misma dirección de memoria.
El tipo cola
El tipo cola difiere del tipo pila únicamente en la forma de inserción y extracción de
elementos. Una cola es una estructurade tipo FIFO (del inglés, First-Input, First-
Output), es decir, primero en entrar, primero en salir. Se conoce con el nombre de cola
por ser la que habitualmente se utiliza en las colas de la vida cotidiana: el primero que
llega (que entra en la cola) es el primero en ser atendido (en ser eliminado de la cola).
TPNodoColaCars = TIPO Apuntador a NodoColaCars;
NodoColaCars = TIPO < dato: carácter; sig TPNodoColaCars >;
ColaCars = TPNodoColaCars;
Las operaciones propias del tipo cola son las siguientes:
ColaVacia : Cola
EsColaVacia : Cola Booleano
Encolar : Cola X TipoBase Cola
Primero : Cola TipoBase
EliminarPrimero : Cola Cola
El tipo lista
El tipo lista es el caso más general de estructura de datos lineal. No existe una forma
prefijada de inserción y extracción de elementos, de modo que éstos pueden ser
insertados en, y también eliminados de, cualquier posición arbitraria.
Con las listas existe un repertorio más amplio de operaciones básicas que se pueden
realizar:
1. Añadir o insertar elementos.
2. Buscar o localizar elementos.
3. Borrar elementos.
4. Moverse a través de una lista, anterior, siguiente, primero.
Algoritmo de inserción:
2. El primer paso es crear un nodo para el dato que vamos a insertar.
3. Si Lista es NULO, o el valor del primer elemento de la lista es mayor que el del
nuevo, insertaremos el nuevo nodo en la primera posición de la lista.
4. En caso contrario, se debe buscar el lugar adecuado para la inserción, tenemos
un puntero "anterior". Lo inicializamos con el valor de Lista, y avanzaremos
mientras anterior^.siguiente no sea NULO y el dato que contiene
anterior^.siguiente sea menor que el dato a insertar.
5. Ahora anterior señalando al nodo previo al insertar, por lo que se enlazan lo
puntero de modo que el siguiente del nuevo sea el que era siguiente del anterior
y al siguiente del anterior se lo hace apuntar al nuevo nodo. Se debe recordar que
anterior contiene el valor inmediato anterior al valor insertado y el siguiente es
mayor. El nuevo nodo debe intercalarse entre ambos.
En el capitulo de algoritmos puntuales se ofrecen varios ejemplos para insertar nodos en
situaciones distintas y con distintos fines
Listas circulares
Una lista circular es una lista lineal en la que el último nodo a punta al primero.
Las listas circulares evitan excepciones en la operaciones que se realicen sobre ellas. No
existen casos especiales, cada nodo siempre tiene uno anterior y uno siguiente.
En algunas listas circulares se añade un nodo especial de cabecera, de ese modo se evita
la única excepción posible, la de que la lista esté vacía.
El nodo típico es el mismo que para construir listas abiertas es una estructura
autoreferenciada que tiene un campo con la información y otro campo con un puntero a
una estructura del mismo tipo:
Lista es el puntero para contener el inicio de las listas, tanto abiertas como circulares.
En el caso de las circulares, apuntará a un nodo cualquiera de la lista.
A pesar de que las listas circulares simplifiquen las operaciones sobre ellas, también
introducen algunas complicaciones. Por ejemplo, en un proceso de búsqueda, no es tan
sencillo dar por terminada la búsqueda cuando el elemento buscado no existe.
Por ese motivo se suele resaltar un nodo en particular, que no tiene por qué ser siempre
el mismo. Cualquier nodo puede cumplir ese propósito, y puede variar durante la
ejecución del programa.
Otra alternativa que se usa a menudo, y que simplifica en cierto modo el uso de listas
circulares es crear un nodo especial de hará la función de nodo cabecera. De este modo,
la lista nunca estará vacía, y se eliminan casi todos los casos especiales.
Operaciones básicas con listas circulares
A todos los efectos, las listas circulares son como las listas abiertas en cuanto a las
operaciones que se pueden realizar sobre ellas:
1. Añadir o insertar elementos.
2. Buscar o localizar elementos.
3. Borrar elementos.
4. Moverse a través de la lista.
Cada una de estas operaciones podrá tener varios casos especiales, por ejemplo,
tendremos que tener en cuenta cuando se inserte un nodo en una lista vacía, o cuando se
elimina el único nodo de una lista.
Añadir un elemento:
El único caso especial a la hora de insertar nodos en listas circulares es cuando la lista
esté vacía.
El primer paso es conseguir que lista apunte al nodo anterior al que se quiere eliminar.
1. Esto se consigue haciendo que lista valga lista^.siguiente mientras
lista^.siguiente sea distinto de nodo.
2. Se hacemos que lista^.siguiente apunte a nodo^.siguiente.
3. Se elimina el nodo.
Eliminar el único nodo en una lista circular:
Este caso es mucho más sencillo. Si lista es el único nodo de una lista circular:
1. Borramos el nodo apuntado por lista.
2. Hacemos que lista valga NULO.
Este método también funciona con listas circulares de un sólo elemento, salvo que el
nodo a borrar es el único nodo que existe, y hay que hacer que lista apunte a NULO.
En una lista circular desde cualquier nodo se puede alcanzar cualquier otro. En una lista
simplemente enlazada apuntando a un nodo no es posible volver a nodos anteriores, es
necesario volver a recorrer desde el principio. En una lista circular apuntando a un nodo
se puede volver al predecesor recorriendo hacia delante.
El riesgo con esta estructura es que de no ser riguroso se puede caer en ciclos infinitos
la lista esta sin orden el puntero a lista puede apuntar, como señalamos a cualquier
nodo.
Estas lista tienen código sencillo pero ineficiente. Para insertar un nodo en esta lista sin
orden:
1. Si se aceptan repeticiones simplemente habrá que insertar el nodo en el lugar
donde se encuentra el puntero.
2. Si no se aceptan repeticiones habrá que recorrer la lista, sin caer en ciclos
infinitos para ver si el elemento a insertar ya existe. Para evitar ciclos
infinitos se debe:
a. Guardar en un puntero auxiliar el nodo donde se encuentra el comienzo
del recorrido.
b. Detener el ciclo al encontrar el dato, si es que esta o al igualar el puntero
de recorrido con el auxiliar.
Definiciones
Nodo = (header, común) // enumera dos valores posibles si es header o común.
TipoPunteroCircularCon Header = TIPO Apuntador a TipoNodo;
TipoNodo = <
Sig : TipoPuntero;
Según tag: Nodo
Header : (cant: Entero; promedio Real; mínimo Real);
// parte del nodo cabecera//
común : (nombre Cadena; Sueldo : Real)
// Parte común del resto de los nodos//
FIN_SEGUN
>;
TipoDato = <nombre : Cadena; sueldo : Rel>
Añadir un elemento:
La estructura s diferente a las vistas anteriormente por lo que se analizaran todos los
casos posibles de inserción.
El proceso es el siguiente:
Nodo^.siguiente debe apuntar a Lista.
Nodo^.anterior apuntará a Lista^.anterior.
Lista^.anterior debe apuntar a nodo.
Lista no tiene por qué apuntar a ningún miembro concreto de una lista doblemente
enlazada, cualquier miembro es igualmente válido como referencia.
El proceso es el siguiente:
1. Nodo^.siguiente debe apuntar a Lista^.siguiente (NULO).
2. Lista^.siguiente debe apuntar a nodo.
3. Nodo^.anterior apuntará a Lista.
Bien, este caso es más genérico, ahora partimos de una lista no vacía, e insertaremos un
nodo a continuación de uno nodo cualquiera que no sea el último de la lista:
El proceso sigue siendo muy sencillo:
1. Que Nodo^.siguiente apunte a lista^.siguiente.
2. Que Lista^.siguiente apunte a nodo.
3. Que nodo^.anterior apunte a lista.
4. Que nodo^.siguiente^.anterior apunte a nodo.
Añadir elemento en una lista doblemente enlazada, caso general:
1. Si lista está vacía se hace que Lista apunte a nodo, y nodo^.anterior y
nodo^.siguiente a NULO.
2. Si lista no está vacía, entonces que nodo^.siguiente apunte a Lista^.siguiente.
3. Luego que Lista^.siguiente apunte a nodo.
4. Que nodo^.anterior apunte a Lista.
5. Si nodo^.siguiente no es NULO, entonces nodo^.siguiente^.anterior apunte a
nodo.
El paso 2 separa el nodo a borrar del resto de la lista, independientemente del nodo al
que apunte Lista.
Introducción
En este capitulo se introduce el concepto de estructuras enlazadas arborescentes.
Estructuras de datos dinámicas arborescentes y el concepto de tablas hash.
Árboles
Una de las estructuras de datos no lineales más utilizadas en la práctica son los árboles.
Se puede definir el árbol como colección de elementos llamados nodos, con una
relación jerárquica entre los mismos, uno de los nodos es distinguido y se llama raíz.
Desde un punto de vista matemático, se puede definir un árbol como un caso particular
de grafo en el que:
1. Existe un nodo distinguido, que se llama nodo raíz
2. Los arcos que conectan los nodos de este grafo son dirigidos, y la dirección
de éstos establece las relaciones de predecesor y sucesor. Si en el árbol
existe un arco que va de un nodo A a un nodo B (AB), entonces se dice que
A es predecesor de B, y que B es sucesor de A.
3. Cada nodo tiene uno y sólo un nodo predecesor, con la única excepción del
nodo raíz, que no tiene predecesor.
4. Cada nodo puede tener cero o más nodos sucesores.
Si A es predecesor de B y B lo es de C (ABC), se dice que A es predecesor directo
de B y predecesor indirecto de C o que B es sucesor directo de A y que C es sucesor
indirecto de A.
De la definición de árbol se deducen las siguientes propiedades:
Un árbol es necesariamente acíclico, es decir, un nodo no puede ser sucesor (sucesor*)
ni predecesor (predecesor*) de sí mismo.
Una estructura árbol establece una partición entre sus nodos componentes: el nodo raíz,
y cada conjunto de sucesores por cada arco que parte el nodo raíz. Esta misma partición
se puede establecer con respecto a cada nodo y sus sucesores (el subárbol delimitado
por ese nodo).
Un árbol es una estructura no lineal en la que cada nodo puede apuntar a uno o varios
nodos. Se puede dar una definición recursiva: un árbol es una estructura en compuesta
por un dato y varios árboles.
Definiciones comunes
1. Nodo hijo: cualquiera de los nodos apuntados por uno de los nodos del
árbol. En el ejemplo, 'L' y 'M' son hijos de 'G'.
2. Nodo padre: nodo que contiene un puntero al nodo actual. En el ejemplo, el
nodo 'A' es padre de 'B', 'C' y 'D'.
3. Hermano: un nodo n1 se dice que es hermano de otro nodo n2 si ambos
comparten un mismo nodo predecesor.
Los árboles con los que trabajara tienen otra característica importante: cada
nodo sólo puede ser apuntado por otro nodo, es decir, cada nodo sólo tendrá
un padre. Esto hace que estos árboles estén fuertemente jerarquizados, y es lo
que en realidad les da la apariencia de árboles.
En cuanto a la posición dentro del árbol:
4. Nodo raíz: nodo que no tiene padre. Este es el nodo que usaremos para
referirnos al árbol. En el ejemplo, ese nodo es el 'A'.
5. Nodo hoja: nodo que no tiene hijos. En el ejemplo hay varios: 'F', 'H', 'I', 'K',
'L', 'M', 'N' y 'O'.
6. Nodo rama: aunque esta definición apenas la usaremos, estos son los nodos
que no pertenecen a ninguna de las dos categorías anteriores. En el ejemplo:
'B', 'C', 'D', 'E', 'G' y 'J'.
7. árbol completo árbol en el que en cada nodo o bien todos o ninguno de los
hijos existe.
8. Orden: es el número potencial de hijos que puede tener cada elemento de
árbol. De este modo, diremos que un árbol en el que cada nodo puede
apuntar a otros dos es de orden dos, si puede apuntar a tres será de orden
tres, etc.
Orden n: Existe un nodo con n descendientes y no hay nodos con mas
descendientes que el. No importa el orden.
n-ario: se dice que el árbol es n-ario si cada nodo puede tener como máximo
n nodos sucesores e importa el orden en el que están ubicados. Guardan un
solo elemento de información con n punteros como máximo
9. Grado: el número de hijos que tiene el elemento con más hijos dentro del
árbol. En el árbol del ejemplo, el grado es tres, ya que tanto 'A' como 'D'
tienen tres hijos, y no existen elementos con más de tres hijos.
10. Nivel: se define para cada elemento del árbol como la distancia a la raíz,
medida en nodos. El nivel de la raíz es cero y el de sus hijos uno. Así
sucesivamente. En el ejemplo, el nodo 'D' tiene nivel 1, el nodo 'G' tiene
nivel 2, y el nodo 'N', nivel 3.
11. Altura: la altura de un árbol se define como el nivel del nodo de mayor
nivel. Como cada nodo de un árbol puede considerarse a su vez como la raíz
de un árbol, también podemos hablar de altura de ramas. El árbol del
ejemplo tiene altura 3, la rama 'B' tiene altura 2, la rama 'G' tiene altura 1, la
'H' cero, etc.
12. Profundidad: la profundidad de un árbol es el máximo nivel de un nodo en
ese árbol. Profundidad de un nodo es la cantidad de arcos del nodo al
descendiente hoja más lejano.
13. Longitud: la longitud de un árbol es el máximo número de nodos en el
mismo nivel.
14. Recorrido: Existen 6 formas de recorrido Pre, In, Pos orden y Pre, In y Pos
Orden Inverso para recorrerlos primero en profundidad, también pueden ser
recorridos en amplitud primero.
15. árbol perfectamente balanceado: Es tal que el único nivel que puede estar
incompleto es el ultimo.
16. Factor de balance de un nodo: Diferencia entre la altura de su subárbol
izquierdo y su subárbol derecho.
17. árbol AVL: Es aquel en el cual la altura de los subárboles de cualquier nodo
no difieren en mas de uno. Todos los nodos del árbol AVL tienen factor de
balance –1, 0, 1.
18. Un árbol perfectamente balanceado es AVL, aunque para ser AVL no es
necesario que el árbol este perfectamente balanceado. Ver definiciones.
Los algoritmos de inserción y borrado dependen en gran medida del tipo de árbol que se
implemente.
Operaciones en ABB
El repertorio de operaciones que se pueden realizar sobre un ABB es similar al que se
realiza sobre otras estructuras y además posee e un conjunto de operaciones propias
1. Buscar un elemento.
2. Insertar un elemento.
3. Borrar un elemento.
4. Movimientos a través del árbol:
5. Comprobar si un árbol está vacío.
6. Calcular el número de nodos.
7. Comprobar si el nodo es hoja.
8. Calcular la altura de un nodo.
9. Calcular la altura de un árbol
10. Rotaciones.
Buscar un elemento
1. Se parte del nodo raíz, y las operaciones mas claras y eficientes son
recursivas.
2. Si el árbol está vacío, Finaliza búsqueda, el valor buscado no esta en la
estructura.
3. Si el valor del nodo raíz es igual al elemento que buscamos, finaliza
exitosamente la búsqueda.
4. Si nada de esto ocurre se sigue la búsqueda recursivamente
5. Si el valor buscado es menor que la información del nodo se continúa la
búsqueda en el sub árbol izquierdo.
6. Si el valor es mayor continúa la búsqueda en el sub árbol derecho.
7. El valor de retorno de una función de búsqueda en un ABB puede ser un
puntero al nodo encontrado, o NULO, si no se ha encontrado.
Definiciones
Elem = TIPO Entero;
árbol = TIPO Apuntador a TipoNodo;
TipoNodo = TIPO <izq, der :árbol; info Elem
FIN
Insertar un elemento
Las inserciones se hacen siempre en las hojas, se utiliza un criterio similar al de las
búsquedas, hasta encontrar el nodo hoja del cual insertar. Los valores deben ser
diferentes por lo que si ya esta el valor en el árbol no se ingresa y se informa error. Se
supone la misma definición de tipos que en el ejemplo anterior.
Borrar un elemento
Para borrar un elemento también hay que basarse algoritmo de búsqueda.
1. Si el elemento no está en el árbol no se lo puede borrar.
2. Si está, puede ocurrir que:
a. Se trata de un nodo hoja: en ese caso se lo borra directamente.
b. Se trata de un nodo rama: en ese caso no se puede eliminar
directamente, pues se perderían todos los elementos del árbol del que es
el padre. Lo que debe hacerse es buscar el nodo más a la izquierda del
subárbol derecho, o el más a la derecha del subárbol izquierdo e
intercambiamos sus valores y a continuación se elimina ese nodo hoja.
Suponiendo el siguiente árbol de búsqueda en el que se harán los borrados
10
5 14
5 14
Borrar 10
10
5 14
14
5
LEXICO
Aux : árbol
ALGORITMO
SI (T = NULO) Imprimir(ERROR)
(T <>NULO)
SI (x<t^.info) Borrar(t^.izq,x)
(x>t^.info) Borrar(t^.der,x)
(x=t^.info)
Aux = t
FIN_SI
FIN_SI
FIN
árbol AVL
Definición
Un árbol AVL (llamado así en homenaje a Adelson-Velskii y Landis) es un árbol
binario de búsqueda en el que para cada nodo, las alturas de sus subárboles izquierdo y
derecho no difieren en más de 1.
Los AVL son Árboles de búsqueda por lo que se mantienen las operaciones de los
mismas y se agregan operaciones equilibrio o rotación.
Factor de equilibrio
Cada nodo, además de la información y los punteros a los hijos derecho e izquierdo,
debe tener un campo para almacenar el factor de equilibrio.
El factor de equilibrio, como ya se definió, es la diferencia entre las alturas del árbol
derecho y el izquierdo:
FE = altura subárbol derecho - altura subárbol izquierdo;
Por definición, para un árbol AVL, este valor debe ser -1, 0 ó 1.
Los árboles-B.
Desarrollado Rudolf Bayer y Eduard M. McCreight.
Los árboles-B no son binarios.
No son árboles de búsqueda, ya que en inglés se denominan B-trees.
No son, en general balanceados.
Los Árboles binarios de búsqueda
1. Son eficientes para la búsqueda si están balanceados.
2. No siempre entran completos en memoria.
Si la memoria es insuficiente para almacenar gran número de datos en un árbol se
pueden reemplazar punteros del árbol por direcciones de almacenamiento en disco
Los árboles-B son árboles de búsqueda de m ramas, y cada nodo puede almacenar un
máximo de m-1 claves.
Árboles Multicaminos: Los nodos guardan hasta M-1 clave con un máximo de M hijos.
Un árbol multicamino M-ario: Cada nodo se guarda en disco en un mismo bloque físico
por lo que se lo accede solo una vez. Cada clave de un cierto nodo será tal que las claves
almacenadas en su subárbol izquierdo serán menores y las almacenadas en el subárbol
derecho serán mayores a el.
Tienen la ventaja que compactan la información en una zona física (bloque físico de
disco) por lo que no se requieren muchos accesos para encontrar una clave entre
muchas.
Estos arboles resultan óptimos para la implementación de bases de datos.
Para que sean realmente útiles se los debe mantener balanceados
Definiciones
Dado un conjunto de llaves posibles, X, y un conjunto de direcciones de memoria, D,
una función de transformación, H(x), es una aplicación de muchos contra uno del
conjunto de llaves posible en el conjunto de direcciones de memoria:
H (x) -> D
Lo que buscaremos será una función:
FUNCTION hash (K: clave): [0..H-1];
que clasifica cada dato en una clase.
La idea básica de este método consiste en aplicar una función que traduce un conjunto
de posibles valores llave en un rango de direcciones relativas. Un problema potencial
encontrado en este proceso, es que tal función no puede ser uno a uno.
El TDA Tabla de Dispersión es un tipo de datos homogéneo, denominadas celdas, de
tamaño fijo, Tabla, compuesto por un número fijo de componentes a las que se accede
mediante una dirección de memoria resultante de una función de transformación. Sobre
este TDA se definen los operadores Insertar, Buscar y Eliminar.
Se dice que dos llaves distintas X1 y X2 son sinónimas para una función de
transformación H(X) si H(X1) = H(X2). A dos llaves diferentes que les corresponda la
misma dirección relativa se les llama sinónimos.
Se dice que se ha producido desbordamiento cuando una nueva llave se aplica a una
dirección de memoria completamente ocupada, y se dice que se ha producido una
colisión cuando dos llaves distintas se aplican sobre la misma celda.
Función Hash
La función hash debe ser fácil de calcular, es decir, la dirección de memoria debe ser
calculada con sencillez y rapidez a partir de la llave, pero además es importante que se
produzcan el menor número posible de colisiones.
Si la clave es un entero, puede utilizarse algo tan simple como
F(X) = hash (X) = X MOD H
Con esta función se divide la llave en uso X por M, y el resto será la dirección.
Esta función hash funciona bien si el rango de X es muy amplio comparado con H. A la
hora de elegir H, suele ser buena idea optar por un número primo.
A veces se utilizan técnicas de truncado (sólo se usan unos cuantos dígitos), o se eleva
el número al cuadrado y se eligen algunos dígitos del resultado (truncado, de nuevo), o
se pliega el número sobre sí mismo (y se suman los dígitos que coinciden), o ... la
imaginación tiene pocos límites.
Las funciones hash más comunes son:
• Residuo de la división
• Medio del cuadrado
• Pliegue
HASHING POR RESIDUO DE LA DIVISIÓN
Es la función de transformación más conocida es el resto de la división entera (mod)
F(x) = X mod M (tamaño d e tabla)
Con esta función se divide la llave en uso X por M y el resto será la dirección. Por lo
tanto el espacio de direcciones de los bloques será [0, M-1]
La idea de este método es la de dividir el valor de la llave entre un numero apropiado, y
después utilizar el residuo de la división como dirección relativa para el registro
(dirección = llave módulo divisor).
Ejemplo
Function H (x: array[1..10]) : 0 ... N-1
Var
i, suma: integer,
begin
suma := 0 ;
for i:= 1 to 10 do
suma := suma + ord ( x[i] );
h := suma MOD N
end,
HASHING POR MEDIO DEL CUADRADO
En esta técnica, la llave es elevada al cuadrado, después algunos dígitos específicos se
extraen de la mitad del resultado para constituir la dirección relativa. Si se desea una
dirección de n dígitos, entonces los dígitos se truncan en ambos extremos de la llave
elevada al cuadrado, tomando n dígitos intermedios. Las mismas posiciones de n dígitos
deben extraerse para cada llave.
TIPOS DE HASHING
Se considera dos formas de dispersión:
La primera llamada dispersión abierta (o externa), que permite que se almacene en un
espacio potencialmente ilimitado.
La segunda llamada dispersión cerrada (o interna), usa un espacio fijo para el
almacenamiento, por lo que limita el tamaño de los conjuntos.
Es decir, ante la aparición de colisiones hay dos soluciones fundamentales:
solución abierta: Se organizan de alguna forma los datos del mismo subproblema, de
forma que "hash" me lleva simplemente al subproblema correspondiente a una clave K,
y ahí se aplican de nuevo técnicas de gestión de tablas.
solución cerrada: Se retoca el valor de "hash (k)" hasta que no existe ninguna colisión.
De alguna forma, si la "clase natural" ya está ocupada se le busca al dato una clase
alternativa que esté libre. Esto sólo vale si H > N.
Hashing Abierto
El conjunto de datos que colisionan en una misma clase se organiza de alguna forma:
tabla, lista encadenada, árbol binario, etc.
Lo más habitual es la práctica es utilizar listas encadenadas. La razón es que estas listas
no ocupan nada si no hay datos en una clase, y van adaptándose dinámicamente a un
número variable de datos. Su complejidad es lineal; pero si logramos que trabajen sobre
listas breves, esto tiene poco impacto.
A efectos de código, tenemos
VAR TT: ARRAY [0..H-1] OF Pnodo;
0
1
N-1
La idea fundamental es que el conjunto de miembros potenciales se divide en un
número finito de clases ; si se desea tener N clases, numeradas de 0 a N-1, se usa una
función de hashing H tal que para cada objeto X del tipo de datos de los miembros del
conjunto que se va a representar, h(X) sea uno de los enteros de 0 a N-1. El valor de
H(x) es la clase a la cual X pertenece.
A X se le llama la clave y a H(X) la función hashing o de dispersión.
En un arreglo llamado tabla de clases, indicado por los números de clases de 0 a N-1, se
tienen los encabezamientos de N listas.
Los elementos de la i-esima lista son los miembros del conjunto que se está
representando y que pertenecen a la clase i, esto es aquellos elementos X del conjunto
tales que H(x) = i.
Se espera que cada clases tenga el mismo tamaño. Entonces si hay M elementos en el
conjunto, en promedio cada clases tendrá M / N miembros.
También es posible agrupar por medio de árboles de desbordamiento en lugar de listas,
esto es, de organizar las llaves que tienen colisiones como estructuras de árboles. Cada
entrada de la tabla de hashing es la raíz de un árbol.
Hashing Cerrado
Una tabla de dispersión (hashing) cerrada guarda los miembros en la tabla de clases, en
vez de usar esa tabla para almacenar los encabezamientos de listas.
En consecuencia, parece que sólo es posible colocar un elemento en una clase; sin
embargo la dispersión cerrada tiene asociada una estrategia de redispersión.
Si se intenta colocar X en una clase H(x) y esta ya tiene un elemento (colisión) la
estrategia de redispersión elige una sucesión de localidades alternas H1(X), H2(X),...
dentro de la tabla de clases, en la cual es posible colocar X. Se prueba en todas las
localidades en orden, hasta encontrar una vacía.
H = H(x);
I := 0;
Repeat
If T[h].key := k then
elemento encontrado
Else IF T[h].key := vacío then
elemento no está en la tabla
else
*/ colisión
i := i+1
h := H(k) + G(i);
end
Until encontrado o no esta en la tabla o tabla llena
Al principio todas las posiciones están VACIAS, y se van llenando poco a poco. Si
eliminamos datos, no podemos simplemente vaciar la posición que ocupaban, pues el
método de búsqueda por saltitos podría dar resultados diferentes según se encuentre las
posiciones intermedias ocupadas o no. Es un poquito lioso de programar; pero nada
más.
var
c: integer; / contador para asegurar que la tabla no este llena /
p: integer; / posición actualmente probada en H /
begin
p := Calculo_direccion ( r.llave);
c:=0
ELIMINACIONES
Por ello cuando se deban hacer supresiones (eliminar), el enfoque más efectivo para
resolver este problema es colocar una constante "suprimido", cosa de que se siga
buscando un elemento al encontrar esta constante. Es importante que haya una
diferencia entre "suprimido" y "vacío" (donde no se ha colocado nada), dado que una
localización vacía se utiliza como señal para detener la búsqueda de una llave objetivo.
Para entender esto supongase que, antes de la eliminación, han tenido lugar una
colisión o varias y que algún elemento, cuya posicion de dirección de hashing (calculo
de dirección) es la posicion ahora eliminada, en realidad esta almacenado en otro lugar
de la tabla. Si intentamos recuperar ese elemento, la posicion ahora vacía detendrá la
búsqueda y será imposible encontrar el elemento aun cuando esté todavía en la tabla.
El método con que se supera esta dificultad es inventar otra llave espacial, para situarse
en cualquier posición eliminada. Esta llave especial indicaría que esta posición se
encuentra libre para recibir una inserción cuando se desee.
MANEJO DE DESBORDAMIENTO O SOBRECARGA en Hashing Abierto
MÉTODOS PARA RESOLVER EL PROBLEMA DE LAS COLISIONES
Considere las llaves K1 y K2 que son sinónimas para la función hash R. Si K1 es
almacenada primero en el archivo y su dirección es R(K1), entonces se dice que K1 esta
almacenado en su dirección de origen.
Existen dos métodos básicos para determinar donde debe ser alojado K2 :
Direccionamiento abierto.- Se encuentra entre dirección de origen para K2 dentro del
archivo.
Separación de desborde (Área de desborde).- Se encuentra una dirección para K2 fuera
del área principal del archivo, en un área especial de desborde, que es utilizada
exclusivamente para almacenar registro que no pueden ser asignados en su dirección de
origen
Los métodos mas conocidos para resolver colisiones son:
Sondeo lineal
Que es una técnica de direccionamiento abierto. Este es un proceso de búsqueda
secuencial desde la dirección de origen para encontrar la siguiente localidad vacía. Esta
técnica es también conocida como método de desbordamiento consecutivo.
Para almacenar un registro por hashing con sondeo lineal, la dirección no debe caer
fuera del límite del archivo, En lugar de terminar cuando el límite del espacio de
dirección se alcanza, se regresa al inicio del espacio y sondeamos desde ahí. Por lo que
debe ser posible detectar si la dirección base ha sido encontrada de nuevo, lo cual indica
que el archivo esta lleno y no hay espacio para la llave.
Para la búsqueda de un registro por hashing con sondeo lineal, los valores de llave de
los registros encontrados en la dirección de origen, y en las direcciones alcanzadas con
el sondeo lineal, deberá compararse con el valor de la llave buscada, para determinar si
el registro objetivo ha sido localizado o no.
El sondeo lineal puede usarse para cualquier técnica de hashing. Si se emplea sondeo
lineal para almacenar registros, también deberá emplearse para recuperarlos.
Doble hashing
En esta técnica se aplica una segunda función hash para combinar la llave original con
el resultado del primer hash. El resultado del segundo hash puede situarse dentro del
mismo archivo o en un archivo de sobre flujo independiente; de cualquier modo, será
necesario algún método de solución si ocurren colisiones durante el segundo hash.
La ventaja del método de separación de desborde es que reduce la situación de una
doble colisión, la cual puede ocurrir con el método de direccionamiento abierto, en el
cual un registro que no esta almacenado en su dirección de origen desplazara a otro
registro, el que después buscará su dirección de origen. Esto puede evitarse con
direccionamiento abierto, simplemente moviendo el registro extraño a otra localidad y
almacenando al nuevo registro en la dirección de origen ahora vacía.
Puede ser aplicado como cualquier direccionamiento abierto o técnica de separación de
desborde.
hda(x,i)=( h(x)+i*h'(x) ) mod m
h(x)=x mod m
h'(x)=1 + (x mod m') siendo m'<m
Especificación informal
Se describen los datos y las operaciones en un lenguaje informal y esta descripción
consta de dos partes
• Detallar en los datos del tipo los valores que pueden tomar
• Describir las operaciones relacionadas con los valores
Especificación formal
La especificación formal proporciona un conjunto de axiomas que describen el
comportamiento de las operaciones. La descripción debe incluir_
• Una parte de sintaxis: en cuanto a los tipos d argumentos y los tipos de
resultados.
Operación(Tipo de argumentos) -> Tipo Resultado
Retirar (Conjunto, Elemento) -> Conjunto
• Una parte de semántica: detallando para determinados valores particulares de
argumentos la expresión del resultado que se obtiene.
Pertenece (Conjunto, e1)-> Falso
Debe cumplir con el objetivo de poder verificar la implementación correcta del TAD.
CAPITULO 7 ALGORITMOS
Objetivos de aprendizaje
Dominando los temas del presente capitulo Usted podrá.
1. Evaluar algoritmos puntuales aplicables a distintas estructuras de datos.
2. Conocer las precondiciones para la utilización de algoritmos puntuales
Introducción
Se describen a continuación un conjunto de algoritmos puntuales para el manejo de
estructuras estáticas y dinámicas. Con una descripción breve de las precondiciones y
poscondiciones, utilizando definiciones genéricas para todos los casos
Definiciones de tipos
Definiciones de los tipos de datos utilizados en los algoritmos que se describen
MAX_FIL = 100;
Tinfo = TIPO Entero;
TinfoC = TIPO <C1 : Td1, C2 : Td2>
TVector = TIPO Tabla [1,MAX_FIL] de Tinfo;
TPNodo = TIPO Apuntador a Nodo;
Nodo = TIPO <Info : Tinfo; sgte : TPNodo>
Arbol = TIPO <Info : Tinfo; Izq, Der : TPArbol>
FIN_SI
LEXICO Establece valores para las posiciones de los elementos del vector, Pri
j : Entero; contiene el indice del primero, es decir el valor 1, U el indice del ultimo
elemento logicio, es decir N. Ademas se coloca en Posic. El valor cero,
u,m : Entero; utilizando este valor como bandera para salir del ciclo cuando encuentar
ALGORITMO el valor buscado
Posic = 0;
Pri = 1;
U = N;
MIENTRAS (Pri < = U y Pos = 0) HACER
M = (Pri + U ) div 2 Permanece en el ciclo mientras no encuentre lo
SI V[M] = Clave buscado, al encontrarlo le asigna a pos el indice
donde lo encontro, como es un valor > que cero
ENTONCES hace false la expresión logica y sale del ciclo. Si no
Posic = M; lo encuentra, y para evirtar ciclo infinito verifica
SI_NO que el primero no tome un valor mayor que el
SI Clave > V[M] ultimo. Si eso ocurre es que el dato buscado no esta
y se debe salir
ENTONCES
Pri = M+1
SI_NO
U=M–1 Si el dato buscado lo encuentra le asigna a posición
FIN_SI el indice para salir. Si no lo encuentra verifica si
esmayor el dato buscabo a lo que se encuentra
FIN_SI revisa en la mitad de los mayores por lo que le
FIN_MIENTRAS; asigna al primero el indice siguiente al de la mitad
dado que alli no estab y vuelve a dividir el conjunto
de datos en la mitas, de ser menor pone como tome
FIN. // Búsqueda binaria en vector ultimo el anterior al de la mitad actual
LEXICO
j : Entero;
u,m : Entero;
ALGORITMO
Posic = 0; La busqueda es bastante parecida a lo
Pri = 1; desarrollado anteriormente, pero en pos debe
tener la primera aparicion de la clave buscada
U = N; que puede repetirse.
MIENTRAS (Pri < U ) HACER En la busqueda anterior utilizabamos esta pos
M = (Pri + U ) div 2 como bandera, para saber cuando Sali si lo
encontro. En este caso si lo utilizamos con el
SI V[M] = Clave mismo proposito saldria cuando encuentra un
ENTONCES valor oincidente con la clave que no
Posic = M; necesariamente es el primero, por lo que esa
condicion se elimina. Al encontrarlo en m se le
U = M; asigna ese valoe a pos, alli seguro esta. No
SI_NO sabemos si mas arriba vuelve a estar por lo que
SI Clave > V[M] se asigna tambien esa posición al ultimo para
seguir iterando y ver si lo vuelve a encontrar.
ENTONCES Debe modificarse el operador de relacion que
Pri = M+1 compara primero con ultimo para evitar un
SI_NO ciclo infinito, esto se hace eliminando la
relacion por igual. Insisto en el concepto de los
U=M–1 valores de retorno de la busqueda binaria. Una
FIN_SI particularidad de los datos es que si lo que se
FIN_SI busca no esta puede retornal en el primero la
posición donde esa clave deberia estar
FIN_MIENTRAS;
FIN. // Búsqueda binaria en vector
LEXICO
I,J, : Entero;
Aux : Tinfo;
Ord : Boolean;
ALGORITMO El algoritmo es similar al anterior, solo que
I = 0; el ciclo de repetición externo no lo hace si es
que tiene la certeza, en el paso anterior que
REPETIR los datos ya estan ordenados. Es por eso que
Inc(i); agrega una bandera para verificar si ya esta
Ord = TRUE; ordenado y cambia el cilo exactp por un
ciclo pos condicional. Es decir reemplaza la
PARA j [1, N - i] HACER composición para por la composición repetir
SI (v[j] > v[j + 1]) hasta
ENTONCES
Ord = False;
Aux = v[j];
V[j] = v[j + 1];
V[j + 1] = Aux;
FIN_SI;
FIN_PARA;
HASTA ( Ord o I = N – 1);
FIN
LEXICO
I,J, : Entero;
Aux : Tinfo;
ALGORITMO
PARA I[1..N-1]
J = I;
Aux = A[i] ;
MIENTRAS (J >0 Y AUX < A[J - 1])HACER
A[J] = A[J - 1] ;
Dec(J) ;
FIN MIENTRAS ;
A[J] = Aux ;
FIN PARA;
FIN
ALGORITMO
Intervalo = n / 2;
LEXICO
I : Entero;
ALGORITMO
I = 1;
Anterior = TipoInfo;
// Inicializar contadores generales
MIENTRAS (I<=N) Hacer
//inicializar contadores de cada sublote
Anterior = V[i]
MIENTRAS (I<=N Y Anterior = V[i] HACER
// Ejecutar acciones del ciclo
FIN
Se identifican dos tipos de archivos, archivos de texto y archivos biarios. Los archivos
de texto son un conjunto de lineas las cuales tienen 0, 1 o mas caracteres que finalizan
con un carácter particular que representa el fin de la linea. Los datos son interpretados
como caracteres. <los archivos binario, en cambio, son una secuencia de bytes
lmacenados según su representación interna y sin interpretar, en este caso es necesario
que sean leidos los datos tal como fueron guardados para poder ser reconocidos como
iguales.
Operaciones elementales de acceso a archivos:
Accion Efecto
Asignar(a,s) Asigna al idntificador a la cadena s que representa ub archivo
en disco
Abrir(a) Prepara el archivo asignado a la variable a para su utilizacion
Crear(a) Crea el archivo asignado al identificador a y lo prepra para su
acceso
Cerrar(a) Cierra el arhivo apuntado por a, actualiza la marca de fin si
corresponde
LeerCaracter(a,c) Lee el siguiente carácter del flujo apuntado por a y lo almacena
en c
LeerLinea(a,s) Lee la siguente linea del flujo apuntado por a y la almacena en s
GrabarCaracter(a,c) Escribe secuencialmente en el flujo a el caracter c
GrabarCadena(a,s) Escribe en el flujo a la cadena s
LeerArchivo(a,r) Lee el siguiente tipo de dato (ejemplo registro) del flujo a y lo
almacena en r
GrabarArchivo(a,r) Graba el siguiente tipo de dato, r, en el flujo a
LeerPosicion(a,p,r) Lee del flujo a el valor contenido en l posición p y lo almacena
en r
GrabarPosicion(a,p,r) Graba en la posicion p del flujo a el valor contenido en r
NumeroElementos(a) Retorna el numero de elementos almacenados en el archivo
PosicionActual(a) Retorna la posición en la que se encuentra posicionado el
puntero actual del archivo.
ApuntarA(a,p) Accede a la posición indicada por p en el archivo a.
FinArchivo(a) Retorna verdadero si se ha alcanzado la marca de fin de archivo
Definiciones de tipos
TipoArchivoEntero = TIPO Entero;
TipoRegistro = TIPO <Clave : Entero ; Nombre : Cadena>;
TipoArchivoRegistro = TIPO Archivo de TipoRegistro;
Declaracion de variables
ArchTexto : Texto //declra una varible de tipo archivo de texto
ArchEntero : TipoArchivoEntero // declara una variable de tipo archivo entero
ArchRegistro: TipoArchivoRegistro //declara una variable de tipo archivo registro
Pila estructura del tipo LIFO el ultimo en entrar es el primero en salir, en general el
uso de esta estructura es adecuado cuando se persigue el propósito de invertir el orden
de una estructura dada. Su uso es reservado para los compiladores que los utilizan
para recursividad y las notaciones sufijas, por ejemplo.
LEXICO
PRE Colafte Apunta al Inicio o a NULO si esta vacía, Cola Fin al final o
NULO
POS ColaFte apunta al primer elemento de la estructura.
ColaFin apunta al ultimo elemento de la estructura (coincide con el
creado)
La estructura queda perfectamente enlazada.
Ptr : TPNodo;
ALGORITMO Se pide memoria se guarda la información y el
Nuevo(Ptr); siguiente del nuevo nodo siempre es el valor
NULO. Para enlazarlo, como vimos, hay dos
Ptr^ <Valor, NULO>; situaciones posibles cuando es el primer nodo, en
SI (Colafte = NULO) ese caso el puntero al inicio apunta al nuevo nodo
ENTONCES y cuando ya hay por lo menos un nodo, en ese
caso se debe enlazar el nuevo nodo a
Colafte Ptr continuación del anterior ultimo. En todos los
SI_NO casos el puntero al ultimo siempre apuntara al
ColaFin^.sgte = Ptr; nodo creado.
FIN_SI;
ColaFin Ptr
FIN.
LEXICO
PRE Lista Apunta al Inicio o a NULO si esta vacia.
POS Lista apunta al primer elemento de la estructura.
No retorna la direccion del nodo creado, salvo si es el primero
La estructura queda perfectamente enlazada y ordenada creciente.
Ptr, PtrNuevo : TPNodo;
ALGORITMO
SI_NO
Se pide memoria y se guarda la informacion
Nuevo(PtrNuevo);
PtrNuevo^ <Valor, NULO>;
Ptr = Lista;
MIENTRAS (Ptr^.sgte <>NULO y Valor > Ptr^.sgte^.info)
HACER
Pregunta anticipadamente por el valor contenido en el
Ptr ptr^.sgte nodo siguiente al apuntado por ptr. Si la informacióna
FIN_MIENTRAS ingresar es mayor a la del nodo siguiente debe avanzar
una posicion
LEXICO
PRE Lista Apunta a NULO porque esta vacia.
POS Lista apunta al primer elemento de la estructura.
Ptr: TPNodo;
ALGORITMO
Nuevo(Ptr);
Ptr^ <Valor, NULO>;
Lista Ptr
FIN.
LEXICO
PRE Lista Apunta al Inicio y noesta vacia.
POS Lista apunta al primer elemento de la estructura.
Ptr: TPNodo;
ALGORITMO
Nuevo(Ptr);
Ptr^ <Valor, Lista>;
Lista Ptr
FIN.
LEXICO
PRE Lista Apunta al Inicio o a NULO si esta vacia.
POS Lista apunta al primer elemento de la estructura.
No retorna la direccion del nodo creado.
Ptr, PtrNuevo : TPNodo;
ALGORITMO
SI (lista = NULO )
ENTONCES
Nuevo(Ptr);
Ptr^ <Valor, Lista>;
Lista Ptr
SI_NO
Nuevo(PtrNuevo);
PtrNuevo^ <Valor, NULO>;
Ptr = Lista;
MIENTRAS (Ptr^.sgte <>NULO)
HACER
Ptr ptr^.sgte
FIN_MIENTRAS
PtrNuevo`.sgte Ptr^.sgte;
Ptr^.sgte Ptrnuevo;
FIN_SI;
FIN.
LEXICO
PRE Lista Apunta al Inicio o a NULO si esta vacia.
POS Lista apunta al primer elemento de la estructura.
Info es un rec¡gistro con al menos dos campos para ordenar
No retorna la direccion del nodo creado, salvo si es el primero
La estructura queda perfectamente enlazada y ordenada creciente.
Ptr, PtrNuevo : TPNodo;
ALGORITMO
SI (lista = NULO) o (Valor.C1 < Lista^.info.C1) o
(Valor.C1 =Lista^.info.C1)y(Valor.C2<Lista^.info.c2)
ENTONCES
Nuevo(Ptr);
Ptr^ <Valor, Lista>;
Lista Ptr
SI_NO
Nuevo(PtrNuevo);
PtrNuevo^ <Valor, NULO>;
Ptr = Lista;
MIENTRAS (Ptr^.sgte <>NULO) y ((Valor > Ptr^.sgte^.info) o
((valor.C1 = Lista*.info.C1)y(Valor.C2 >Lista*.info.C2))
HACER
Ptr ptr^.sgte
FIN_MIENTRAS
PtrNuevo`.sgte Ptr^.sgte;
Ptr^.sgte Ptrnuevo;
FIN_SI;
FIN.
LEXICO
PRE Lista Apunta al Inicio y no esta vacia.
Se busca encontrar el nodo que contenga valor como informacion
POS Lista apunta al primer elemento de la estructura.
Retorna la direccion del nodo con el valor buscado o NULO esta
Ptr : TPNodo;
ALGORITMO
Ptr = NULO;
MIENTRAS (Lista <>NULO Y Ptr = NULO) HACER
SI valor = Lista ^.info
ENTONCES
Ptr = Lista {lo encontro y sale}
SINO
Lista = Lista ^.sgte {avanza al proximo nodo}
FIN_SI
FIN_MIENTRAS
BuscarNodo = Ptr;
FIN.
LEXICO
PRE Lista Apunta al Inicio o a NULO si esta vacia.
POS Lista apunta al primer elemento de la estructura.
Retorna la direccion del nodo creado, salvo si es el primero
La estructura queda perfectamente enlazada y ordenada creciente.
No se repite la clave
PtrNuevo : TPNodo;
ALGORITMO
SI (lista = NULO o Valor < Lista^.info
ENTONCES
Nuevo(PtrNuevo);
PtrNuevo^ <Valor, Lista>;
Lista Ptr
SI_NO
Ptr = Lista;
MIENTRAS (Ptr^.sgte <>NULO y Valor >= Ptr^.sgte^.info)
HACER
Similar al procedimiento inserta nodo, pero como no
Ptr ptr^.sgte debe insertar siempre se cambia el orden de las
FIN_MIENTRAS accines cuando se debe insertar en el medio. En este
SI (Ptr^.Info <> Valor) caso se busca primero y si no esta se crea el nodo y se
enlazan los punteros. Como ptr en este estado no
ENTONCES puede valer NULO y el siguiente si el ciclo de
PtrNuevo^.sgte Ptr^.sgte; repetición se hace modificando el operador de relacion
Ptr^.sgte Ptrnuevo; por >= en lugar de >. Al salir del ciclo si el valor
contenido en el nodo actual es distinto al buscado
Ptr PtrNuevo; significa de ese dato no esta en la lista y se debe
FIN_SI; inserta, de lo contrario no se inserta.
FIN_SI;
FIN.
LEXICO
PRE Lista Apunta al Inicio o a NULO si esta vacia.
POS Lista apunta al primer elemento de la estructura.
No retorna la direccion del nodo creado, salvo si es el primero
La estructura queda perfectamente enlazada y ordenada creciente.
P,Q,Ptr : TPNodo;
ALGORITMO
Nuevo(Ptr);
Ptr^ <Valor, NULO>;
P Lista;
Q NULO;
MIENTRAS P <> NULO y Valor > p^.info HACER
Q P;
P P^.sgte;
FIN_MIENTRAS;
SI (P = Lista)
ENTONCES
Lista Ptr;
SI_NO
Q^.sgte Ptr;
FIN_SI;
Ptr^.sgte P;
FIN.
FIN_SI;
FIN.
LEXICO
PRE ListA y ListB Listas ordenadas simplemente enlazadas.
POS ListAB Lista enlazada producto del apareo de las listas dato.
Ptr : TPNodo; Puntero auxiliar para insertar como cola
VA,VB,VAB: TipoInfo el valor asociado a cada nodo de la lisra
ALGORITMO
ListAB = NULO;
Ptr = NULO;
MIENTRAS (ListA <> NULO) O (ListB <> NULO) HACER
SI ((ListB = NULO)O((ListA<>NULO)Y(ListA^.info<ListB^.info)))
ENTONCES
VAB = VA
SI_NO
VAB = VB
FIN_SI
Agregar(ListAB,Ptr,VAB);
FIN_MIENTRAS;
Ptr = NULO;
FIN.
LEXICO
PRE ListA Lista Paralela al archivo de datos, con tantos nodos como registros tiene
ese archivo. Las restricciones del nodo no alcanzan para poner la clave de ordenamiento
y solo se puede poner un campo que agrupe datos del archivo de proceso.
ArchDatos es el archivo de datos personales, contiene todas las claves, esta ordenada
por esa por lo que es posible hacer búsqueda binaria. ArchProceso es el archivo a
procesar contiene la clave por la que se vincula con el archivo de datos con lo que esta
ordenado.
Se busca hacer una lista paralela para agrupar evitando busquedas secuenciales en
archivos.
POS ListA contendra el valor acumulado de cada clave del archivo de datos
relacionados posicionalmente.
ALGORITMO
ListA = NULO;
PARA I [1 .. CantidadRegistros(ArchDatos)] HACER
InsertaDelante(ListA, 0);
//Crea la lista con los contadores en cero//
FIN_PARA
FIN.
LEXICO
PRE ListA Lista Paralela al archivo de datos, con tantos nodos como registros tiene
ese archivo. Las restricciones del nodo no alcanzan para poner la posición en el
archivoclave de ordenamiento y solo se puede poner un campo que agrupe datos del
archivo de proceso.
ArchDatos es el archivo de datos personales, contiene todas las claves, No esta
ordenado lo que no es posible hacer busqueda directa ni búsqueda binaria.Se lo carga
en una lista como cola para poder buscar en la lista y luego accedre al archivo según el
numero de nodo
ArchProceso es el archivo a procesar contiene la clave por la que se vincula con el
archivo y hay que ir a buscar un valor.
Se busca hacer una lista paralela, cargada como cola para buscar evitando busquedas
secuenciales en archivos.
POS ListA contendra el la clave de busqueda y para cada clave del archivo de datos
se relacionan posicionalmente según el numero de nodo.
ALGORITMO
ListA = NULO;
Aux = NULO
PARA I [1 .. CantidadRegistros(ArchDatos)] HACER
Leer(Registro(ArchDatos, Registro)
Agregar(ListA, Aux, Registro.clave);
//Crea la lista con la clave a buscar//
FIN_PARA
Aux = NULO // Para que el resultado sea una lista y no una cola
MIENTRAS (HayaDatos(ArchProceso)) HACER
LeerRegistro(ArchProceso, Registro);
Ptr = Lista;
NumeroNodo = 0;
MIENTRAS Ptr*.info <> Registro.clave HACER
Ptr = Ptr*.sgte;
Incrementar(NumeroNodo);
FIN_MIENTRAS
AccesoDirectoArchivo(ArchDatos, NumeroNodo);
LeerRegistro(ArchDatos, RegistroDatos);
Imprimir(Registrodatos.Otra clave);
FIN_MIENTRAS;
FIN.
Nuevo(Auxiliar);
Aux^ <Valor, NULO>;
Lista Ptr
SI (lista = NULO)
ENTONCES
Auxiliar^.sgte = Auxiliar;
Lista = Auxiliar;
SI_NO {La lista no esta vacia}
Ptr = Lista;
Salir = FALSO
REPETIR
SI (Ptr^.sgte^.info< Valor)
ENTONCES
Ptr ptr^.sgte
SINO
Salir = Verdadero
FIN_SI
HASTA Salir o Ptr = Lista
Auxiliar^.sgte Ptr^.sgte;
Ptr^.sgte Auxiliar;
SI (Salir = FALSO)
ENTONCES
Lista = Auxiliar
FIN_SI; {de la lista no vacia}
FIN.
FIN.
FIN.
FIN.
FIN.
FIN.
FIN.
FIN_MIENTRAS;
FIN.
SINO
AlturaArbol -1
FIN_SI;
FIN.
SINO
BuscarEnArbol FALSO;
FIN_SI;
FIN.
ALGORITMO
SI (Arbol <> NULO)
ENTONCES
Si (Valor< Arbol^.info)
ENTONCES
Borrar(Arbol^.izquierda, Valor)
SINO SI (Valor>Arbol^.info)
ENTONCES
Borrar(Arbol^.derecho, valor)
SINO /* en este estado lo encontro*/
Auxiliar = Arbol;
SI (Arbol^.derecha = NULO)/* No tiene hijo derecho
ENTONCES
Arbol = Arbol^.Izquierda
SINO SI(Arbol^.izquierda = NULO)
ENTONCES
Arbol = Arbol^.derecha
SINO
BorrarInOrder(Arbol^.izquierdo)
FIN_SI
FIN_SI
Liberar(Auxiliar);
FIN_SI
FIN_SI
SINO
ERROR;
FIN_SI;
FIN.
El flujo de control de una función recursiva requiere tres condiciones para una
terminación normal:
1. Un test para detener o continuar con la recursion.
2. Una llamada recursiva para continuar la recursion
3. Un caso base para terminar la recursion
Recursividad
SI (Es el caso base)
ENTONCES
Ejecutar la accion final
Terminr recursion
SINO
Ejecutar accion para cercar a la solucion
Invocar nuevamente a la funcion
Factorial
(Dato N : Entero): Entero una funcion;
ALGORITMO
SI (N = 0)
ENTONCES
Factorial = 1
SINO
Factorial = N * Factorial(N-1)
FINSI
FIN.
Fibonacci
(Dato N : Entero): Entero una funcion;
ALGORITMO
SI (N = 0) O (N = 1)
ENTONCES
Fibonacci = N
SINO
Fibonacci = Fibonaci(N-2)+Fibonacci(N-1)
FINSI
FIN.
EuclidesMaxCD
(Dato M,N : Entero): Entero una funcion;
ALGORITMO
SI (N <= M) Y (M Modulo N = 0)
ENTONCES
EuclidesMaxCD = N
SINO SI (M<N)
ENTONCES
EuclidesMaxCD(N,M)
SINO
EuclidesMaxCD(N, M Mod N)
FINSI
FIN.
Hanoi
(Dato Inicio, Medio, Centro : Cráter: N : Entero): NULO una funcion;
ALGORITMO
SI (N = 1)
ENTONCES
Imprimir(Mover Disco N de inicial a final)
SINO
Hanoi(Inicial, Final, Central, N- 1);
Imprimir(Mover Disco N de inicial a final)
Hanoi(Central, Final, Inicial, N- 1);
FINSI
FIN.
Recursividad indirecta
void FuncionA(char c)
{
if( c > ‘A’ )
FuncionB(c);
Printf(“%c”,c);
}
void FuncionB(char c)
{
FuncionA(--c);
}
int FuncionPar(int n)
{
if (n == 0)
return 1;
else
return FuncionImpar(n-1);
int FuncionImpar(int n)
{
if (n == 0)
return 0;
else
return FuncionPar(n-1);
}
Archivos en pascal
Pascal Dispone del tipo de dato text que permite vincular a una variable interna con un
archivo de texto.
En el caso de C los nombres internos se declaran como un puntero a una struct FILE, y
la discriminacion del tipo, texto o binario se realiza en la apertura
Para trabajar con archivos de texto en Pascal se debe:
1. Declarar el nombre lógico del archivo con un identificador de tipo text.
2. Relacionar el nombre interno o lógico con l externo o físico, mediante
Assign.
3. Abrirlo con Reset o Rewrite según corresponda.
En el ejemplo que sigue se desarrollan las acciones para lectura completa de un archivo
de texto. Se lee
1. Carácter a carácter.
2. Por línea completa.
3. Con formato
a. En datos simples
b. En registros
program LeerCaracteres;
{Lee un archivo de texto caracter a caracter y lo muestra por pantalla}
Arch : text;
Letra : char;
begin
Assign (Arch,'C:\....\archivo.txt');
Reset(Arch);
while not EOF(Arch) do
begin
{Lectura caracter a caracter}
read(Arch, Letra);
write(Letra);
end;
Close(Arch);
End.
program LeerPorLineas;
Arch : text;
Nombre : string;
begin
Assign (Arch,'C:\...\archivo.txt');
Reset(Arch);
while not EOF(Arch) do
begin
{Lectura por linea}
readln(Arch, nombre);
writeln(nombre);
end;
Close(Arch);
End.
program LeerConFormato;
var
Arch : text;
Entero : Integer;
Texto : string[10];
begin
Assign (Arch,'C:\...\archivo.txt');
Reset(Arch);
while not EOF(Arch) do
begin
{Lectura con formato}
readln(Arch, Entero, Texto);
writeln('Este es el entero : ', Entero:8);
writeln('Este es el texto : ', Texto:8);
end;
Close(Arch);
End.
program LeerConFormatoEnRegistro;
TipoRegistro = Record
Numero : Integer;
Cadena : string[10];
End;
var
Arch : text;
Registro : TipoRegistro;
begin
Assign (Arch,'C:\...\archivo.txt');
Reset(Arch);
while not EOF(Arch) do
begin
{Lectura con formato en regitro}
readln(Arch, Registro.Numero, Registro.Cadena);
writeln('El entero del reg.: ', Registro.Numero:8);
writeln('El texto del re. : ', Registro.Cadena:8);
end;
Close(Arch);
End.
Los archivos de tipo o de acceso directo es una colección de datos del mismo tipo con
almacenamiento sin interpretar y guardadas según su representación interna. Solo se
pueden comparar como iguales si son leídos del modo en que fueron escritos Requieren
en pascal que para poder utilizarlos se deba hacer y en orden los siguientes pasos
1. El la cláusula Type definir los tipos
a. Para el registro si corresponde, o podria evitarse se se utilizan archivo de
de tipos de datos primitivos
b. Para el archivo NombreDelTipo = FILE OF TIPO DE DATO
2. Declarar las variables en la clausula Var
a. Una para el archivo
b. Otra para el tipo de dato de cada posición.
3. Asignar y abrir los archivos
a. La asignación a traves de ASSIGN vinculando el nombre interno con el
externo.
b. La apertura según corresponda con Reset o Rewrite
program ConvierteTextoABinario;
Type
TipoRegistro = Record
Numero : Integer;
Cadena : string[10];
End;
TipoArchivo = FILE OF TipoRegistro;
var
Arch : text;
Registro : TipoRegistro;
Binario : TipoArchivo;
I : Integer;
begin
clrscr;
Assign (Arch,'C:\borlandc\archivo.txt');
Reset(Arch);
Assign (Binario,'C:\...\Binario.Dat');
Rewrite(Binario);
while not EOF(Arch) do
begin
{Lectura con formato en regitrode un archico de texto
y lo almacena en un archivo binario}
readln(Arch, Registro.Numero, Registro.Cadena);
write(Binario,Registro);
end;
Close(Arch);
Close(Binario);
End.
program RecorrerBinario;
uses crt;
Type
TipoRegistro = Record
Numero : Integer;
Cadena : string[10];
End;
TipoArchivo = FILE OF TipoRegistro;
var
Arch : text;
Registro : TipoRegistro;
Binario : TipoArchivo;
begin
Assign (Binario,'C:\...\Binario.Dat');
Reset(Binario);
while not EOF(Binario) do
begin
{Lectura con formato en regitro del archivo Binario}
read(Binario, Registro);
writeln('El entero del Bin.: ', Registro.Numero:8);
writeln('El texto del Bin. : ', Registro.Cadena:8);
end;
Close(Binario);
end.
program RecorreBinarioInverso;
Type
TipoRegistro = Record
Numero : Integer;
Cadena : string[10];
End;
TipoArchivo = FILE OF TipoRegistro;
Var
Binario : TipoArchivo;
Registro : TipoRegistro;
I : Integer;
Begin
Assign (Binario,'C:\...\Binario.Dat');
Reset(Binario);
For i := FileSize(Binario) - 1 downto 0 do
begin
{Lectura con formato en regitro del archivo Binario
en orden inverso}
Seek(Binario,I);
read(Binario, Registro);
writeln('El entero del Bin.: ', Registro.Numero:8);
writeln('El texto del Bin. : ', Registro.Cadena:8);
end;
Close(Arch);
Close(Binario);
end.
Ejemplo en C con aplicaciones de estructuras enlzadas
/* Toma una cadena que representa una expresión aritmética y la convierte a notación
sufijo usando procedimientos de pilas*/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int main()
{
char cad1[10];
clrscr();
Sufijo("5*8+3",cad1);
printf("%s\n", cad1);
getchar();
return 0;
}
Diagrama de Nassi-Sneiderman
Condición 1
T F
Acción 1 Acción 5
Acción 2 Mientras condición 3
Condición 2 Acción 6
T F
Acción 7
Acción 3
Acción 8
Acción 4 Acción 9
Acción 10
Acción 11
Acción 12
Diagramas de Jackson
Diagramas de Lindsay.
IF condición
THEN Condición
sentencia 1
ELSE Instrucción 1 Instrucción 2
sentencia 2
REPEAT
sentencia; Instrucción 1
: :
: :
sentencia n Instrucción n
Condición
UNTIL condición
CASE expresión
OF Expresión
const1....... const n : instrucción1
Const1... Const n Const1... Const p
:
: Instrucción1 Instrucción m
const1...... const p : instrucción m
END
Cualquier sentencia puede ser reemplazada por Correspondiendo en el diagrama de detalle
un bloque: BEGIN la secuencia:
sentencias 1; instrucción 1;
: :
: :
sentencias n instrucción n
END
Llaves de Warniel
HacerUnaCosa)
Si HacerOtraCosa
Condición Exclusión
No Vacio
Algoritmo 1
Case
Ordinal 2
HacerUnaCosa;
Mientras HacerOtraCosa;
Condición HacerAlgoMas
Notación Algoritmica
LEXICO {Léxico Global del algoritmo}
{Declaración de tipos, constantes, variables y acciones}
Acción 1
PRE {Precondición de la acción 1}
POS {Poscondición de la acción 1}
LEXICO {Léxico local, propio de la acción 1}
Declaraciones locales
ALGORITMO {Implementación de la acción 1}
{Secuencia de instrucciones de la acción 1}
FIN {Fin implementación algoritmo de la acción 1}
ALGORITMO
PRE {Precondición del algoritmo principal}
POS {Poscondición del algoritmo principal}
{Secuencia de instrucciones del algoritmo principal}
FIN {Fin del algoritmo principal}
Equivalencias entre notación algorítmica y lenguajes de programación
CONDICIONAL
Formato Pascal C
SI Condicion If expresion condicional If (expresion)
ENTONCES Then S;
S S Else
SI_NO Else R;
R R;
FIN_SI
Formato Pascal C
SEGÚN expr Case selector of switch (selector) {
V1 : S1 Valor1 : S1; case etiqueta:S; break;
V2 : S2 ......................... ..............................
EN_OTRO_CASO : Sn else Ve: Se; default: R;
FIN_SEGÚN end; }
ITERACION
Formato Pascal C
Mientras Cond. Hacer While Expresion logica do while(expresion)
S S; S;
FIN_MIENTRAS
Formato Efecto
REPETIR Repeat do
S S S;
HASTA Cond Until expresion logica while(expresion)
Formto Efecto C
PARA i [Vi..Vf] HACER For i:=Vi to vf do for(i=0;i<vf;i++)
S S; S;
FIN_PARA
Estilos de Indentación
BSD/Allman.
while( SeaVerdad() )
{
HacerUnaCosa();
HacerOtraCosa();
}
HacerUnaUltimaCosaMas();
Estilo Whitesmiths
while( SeaVerdad() )
{
HacerUnaCosa();
HacerOtraCosa();
}
HacerUnaUltimaCosaMas();
Estilo GNU
while( SeaVerdad() )
{
HacerUnaCosa();
HacerOtraCosa();
}
HacerUnaUltimaCosaMas();
Estilo Pico
while( SeaVerdad()
{ HacerUnaCosa();
HacerOtraCosa(); }
HacerUnaUltimaCosaMas();
Estilo Banner
while( SeaVerdad() ) {
HacerUnaCosa();
HacerOtraCosa();
}
HacerUnaUltimaCosaMas();
BIBLIOGRAFIA
Behrouz Forouzan. Introducción a la ciencia de la computación. 2005. Ciencias e
Ingenieria.
De Giusti. Algoritmos datos y programas. 2001. Prentice Hall.
Garcia Molina. Una introducción a la programación. 2005. Thomson.
Kernighan - Ritchie. El lenguaje de programacion C. 1988. Pearson
Kerighan – Pike. La practica de la programacion. 2000. Pearson
Perkins David. La bañera de Arquimedes y otras historias del descubrimiento
cientifico. Paidos 2004
Iranzo, j. Logica simbolica para informaticos. 2006. Alfaomega.
Perez M. Matematica discreta y algoritmos. 2005. Answer.
Joyanes Aguilar, L.Algoritmos y estructura de datos, una perspectiva en C. 2004
Mc Graw Hill
El Autor
Oscar Ricardo Bruno
Formación Academica
Doctorando en educación UnTref
Licenciado en Sistemas (Instituto Tecnologico de Buenos Aires)
Magíster en Docencia Universitaria UTN FRBA
Especialista en Ingenieria en Sistemas (Maestrando) UTN FRBA
Especialista en Docencia Universitaria UTN FRBA
Profesor en Docencia Universitaria en Sistema INSPT UTN FRBA
Espeialista en Investigación Operativa ESIO DIGID
Docente Investigador UTN.
Actividad Docente
Profesor Adjunto Algoritmos y Estructura de Datos y Sintaxis y Semántica de Lengujes
UTN FRBA .
Profesor Adjunto Estructura de Datos Univeridad del Salvador
Profesor Programación II CENT 24.
Profesor Titular Laboratorio II y III Escuela Tecnica Nro 1 Otto Krause
Coordinador Area Computación EIOK
Director y jurado de tesis UTN FRBA
Ex Consejero Departamental docente UTN FRBA departamento Sistemas.
Menciones
Finalista premios Sadosky 2006 a la inteligencia argentina categoría calidad docente