Sei sulla pagina 1di 258

Anlisis de Algoritmos

para Ingeniera de
Sistemas y Computacin

Sergio Augusto Cardona Torres


Adscrito al
Programa de Ingeniera de Sistemas y Computacin
Facultad de Ingeniera
Universidad del Quindo

Sonia Jaramillo Valbuena


Adscrita al
Programa de Ingeniera de Sistemas y Computacin
Facultad de Ingeniera
Universidad del Quindo

Jorge Ivn Trivio Arbelez


Adscrito al
Programa de Ingeniera de Sistemas y Computacin
Facultad de Ingeniera
Universidad del Quindo

Anlisis de Algoritmos
para Ingeniera de
Sistemas y Computacin

No est permitida importar, vender, difundir, distribuir y exportar total o parcialmente


esta obra, ni su tratamiento o transmisin por cualquier mtodo sin autorizacin escrita
del editor. El contenido de la presente obra es exclusivo de los autores

Derechos reservados
Diciembre de 2011
ISBN: 978-958-99930-2-6

ELIZCOM S.A.S
www.elizcom.com
ventas@elizcom.com
NIT 90004331-7
Armenia, Quindo Colombia
Tel. 7493244
Cel: 57+3113349748
Java y todas las marcas y logos basados en Java son marcas comerciales o marcas registradas de ORACLE Sun,
La marca de Eclipse y sus logotipos aparecen de acuerdo con las condiciones legales de Eclipse, expuestas en
http://www.eclipse.org/legal/main.html
Microsoft Office y su logotipo es una marca registrada por Microsoft Corporation.
CMAP Tools son marca registrada por ihmc - Florida institute for Human & Machine Cognition

ANLISIS DE ALGORITMOS PARA INGENIERA DE SISTEMAS Y


COMPUTACIN
1

ANLISIS DE ALGORITMOS .............................................................................. 9


1.1
Introduccin .................................................................................................... 9
1.2
Definicin de Algoritmo ................................................................................. 9
1.3
Caractersticas de los Algoritmos ................................................................ 15
1.4
Buenos hbitos de diseo de Algoritmos .................................................... 16
1.5
Fases de desarrollo de un programa ........................................................... 17
1.6
Tiempo de Ejecucin de los Algoritmos ..................................................... 18
1.6.1 Anlisis de Algoritmos Iterativos ............................................................... 19
1.6.2 Tiempo de ejecucin para instrucciones simples ....................................... 19
1.6.3 Tiempo de ejecucin para ciclos simples ................................................... 20
1.6.4 Tiempo de ejecucin para ciclos anidados ................................................. 24
1.6.5 Tiempo de ejecucin con llamada a mtodos ............................................. 30
1.7
Caso de estudio: Biblioteca .......................................................................... 34
1.8
Comparacin de tiempos de ejecucin ....................................................... 39
1.9
Actividad Independiente: Agenda Telefnica ............................................ 43
1.10 Complejidad Computacional ....................................................................... 48
1.10.1
Notacin Asinttica ................................................................................ 49
1.10.2
Notacin Big Oh O. ................................................................................ 49
1.10.3
Notacin Omega ................................................................................. 51
1.10.4
Notacin .............................................................................................. 51
1.10.5
Regla del Lmite ..................................................................................... 52
1.10.6
Ordenes de Complejidad ........................................................................ 54
1.11 Actividad Independiente: Concurso Docente ............................................ 62
1.12 Anlisis de Algoritmos Recursivos .............................................................. 68
1.12.1
Algoritmos Recursivos ........................................................................... 69
1.12.2
Tiempo de ejecucin de algoritmos recursivos ...................................... 73
1.12.3
Resolucin de Recurrencias por induccin ............................................ 78
1.12.4
Resolucin de Recurrencias por sustitucin ........................................... 82
1.13 Anlisis de Mtodos de Ordenamiento ....................................................... 85
1.13.1
Mtodo de Ordenamiento ShakerSort .................................................... 86
1.13.2
Mtodo de Ordenamiento Burbuja ......................................................... 87
1.13.3
Mtodo de Ordenamiento por Seleccin ................................................ 87
1.13.4
Mtodo de Ordenamiento Insercin ....................................................... 88
1.13.5
Mtodo de Ordenamiento QuickSort. ..................................................... 89
1.13.6
Mtodo de Ordenamiento ShellSort. ...................................................... 91
1.13.7
Mtodo de Ordenamiento StoogeSort .................................................... 93
1.14 Mtodos de Bsqueda................................................................................... 93
1.14.1
Bsqueda Lineal Iterativa ....................................................................... 94
1.14.2
Bsqueda Lineal Limitada ...................................................................... 95
1.14.3
Bsqueda Lineal Iterativa con extremos ................................................ 96
1.14.4
Bsqueda Binaria.................................................................................... 97
1.15 Caso de Estudio: Registro de notas ............................................................. 98

ESTRATEGIAS DE PROGRAMACIN ........................................................... 107


2.1
Introduccin ................................................................................................ 107

2.2
Algoritmos Divide y Vencers ................................................................... 107
2.2.1 Bsqueda Binaria...................................................................................... 107
2.2.2 Ordenamiento por el mtodo MergeSort .................................................. 108
2.2.3 Multiplicacin de Nmeros Grandes ........................................................ 110
2.3
Algoritmos Devoradores ............................................................................ 114
2.3.1 El Problema de la mochila ........................................................................ 115
2.3.2 Elementos de los Algoritmos Voraces...................................................... 120
2.3.3 El problema de la Devuelta. ..................................................................... 121
2.4
Actividad Independiente: El problema de la Devuelta ........................... 123
2.5
Programacin Dinmica ............................................................................ 126
2.5.1 Serie de Fibonacci .................................................................................... 126
2.5.2 Problema de la Mochila ............................................................................ 130
2.5.3 Problema de la Devuelta ........................................................................... 132
2.5.4 Algoritmo de Dijkstra ............................................................................... 134
2.5.5 Hoja de trabajo: Lotera ............................................................................ 135
3

ALGORITMOS APLICADOS A GRAFOS Y ARBOLES ................................. 141


3.1
Introduccin ................................................................................................ 141
3.2
Arboles Binarios ......................................................................................... 141
3.2.1 rboles Binarios de Expresin ................................................................. 143
3.2.2 Implementacin de Arboles Binarios ....................................................... 146
3.2.3 Recorridos en Arboles Binarios ................................................................ 153
3.3
Caso de estudio: Agenda Telefnica ......................................................... 155
3.4
Hoja de trabajo: Graficador de rboles ................................................... 158
3.5
Grafos .......................................................................................................... 166
3.5.1 Conceptos fundamentales de los Grafos ................................................... 166
3.5.2 Grafos Dirigidos ....................................................................................... 166
3.5.3 Grafos No Dirigidos ................................................................................. 167
3.6
Arboles de Recubrimiento Mnimo ........................................................... 168
3.6.1 Algoritmo de Prim .................................................................................... 168
3.6.2 Algoritmo de Kruskal ............................................................................... 169
3.6.3 Algoritmo de Dijkstra ............................................................................... 170
3.7
Arboles n-arios ............................................................................................ 174
3.8
Actividad Independiente: Organigrama de empresa .............................. 177
3.9
Arboles AVL ............................................................................................... 178
3.9.1 Elementos de los rboles AVL ................................................................ 178
3.9.2 Operaciones de los rboles AVL ............................................................. 179
3.10 Caso de estudio Grupo de estudiantes ...................................................... 184
3.11 Backtracking ............................................................................................... 193
3.12 Caso de estudio: Reinas.............................................................................. 194
3.13 Actividad Independiente: Laberinto......................................................... 197

ANLISIS DE ESTRUCTURAS DE DATOS ................................................... 201


4.1
Introduccin ................................................................................................ 201
4.2
Estructura de Datos Lista .......................................................................... 201
4.2.1 Lista Sencillamente Enlazada Implementacin 1. ................................. 201
4.2.2 Lista Sencillamente Enlazada Implementacin 2. ................................. 204
4.2.3 Lista Sencilla Circular .............................................................................. 209
4.2.4 Lista Doblemente Enlazada ...................................................................... 213

4.2.5 Lista Circular Doblemente Enlazada ........................................................ 217


4.3
Actividad Independiente: Ciudades .......................................................... 221
4.4
Estructura de Datos Pila ............................................................................ 224
4.4.1 Implementacin Basada en un arreglo ..................................................... 224
4.4.2 Implementacin utilizando Listas ............................................................. 227
4.5
Estructura de Datos Cola ........................................................................... 228
4.5.1 Implementacin Basada en un arreglo ..................................................... 229
4.5.2 Implementacin utilizando Listas ............................................................. 232
4.6
Estructura ArrayList ................................................................................. 233
4.7
Caso de estudio Universidad................................................................... 234
5

OPTIMIZACIN, PRUEBAS Y LMITES DE LA LGICA ........................... 241


5.1
Introduccin ................................................................................................ 241
5.2
Tcnicas de Optimizacin .......................................................................... 241
5.2.1 Desenvolvimiento de ciclos ...................................................................... 241
5.2.2 Reduccin de Esfuerzo ............................................................................. 243
5.2.3 Tipos de Variables .................................................................................... 244
5.2.4 Fusin de ciclos ........................................................................................ 245
5.2.5 Expresiones redundantes .......................................................................... 246
5.2.6 Folding ...................................................................................................... 247
5.3
El diseo por contrato ................................................................................ 249
5.4
Pruebas con JUnit ....................................................................................... 252
5.5
Lmites de la Lgica .................................................................................... 256
5.5.1 Clase P ...................................................................................................... 256
5.5.2 Clase NP ................................................................................................... 256

BIBLIOGRAFA ................................................................................................. 257

PRESENTACIN
El Anlisis de Algoritmos se considera como una temtica fundamental dentro del proceso de
formacin de los estudiantes de Ingeniera de Sistemas y Computacin, pues posee una
estrecha relacin con otras reas de formacin del ingeniero, como lo son la programacin de
computadores, las estructuras de datos, las Matemticas Discretas y la teora de grafos entre
otras.
Las orientaciones sociolgicas y pedaggicas que permitieron la elaboracin de este libro y su
viabilidad en el campo profesional y terico, estn orientadas a fortalecer el objetivo o propsito
general de formacin del Ingeniero de Sistemas y Computacin. Desde la perspectiva
sociolgica, quienes comparten los conceptos del anlisis de algoritmos, son capaces de
emplearlo para: comprender, explicar, demostrar, solucionar problemas, crear o hacer
representaciones, orientados a compartir su significado con otras personas. Desde la
perspectiva pedaggica, se pretenden establecer estrategias de enseanza que faciliten a los
estudiantes los aprendizajes requeridos en esta disciplina tanto en lo cientfico como en lo
formal, cotidiano y viceversa.
El presente libro pretende con su estrategia de enseanza, la resolucin de problemas usando
el anlisis de algoritmos. En ste, se agregan actividades que contribuyen a la mejor
comprensin de los conceptos plasmados a lo largo de los captulos. Al poseer una estructura
coherente, el estudiante podr trabajar a travs de cada uno de ellos, convirtindose en un
verdadero actor del proceso de aprendizaje, esto conlleva a que el rol del docente sufra una
profunda transformacin, se ha migrado hacia la idea de un consultor. Teniendo en cuenta que
el anlisis de algoritmos comprende una amplia variedad de temas, consideramos que es
fundamental que los estudiantes identifiquen la importancia de esta disciplina dentro de su
proceso de formacin.
Por un lado el libro recoge nuestra experiencia en la enseanza de las asignaturas
relacionadas con la algoritmia y la programacin, y nos ha permitido ver la importancia que
tiene el disponer de una metodologa de trabajo que permita abordar la resolucin de los
problemas de una forma simple, coherente y estructurada. Por otro lado este libro es una
versin que retoma los conceptos fundamentales de los libros de anlisis de algoritmos en java
y Tcnicas de Diseo de Algoritmos en java, en el cual, continua siendo un objetivo de los
autores proporcionar una introduccin comprensible y slida de los elementos fundamentales
del anlisis de algoritmos. Teniendo en cuenta las observaciones y las oportunidades de
mejora propuestas tanto por estudiantes como profesores que usaron estos libros, se
realizaron cambios para este libro, con el objetivo de aportar al proceso de formacin de los
profesores. El libro se adapt teniendo en cuenta el micro currculo vigente del curso anlisis
de algoritmos I.
El texto fue escrito pensando principalmente en aquellos estudiantes de Ingeniera de Sistemas
y afines que tienen los conocimientos fundamentales de las Matemticas Discretas y Lgica de
Programacin y que deseen aprender los conceptos bsicos a tener en cuenta en el anlisis de
algoritmos. Este libro se utilizar como referencia para la asignatura Anlisis del Algoritmos I
del programa de Ingeniera de Sistemas y Computacin de la Universidad del Quindo.
Este libro pretende ser flexible en la forma como puede impartirse a las personas interesadas.
La comprensin de los temas, depende fundamentalmente de la preparacin de los
estudiantes. Se presentan conceptos bsicos fundamentales e intermedios, los cuales se
pueden aplicar en la prctica, as como tambin realizar un anlisis riguroso de los conceptos
tericos que se imparten. El texto est orientado al estudiante, y hemos puesto el mayor
empeo para explicar cada tema tan claramente como sea posible.

INTRODUCCIN
El objetivo de este libro de texto consiste en mostrar los elementos fundamentales del anlisis
de algoritmos, estrategias de programacin, el anlisis de algoritmos aplicados a estructuras de
datos, anlisis de grafos y tcnicas de optimizacin de cdigo, los cuales son temas que tienen
una serie de aplicaciones en diferentes reas de las ciencias computacionales. Se pretende
establecer una relacin de aplicacin entre los conceptos tericos y su aplicacin especfica en
el campo de la Ingeniera de Sistemas y Computacin.
El libro tiene los siguientes objetivos:

Mostrar la importancia del anlisis de algoritmos como rea de fundamentacin que


aporta al proceso de formacin de los estudiantes e ingeniera de sistemas y
computacin.

Presentar a los estudiantes de ingeniera de sistemas y computacin, los temas


fundamentales de algoritmia y mostrar la relacin existente entre los conceptos
matemticos con su aplicacin en el contexto de las ciencias de la computacin.

Mostrar al estudiante las tcnicas bsicas utilizadas para establecer la complejidad


computacional y la eficiencia en trminos de tiempo de ejecucin en algoritmos de
diferente naturaleza, y en paralelo, formalizar la idea de mayor eficiencia,
estableciendo el concepto de complejidad desde el punto de vista computacional.

Presentar casos de estudio y hojas de trabajo. En este aspecto, todos los ejemplos se
encuentran desarrollados en java, dado que es un lenguaje de programacin de
aceptacin mundial tanto en mbito industrial como en el mbito acadmico.

Desarrollar en el estudiante habilidades para la construccin de algoritmos recursivos


correctos, algoritmos ordenamiento y algoritmos de bsqueda entre otros.

Aplicar a casos de estudio, tcnicas de solucin de problemas computacionales


complejos, considerados fundamentales en la formacin del ingeniero de sistemas y
computacin.

Es de suponer que los lectores de este libro tienen los conocimientos bsicos de algoritmia y
est familiarizado con algn lenguaje de programacin. Los ejemplos sern implementados en
su totalidad en el lenguaje de programacin java y se dar importancia nicamente a los
aspectos ms esenciales, sin sobrecargar al lector en temas que pueden ser objeto de estudio
en otros libros relacionados con la programacin y la algoritmia.
El libro est estructurado en 5 captulos los cuales pretenden de forma progresiva mostrar
diferentes temas que son abordados de forma simple y estructurada, a continuacin, se
muestra un breve resumen de los temas que se presentan en el mismo.
En el primer captulo denominado anlisis de algoritmos, se analiza el tiempo de ejecucin de
los algoritmos y presentamos su forma de calcularlo. Se muestra una amplia variedad de
ejemplos los cuales permitirn al estudiante conocer y entender los diferentes tiempos de
ejecucin que se pueden obtener de los algoritmos. Adicionalmente se trabajarn temas como
el tiempo de ejecucin de los algoritmos iterativos, la complejidad computacional y notaciones
asintticas, el anlisis de algoritmos recursivos, de los mtodos de ordenamiento y los
algoritmos de bsqueda.
En el segundo captulo llamado estrategias de programacin, se trabajar con algoritmos que
se emplean en problemas de optimizacin en los cuales se pretende maximizar o minimizar
algn valor. Se mostrarn casos considerados tpicos como el problema de la mochila, el

problema de la devuelta; en los cuales es posible el uso de algoritmos divide y vencers,


algoritmos devoradores o vidos y algoritmos que aplican programacin dinmica.
El captulo tercero denominado algoritmos aplicados a grafos y rboles, se muestra el anlisis
de los algoritmos ms comunes aplicados a las estructuras de datos no lineales. Se analizan
los rdenes de complejidad de los mtodos de implementacin de grafos y arboles ms
importantes. Se explicarn las operaciones fundamentales aplicadas a los rboles y finalmente
se analizan conceptos de rbol n-rio, rbol AVL y la tcnica de backtraking.
El captulo cuarto muestra los algoritmos aplicados a estructuras de datos, se analiza la
implementacin de las estructuras de datos lineales con su respectivo anlisis de orden de
complejidad. Se explicarn los rdenes de complejidad de los mtodos ms importantes para
las listas, pilas y colas. Adicionalmente se muestra un caso de estudio que usa estructuras
predefinidas del lenguaje de programacin conocidas como ArrayList.
En el ltimo captulo del libro se muestra la optimizacin y pruebas de algoritmos, se analizan
algunas tcnicas que se pueden aplicar para la optimizacin de cdigo. Algunas de las tcnicas
que se trataran son el desenvolvimiento de ciclos, la fusin de ciclos, la eliminacin de
expresiones redundantes, entre otras. Tambin se muestra algunas herramientas
proporcionadas por el lenguaje de programacin que permiten la realizacin de pruebas de
correccin las cuales buscan una mejor calidad en el cdigo de programacin y en su
funcionalidad. Adicionalmente, se muestran los conceptos asociados a los lmites de la lgica y
de la complejidad computacional, se muestra el planteamiento de problemas asociados a
algoritmos que conforman la clase P, as mismo se muestran casos en los cuales se
consideran problemas intratables denominados clases NP.

1.1

ANLISIS DE ALGORITMOS

Introduccin

Actualmente la construccin de aplicaciones informticas debe responder a requerimientos


muy complejos y de carcter crtico de las organizaciones. La complejidad inmersa en los
proyectos de desarrollo de software est asociada a mltiples fuentes: metodologas utilizadas,
tecnologas de apoyo, capacidad y competencias de las personas, productividad de los equipos
de trabajo, requerimientos cambiantes de los clientes, presupuesto disponible, entre otras.
Es por ello que muchos de los productos de software deben ser construidos por personas que
tengan diversos tipos de habilidades al momento de construir software. En ese sentido la
actividad de la programacin est relacionada directamente con la tarea de disear e
implementar algoritmos que resuelvan problemas con eficiencia.
El anlisis de algoritmos, tiene como objetivo fundamental medir la eficiencia de uno o ms
algoritmos en cuanto a consumo de memoria y tiempo. Es una actividad muy importante en el
proceso de desarrollo de software, especialmente en entornos con recursos restringidos. Por
ello, es necesario realizar estimaciones en cuanto al consumo de tiempo y de memoria que
puede requerir una aplicacin para su ejecucin.
En este captulo se trabajarn temas como el tiempo de ejecucin de los algoritmos iterativos,
la complejidad computacional y notaciones asintticas, el anlisis de algoritmos recursivos,
mtodos de ordenamiento y bsqueda de datos.

1.2

Definicin de Algoritmo

Se conoce como algoritmo a una secuencia de instrucciones, que son ejecutadas con
esfuerzos finitos en un tiempo razonable, que recibe un conjunto de valores como entrada y
produce un conjunto de valores como salida. Para la ejecucin de estas instrucciones es
necesario contar con una cantidad finita de recursos.
Segn (Valenzuela, 2003), cuando nos referimos al concepto de algoritmo, hacemos referencia
a los pasos encaminados a la consecucin de un objetivo. Un algoritmo puede ser
caracterizado por una funcin lo cual asocia una salida: s= f (E) a cada entrada E.

Se dice entonces que un algoritmo calcula una funcin f. Entonces la entrada es una variable
independiente bsica en relacin a la que se producen las salidas del algoritmo, y tambin los
anlisis de tiempo y espacio (Valenzuela, 2003).
En las Ciencias de la Computacin cuando se dice que un problema tiene solucin, significa
que existe un algoritmo susceptible de implantarse en una computadora, capaz de producir la
respuesta correcta para cualquier instancia del problema en cuestin. De acuerdo a ello, la
construccin de un programa hace referencia directa a la implementacin de uno o ms
algoritmos.
Un problema es resuelto algortmicamente, si se puede escribir un programa que pueda
producir la respuesta correcta, de forma que para cualquier posible entrada, el programa puede
ser ejecutado en tiempo finito, teniendo en cuenta los recursos computacionales para
resolverlo.

Existen diferentes tipos de algoritmos, entre los cuales tenemos:

Algoritmos Determinsticos: Es un algoritmo en el cual, cada uno de sus pasos estn


claramente definidos, y para cada conjunto de entrada es posible predecir una salida
exacta.

Algoritmos No Determinsticos: Es un algoritmo en el cual, para un mismo conjunto


de datos de entrada, se pueden obtener diferentes salidas. No se puede previa a la
ejecucin de estos algoritmos, afirmar cul ser su resultado.

Algoritmos Adaptativos: Son algoritmos con alguna capacidad de aprendizaje. Por


ejemplo, los sistemas basados en conocimientos, las redes neuronales entre otras.

En la literatura asociada al tema se encuentra una cantidad muy amplia de tipos de algoritmos
entre los cuales adicionalmente se pueden citar los algoritmos paralelos, probabilsticos,
voraces, divide y vencers, dinmicos. Estos tres ltimos se tratarn en captulos posteriores
del libro.
La implementacin de un algoritmo debe ser en todos los casos:

Correcta: Si para toda instancia del conjunto de entrada se obtiene la salida esperada,
es decir, que cumpla con el objetivo para el cual est pensado.

Eficiente: Debe ser rpido y usar la menor cantidad de recursos. Es una relacin entre
los recursos consumidos, fundamentalmente tiempo y memoria versus los productos
obtenidos.

Por lo tanto, un algoritmo se considera bueno si considera los siguientes elementos.

Si los anteriores elementos resueltos de forma positiva dan nocin de lo que es un buen
algoritmo. Por lo tanto se pueden generar las siguientes frases que se convertirn en preguntas
cuando estemos analizando cada uno de nuestros programas.

Que cumpla con el objetivo para el cual est pensado.


Que resuelva el problema en el menor tiempo posible.
Que haga uso adecuado de los recursos.
Que permita identificar posibles errores.
Que sea fcil de modificar para aadir funcionalidad

Para aproximar mejor los elementos anteriores, observemos el siguiente ejemplo: se desea
determinar si un nmero entero positivo predeterminado es primo o no lo es. El conjunto de los
nmeros primos es un subconjunto de los nmeros naturales que contiene aquellos que son
divisibles por s mismos y por la unidad.
Son entre otros nmeros primos: 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43. A
continuacin se muestra una implementacin del mtodo que resuelve el problema en
mencin.

10

Implementacin
del Mtodo

public boolean esPrimo(int numero)


{
int resultado = 0;
int i = 2;
while( i < numero ){
if ( numero % i == 0 )
{
resultado = 1;
}
i = i + 1;
}
if ( resultado == 0 )
{
return true;
}
else
{
return false;
}
}

Considerando los anteriores interrogantes, es posible responder para este caso:

Cumple el algoritmo el objetivo para el cual est pensado? Solo en el caso en cual se
ingresa un nmero entero positivo, el algoritmo genera una salida correcta y cumplira
con su objetivo.

El algoritmo resuelve el problema en el menor tiempo posible? No es posible resolver


el interrogante, para poder responder, se debe comparar con la eficiencia de otros
algoritmos que resuelven el mismo problema.

El algoritmo hace uso adecuado de los recursos? Si, dado que se usan las variables
estrictamente necesarias para la solucin y se utiliza un tipo de dato con rango de
valores moderado.

Permite el algoritmo identificar posibles errores? No, dado que el programa no maneja
excepciones que controlen posibles errores.

El algoritmo es fcil de modificar para aadir funcionalidad? Si, dada la sencillez del
problema y de la solucin propuesta.

Una segunda solucin para determinar si el nmero es primo, se muestra a continuacin:

Implementacin
del Mtodo

public boolean esPrimo(int numero)


{
int i;
for ( i = 2 ; i <= numero / 2 ; i++ )
{
if ( numero % i == 0 )
{
break;
}
}
if ( i > numero / 2 )
{
return true;
}

11

else
{
return false;
}
}
Para esta implementacin es posible dar respuesta a los siguientes interrogantes:

Cumple el algoritmo el objetivo para el cual est pensado? Solo, en el caso en el que
se ingrese un nmero entero positivo, el algoritmo genera una salida correcta.

Resuelve el algoritmo el problema en el menor tiempo posible? Si se compara con el


algoritmo anterior, este algoritmo es mejor dado que una vez que encuentra un nmero
divisible, termina la ejecucin y muestra el mensaje al usuario, teniendo en cuenta la
condicin if.

Hace el algoritmo uso adecuado de los recursos? Si, pues para algunos casos no
itera toda la cantidad de veces permitida por el ciclo.

El algoritmo permite identificar posibles errores? No, dado que el programa no maneja
excepciones que controlen posibles errores.

El algoritmo es fcil de modificar para aadir funcionalidad? Si, dada la sencillez del
problema y de la solucin propuesta.

ACTIVIDAD

A continuacin se muestra la implementacin de un mtodo el cual lee un nmero entero


positivo y retorna la cantidad de dgitos que este tiene.

Implementacin
del Mtodo

public int calcularCifras (int num)


{
int aux = 0, con = 0;
if(num==0)
{
return 1;
}
for(int i=10;num!=0;i+=10)
{
con = con + 1;
num = num/i;
i = i-10;
}
if(con!=0)
{
aux = con;
}
return aux;
}

Para la anterior implementacin, resuelva los siguientes interrogantes:


Pregunta
Cumple el algoritmo el objetivo
para el cual est pensado?

Respuesta

12

Resuelve el algoritmo el problema


en el menor tiempo posible?
Hace el algoritmo uso adecuado
de los recursos?
El algoritmo permite identificar
posibles errores?
El algoritmo es fcil de modificar
para aadir funcionalidad?
A continuacin se muestra otra implementacin de un mtodo el cual lee un nmero entero
positivo y retorna la cantidad de dgitos que este tiene.

Implementacin
del Mtodo

public int calcularCifras (int numero)


{
int acum = 0;
if(numero<10)
{
return 1;
}
else if(numero>=10 && numero<=99)
{
return 2;
}
else
{
for(int i=numero; i>10; i-=10)
{
numero= numero/10;
acum++;
if(numero == 0)
{
break;
}
}
return acum;
}
}

Para la anterior implementacin, resuelva los siguientes interrogantes:


Pregunta
Cumple el algoritmo el objetivo
para el cual est pensado?
Resuelve el algoritmo el problema
en el menor tiempo posible?
Hace el algoritmo uso adecuado
de los recursos?
El algoritmo permite identificar
posibles errores?
El algoritmo es fcil de modificar
para aadir funcionalidad?

Respuesta

ACTIVIDAD

Entre las varias sucesiones interesantes de nmeros que existen en las matemticas discretas
y combinatorias, estn los nmeros armnicos, los cuales tienen la forma:

13

H1 + H2 + H3 + donde:

Y en general,

para cualquier

(Grimaldi, 1998)

La sucesin de los nmeros armnicos se puede definir como:

public int armonico (int numero)


{

Implementacin
del Mtodo

}
Con bases en la implementacin propuesta, responda los siguientes interrogantes.
Pregunta
Cumple el algoritmo el objetivo
para el cual est pensado?
Resuelve el algoritmo el problema
en el menor tiempo posible?
El algoritmo permite identificar
posibles errores?

Respuesta

Realice una comparacin entre el mtodo que usted implement, con el siguiente mtodo y
posteriormente responda la pregunta:

Implementacin
del Mtodo

public int armonico(int numero)


{
double aux, result = 1;
for(int i=2; i<=numero; i++)
{
aux = i;
result = result + (1/aux);
}
return result;
}

Pregunta
Cul de los dos algoritmos es
ms eficiente?

Respuesta

En la siguiente seccin se muestra las caractersticas fundamentales y sus algoritmos.

14

1.3

Caractersticas de los Algoritmos

Cuando que se desarrolla una solucin a un problema desde la perspectiva algortmica, se


debe procurar en la medida de lo posible que los algoritmos cumplan las siguientes
caractersticas:

Preciso: Un algoritmo preciso, posee un orden especfico en cada uno de los pasos
que este ejecuta. Por esta razn cuando se ejecuta un paso del algoritmo, se conoce
con certeza cul es el paso siguiente a ejecutar.
Finito: El algoritmo debe finalizar tras una secuencia o nmero finito de pasos.
Efectivo: La eficiencia hace alusin al logro de la solucin a un problema de la mejor
manera posible (en trminos de tiempo). En el ambiente computacional, lo son el buen
uso de los recursos de hardware.

Determine si la siguiente implementacin del mtodo, cumple con las caractersticas de los
algoritmos definidas anteriormente.

Implementacin
del Mtodo

public int calcularCifras (int numero)


{
int acum = 0;
if(numero<10)
{
return 1;
}
else if(numero>=10 && numero<=99)
{
return 2;
}
else
{
for(int i=numero; i>10; i-=10)
{
numero= numero/10;
acum++;
if(numero == 0)
{
break;
}
}
return acum;
}
}

Para la anterior implementacin, resuelva los siguientes interrogantes:


Pregunta
Es preciso el mtodo?
Es finito el mtodo?
Es efectivo el mtodo?

Respuesta

Para finalizar esta seccin cabe mencionar que dentro de la Ingeniera de software, tambin se
contemplan factores fundamentales en la calidad del software, entre los cuales tenemos:

Eficiencia: Atributo que determina el buen aprovechamiento de los recursos que


utiliza.
Facilidad de uso: Facilidad para que el usuario interactu con el programa.

15

1.4

Compatibilidad: Facilidad para que un programa pueda usarse en unin con otros
programas.
Extensibilidad: Es la capacidad que tiene un programa de ampliar la funcionalidad de
acuerdo a nuevas necesidades o requerimientos.
Verificabilidad: Es la capacidad que tiene un programa para soportar casos de prueba
y para identificar posibles errores.
Exactitud: Es el atributo que determina el nivel de precisin de los resultados
obtenidos por un programa.

Buenos hbitos de diseo de Algoritmos

Al disear un algoritmo y posteriormente implementarlo en un lenguaje de programacin, se


deben tener en cuenta buenos hbitos y tcnicas de diseo. A continuacin se especifican
algunos aspectos a considerar por los equipos de desarrollo de software y por los
desarrolladores de software al momento de disear e implementar algoritmos (Ramirez, 2003).

Analizar el problema: La correcta resolucin de un problema viene determinada en


gran medida por el planteamiento inicial. Un planteamiento correcto evitar perder
tiempo en la implementacin de los algoritmos.

Evaluar posibles soluciones: Es necesario conocer los recursos algortmicos


disponibles y las distintas metodologas de diseo para plantear la solucin adecuada.
Una vez hecho esto, deber optar por la mejor de ellas para su posterior
implementacin.

Disear la solucin: Esta actividad se refiere a un modelamiento de lo que ser la


solucin definitiva. Bajo el paradigma orientado a objetos, es posible especificar la
jerarqua de clases con una descripcin completa del diagrama de clases. Tambin se
recomienda al disear la solucin, modelar diagramas de secuencia, los cuales
permiten visualizar la interaccin de los objetos por medio del envo de mensajes.

Documentar el cdigo: Con frecuencia los algoritmos adolecen de documentacin y


en algunos casos esta es inexistente. Este tipo de situaciones perjudican el
mantenimiento y reutilizacin de los mismos, y dificultan su entendimiento. Los
comentarios propiamente dichos son pequeos fragmentos de tipo explicativo,
aclaratorio o de advertencia que se intercalan entre las instrucciones del programa.
Cada programador debe aprender a escribir la especificacin de su programa (o sea, la
documentacin), antes de escribir el programa (Di Mare, 1998). Documentar permite
entender el programa a medida que crece y tambin, identificar posibles fuentes de
error. Javadoc es una herramienta creada para tal fin. Est pensado para lograr que la
arquitectura de la solucin sea mucho ms comprensible, es decir, su formato comn
hace que los comentarios escritos sean ms comprensibles por otro programador.
Una adecuada documentacin requiere agregar comentarios a todas las clases y
mtodos que componen el programa. Un comentario debe contener la informacin
suficiente para que otras personas puedan entender lo que se ha realizado y el porqu
de lo que se ha realizado.

Manejo de versiones: Es necesario el uso de herramientas que permitan el control de


versiones. Teniendo en cuenta el carcter evolutivo y progresivo de los proyectos de
desarrollo de software.

Estndar de programacin: Es recomendable establecer un lineamiento particular en


la forma en la cual se construyen los programas. Se deben estandarizar por ejemplo

16

los nombres de variables, de las clases y de los mtodos, todos estos deben estar
acordes a las tareas que realizan.
Existen estndares para usar convenciones de programacin. Las convenciones de
cdigo son importantes para los programadores por un gran nmero de razones
(Molpeceres, 2001) :
o
o
o
o

El 80% del coste del cdigo de un programa va a su mantenimiento.


Casi ningn software lo mantiene toda su vida el auto original.
Las convenciones de cdigo mejoran la lectura del software, permitiendo
entender cdigo nuevo mucho ms rpidamente y ms a fondo.
Si distribuyes tu cdigo fuente como un producto, necesitas asegurarte de que
est bien hecho y presentado como cualquier otro producto.

Para conocer las convenciones de cdigo definidas por el proveedor oficial de java se
recomienda visitar el site (octubre de 2011), el cual contiene los elementos bases para
el uso de estndares: http://www.oracle.com/technetwork/java/codeconv-138413.html
1.5

Fases de desarrollo de un programa

Dentro del entorno de la informtica no siempre los problemas necesidades de las personas
o las organizaciones, se encuentran claramente definidas, es necesario en muchas ocasiones
realizar una clara formulacin del problema. Solo partiendo de una correcta formulacin de
este, ser posible especificar una metodologa para su solucin.
Segn (Cardona, Jaramillo, & Carmona, Anlisis de Algoritmos en Java, 2007), una buena
planificacin de las tareas a realizar para desarrollar un programa favorece el xito en la
implementacin. La planificacin debe estar basada en el establecimiento de fases. Las fases,
tanto para realizar programas sencillos como para llevar a cabo proyectos de envergadura de
construccin de aplicaciones informticas se pueden ver en la figura.

La etapa de definicin del problema, se enfoca en el entendimiento del espacio del


problema, se identifican las posibles necesidades y restricciones sobre la solucin del
problema. Al culminar esta actividad, se debe tener un completo entendimiento del
problema a solucionar.

Una vez definido el problema, se realiza la especificacin del problema, por medio de
los que se conoce como diseo detallado o algortmico, en donde se definen y
documentan los detalles y algoritmos de cada una de las funciones

Con base en el diseo, se construyen los algoritmos necesarios para la solucin del
problema, para posteriormente estos se implementados en un lenguaje de
programacin. Cada algoritmo implementado, debe ser verificado para comprobar las
operaciones para las cuales fue construido, estos se deben someter a un conjunto de
pruebas.

Finalmente se tiene una aplicacin de software, que debe dar la solucin esperada de
acuerdo a los requerimientos planteados inicialmente.

17

1.6

Tiempo de Ejecucin de los Algoritmos

Cuando se aborda la solucin de un problema computacional se tiene la disyuntiva de


seleccionar entre varios algoritmos y la cual se realiza basndose fundamentalmente en los
siguientes aspectos: el algoritmo fcil de entender, codificar, depurar versus el algoritmo que se
ejecute con la mayor rapidez posible, siempre con el objetivo de hacer un uso eficiente de los
recursos de la mquina. Lograr uno de estos objetivos generalmente implica entrar en
contradiccin con el cumplimiento del otro, por ello se debe valorar los casos en que se debe
priorizar, el uno o el otro.
Para calcular el tiempo de ejecucin, se deben apropiar dos conceptos que fundamentan el
anlisis de algoritmos, estos conceptos algortmicos se conocen como el mejor y el peor caso.

El peor caso se considera como la situacin en la cual el algoritmo consume mayor


cantidad de tiempo o mayor cantidad de instrucciones para resolver un problema.
El mejor caso est relacionado con la situacin en la cual el algoritmo consume menor
cantidad de tiempo o menor cantidad de instrucciones para resolver un problema.

Para este libro, tendremos en cuenta el anlisis para el peor de los casos.
Tradicionalmente se usan estrategias para la estimacin de los tiempos de ejecucin, el
siguiente mapa conceptual nos muestra los elementos fundamentales:

Ms formalmente las siguientes tcnicas se utilizan para estimar el tiempo de ejecucin de un


programa (Aho & Ullman, 1995):

La tcnica de benchmarking consiste en comparar dos o ms algoritmos con un mismo


conjunto de datos de entrada, se establece cual es el que resuelve el problema de
forma ms eficiente; es decir, cual consumi menos tiempo al ejecutarse. Es necesario
que el hardware sobre el cual se realiza la prueba a los algoritmos tenga las mismas
caractersticas.

La tcnica de Profiling consiste en asociar a cada instruccin de un programa un


nmero que representa la fraccin del tiempo total tomada para ejecutar esa
instruccin particular. Una de las tcnicas ms conocidas (e informal) es la Regla 9010, que afirma que el 90 % del tiempo de ejecucin, se invierte en el 10 % del cdigo

18

(Valenzuela, 2003). De acuerdo a lo anterior, un programa pasa la mayor parte del


tiempo de la ejecucin en un trozo de cdigo pequeo. Este 90 % del cdigo suele
estar constituido por ciclos.

1.6.1

La tcnica de anlisis considera aspectos ms rigurosos para la estimacin del tiempo


de ejecucin. Su objetivo es analizar de forma matemtica y detallada cada uno de los
pasos que ejecuta el algoritmo. Consiste en asignar a cada una de las instrucciones un
costo en consumo de tiempo, sumar cada uno de estos y establecer la funcin de
consumo de tiempo. Esta tcnica utiliza una funcin T(n), la cual representa el nmero
de unidades de tiempo que consume el algoritmo para cualquier entrada de tamao n.
Por lo anterior, T(n) es el tiempo de ejecucin del algoritmo. El tiempo de ejecucin en
la funcin T(n) no se le est asignando ninguna unidad de medida del tiempo, lo que se
est haciendo es calculando el monto de instrucciones que el algoritmo ejecutar, dado
el tamao del conjunto de datos de entrada

Anlisis de Algoritmos Iterativos

Despus de realizar un estudio sobre las diversas tcnicas para el anlisis de algoritmos, se
utilizar la tcnica de anlisis. Esta tcnica permitir que la estimacin, se realizar en funcin
del nmero de operaciones elementales que realiza dicho algoritmo para un tamao de entrada
dado.
Entendiendo por operaciones elementales como aquellas operaciones cuyo tiempo de
ejecucin se puede acotar superiormente por una constante (Guerequeta & Vallecillo, 2000).
As, se consideran como operaciones elementales:
Nombre
Operaciones aritmticas bsicas
Asignaciones a variables
Comparaciones lgicas o relacionales
Acceso a estructuras de datos estticas y dinmicas
Parmetros que llegan a los mtodos
Instrucciones de salto
Retorno de valores
Instrucciones condicionales
Creacin de Objetos
Expresiones que con incrementos y decrementos
Referencias a objetos
Operadores de acumulacin

Operadores o instrucciones
+,-,*,/,%
=
&&,||,<,<,>=,=<,==,!=
[], [][]
mtodo(variable valor)
break, continue
return
if, if-else
new
++, -null, object
+=,-=

A continuacin se muestra la forma en la cual se realizar el clculo del tiempo de ejecucin


para instrucciones simples.

1.6.2

Tiempo de ejecucin para instrucciones simples

El tiempo de ejecucin para instrucciones simples consiste en el conteo de operaciones


elementales por lnea de cdigo. No se considera para este tipo de conteo la declaracin de
variables, pues el conteo se har sobre los variables en ejecucin dinmica. A continuacin se
mostrarn ejemplos en los cuales se calcula el tiempo de ejecucin de instrucciones sencillas
dentro de los mtodos.
Como estrategia se analizar el mtodo en trminos de la cantidad de instrucciones que se
llevan a cabo.

19

Ejemplo

Mtodo

Anlisis del
tiempo de
ejecucin

T(n)

Objetivo: Comprender la estimacin del tiempo de ejecucin para


mtodos que tienen operaciones elementales.
public int metodo1( int n )
{
int x,y,z;
x = 2;
y = x++;
z = y + x;
return z;
}
El tiempo de ejecucin de una secuencia consecutiva de
instrucciones se calcula sumando cada lnea de cdigo que posee
el mtodo, independiente de la cantidad de operaciones que se
ejecuten en la lnea. Adicinalmente se debe considerar el parmetro
que llega al mtodo el cual se cuenta como una instruccin.
int n, se ejecuta 1 vez
x = 2, se ejecuta 1 vez
y = x++, se ejecuta 1 vez
z = y + x, se ejecuta 1 vez
return z, se ejecuta 1 vez
T(n) = 5

ACTIVIDAD

Estime y argumente el clculo del tiempo de ejecucin para el siguiente mtodo:


Ejercicio

Mtodo

Objetivo: Estimar el tiempo de ejecucin del mtodo.


public int ejercicio( int x, int y, int z )
{
int r;
r = 0;
x += 2;
y = x + 3;
z = x + 2;
r = x + y + z;
return r
}

Explicacin del
tiempo de
ejecucin
T(n)

1.6.3

Tiempo de ejecucin para ciclos simples

Muchos de los problemas que se resuelven a nivel de programacin, estn relacionados con el
uso de ciclos. A continuacin, se muestran una serie de ejemplos que permitirn establecer el
tiempo de ejecucin para mtodos que poseen ciclos en su implementacin. Para cada uno de
los mtodos se considerar el peor caso.

20

Ejemplo

Mtodo

Anlisis del
Tiempo de
ejecucin

Objetivo: Crear habilidad para calcular el tiempo de ejecucin de


un mtodo cuando este tiene ciclos.
public void metodo ( int n)
{
int i, x, y;
i = 0;
while ( i < n )
{
x = i + 3;
y = x + 1;
i++;
}
}
int n
Se ejecuta 1 vez
i = 0
Se ejecuta 1 vez
while ( i < n ) Se ejecuta n + 1 veces
x = i + 3
Se ejecuta n veces
y = x + 1
Se ejecuta n veces
i = i + 1
Se ejecuta n veces
La instruccin while(i < n) se repite n+1 veces, esto teniendo
en cuenta la ltima comparacin del ciclo. Las instrucciones que
hay dentro del ciclo, se repiten n veces.

T(n)

T(n) = 4n+3.

A continuacin se muestra otro ejemplo en el cual se estima el tiempo de ejecucin del mtodo.
El mtodo recibe tres parmetros por valor, lo que implica que cada uno de esos valores se
ejecuta una vez.
Ejemplo

Objetivo: Crear habilidad para calcular el tiempo de ejecucin del


mtodo.

Mtodo

public void metodo ( int x, int y, int n )


{
for ( int i = 3 ; i <= n ; i++ )
{
x = i * j;
y = w * x;
}
}

Anlisis del
Tiempo de
ejecucin

T(n)

int x
int y
int n
i = 3
i <= n
x = i * j
y = w * x
i++

Se ejecuta 1 vez
Se ejecuta 1 vez
Se ejecuta 1 vez
Se ejecuta 1 vez
Se ejecuta ((n + 1) - 3) + 1 veces
Se ejecuta (n-2) veces
Se ejecuta (n-2) veces
Se ejecuta (n-2) veces

T(n) = 4+(n-1)+3(n-2) = 4n-3, n >=2

Es frecuente que dentro de los ciclos se implementen decisiones, a continuacin se muestra un


caso el cual el condicional if se considera siempre como verdadero y con ello su peor caso.

21

Ejemplo

Mtodo

Anlisis del
Tiempo de
ejecucin

T(n)

Objetivo: Crear habilidad para calcular el tiempo de ejecucin del


mtodo cuando se utilizan condicionales.
public void metodo( int n, int arreglo[] )
{
int temp;
for ( int i = 0 ; i <= n; i++ )
{
if ( i < n )
{
temp
= arreglo[ i ];
arreglo [ i-1 ] = arreglo[ i ];
}
}
}
Cuando se analiza la instruccin if(i<n), se asume para el peor
caso que sta siempre ser verdadero, lo que implica una mayor
cantidad de instrucciones que se deben procesar.
int n
Se ejecuta 1 vez
int arreglo[]
Se ejecuta 1 vez
i = 0
Se ejecuta 1 vez
i <= n
Se ejecuta (n+1) + 1 veces
if ( i < n )
Se ejecuta (n+1) veces
temp = arreglo[ i ]
Se ejecuta (n+1) veces
arreglo[i-1] = arreglo[i] Se ejecuta (n+1) veces
i++
Se ejecuta (n+1) veces
T(n) = 4 + 5(n+1) = 5n+9, n >=-1

A continuacin se muestra una forma de estimar la funcin T(n) cuando se tiene la estructura ifelse. Segn la sintaxis de los lenguajes de programacin, cuando se cumple el if el else no
se analiza, en el caso en el que no se cumpla el if el else se ejecuta.
Cuando if o else tienen igual nmero de lneas de implementacin, se puede escoger
cualquiera de los dos. Para este ejemplo el valor del parmetro n, debe ser n > 1.
Ejemplo

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


condicionales.
public void metodo( int n, int a[] )
{
int temp, x, k=0, i;
for ( i = 0 ; i < n - 2 ; i++ )
{
if ( a[i] < a[i+1] )
{
temp
= a[i];
a[i+1] = temp;
}
else
{
temp = a[i+1];
x
= a[i];
k++;
}
}
}

22

Anlisis del
Tiempo de
ejecucin
T(n)

El else tiene en su bloque ms cantidad de lneas de cdigo. Por


lo tanto el peor de los casos, es que la condicin if al momento de
su evaluacin sea falsa y el flujo del programa continu en el else.
T(n)= 6n-7, para n>1.

ACTIVIDAD

Estime y argumente el clculo del tiempo de ejecucin para los siguientes mtodos:
Ejercicio

Mtodo

Objetivo: Estimar el tiempo de ejecucin del mtodo.


public void metodo( int n, int a[] )
{
int temp, x, i;
i = 0;
while(i <= n+3)
{
if ( a[i-1] < a[i+1] )
{
temp
= a[i];
a[i+1] = temp;
x
= a[i];
}
else
{
x
= a[i];
}
i++;
}
}

Explicacin del
tiempo de
ejecucin
T(n)

T(n) =

Estimar el tiempo de ejecucin del siguiente mtodo, analizando el caso en el cual el


condicional if (w % i == 1) es verdadero en la ltima iteracin.
Ejercicio

Mtodo

Objetivo: Conocer cmo se calcula el tiempo de ejecucin cuando


un ciclo tiene una instruccin de salto.
public void ejercicio ( int n, int a[], int x )
{
for ( int i = 3 ; i < n + 2 ; i++ )
{
if ( x % i == 1)
{
a[i] = i * 2;
break;
}
a[i] = 5;
}
}

23

Anlisis del
Tiempo de
ejecucin
T(n)
Estimar el tiempo de ejecucin del siguiente mtodo, analizando el caso en el cual el
condicional if (w % i == 1) es verdadero en la ltima iteracin.
Ejercicio

Mtodo

Objetivo: Conocer cmo se calcula el tiempo de ejecucin cuando


un ciclo tiene una instruccin de salto.
public void ejercicio ( int n, int a[], int x )
{
int j = 0;
for ( int i = 0 ; i < n ; i++ )
{
if ( x % i == 0)
{
a[i] = i * 2;
j++;
break;
}
a[i] = 5;
a[j] = i * 2;
x--;
}
}

Anlisis del
Tiempo de
ejecucin
T(n)

1.6.4

Tiempo de ejecucin para ciclos anidados

El uso de los ciclos anidados es una prctica muy frecuente y necesaria para la
implementacin de diferentes tipos de algoritmos. A continuacin vamos a estimar el tiempo de
ejecucin del mtodo que posee dos ciclos anidados.
Es comn cuando se tienen dos ciclos anidados que inician en un valor entero positivo e iteran
hasta n (siendo n un entero positivo muy grande) o viceversa, encontramos que la funcin T(n)
tiene un orden cuadrtico. Para verificar el correcto clculo del tiempo de ejecucin, se
recomienda asignar algn valor entero a la variable n y realizar un conteo de las instrucciones.
Ejemplo

Objetivo: Crear habilidad para calcular el tiempo de ejecucin del


mtodo cuando se utilizan ciclos anidados.

Mtodo

public void metodo ( int n, int a1[], int a2[] )


{
for ( int i = 0 ; i < n; i++ )
{
for ( int j = 0 ; j < n ; j++ )
{
a1[ j ] = a2[ i ];
}
}
}

24

Anlisis del
Tiempo de
ejecucin

T(n)

Para la estimacin del tiempo de ejecucin, se comenzar con el


anlisis del ciclo ms interno, para nuestro caso lo llamaremos
T1(n). T1(n) = 3n +2
El ciclo externo se ejecuta n veces por lo tanto multiplicaremos n
por el T1(n) y adicionaremos el tiempo de ejecucin del ciclo
externo. Utilizaremos un T(n) para indicar el tiempo de ejecucin
total del algoritmo.
T(n) = ( ( 3n + 2 ) * n ) + 2n + 2 + 3
2
T(n) = 3n + 4n +5

El mtodo tiene tres ciclos anidados, cada uno de los cuales se analizar por separado para
posteriormente estimar el tiempo de ejecucin total. Cada uno de los ciclos itera desde un valor
pequeo hasta n, por lo que el orden de la funcin de tiempo de ejecucin debera ser cbico.
Ejemplo

Mtodo

Tiempo de
ejecucin

Objetivo: Crear habilidad para calcular el tiempo de ejecucin de


un mtodo cuando este utiliza ciclos anidados.
public void metodo( int n )
{
int x = 0;
for ( int i = 0 ; i < n ; i++ )
{
for ( int j = 0 ; j < n; j++ )
{
for ( int k = 0 ; k < n ; k++ )
{
x++;
}
}
}
}
Se analiza el ciclo ms interno, a ste lo llamaremos T1(n).
T1(n)= 3n + 2
A continuacin, se analiza ciclo del medio, lo llamaremos T2(n).
T2(n) = 2n+2
Se calcula el tiempo de ejecucin de los dos ciclos ms internos, al
cual llamaremos T3(n).
2
T3(n)= ((3n+2)*n) + 2n+2= 3n +4n+2
Finalmente me estima el tiempo de ejecucin total del mtodo

T(n)

T(n) = ((3n +4n+2)*n) + 2n+2+2= 3n +4n +4n+4

ACTIVIDAD

Estime y argumente el clculo del tiempo de ejecucin para el siguiente mtodo:

25

Ejercicio

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


mtodos.
public void ejercicio ( int n )
{
int x, y, i, j;
i = 1;
while ( i <= n )
{
j=1;
while( j <= n )
{
x++;
j++;
}
i++;
}
}

Anlisis del
Tiempo de
ejecucin
T(n)
Estime y argumente el clculo del tiempo de ejecucin para el siguiente mtodo:
Ejercicio

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


mtodos.
public void ejercicio ( int n )
{
int i, j, temp, resultado;
temp
= 0;
resultado = 0;
for ( i = n ; i >= 0 ; i-- )
{
for ( j = n ; j > 0 ; j-- )
{
temp
= 4;
resultado = 1;
}
}
}

Anlisis del
Tiempo de
ejecucin
T(n)

El siguiente cuadro, facilitar la forma para estimar la cantidad de veces que se repite un ciclo.
En ella se puede observar cual es la cantidad de iteraciones, teniendo claramente establecido:

Lmite inferior o superior del ciclo.


Tope mximo o mnimo del ciclo.
Incrementos o decrementos de la variable que controla las iteraciones.

26

Estructura del ciclo


for (i=0; i < n; i++)
for (i=0; i <= n; i++)
for (i=k; i < n; i++)
for (i=n; i > 0; i--)
for (i=n; i >=0; i--)
for (i=n; i > k; i--)
for (i=0; i < n; i+=k)
for (i=j; i < n; i+=k)
for (i=1; i < n; i*=2)
for (i=n; i > 0; i/=2)

Cantidad de iteraciones
n
n+1
n-k
n
n+1
n-k
n/k
(n-j)/k
log2 (n)
log2 (n) + 1

A continuacin se muestra un ejemplo que usa una estructura similar a la del ciclo for (i=n;
i > 0; i/=2). Para el anlisis de este algoritmo, debemos tener en cuenta el valor asignado
inicialmente a la variable i = 32, esta variable dentro del ciclo se va dividiendo cada vez por 2.
Se puede afirmar entonces la cantidad de veces que itera el ciclo es: log2(n) + 1.
Ejemplo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


mtodos con tiempo de complejidad logartmica.

Mtodo

public void metodo ( )


{
int temp = 0, x = 0, y = 1, i;
i = 32;
while( i > 0 )
{
x++;
temp++;
y = y * 1;
i = i / 2;
}
}

Anlisis del
Tiempo de
ejecucin

temp = 0
Se ejecuta 1 vez
x = 0
Se ejecuta 1 vez
y = 1
Se ejecuta 1 vez
i = 32
Se ejecuta 1 vez
while(i > 0) Se ejecuta log2(i) + 2 veces
x++
Se ejecuta log2(i) + 1 veces
temp++
Se ejecuta log2(i) + 1 veces
y = y * 1
Se ejecuta log2(i) + 1 veces
i = i/2
Se ejecuta log2(i) + 1 veces
Se realizan 6 comparaciones en el ciclo while(i > 0) ms la
ltima comparacin, basndose en la forma como cambia el
tamao de i. En este caso encontramos una relacin entre el
nmero de comparaciones del ciclo, (6 comparaciones) y el valor de
5
i = 32. Se puede determinar que 2 = 32, (5 es logaritmo en base 2
de 32), entonces las veces que se repite el ciclo es log2(i)+1.

T(n)

T(n) = 5(log2(i) + 1) + 5

A continuacin se muestra un ejemplo que usa una estructura similar a la del ciclo for (i=1;
i < n; i*=2).

27

Ejemplo

Mtodo

Anlisis del
Tiempo de
ejecucin

T(n)

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


mtodos con tiempo de complejidad logartmica.
public void metodo ( int n)
{
int x = 0, i;
i = 1;
while( i < n )
{
x++;
i= i * 2;
}
}
int n
Se ejecuta 1 vez
x = 0
Se ejecuta 1 vez
i = 1
Se ejecuta 1 vez
while(j < n)
Se ejecuta log2 (n) +1 veces
x + +
Se ejecuta log2 (n) veces
i = i * 2
Se ejecuta log2 (n) veces
T(n) = 3(log2(n)) + 4

ACTIVIDAD

Calcular el tiempo de ejecucin para el siguiente algoritmo iterativo.


Ejercicio

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


mtodos.
public void ejercicio ( )
{
int x = 0, y = 1, w = 0, i;
i = 128;
while( i > 0 )
{
x++;
y = y * 1;
i = i / 2;
}
for ( i = n ; i > 0 ; i-- )
{
w++;
y++;
}
}

Anlisis del
Tiempo de
ejecucin
T(n)

Calcular el tiempo de ejecucin para el siguiente algoritmo iterativo.

28

Ejercicio

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


mtodos.
public void ejercicio ( int n )
{
int t = 0, i, j=256;
while( j > 1 )
{
t++;
j = j / 2;
}
for (i = 2; i <= n+3; i++)
{
for (j = n; j >= 0; j--)
{
t--;
}
}
}

Anlisis del
Tiempo de
ejecucin
T(n)
Explique detenidamente el funcionamiento del siguiente algoritmo y calcule su tiempo de
ejecucin. Deduzca el problema que resuelve y compare el mismo con otras implementaciones.

Mtodo

public boolean misterio(int numero)


{
int raiz;
int valor = 0;
if (numero==1 || numero==2 || numero==3){
return true;
}
else
{
raiz = (int)Math.sqrt(numero);
for(int i=2; i<=raiz; i++)
{
for(int j=i; j<=raiz; j+=i)
{
if(numero%j == 0)
{
valor = 1;
break;
}
}
}
}
if (valor == 0)
{
return true
}
else
{
return false;
}
}

29

Anlisis del
Tiempo de
ejecucin
T(n)

1.6.5

Tiempo de ejecucin con llamada a mtodos

Muchos de los problemas que se resuelven a nivel de programacin estn relacionados con los
llamados a los mtodos de las clases. A continuacin, se mostrarn ejemplos en los cuales se
calcula el tiempo de ejecucin para algoritmos que realizan este tipo de llamado.
Para la estimacin del tiempo de ejecucin realizando llamadas a mtodos, es necesario
analizar el tiempo de cada uno de los mtodos, y de esta forma establecer cul es el peor caso,
siempre considerando valores enteros grandes.

Ejemplo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


llamados a mtodos con un tiempo de ejecucin predefinido.

Mtodo

public void metodo( int n, int x, int y)


{
int i;
for ( i = 0; i < n; i++ )
{
if(x == y)
{
metodo1();
}
else
{
metodo2()
}
}
}
metodo1() > T(n) = 3n + 10
metodo2() > T(n) = 2n + 500

Anlisis del
Tiempo de
ejecucin

T(n)

Dado que el metodo1 es mayor que el metodo2 (para valores


grandes), se establece entonces el tiempo de ejecucin con el
metodo1. Se procede de la misma forma en la cual se estaba
calculando el tiempo de ejecucin.
El tiempo de ejecucin es: T(n)= ((3n + 10) * n) + (3n + 2)+ 3
2
T(n)= 3n +13n+5

Es frecuente cuando se usan mtodos el uso de instrucciones condicionales, a continuacin se


muestra un ejemplo que utiliza la instruccin switch.
La estructura de seleccin mltiple switch permite elegir una ruta de entre varias rutas
posibles, usando para ello una variable denominada selector. El selector se compara con una
lista de constantes C1, C2, ..., Cn para cada una de las cuales hay una accin A1, A2, ..., An y:
Si el selector coincide con una constante de la lista, se ejecuta la accin correspondiente a
dicha constante. Si el selector no coincide con ninguna constante de la lista, se ejecuta la
accin default correspondiente al de lo contrario, si es que existe.

30

Las acciones A1, A2, A3, ..., An pueden ser acciones simples (una sola accin) o acciones
compuestas (un conjunto de acciones).
El tipo de la variable que se utiliza para controlar el switch es llamada selector. Cada uno de
los valores presentes en los casos (C1....Cn) debe ser de tipo compatible con el del selector.
Cada uno de estos valores debe ser nico, es decir, no se aceptan rangos.
El default (por defecto) se utiliza cuando ninguno de los casos coincide con el selector. Sin
embargo, la sentencia default es opcional. Si ningn case coincide y no hay sentencia
default, no se hace nada.
El break se utiliza en sentencias switch para terminar una secuencia de acciones. Cuando
se encuentra un break, la ejecucin salta a la primera lnea de cdigo que sigue a la sentencia
switch completa. Esto tiene el efecto de saltar fuera del switch.

Ejemplo

Mtodo

Anlisis del
Tiempo de
ejecucin
T(n)

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


llamados a mtodos en que tengan la instruccin switch.
public void metodo( )
{
switch (0)
{
case 0 : metodo1();
case 1 : metodo2();
case 2 : metodo3();
}
if(m==x+2)
{
metodo4();
}
}
metodo1() > T(n) = n + 3
metodo2() > T(n) = n2 + 5
metodo3() > T(n) = 4n2
metodo4() > T(n) = 4n3 + 8
Dado que el valor del parmetro del switch es cero, se evalan
todos los case y por lo tanto el tiempo de ejecucin es la sumatoria
de cada uno de los tiempos de ejecucin.
3

T(n) = 4n + 5n + n + 18

ACTIVIDAD

Calcular el tiempo de ejecucin para el siguiente mtodo.


Ejercicio

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


llamados a mtodos.

31

Mtodo

public void ejercicio( int n, int a, int b)


{
if (n>a && b>= d)
{
for (int i=0 ; i<2n ; i+=2)
{
if (n>c)
{
metodo1();
metodo2();
}
}
}
else
{
metodo3();
}
}
metodo1() > T(n) = n4
metodo2() > T(n) = n2
metodo3() > T(n) = n5 + 5

Anlisis del
Tiempo de
ejecucin
T(n)
Calcular el tiempo de ejecucin para el siguiente mtodo.

Ejercicio

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


llamados a mtodos en los cuales se encuentre la instruccin
switch.
public void ejercicio( )
{
switch (2)
{
case 0 : metodo1();
case 1 : metodo2();
case 2 : metodo3();
}
metodo4();
}
metodo1() > T(n) = 2n +
metodo2() > T(n) = 10n2
metodo3() > T(n) = n2 +
metodo4() > T(n) = n3 +

Anlisis del
Tiempo de
ejecucin
T(n)
Calcular el tiempo de ejecucin para el siguiente mtodo.

32

30
+ 5
n + 3
5

Ejercicio

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


llamados a mtodos. Verificar que el tiempo de ejecucin es el que
aparece al final del ejercicio.
public void ejercicio ( int n, int b, int c)
{
if (n>b)
{
if (n>c)
{
for ( int i=1; i <= n; i++)
{
metodo1();
}
}
else
{
for (int i=0; i <= n+1; i++)
{
metodo2();
}
}
}
else
{
metodo3();
}
}
metodo1() ---> T(n)= 3n + 100
metodo2() ---> T(n)= 3n + 120
metodo3() ---> T(n)= 2n2 + 10

Anlisis del
Tiempo de
ejecucin
T(n)
Calcular el tiempo de ejecucin para el siguiente mtodo.

Ejercicio

Mtodo

Objetivo: Crear habilidad en el clculo de tiempo de ejecucin para


llamados a mtodos en los cuales se encuentre la instruccin
switch.
public void ejercicio( )
{
switch (0)
{
case 0 : metodo1();
case 1 : metodo2();
break;
case 2 : metodo3();
case 3 : metodo4();
}
}

Anlisis del
Tiempo de
ejecucin
T(n)

33

1.7

Caso de estudio: Biblioteca

A continuacin se muestra un caso de estudio aplicado, en el cual se aplican actividades de un


proceso de desarrollo de software. El objetivo del caso es implementar los mtodos ms
importantes y su posterior anlisis de tiempo de ejecucin. A continuacin se describe el caso
de estudio.
Se desea crear una Aplicacin para manejar la informacin de una Biblioteca o librera. En ella
hay libros. Cada libro tiene un cdigo isbn, un valor y una editorial. Cada editorial tiene un
cdigo y un nombre asignado. Se debe permitir agregar un nuevo libro, mostrar cuntos libros
se tienen de cada editorial (Solamente se tendrn 3 editoriales), listar los libros adquiridos y
mostrar el valor total de los libros que hay en la biblioteca.

Inicialmente se realiza una descripcin de los requerimientos funcionales para la aplicacin.


a) Requerimientos funcionales
NOMBRE
R1 Agregar libro al listado general de libros.
RESUMEN Permite agregar un nuevo libro.
ENTRADAS
Isbn, valor, codigo Editorial, nombre Editorial.
RESULTADOS
Un nuevo libro ha sido agregado.
NOMBRE
R2 - Contar cantidad de libros por cada editorial.
RESUMEN Permite contar la cantidad la cantidad de libros que tiene cada editorial.
ENTRADAS
RESULTADOS
Un mensaje con la cantidad de libros por editorial.
NOMBRE
RESUMEN

R3- Mostrar el listado de todos los libros.


Genera un listado con todos los libros.

ENTRADAS
RESULTADOS
Listado con todos los libros

34

NOMBRE
R4 - Mostrar el valor total de los libros que hay en la biblioteca.
RESUMEN Muestra el valor total de los libros que hay en la biblioteca.
ENTRADAS
RESULTADOS
El valor total de los libros.

b) Identificacin de las clases


Las clases principales para el caso de estudio se identifican y se describen a continuacin:
CLASE
Biblioteca
Libro
Editorial

DESCRIPCIN
Es la clase principal del mundo
Permite manejar la informacin de un docente
Contiene la informacin de la Editorial

c) Modelo de clases
Para este caso de estudio se muestran las clases del principales del dominio, estas clases
contienen todos los mtodos que cumplen con los requerimientos funcionales especificados en
el problema.

Se contina con la implementacin de los mtodos de las clases.

35

d) Implementacin de mtodos
Se mostraran los mtodos ms relevantes para la clase Biblioteca. Se debe calcular el
tiempo de ejecucin para cada uno de los mtodos.
Implementacin de los mtodos
public Biblioteca()
{
contador1=contador2=contador3=0;
mLibro=new ArrayList<Libro>();
}
T(n) =
void setMLibro(ArrayList <Libro> mLibro)
/**
* Permite fijar el array de libros
* @param mLibro se obtiene del archivo
*/
public void setMLibro(ArrayList <Libro> mLibro)
{
this.mLibro=mLibro;
}
T(n) =
ArrayList <Libro> getMLibro()
/**
* Devuelve el valor del array que contiene los libros
* @return ArrayList <Libro>
*/
public ArrayList <Libro> getMLibro()
{
return mLibro;
}
T(n) =
agregarLibro( String codEditorial, String

isbn, double valor)

/**
* Permite agregar un libro
*/
public void agregarLibro(String codEditorial,String isbn,double valor)
{
Libro unLibro = new Libro(codEditorial);
if(codEditorial.equals("001"))
{
contador1++;
}
else
{
if(codEditorial.equals("002"))
{
contador2++;
}

36

else
{
contador3++;
}
}
unLibro.setIsbn(isbn);
unLibro.setValor(valor);
mLibro.add(unLibro);
}
T(n) =
obtenerCantidaFabricantes()
/**
* Permite obtener la cantidad de fabricantes
* @return String Contiene la cantidad de artculos por fabricante
*/
public String obtenerCantidaFabricantes()
{
return "Addison Wesley: "+contador1+" Mc Graw Hill: "+contador2+
"Prentice Hall: "+contador3;
}
T(n) =
determinarValorMercancia()
/**
* determina el valor total de la mercanca que hay en la biblioteca.
* @return double valor de los libros en la biblioteca
*/
public double determinarValorMercancia()
{
double valor=0;
for(int i=0; i<mLibro.size(); i++)
{
valor+=mLibro.get(i).getValor();
}
return valor;
}
T(n) =
getInfoLibros()
/**
* Devuelve la informacin de los libros que hay en la biblioteca
* @return String[]
*/
public String[] getInfoLibros()
{
String []info=new String [mLibro.size()];
for(int i=0; i<mLibro.size(); i++)
{
info[i] = mLibro.get(i).toString();
}
return info;
}
T(n) =

37

Nuestro proceso no ha finalizado aqu puesto que debemos pasar del diseo a la
implementacin. Esta fase se inicia con la evaluacin (en trminos de complejidad de
algoritmos y de espacio ocupado, de dificultad y de grado de desacoplamiento) de los diseos
que fueron propuestos, los cuales debern refinarse hasta que sea posible argumentar por qu
ese diseo es el mejor, siendo importante llevar a cabo las comparaciones pertinentes respecto
a los diseos obtenidos, fruto de la cual se seleccionar uno de ellos.

ACTIVIDAD
Escriba los mtodos para la clase Biblioteca que resuelven los problemas que se describen
a continuacin.

Ejercicio

Escriba un
mtodo que
retorne la
posicin en la
cual se
encuentra el
libro con el
mayor valor.

Objetivo: Generar habilidad en la construccin de mtodos para


posteriormente calcular su tiempo de ejecucin.
public int mayorPosicion()
{

}
Anlisis del
Tiempo de
ejecucin
T(n)

Ejercicio
Escriba un
mtodo que
muestre el isbn
de los libros que
se encuentran
las posiciones
impares del
ArrayList.

Objetivo: Generar habilidad en la construccin de mtodos para


posteriormente calcular su tiempo de ejecucin.
public String mostrarIsbnPosicionesImpares()
{

}
Anlisis del
Tiempo de
ejecucin
T(n)

38

Ejercicio

Objetivo: Generar habilidad en la construccin de mtodos para


posteriormente calcular su tiempo de ejecucin.
public String mostrarPosicionesIsbn()
{

Escriba un
mtodo que
muestre las
posiciones de
los libros cuyo
cdigo isbn
termina en el
carcter 5.
}
Anlisis del
Tiempo de
ejecucin
T(n)

Ejercicio
Escriba un
mtodo que
permita
determinar el
valor total de la
mercanca de
los elementos
que se
encuentran en
las posiciones
pares del
ArrayList.

Objetivo: Generar habilidad en la construccin de mtodos y


posteriormente calcular su tiempo de ejecucin.
public String sumarElementosPares()
{

Anlisis del
Tiempo de
ejecucin
T(n)

1.8

Comparacin de tiempos de ejecucin

La comparacin de los algoritmos proporciona una medida concreta de cuando un algoritmo es


ms eficiente que otro de acuerdo a un conjunto de datos de entrada. El siguiente cuadro
permite establecer una comparacin del crecimiento de algunas funciones, en las cuales se
muestra su comportamiento y diferencias con otras funciones de acuerdo a un valor n
particular.

39

Cuando en una aplicacin se van a procesar pocos datos de entrada, es poco importante
analizar un algoritmo independiente del crecimiento de la funcin. Por ejemplo, si se desea
procesar 50 datos, es casi indistinto que se utilice un algoritmo con tiempo de ejecucin n o n
log(n), pero si se desea procesar 2000000 datos, el algoritmo con n log(n) crece
considerablemente con relacin a n. Es por ello necesario entrar a realizar un estudio sobre el
crecimiento y comportamiento asinttico de los tiempos de ejecucin de los algoritmos.
Funcin
Log2(n)
n
n log(n)
2
n
3
n
n
2

nombre
(Logartmico)
(Lineal)
n log(n)
Cuadrtico
Cbico
Exponencial

A continuacin se muestra una tabla con el crecimiento de algunas funciones polinmicas y las
cuales son las ms frecuentemente usadas en el anlisis de algoritmos.

Los siguientes ejemplos permiten comparaciones de los tiempos de ejecucin. Considerando


su crecimiento, se recomendara alguno de ellos considerando que en cada caso se resuelve el
mismo problema,
Se tienen dos algoritmos los cuales tienen un tiempo de ejecucin dado respectivamente por
2n+10 y n log(n), para el mismo problema, cul de ellos recomendar y por qu?. En la siguiente
figura se observa que a partir del valor 15, la funcin n log(n) est por encima de la funcin 2n
+ 10, por lo que se recomienda que a partir del valor 15, se debe recomendar el algoritmo con
tiempo de ejecucin 2n + 10.

40

En la siguiente figura se ve el comportamiento de dos algoritmos que tienen tiempos de


2
ejecucin 2n + n + 5 y log(n). Segn su comportamiento, se observa que en todo caso la
funcin logartmica log(n) siempre est por debajo de la cuadrtica, por ello se recomienda el
algoritmo que tiene la funcin log(n).

Como se pudo observar, es fundamental estimar y entender el comportamiento de los


algoritmos de acuerdo a su crecimiento. Una vez que se tenga claro cul es la cantidad de
datos a procesar, es necesario tomar la decisin de cual algoritmo recomendar.

ACTIVIDAD

Dadas la siguiente grafica con sus correspondientes funciones, realice un anlisis del
comportamiento de las mismas e indique cuando la una es ms eficiente que la otra. Las
2
funciones a comparar son 2n y log(n).

Para una implementacin completa que permita comparar dos algoritmos, se desea determinar
el mximo comn denominador de dos nmeros enteros positivos. El mximo comn divisor
(MCD) se define de la forma ms simple como el nmero ms grande posible, que permite
dividir a esos nmeros.
A continuacin se muestran dos implementaciones diferentes del mismo problema.

41

Mtodo 1
Mximo comn
Divisor

public int mcd(int a,int b)


{
int valora, valorb;
valora = a;
valorb = b;
while (valora!=valorb)
{
if(valora<valorb)
{
valorb = valorb - valora;
}
else
{
valora = valora - valorb;
}
}
return valora;
}

Una segunda implementacin del mximo comn divisor se muestra a continuacin.

Mtodo 2
Mximo comn
Divisor

public int mcd(int a, int b)


{
int resultado = 1;
for(int i = b; i > 0; i--)
{
if(a%i==0 && b%i==0)
{
resultado = i;
break;
}
}
return resultado;
}

Se comparan ambos algoritmos asumiendo que los valores que se envan por parmetro al
mtodo son: a = 30 y b = 18. En el siguiente cuadro se observan las instrucciones necesarias
para la deduccin del MCD del segundo algoritmo.
A
30

b
18

..

..

resultado
1

i
18
17
16
15
14
..
7
6

i>0
18>0
17>0
16>0
15>0
14>0
..
7>0
6>0

If(a%i==0 b%i==0)
30%18==0 && 18%18==0
30%17==0 && 18%17==0
30%16==0 && 18%16==0
30%15==0 && 18%15==0
30&14==0 && 18%14==0

30%7==0 && 18%7==0


30%6==0 && 18%6==0

6
En el siguiente cuadro se observan las instrucciones que son necesarias para la deduccin del
MCD del primer algoritmo.
Valora
30
12
12
6

Valorb
18
18
6
6

While(valora!=valorb)
30!=18
12!=18
12!=6
6!=6

42

If(valora<valorb)
30<18
12<18
12<6

Considerando los cuadros anteriores, se puede deducir por los valores ingresados, que el
primer algoritmo es ms eficiente que el segundo. Esto dada la cantidad de instrucciones que
se ejecutan. Pero es posible afirmar lo mismo si los valores que se ingresan son
respectivamente: a = 38 y b = 4.
En el siguiente cuadro se observan las instrucciones necesarias para la deduccin del MCD del
segundo algoritmo.
a
38

B
4

resultado
1
2

i
4
3
2

i>0
4>0
3>0
2>0

If(a%i==0 b%i==0)
38%4==0 && 4%4==0
38%3==0 && 4%3==0
38%2==0 && 4%2==0

En el siguiente cuadro se observan las instrucciones que son necesarias para la deduccin del
MCD del primer algoritmo, con los valores anteriormente dados.
Valora
38
34
30
26
..
6
2
2

Valorb
4
4
4
4
..
4
4
2

While(valora != valorb)
38 != 4
34 !=4
30 != 4
26 != 4
.
6 != 4
2 != 4
2 != 2

If(valora < valorb)


38<4
34<4
30<4
26<4
..
6<4
2<4
.

De acuerdo al anterior conjunto de datos de entrada, el primer algoritmo no es ms eficiente


que el segundo algoritmo, lo que entra en contradiccin con lo concluido anteriormente. Este
caso claramente nos muestra que para poder decir cuando un algoritmo es ms eficiente que
otro, es necesario analizarlo por medio de diferentes conjuntos de datos de entrada.

1.9

Actividad Independiente: Agenda Telefnica

Se desea crear una Aplicacin para manejar la informacin de una Agenda Telefnica.
Se debe permitir Agregar Persona, Eliminar Persona, mostrar el listado de todas las personas,
buscar persona por nombre y generar un listado de personas que estn cumpliendo aos el
da de hoy.

43

a) Requerimientos funcionales
NOMBRE

R1- Agregar Persona

RESUMEN
ENTRADAS
RESULTADOS

NOMBRE

R2- Eliminar Persona

RESUMEN
ENTRADAS
RESULTADOS

NOMBRE

R3- Mostrar el listado de todas las personas

RESUMEN
ENTRADAS
RESULTADOS

NOMBRE
RESUMEN
ENTRADAS

R4- Buscar una persona por nombre

RESULTADOS

NOMBRE

R5- generar un listado de personas que estn cumpliendo aos el


da de hoy

RESUMEN
ENTRADAS
RESULTADOS

b. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
AgendaTelefonica
Persona
Fecha

DESCRIPCIN

44

c. Especificar las relaciones entre las clases ( AgendaTelefonica, Pesona, Fecha).

d. Construccin de la aplicacin
Para las siguientes clases escriba en java las los mtodos e instrucciones necesarias para el
correcto funcionamiento de la aplicacin. Como estrategia se dejan parcialmente planteados
mtodos de implementacin y los cuales deben ser completados.
En la clase AgendaTelefonica puedes observar que se ha hecho uso de ArrayList. Un
ArrayList es una estructura contenedora dinmica que crece o disminuye dinmicamente.
import
import
import
import

java.io.Serializable;
java.util.ArrayList;
java.util.Calendar;
java.util.GregorianCalendar;

public class AgendaTelefonica implements Serializable


{
ArrayList <Persona> listaPersona ;
int contadorContactos, contadorTotal, contadorCumpleaos;
/**
* Constructor de la clase AgendaTelefonica
*/
public AgendaTelefonica()
{
contadorContactos = ___________;
contadorTotal
= ___________;
contadorCumpleaos = ___________;
listaPersona = new _____________;
}
/**
* Permite inicializar el atributo listaPersona
* @param listaPersona contiene la informacin de los contactos
*/
public void setListaPersona( ArrayList <Persona> listaPersona )
{
this.listaPersona = ___________________;
}

45

/**
* @return ArrayList <Persona> devuelve la informacin de las
* personas contenidas en la agenda.
*/
public ArrayList <Persona> getListaPersona()
{
__________________________;
}
/**Constructor de la AgendendaTelefonica
* @param listaPersona */
public AgendaTelefonica( ArrayList <Persona> listaPersona )
{
this.listaPersona = ____________________;
}
public void agregarPersona( String cedula, String nombre,
String direccion, int dia, int mes,
int anio, ArrayList telefono )
{
miPersona.____________________ ( cedula );
miPersona.____________________ ( nombre );
miPersona.setDireccion
( ___________________ );
miPersona.setMiFechaNacimiento ( ________________ );
miPersona._____________________( telfono );
listaPersona.add(_________________);
}
/**
* @return String[] devuelve la informacin de los contactos
*/
public String[] obtenerContactos()
{
int i;
contadorTotal = 0;
String[] contactos;
Contactos = new String[ listaPersona.size() + 1 ];
contadorTotal = listaPersona.size();
for( i=0 ; i < listaPersona.size() ; i++ )
{
String infoT = listaPersona.get(i).getTelefono().
toString();
String info = listaPersona.get( i ).getCedula() + "" +
__________________________________+ infoT;
contactos[ i ] = info;
}
contactos[ i ] = "En total: " + contadorTotal + " contactos";
return contactos;
}
/**
* Permite obtener un contacto que contenga a nombre.
* @return String[] informacin del contacto que coincide con el
* nombre especificado.
*/

46

public String[] obtenerContatosNombre( String nombre )


{
int i = 0;
contadorContactos = 0;
String[] contactos;
ArrayList<String> contactosNombre = new ArrayList<String>();

}
/** Devuelve el contador de contactos */
public int getContadorContactos()
{
________________________
}
/** Devuelve el contadorTotal */
public int getContadorTotal()
{
__________________________
}
/** Contador de personas que estn cumpliendo aos */
public int getContadorCumpleaos()
{
_________________________
}
/** Devuelve la informacin de las personas que estn cumpliendo
* aos */
public String[] obtenerContatosCumpleaos()
{
String[] contactos;
int i;
GregorianCalendar go = new GregorianCalendar();
//sacamos los valores del dia, mes y ao
int dia = go.get ( Calendar.DAY_OF_MONTH );
int mes = go.get ( Calendar.MONTH
) + 1;
contadorCumpleaos = 0;
ArrayList <String> contactosNombre = new ArrayList <String>();

47

/** Permite eliminar una persona especificando la cdula */


public void eliminarPersona(String cedula)
{

}
}

ACTIVIDAD
Una vez escritos los mtodos para la clase AgendaTelefonica, se debe calcular el tiempo de
ejecucin de cada uno de ellos.
Tiempo de Ejecucin
T(n)

Mtodo
String[] obtenerContactos()
String[] buscarContatosNombre(String nombre)
int getContadorContactos()
int getContadorTotal()
String[] obtenerContatosCumpleaos()
void eliminarPersona(String cedula)

1.10 Complejidad Computacional


En la seccin anterior se ha trabajado el concepto de tiempo de ejecucin de forma
experimental sobre cada uno de los algoritmos expuestos. Este anlisis resulta particularmente
interesante y til cuando debemos decidir cul algoritmo escoger bajo unos criterios razonables
y sustentables.
Los rdenes polinmicos ms comunes de los tiempos de ejecucin de los algoritmos son los
siguientes:

Log2(n) (Logartmico). Los algoritmos con este tiempo de ejecucin son considerados
como muy eficientes.
n (Lineal). Es comn en aplicaciones que utilizan ciclos sencillos.
n log(n). Comn encontrarla en algoritmos de ordenamiento eficientes.
2
n (Cuadrtico). Habitual cuando se usan 2 ciclos anidados.
3
n (Cbico). Habitual cuando se usan 3 ciclos anidados.
n
2 (Exponencial). No suelen ser muy tiles en la prctica por el elevado tiempo de
ejecucin.

48

La eleccin de un buen algoritmo est orientada hacia la disminucin del costo que implica la
solucin del problema; considerando este enfoque es posible orientar esta eleccin en dos
criterios (Iparraguirre, 2009):

Criterios orientados a minimizar el costo de desarrollo: claridad, sencillez y facilidad de


implantacin, depuracin y mantenimiento.
Criterios orientados a disminuir el costo de ejecucin: tiempo de procesador y cantidad
de memoria utilizados.

Cuando se resuelve un problema, normalmente hay necesidad de elegir entre varios


algoritmos, tpicamente existen dos objetivos que suelen contradecirse (Aho, Ullman, &
Hopcrof, The Design and Analysis of Computer, 1994):

Que el algoritmo sea fcil de entender y codificar


Que el algoritmo use eficientemente los recursos, y en especial, que se ejecute con la
mayor rapidez posible.

Debemos tener claro que los aspectos interesantes a reducir son el tiempo y el espacio en
memoria. En cuanto al tiempo nos hemos centrado en la funcin T(n), la cual determina la
cantidad de operaciones que efecta un algoritmo. De acuerdo a la forma como se calcula el
T(n) es importante hacer dos precisiones:

El tiempo de ejecucin T(n), suele ser difcil de calcular exactamente.


Si cada operacin necesita una fraccin de tiempo muy pequea de tiempo para
ejecutarse, no se precisa una exactitud en el clculo de T(n).

Estas dos precisiones nos permiten dar una aproximacin al concepto de complejidad
computacional de un algoritmo, como un concepto que recoge la naturaleza asinttica de la
funcin T(n) y no su valor exacto. Dicha naturaleza debe dar informacin de cmo crece la
funcin cuando aumenta el tamao de n. Normalmente, el anlisis de la complejidad de los
algoritmos est relacionado con conceptos matemticos que son necesarios precisar.

1.10.1 Notacin Asinttica


Debido a que la eficiencia de un algoritmo no se expresa en alguna unidad de tiempo
determinada debido a que se escoge una medida arbitraria T(n), se ha introducido una notacin
especial llamada notacin asinttica, porque tiene que ver con el comportamiento de dichas
funciones en el lmite, o sea, para valores suficientemente grandes en sus parmetros.

1.10.2 Notacin Big Oh O.


En 1892, Paul Gustav Heinrich Bachmann invent una notacin para caracterizar el
comportamiento asinttico de funciones. Esta invencin se conoce como notacin O grande. El
autor define una funcin que mide el tiempo de ejecucin de un algoritmo, y que debe cumplir
las siguientes condiciones:

El argumento n es estrictamente positivo.


La evaluacin de la funcin T(n) nunca es negativa, para cualquier argumento n.

Si f(n) es alguna funcin definida sobre los enteros no negativos n, se dice entonces que T(n)
es O(f(n)) (McConnell, 2007).

49

Comparacin de O(f(n)) con relacin a T(n)

T(n) es O(f(n)) si existe un entero n0 y una constante c > 0, tal que para todos los enteros
n >= n0 , tenemos que T(n) <= cf(n). En la Figura anterior se realiza una comparacin de
O(f(n)) con relacin a T(n)
Para determinar el orden de complejidad de un algoritmo a partir de su funcin de tiempo, se
eliminan todos los trminos excepto el de mayor grado y se eliminan todos los coeficientes del
trmino mayor. Lo anterior es debido a que al aumentar el tamao de la entrada, es ms
significativo el incremento en el trmino de mayor orden, que el incremento de los dems
trminos de la funcin. En conclusin, la notacin O(n) nos permite conocer lo verdaderamente
importante en la complejidad de un algoritmo, eliminando los pequeos factores de l.
Dada una funcin f, queremos estudiar aquellas funciones g que a lo sumo crecen tan de prisa
como f. Al conjunto de tales funciones se le llama cota superior de f y lo denominamos O(f).
Conociendo la cota superior de un algoritmo podemos asegurar que, en ningn caso, el tiempo
empleado ser de un orden superior al de la cota (Cormen, Leiserson, Rivest, & Stein, 2001).
A continuacin se muestran convenciones o reglas para escribir la notacin O.

Es una prctica comn cuando escribimos expresiones Big Oh, eliminar todos los
trminos menos el trmino ms significativo. Por ejemplo si tenemos una funcin
5
3
5
f(n)=4n -2n , simplemente escribimos O(n ).

Es comn eliminar coeficientes constantes. Por ejemplo si se tiene O(6n ),


3
simplemente escribimos O(n ). Como caso especial de esta regla, si la funcin es una
constante y si tenemos O(8), se escribe O(1).

Segn (Ramirez, 2003), las propiedades de la funcin O son la transitiva, los trminos de
orden inferior y la regla de la suma, a continuacin se muestra una descripcin de cada uno de
ellos.

Transitividad
Esta regla est basada en la transitividad de la relacin menor que <, ya que si A <= B y
B <=C se puede concluir que A<= C. Asociando esto a la funcin O se tiene que: Si f(n) es
O(g(n)) y g(n) es O(h(n)), se puede concluir que f(n) es O(h(n)).

Trminos de Orden Inferior


Suponga que T(n) es de la forma polinomial: entonces es posible, eliminar todos los
k
trminos con exponente inferior k. Por la regla anterior T(n) es O(n ) (Aho & Ullman,
1995)..

50

Regla de la Suma
2

Suponga que un algoritmo est formado por dos secciones, una de ellas con O(n ) y la otra
3
con O(n ). Entonces es posible sumar estos dos rdenes de complejidad para obtener la
complejidad total del algoritmo. La regla es la siguiente:
Suponga que para T1(n) se sabe que es O(f1(n)) y T2(n) es O(f2(n)) y suponga, adems,
que f1 crece ms rpido que f2. Esto se traduce en que f2(n) es O(f1(n)). En consecuencia
se puede concluir que T1(n) + T2(n) es O(f1(n)).
1.10.3 Notacin Omega
Dada una funcin f, queremos estudiar aquellas funciones g que a lo sumo crecen tan
lentamente como f. Al conjunto de tales funciones se le llama cota inferior de f y lo
denominamos f. Conociendo la cota inferior de un algoritmo podemos asegurar que, en ningn
caso, el tiempo empleado ser de un orden inferior al de la cota.
Decir que T(n) es (f(n)) se lee omega grande de f(n), significa que existe una constante
positiva c y tal que para los n, no se cumple que T(n) cf(n), (f(n) es una cota inferior para
T(n)).

1.10.4 Notacin
Dada una funcin g de los enteros no negativos a los nmeros reales positivos. Entonces
(g)=O(g) (g), es decir, el conjunto de funciones que estn tanto en O(g) como en (g).
Comunmente la forma de leer es f a (g), es decir, f es del orden de g.
La funcin f a (g) si

( )
( )

, para alguna constante c tal que 0 < c < .

Asociada a las anteriores notaciones es posible el uso de otros conceptos matemticos entre
las cuales encontramos las denominadas funciones de piso y techo. La funcin piso y techo
puede ser utilizada para cualquier nmero flotante o entero y.
La expresin denotada como y se conoce como la funcin piso de y corresponde al entero
ms grande que es menor o igual que x. 2.1 = 2, 4.5 = 4 y 6.9 = 6.
La expresin denotada como y se conoce como la funcin techo de y corresponde al nmero
entero ms pequeo que es mayor o igual que x. Por ejemplo, [2.8]=2 y [6.2]=7.

51

1.10.5 Regla del Lmite


Una herramienta potente y verstil para demostrar que algunas funciones estn en el orden de
otras y para demostrar lo contrario, es la regla de lmite, la cual manifiesta que dadas las
+
funciones arbitrarias f y g: N Z (Brassard & Bratley, 1997).
De acuerdo al criterio dado anteriormente, evaluaremos cada una de las expresiones utilizando
la regla de L Hpital. Para los siguientes ejemplos, demostraremos cuando una funcin est en
2
el orden de la otra, por ejemplo, si queremos demostrar que n est en el orden de n ,
2
escribiremos n es O(n ).

( )
( )

( )

( )
( )

( ( ))

( )

( )
( )

( ( ))

( )

( ( ))

( )

( )

( )

( ( ))
( ( ))
( ( ))

A continuacin se muestra una serie de ejemplos con lo que se pretende mostrar mediante la
regla del lmite si una funcin est en la otra.

Ejemplo 1
es (

La funcin

, por L Hpital
, por L Hpital

=
=
=
Cuando
de

, la evaluacin de este lmite tiende al infinito, por tanto


y
est en el orden de
Ejemplo 2

La funcin

es

( )

, por L Hpital

( )

, simplificando
( )

( )
( )

52

, simplificando

no es del orden

( )
( )
( )

, por L Hpital

( )
( )

Cuando
( )
de

, simplificando

, la evaluacin de este lmite tiende al infinito, por tanto


( )
, pero
pertenece al orden de
.

no es del orden

Ejemplo 3

La funcin

es (

))

, por L Hpital

( )

( )

( )

, simplificando

, simplificando

, simplificando

( )

( )
( )

( )
( )

, por L Hpital

, simplificando

( )

( )

Cuando
tiende al infinito, la evaluacin de este lmite tiende al infinito
( ), pero
( ) pertenece al orden de
no es del orden de
.

Ejemplo 4

La funcin

es (

por L Hpital

( )

53

por L Hpital

, por lo tanto

por L Hpital

por L Hpital

( )

( )

Cuando
orden de

, la evaluacin de este lmite tiende a cero, por lo tanto


, pero
no pertenece al orden de
.

est en el

Ejemplo 5

La funcin

es

( )

( )

por L Hpital

, simplificando
( )

( )
( )

, por L Hpital

( )

( )

, por L Hpital

( )
( )

( )
( )

, simplificando

, simplificando

Cuando
, la evaluacin de este lmite tiende al infinito
, por lo tanto
no
( )
es del orden de
. Entre ms complejo sea un algoritmo, se puede afirmar que este
es menos eficiente.

1.10.6 Ordenes de Complejidad


Es importante entonces definir el orden de la complejidad de un algoritmo. A continuacin, se
muestra la explicacin de los rdenes de complejidad frecuente en el anlisis de algoritmos,
cada uno de ellos tendr su explicacin terica y ejemplos de aplicacin.

Complejidad Constante O(1)

En este orden de complejidad el tiempo de ejecucin del algoritmo es independiente del


tamao de la entrada, as se tenga un valor considerado como muy grande, siempre se
ejecutar en forma constante. Operaciones de este tipo son las operaciones aritmticas
bsicas, asignaciones a variables, llamadas y retorno a mtodos, comparaciones lgicas,
acceso a estructuras de datos estticas y dinmicas.

54

Es la complejidad considerada como ideal, pero un problema considerado de mediana


dificultad de solucionar, difcilmente tendr este orden de complejidad. La razn por la que a
estas operaciones se les ha asignado un orden constante, es que cada una de ellas es resuelta
por un nmero muy pequeo de instrucciones de lenguaje de mquina. A continuacin se
muestran valores asociados a la complejidad O(1).
Complejidad O(1)
50
2
50000
96
3
En (Preiss, 1998), se definen tres axiomas asociados a la complejidad computacional
constante, a continuacin se da una definicin de cada uno de ellos:

En su primer axioma establece que, el tiempo requerido para recuperar un operando


desde memoria es constante y as mismo el tiempo requerido para almacenar un
resultado en memoria es constante.
En un segundo axioma, el tiempo requerido para realizar operaciones aritmticas, tales
como la adicin, sustraccin, multiplicacin, divisin y comparacin, son todas
constantes.
El tercer axioma, dice que el tiempo requerido para llamar un mtodo es constante y el
tiempo requerido para retornar desde un mtodo tambin es constante. Finalmente el
tiempo requerido para pasar un argumento a un mtodo es el mismo que el tiempo
requerido para almacenar un valor en memoria.

A continuacin se muestra un ejemplo con instrucciones consideradas de orden de O(1).


Ejemplo

Mtodo

Anlisis del
Orden de
Complejidad

Orden de
complejidad

Objetivo: Entender como se deduce el orden de complejidad para


un algoritmo con orden constante.
public void constante( int a[], int n )
{
int cantidad = n;
if ( a[n-1] == a[n-2] )
{
cantidad++;
}
else
{
cantidad--;
}
}
int a[], O(1)
int n, O(1)
int cantidad = n, O(1)
if (a[n - 1] == a[n - 2]) O(1)
cantidad++ O(1)
cantidad-- O(1)
El mtodo llamado constante, tiene orden de complejidad de O(1).

Complejidad Logartmica O(logn)

La complejidad logartmica tiene un mayor orden que la complejidad constante, este orden de
complejidad se obtiene entre otros en algunos algoritmos recursivos clsicos como la bsqueda
binaria y los rboles binarios de bsqueda con sus operaciones de eliminacin, bsqueda e

55

insercin. Este orden de complejidad est relacionado con algoritmos que son consideramos
muy eficientes.A continuacin se muestran valores asociados a la complejidad log(n).
Complejidad O( logn)
5+
2 log(n)
log(n) + 150000
96
log(n) + 3
El mtodo tiene una complejidad logartmica, a continuacin mostraremos su anlisis mediante
la implementacin de un mtodo.
Ejemplo

Mtodo

Anlisis del
Orden de
Complejidad

Orden de
complejidad

Objetivo: Entender como se deduce el orden de complejidad para


un algoritmo con orden logartmico.
public void orden ( )
{
int temp = 0, x = 0, y = 1, i;
i = 32;
while( i > 0 )
{
x++;
temp++;
y = y * 1;
i = i / 2;
}
}
temp = 0
Se ejecuta 1 vez
x = 0
Se ejecuta 1 vez
y = 1
Se ejecuta 1 vez
i = 32
Se ejecuta 1 vez
while(i > 0) Se ejecuta log2(i) + 2 veces
x++
Se ejecuta log2(i) + 1 veces
temp++
Se ejecuta log2(i) + 1 veces
y = y * 1
Se ejecuta log2(i) + 1 veces
i = i/2
Se ejecuta log2(i) + 1 veces
Se realizan 6 comparaciones en el ciclo while(i > 0) ms la
comparacin que rompe el ciclo. Esto basado en la forma como
cambia el tamao de i. Para este caso encontramos una relacin
entre el nmero de comparaciones del ciclo, (6 comparaciones) y el
5
valor de i = 32. Se puede determinar que 2 = 32, donde 5 es el
logaritmo en base dos de 32. Por lo tanto el nmero de veces que
se repite el ciclo es log2(i)+1.
O(log2(i) )

Complejidad Lineal O(n)

La complejidad lineal tiene un orden mayor que la complejidad constante y la complejidad


logartmica, este orden de complejidad normalmente se obtiene cuando se tiene un ciclo
sencillo. A continuacin se muestran algunas funciones con orden lineal.
A continuacin se muestran algunas funciones con orden lineal.

56

Complejidad (n)
10 +
3 log(n) +n
n + 520000
9
n+4
Para el mtodo que aparece en el Cuadro, tenemos que su complejidad est relacionada
directamente con la implementacin de ciclos sencillos. Este mtodo retorna la sumatoria de
los elementos de un arreglo de enteros.
Ejemplo

Objetivo: Entender como se deduce el orden de complejidad para


un algoritmo con orden lineal.
public int orden2( int prueba[] )
{
int sumatoria = 0;
for (int i = 0; i < prueba.length; i++)
{
sumatoria += prueba[i];
}
return sumatoria;

Mtodo

}
Anlisis del
Orden de
Complejidad
Orden de
complejidad

Inicialmente, se analiza el ciclo del mtodo. Se observa que este


tiene un ciclo que itera desde cero hasta n. El tiempo de ejecucin
del algoritmo es: T(n) = 3n+5
O(n)

Complejidad nlogn O(nlogn)

La complejidad nlog(n) tiene un orden mayor que la complejidad constante, logartmica, lineal,
encontramos este orden de complejidad en algunos algoritmos de ordenamiento como el
heapSort y el mergeSort. A continuacin se muestran algunas funciones con orden nlog(n).
Complejidad (n)
nlog(n) +n+10
nlog(n) + log(n)
A continuacin se muestra la implementacin de un mtodo con el orden de complejidad que
se est analizando.
Ejemplo

Objetivo: Comprender como se deduce el orden de complejidad


para un algoritmo con orden nlogn.

Mtodo

public void orden( int n )


{
int x, i, j;
x = 0;
for ( i = 0 ; i < n ; i++ )
{
for ( j = 1 ; j < n ; j*=2 )
{
x = x+2;
}
}
}

57

Inicialmente, se analiza el ciclo ms interno y posteriormente se


analiza el ciclo externo.
Anlisis del
Orden de
Complejidad

Orden de
complejidad

El ciclo interno se repite log2 veces.


El ciclo externo se repite n veces.
Multiplicando el ciclo interno por el ciclo externos, se tiene un
orden de complejidad nlog2(n).

O(nlog2(n))

Complejidad Cuadrtica O(n )

La complejidad cuadrtica tiene un orden mayor que la complejidad constante, logartmica,


lineal y nlog(n). Este orden de complejidad normalmente se obtiene cuando se tienen dos ciclos
anidados. A continuacin se muestran algunas funciones con orden cuadrtico.
2

Complejidad (n )
2
n + nlog(n) +n+10
2
n + log(n)
2
25
n +2
La complejidad cuadrtica est relacionada con el uso de dos ciclos anidados. En el Cuadro
mostramos un ejemplo en el cual se suman los elementos de un arreglo bidimensional de
enteros.
Ejemplo

Mtodo

Objetivo: Comprender como se deduce el orden de complejidad


para un algoritmo con orden cuadrtico.
public int orden(int prueba[][])
{
int sumatoria = 0;
for (int i = prueba.length-1; i >=0 ; i--)
{
for(int j = prueba[0].length-1; j>=0; j--)
{
sumatoria += prueba[i][j];
}
}
return sumatoria;
}

Anlisis del
Orden de
Complejidad

Se tienen dos ciclos anidados, los cuales iteran la misma cantidad


de veces. Van desde la ltima posicin de un arreglo, hasta la
primera posicin del arreglo. Cuando se tienen 2 ciclos anidados, la
ejecucin del interno depende de la cantidad de veces que lo
permita el externo.

Orden de
Complejidad

O(n )

Complejidad Cbica O(n )

La complejidad cbica tiene un orden mayor que la complejidad constante, logartmica, lineal,
nlog(n) y la cuadrtica. Este orden de complejidad normalmente se obtiene cuando se tiene
tres ciclos anidados.

58

A continuacin se muestran algunas funciones con orden cbico.


3

Complejidad (n )
3
2
n + n +n
3
n + log(n) + 500
3
2
15
n +n +5
A continuacin se muestra la implementacin de un mtodo con el orden de complejidad que
se est analizando.
Ejemplo

Mtodo

Anlisis del
Orden de
Complejidad
Orden de
Complejidad

Objetivo: Comprender como se deduce el orden de complejidad


para un algoritmo con orden cbico.
public void cubica( int a[] )
{
int x = 0;
for (int i = 0 ; i < a.length ; i++ )
{
for (int j = 0 ; j < a.length; j++ )
{
for (int k = 0 ; k < a.length ; k++ )
{
a[i] = i + k - j;
}
}
}
}
Encontramos tres ciclos anidados, las variables i, j, k, se
inicializan en cero y se incrementan de uno en uno, estas
iteraciones llegan hasta el tope mximo del tamao del arreglo.
Cada ciclo se ejecuta n veces, al estar anidado, se tiene un orden
3
de complejidad O(n ).
3

O(n )

Complejidad Exponencial

La complejidad exponencial es la menos deseable de todas las complejidades, tiene un orden


mayor que la complejidad constante, logartmica, lineal, nlog(n), cuadrtica y la cbica. Este
orden de complejidad normalmente se obtiene en algunos algoritmos recursivos como el de
Fibonacci.
A continuacin se muestran algunas funciones con exponencial.
n

Complejidad (2 )
n
3
2 + n +n
n+5
2 + log(n)
n
3
2
45
2 +n +n +7
n+2
100
2 +n
A continuacin se muestra la implementacin de un mtodo con el orden de complejidad que
se est analizando.

Ejemplo

Objetivo: Comprender como se deduce el orden de complejidad


para un algoritmo con orden exponencial.

59

Mtodo

public int
{
int
m =
for
{

potencia( int n )
m, i;
1;
( i = 1 ; i <= Math.pow( 2, n ) ; i++ )

m = m * i;
}
return m;
}
Anlisis del
Orden de
Complejidad
Orden de
Complejidad

La cantidad de veces que se repite el ciclo es 2 veces. El tiempo


n
de ejecucin de este algoritmos es: T(n) = 3(2 ) + 6
n

O(2 )

Los algoritmos donde aparece esta complejidad se llaman algoritmos de explosin


combinatoria y slo es recomendable utilizarlos con un conjunto de datos de entrada muy
pequeo.

ACTIVIDAD

Dado el siguiente mtodo explique qu problema resuelve y determine su tiempo de ejecucin.


Ejercicio

Mtodo

public boolean misterio( int numero )


{
for(int i = 2; i<=(int)Math.sqrt(numero); i++)
{
if ( numero % i == 0 )
{
return false;
}
}
return true;
}

Anlisis del
Orden de
Complejidad
Orden de
complejidad
Dado el siguiente mtodo explique qu problema resuelve y determine su orden de
complejidad.
Ejercicio

Mtodo

public int misterio(int matriz [][])


{
int i, j, n = matriz.length, suma = 0;
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
if (!((i==0)||i==(n-1))&&
!(j==0||j==(n-1)))

60

{
suma= suma + matriz[i][j];
}
}
}
return suma;
}
Anlisis del
Orden de
Complejidad
Orden de
complejidad
Dado el siguiente mtodo explique qu problema resuelve y determine su orden de
complejidad.
Ejercicio

Mtodo

public int misterio (int num)


{
int i,col = num, contador=0;
if(num==0)
{
return num;
}
for(i = 10; num!=0; i+=10)
{
contador+= (num%i);
num/=i;
i-=10;
}
if(contador!=0)
{
return contador;
}
}

Anlisis del
Orden de
Complejidad
Orden de
complejidad
Dado el siguiente mtodo explique qu problema resuelve y determine su tiempo de ejecucin.
Ejemplo

Mtodo

public int misterio( int n, int x )


{
int m, i;
m = 1;
for ( i = 1 ; i<=Math.pow( 5, n ) ; i+=2 )
{
m = m * x;
}
return m;
}

Anlisis del
Tiempo de
ejecucin
Orden de
complejidad

61

Dado el siguiente mtodo explique qu problema resuelve y determine su tiempo de ejecucin.


Ejemplo

Mtodo

public int misterio (int a, int b)


{
int resultado = 1;
for(int i = 1; i < a; i++)
{
if(a%i==0 && b%i==0)
{
resultado = i;
}
}
return resultado;
}

Anlisis del
Tiempo de
ejecucin
Orden de
complejidad

1.11 Actividad Independiente: Concurso Docente


Se desea crear una Aplicacin que permita manejar la informacin de un concurso docente. Se
debe permitir agregar un nuevo docente, calcular el puntaje obtenido por dicho docente,
ordenar por nombre a los docentes que estn participando en el concurso y listar los docentes
de acuerdo al puntaje obtenido en orden ascendente. Se deber permitir al seleccionar un
docente conocer su informacin bsica (nombre, apellido y total de puntos obtenidos).
El docente que participe debe tener como mnimo ttulo de pregrado. De lo contrario no podr
ser aceptado en el concurso. Un pregrado da 178 puntos, una especializacin da 30 puntos. Si
el docente tiene ttulo de maestra se dan 60 puntos (esto en el caso de que tenga tambin
especializacin). Si no tiene especializacin se le otorgarn 120 puntos. Si el docente tiene ya
tiene maestra o estudios de especializacin se le reconocern por concepto de doctorado 90
puntos. Si no tiene estos estudios se le reconocern 170 puntos.
a) Requerimientos funcionales
NOMBRE
R1 Crear un nuevo docente.
RESUMEN
ENTRADAS
RESULTADOS

NOMBRE

R2 Calcular el puntaje del docente.

RESUMEN
ENTRADAS
RESULTADOS
El puntaje del docente

62

NOMBRE
R3 Ordenar por nombre a los docentes.
RESUMEN
ENTRADAS
RESULTADOS

NOMBRE
R4 Ordenar por puntaje.
RESUMEN
ENTRADAS
RESULTADOS

NOMBRE
R5 Mostrar la informacin de un docente.
RESUMEN
ENTRADAS
RESULTADOS

b. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
ConcursoProfesor
Profesor

DESCRIPCIN

c. Descripcin de los mtodos de la clase Profesor.


NOMBRE
Profesor(String cedula, String
nombre,
String apellido, String
foto, boolean pregrado,boolean
especializacion, boolean maestria,
boolean doctorado,
int cantidadLibros)
getNombre()
setNombre(String nombre)
getApellido()
setApellido(String apellido)
isPregrado()
setPregrado(boolean pregrado)
isEspecializacion()
setEspecializacion(boolean
especializacion)
isMaestria()
setMaestria(boolean maestria)
isDoctorado()

63

DESCRIPCIN

setDoctorado(boolean doctorado)
getCantidadLibros()
setCantidadLibros(int
cantidadLibros)
fijarPuntos()
getPuntos()
c. Relaciones entre las clases
Construya el diagrama de clases para Profesor y concursoProfesor.

d) Mtodos de la clase Profesor


Los siguientes son los mtodos ms importantes para la clase profesor. Se omiten los mtodos
accesores y modificadores.
public Profesor(String cedula, String nombre, String apellido, boolean
pregrado, boolean especializacion, boolean maestria, boolean doctorado,
int cantidadLibros)
/**
* @param cedula. Es la cdula del docente. cedula !=null cedula!=""
* @param nombre. Nombre del docente. nombre !=null nombre!=""
* @param apellido. Apellido del docente. apellido !=null apellido!=""
* @param pregrado. Indica si el docente tiene pregrado . pregrado == true
* @param especializacin. Indica si el docente maestra.
* @param doctorado. Indica si el docente tiene doctorado .
* @param cantidadLibros. Cantidad de libros del docente. cantidaLibros>=0
*/
public Profesor(String cedula, String nombre, String apellido, boolean
pregrado, boolean especializacion, boolean maestria, boolean doctorado,
int cantidadLibros)
{

64

fijarPuntos()
/** Permite fijar la cantidad de puntos */
public void fijarPuntos()
{
puntos=178;
if(especializacion==true)
{
puntos+=30;
}
if(_________________________)
{
puntos+=120;
}
if(_________________________)
{
puntos+=60;
}
if(doctorado==true)
{
if(______________________)
{
puntos+=170;
}
else
{
puntos+=80;
}
}
puntos += ( _________________*15);
}

ACTIVIDAD

1. Escriba una interpretacin en torno a la forma como se calcula el puntaje de cada docente.

A continuacin se muestran los mtodos para la clase ConcursoProfesor


/**
* Permite agregar un nuevo Profesor
* @param cedula. Es la cdula del profesor. cedula!=null, cedula!=""
* @param nombre. Es el nombre del profesor. nombre!=null, nombre!=""
* @param apellido. Apellido del profesor. apellido!=null, apellido!=""
* @param pregrado. Indica si tiene pregrado.pregrado==true

65

* @param especializacin. Indica si tiene especializacin


* @param maestria. Indica si tiene maestra
* @param doctorado. Indica si tiene doctorado
* @param cantidadLibros. Indica la cantidad de libros.
* @return verdadero si se pudo agregar el concursante
*/
public boolean agregarParticipante (String cedula, String nombre,
String apellido, boolean pregrado,boolean especializacion, boolean
maestria, boolean doctorado, int cantidadLibros)
{
boolean esta=false;
if(miProfesor.size()>0)
{
esta = determinarSiExisteProfesor(cedula);
}
if(esta==false)
{
Profesor miProfe = new Profesor(_________________________
);
miProfe.________________
miProfesor.add(_______________);
return _______________;
}
return _______________;
}
ConcursoProfesor()
/**
* Este es el mtodo constructor
*/
public ConcursoProfesor()
{
}
determinarSiExisteProfesor(String cedula)
/**
* Permite verificar si un profesor existe en el listado
* @param cedula. Es la cdula del profesor
* @return booleano que indica si existe o no
*/
public boolean determinarSiExisteProfesor(String cedula)
{

66

determinarSiNoHayRepeticiones()
/**
* Permite determinar si no hay profesores repetidos.
* @return booleano que indica si existe o no.
*/
public boolean determinarSiNoHayRepeticiones()
{
for(___________________________)
{
for(______________________________)
{
if(________________________________________________________)
{
return ____________;
}
}
}
return ____________;
}
generarListadoPorPuntaje()
/**
* Permite generar el listado de docentes por puntaje.
* @return un array de String con la informacin de los docentes.
*/
public String[] generarListadoPorPuntaje()
{

}
generarListado()
/**
* Permite generar el listado de los docentes.
* @return un array de String.
*/
public String[] generarListado()
{
String info[]=new String[miProfesor.size()];

67

generarListadoOrdenadoPorNombre()
/**
* Permite generar el listado ordenado por nombre.
* @return un array con la informacin de los docentes.
*/
public String[] generarListadoOrdenadoPorNombre()
{
String info[]=new String[miProfesor.size()];
for(int i=0; i<miProfesor.size(); i++)
{
for(int j=0; j<miProfesor.size()-1; j++)
{

}
}
return info;
}

ACTIVIDAD
Una vez escritos los mtodos para la clase AgendaTelefonica, se debe calcular el tiempo de
ejecucin de cada uno de ellos.
Mtodo

Orden de Complejidad
O(
)

fijarPuntos()

agregarParticipante()
ConcursoProfesor()
determinarSiExisteProfesor(String cedula)
determinarSiNoHayRepeticiones()
generarListadoPorPuntaje()
generarListado()
generarListadoOrdenadoPorNombre()

O(

O(

O(

O(

O(

O(

O(

1.12 Anlisis de Algoritmos Recursivos


Si un problema puede resolverse usando la solucin de versiones ms pequeas de s mismo,
y esas versiones ms pequeas, se reducen fcilmente a problemas solubles, entonces
nosotros tenemos un algoritmo recursivo (Baldwin & Scragg, 2004). La recursin es una forma
alternativa, para la resolucin de problemas algortmicos. Muchos algoritmos se pueden
expresar ms fcilmente usando una formulacin recursiva.

68

En toda definicin recursiva de un problema se debe establecer un estado base, es decir, un


estado en el cual la solucin no se presente de manera recursiva sino directamente. Esto
permite que las llamadas recursivas no continen indefinidamente. Por lo anterior, una solucin
recursiva est compuesta por:

Caso base: El problema se resuelve directamente (generalmente valores pequeos).


Paso recursivo: Se divide el problema en varias partes, luego se resuelve cada una de ellas
y finalmente se combinan las soluciones de las partes para dar una solucin al problema.

La llamada a un algoritmo recursivo conduce a una nueva versin del mtodo que comienza a
ejecutarse, tambin a una organizacin de la gestin de los parmetros y la memoria. Estos
datos deben estar organizados de forma que al terminar cada llamado que se est ejecutando,
se devuelvan los datos correctos y se actualice la informacin que permita su gestin.
A nivel del diseo de los algoritmos, el uso de la recursividad puede presentar soluciones
cortas y claras, adems de permitir en muchas ocasiones la solucin de problemas complejos.

1.12.1 Algoritmos Recursivos


Los mtodos recursivos se clasifican de acuerdo al lugar en donde se haga la llamada
recursiva, se tiene por lo tanto la recursividad directa, la cual se tiene cuando un mtodo se
llama a s mismo, y tambin existe la recursividad indirecta en la cual un mtodo llama a otro
mtodo y viceversa, siempre resolviendo el mismo problema. Los siguientes mtodos usan
recursividad directa.

Algoritmo recursivo: multiplicacin

El siguiente mtodo retorna la multiplicacin de dos nmeros enteros de forma recursiva.


Definamos un caso de prueba que permita multiplicar los (5 * 3) recursivamente:
5*3=5+5*2
5 * 2 = 5 + 5 = 10
5*1=5
Generalizando tenemos:
a*b=a
, si b = 1
a * b = a + a * (b -1) , si b > 1
La siguiente es la implementacin del mtodo multiplicar, los valores se reciben por parmetro.

Mtodo

public int multiplicar ( int a, int b )


{
if ( a == 0 || b == 0 ){
return 0;
}
else
{
if( b == 1 )
{
return a;
}
else
{
return a + multiplicar ( a, b-1);
}
}
}

69

El algoritmo tiene dos condiciones de parada:

Si algn valor que recibi por parmetro es cero retorna cero.


Si el segundo parmetro es igual a uno, entonces retorna el valor del primer parmetro.

A continuacin, se muestra una prueba para este algoritmo recursivo.


multiplicar(5, 4) = 5 + multiplicar (5, 3)
= 5 + (5 + (multiplicar (5,2)))
= 5 + (5 + (5 + multiplicar (5,1))
= 5 + (5 + (5 + 5))
= 5 + (5 + 10)
= 5 + 15
= 20
En la siguiente figura se muestra como se puede representar este algoritmo recursivo.

Algoritmo recursivo: Contar ceros en el Arreglo

El siguiente algoritmo recursivo retorna la cantidad de ceros que se encuentran dentro de un


arreglo unidimensional de enteros.

Mtodo

public int cantidadCeros (int arreglo[], int n)


{
if (n == 0)
{
return 0;
}
else
{
if (arreglo[n - 1] == 0)
{
return 1+cantidadCeros(arreglo,n-1);
}
else
{
return cantidadCeros (arreglo, n-1);
}
}
}

Este mtodo recibe por parmetros un arreglo y una variable n la cual contiene el tamao del
arreglo. La instruccin if(arreglo[n-1]==0) es la encargada de verificar si el elemento en
la posicin n es igual a cero. El algoritmo termina cuando el valor de n es cero.

70

La siguiente es una representacin para este algoritmo recursivo.

ACTIVIDAD

Dados los siguientes mtodos recursivos, determinen si resuelven el problema que se plantea,
en el caso en el cual exista un error se debe organizar el mtodo en la parte de observaciones
de forma tal que este quede correcto.
Ejercicio

Mtodo

Problema: Este mtodo verifica si una matriz es simtrica o no lo


es.
public boolean matrizSimetrica (int matriz[][],
int i, int j)
{
if(i==matriz.length-1 && j==matriz.length-1)
{
return (matriz[i][j]==matriz[j][i]);
}
if(matriz[i][j]==matriz[j][i])
{
if(matriz.length-1==j)
{
return matrizSimetrica(matriz,i+1,0);
}
else
{
return matrizSimetrica(matriz,i,j+1);
}
}
else
{
return false;
}
}

Observaciones

71

Ejercicio

Problema: Este mtodo determina si un nmero es perfecto o no lo


es. Los valores de los parmetros son: aux = 1 e i = 2.

Mtodo

public int numeroPerfecto(int valor,int aux,int i)


{
if(i == valor-1)
{
return aux;
}
if (valor%i==0)
{
aux= aux + i;
}
return numeroPerfecto(valor, aux, ++i);
}

Observaciones

Ejercicio

Problema: Este mtodo retorna la suma de los elementos de un


arreglo bidimensional de enteros.

Mtodo

public int sumaNumeros(int matriz[][],int i,int j)


{
if (j<0)
{
i = i-1;
j = matriz.length-1;
}
if (i<0)
{
return 0;
}
return matriz[i][j]+sumaNumeros(matriz,i,*--j);
}

Observaciones

Escriba los siguientes algoritmos recursivos:


Ejercicio

Mtodo

Problema: Se necesita un mtodo que determine si una cadena de


caracteres es o no palndroma.
/**
* Determina si la palabra es o no palndroma
* @param cadena contiene la cadena a analizar
* @param i toma el valor inicial i=0
* @param j toma el valor de cadena.length()-1
*/
public boolean palindroma(String pal,int i, int j)
{

72

Ejercicio

Problema: Se necesita un mtodo sume los elementos de un


arreglo unidimensional de enteros.
/**
* calcula la suma de los elementos de un arreglo
* @param x[] contiene los elementos enteros
* @param n toma el tamao del arreglo
*/
public int suma( int x[], int n )
{

Mtodo

1.12.2 Tiempo de ejecucin de algoritmos recursivos


El anlisis de funciones recursivas requiere que a cada funcin F se le asocie un tiempo de
ejecucin TF(n). Una vez hecho esto, se establece una definicin inductiva denominada
relacin recurrente para TF(n), que relaciona TF (n) con otras funciones de la forma TG(k), para
otras funciones G del programa, cuyos datos de entrada son de tamao k. Si F es la funcin
recursiva, entonces una o ms de las funciones G sern la misma F.
El valor de TF (n) se establece normalmente por una induccin de los argumentos de tamao n.
As es necesario seleccionar una nocin correcta del tamao de los argumentos, que garantice
que las funciones sean llamadas con un tamao progresivamente menor de los mismos, a
medida que la recursividad tiene efecto. Una vez se establezca el tamao del conjunto de datos
de entrada, se deben considerar dos casos:

El tamao del parmetro recibido es tan pequeo que no se generan llamadas


recursivas.
Para argumentos de un tamao considerable, una o ms llamadas recursivas pueden
ocurrir, dado que el problema no se puede resolver directamente.

73

De manera ms formal, se plantean dos formas de resolver la relacin de recurrencia:

Sustituir repetidamente la regla inductiva dentro de ella misma, hasta que se obtenga
alguna relacin entre T(n) y T(1), es decir, la expresin de induccin y la base.
Suponer una solucin y probar su correctitud sustituyndola en la expresin base y la
inductiva.

A continuacin se muestran ejemplos que permiten hallar el tiempo de ejecucin y el orden de


complejidad para algoritmos recursivos.

Anlisis del algoritmo recursivo: Fibonacci

Introducidos por el matemtico Leonardo de Pisa, quien se llamaba a si mismo Fibonacci. Cada
nmero de la secuencia se obtiene sumando los dos anteriores.
fib(n) = 0,
n=0
fib(n) = 1,
n=1
fib(n) = fib(n -1) + fib(n - 2), n >= 2
Por definicin, los dos primeros valores son 0 y 1 respectivamente. Los otros nmeros de la
sucesin se calculan sumando los dos nmeros que le preceden. A continuacin se muestran
los primeros 13 nmeros de Fibonacci. A continuacin se muestra la implementacin el
algoritmo recursivo que encuentra el n-simo nmero de la sucesin Fibonacci.

Ejemplo

Mtodo

Objetivo: Entender la forma como se estima el tiempo de ejecucin


y el orden de complejidad para algoritmos recursivos.
public int fibo( int n )
{
if ( n == 0 )
{
return 0;
}
else
{
if ( n == 1 || n == 2 )
{
return 1;
}
else
{
return (fibo (n-2) + fibo (n-1) );
}
}
}
Caso Base:
Induccin:

Anlisis del
Tiempo de
Ejecucin

T(1) = a
T(n) = b + T(n - 2) + T(n - 1)

Para simplificar el anlisis, se toma el peor de los casos entre


T(n- 2) y T(n-1). Se puede afirmar que T(n-1) tomar ms tiempo,
ya que n - 1 es ms grande que n - 2. Se asume entonces que el
caso inductivo es: T(n) = b + 2T(n - 1)

74

Se asignan valores a n:
T(1) = a
T(2) = b + 2T(1) =
T(3) = b + 2T(2) =
T(4) = b + 2T(3) =
T(5) = b + 2T(4) =
T(6) = b + 2T(5) =

b + 2a
b + 2(b + 2a)
=
b + 2(3b + 4a) =
b + 2(7b + 8a) =
b + 2(15b + 16a) =

Se concluye entonces que: T(n) = ( 2


Orden de
Complejidad

( 2 1 )b + 2 a
3
3
( 2 1 )b + 2 a
4
4
( 2 1 )b + 2 a
5
5
( 2 1 )b + 2 a
n-1

1)b + ( 2

n-1

)a

O(2 )

Anlisis del algoritmo recursivo: multiplicacin

A continuacin mostramos el algoritmo recursivo con un tiempo de ejecucin de orden


logartmico, este algoritmo retorna el resultado de multiplicar dos nmeros enteros positivos.

Ejemplo

Objetivo: Entender la forma como se estima el tiempo de ejecucin


y el orden de complejidad para algoritmos recursivos.

Mtodo

public int multiplicacion (int m, int n)


{
if (n == 0)
{
return 0;
}
else
{
if(n == 1)
{
return m;
}
else
{
if ( n % 2==0 )
{
return multiplicacion (m+m,n/2);
}
else
{
return multiplicacion (m+m,n/2)+ m;
}
}
}
}

Anlisis del
Orden de
Complejidad

El tiempo de ejecucin para el caso base lo denotaremos T(1), el


caso base se da para dos casos: cuando el parmetro n = 0 o
cuando el parmetro n = 1, retornan respectivamente los valores 0
y m que es el segundo parmetro. Para este caso que no existe
recursin; se debe calcular el tiempo de ejecucin para el peor caso
T(1) = 3, y por lo tanto su orden de complejidad es O(1). Con lo
anterior se puede decir que T(1) = O(1).

75

Caso base: T(1) = a


Induccin: T(n) = b + T(n/2)
Ahora se asignan valores n y, reemplace la base en la induccin.
Si n = 2,
Si n = 4,
Si n = 8,
Si n = 16,

T(2) = b + T(1) = 1b + a
T(4) = b + T(2) = b + b + a = 2b + a
T(8) = b + T(4) = b + 2b + a = 3b + a
T(16) = b + T(8) = b + 3b + a = 4b + a

El tiempo de ejecucin es: T(n) = b log(n) + a


Orden de
Complejidad

O(log(n))

Anlisis del algoritmo recursivo: Potencia

Este algoritmo recursivo retorna la potencia de un numero X elevado a la Y.

Ejemplo 3

Mtodo

Objetivo: Entender la forma como se estima el tiempo de ejecucin


y el orden de complejidad para algoritmos recursivos.
public int calcularPotencia (int x, int n)
{
if (n==0)
{
return 1;
}
else
{
if (n%2==0)
{
return calcularPotencia(x, n/2) *
calcularPotencia(x, n/2);
}
else
{
return calcularPotencia(x, n/2) *
calcularPotencia(x, n/2) * x;
}
}

Anlisis del
tiempo de
ejecucin

}
Se tiene que para el peor de los casos, el caso base es T(1) = 2.
Para el caso inductivo tenemos que el caso base es de orden
constante O(1) y se tienen 2 llamadas recursivas cada una de
2*(n/2), por lo tanto el caso inductivo T(n) = 2T( n/2 ) + O(1).
Generalizando tenemos, que el caso base
y el caso inductivo, quedan expresados de la siguiente manera:
T(1) = O(1) = a (se reemplaza por una constante):
T(n) = 2T( n/2 ) + b
Reemplazando la base en la induccin, tenemos:
T(2) = 2T(1) + b T(2) = 2a + b
T(4) = 2T(2) + b T(4) = 4a + 3b

76

T(8) = 2T(4) + b T(8) = 8a + 7b


T(16) = 2T(8) + b T(16) = 16a + 15b
T(n) = 2T(n/2) + b T(n) = na + (n - 1)b
Orden de
Complejidad

O(n)

ACTIVIDAD

Dado el siguiente mtodo recursivo, determine su tiempo de ejecucin y su orden de


complejidad.
Ejercicio

Problema: Este mtodo retorna la potencia de un nmero entero.

Mtodo

public int calcularPotencia (int x, int n)


{
if (n==0)
{
return 1;
}
else
{
if (n%2==0)
{
int y = calcularPotencia(x,n/2);
return y * y;
}
else
{
int y = calcularPotencia(x,n/2);
return y * y * x;
}
}
}

Anlisis del
Tiempo de
Ejecucin
Orden de
complejidad
Dado el siguiente mtodo recursivo, determine su tiempo de ejecucin y su orden de
complejidad.
Ejercicio

Mtodo

Problema: Este mtodo retorna la sumatoria de los n primeros


nmeros.
public int sumatoria( int n )
{
if (n == 1 ){
return 1;
}
else
{
return n + sumatoria(n - 1);
}
}

77

Anlisis del
Tiempo de
Ejecucin
Orden de
complejidad
Dado el siguiente mtodo recursivo, determine su tiempo de ejecucin y su orden de
complejidad.
Ejercicio

Mtodo

Problema: Calcular el tiempo de ejecucin del siguiente algoritmo


recursivo.
public int recursivo( int n )
{
if (n < 1)
{
return 1;
}
else
{
return n *(recursivo(n-1) + recursivo(n-1)
+ recursivo(n-1));
}
}

Anlisis del
Tiempo de
Ejecucin
Orden de
complejidad

1.12.3 Resolucin de Recurrencias por induccin


El anlisis del tiempo de ejecucin de los algoritmos recursivos se puede realizar de forma
inductiva. En este apartado se mostrarn algoritmos con la caracterstica de ser recursivos.
Dado que los aspectos en los cuales estamos ms interesados en el anlisis de algoritmos son
particularmente los conceptos de tiempo de ejecucin y orden de complejidad, no nos
detendremos a analizar que hace el algoritmo, nicamente se estudiara su tiempo de ejecucin
y su orden de complejidad.

Ejemplo 1

El siguiente algoritmo calcula la potencia de un nmero entero n.

Mtodo

public int calcularPotencia (int x, int n)


{
if (n==0)
{
return 1;
}
else
{
if (n%2==0)
{
return calcularPotencia(x,n/2) *
calcularPotencia(x, n/2);
}

78

else
{
return calcularPotencia(x,n/2) *
calcularPotencia(x, n/2) * x;
}
}
}
Se tiene que para el peor de los casos, el caso base es T (1) = 2. Para el caso inductivo
tenemos que el caso base es de orden constante O (1) y se tienen 2 llamadas recursivas cada
una de 2*(n/2), por lo tanto el caso inductivo T(n) = 2T(n/2)+O (1). Generalizando tenemos, que
el caso base y el caso inductivo, quedan expresados de la siguiente manera:
T (1) = O (1) = a (se reemplaza por una constante).
T(n) = 2T(n/2) + b
Reemplazando la base en la induccin, tenemos:
T (2) = 2T (1) + b
T (4) = 2T (2) + b
T (8) = 2T (4) + b
T (16) = 2T (8) + b

T (2) = 2a + b
T (4) = 4a + 3b
T (8) = 8a + 7b
T (16) = 16a + 15b
...

T(n) = 2T(n/2) + b

T(n) = na + (n - 1) b O(n)

Ejemplo 2

A continuacin se muestra otro algoritmo recursivo que retorna la potencia de un nmero. Este
algoritmo debe tener un orden de complejidad inferior al anterior. Esto se deduce dada la
cantidad de llamados recursivos que se utilizan para resolver el problema.

Mtodo

public int calcularPotencia (int x, int n)


{
if (n==0)
{
return 1;
}
else
{
if (n%2==0)
{
int y = calcularPotencia(x,n/2);
return y * y;
}
else
{
int y = calcularPotencia(x,n/2);
return y * y * x;
}
}
}

En el anlisis del caso base se tiene que para el peor de los casos el tiempo de ejecucin es:
T (1) = 2. Para el caso inductivo se tienen una llamada recursiva de (n/2) y el caso base es de
orden constante, por lo tanto el caso inductivo T(n) = T(n/2) + O (1).
Generalizando tenemos, que el caso base y el caso inductivo, quedan expresados de la
siguiente manera:

79

T (1) = 8
T(n) = T(n/2) + 8
T (1) = O (1) = a (se reemplaza por una constante)
T(n) = T(n/2) + b
Reemplazando la base en la induccin, tenemos:
T (2) = T (1) + b
T (4) = T (2) + b
T (8) = T (4) + b
T (16) = T (8) + b

T (2) = a + b
T (4) = a + 2b
T (8) = a + 3b
T (16) = a + 4b
...
T(n) = a + b (log n) O (log n)

T(n) = T(n/2) + b

Ejemplo 3

En el siguiente caso se analizar nicamente el algoritmo por medio de la tcnica de induccin.


Suponga que se tienen las siguientes funciones base y de induccin. Determinar cul es su
tiempo de ejecucin y cul es su orden de complejidad.
T (1) = O (1)
T(n) = T(n - 2) + T(n - 3)
Se considera el peor caso, y se tiene que T(n-2)+T(n-3) < 2 * T(n - 1), por lo tanto 2 * T(n - 1) es
el peor caso. Con esta comparacin se puede garantizar una cota superior para este problema.
T (1) = a
T(n) = 2T(n - 1)
Reemplazando la base en la induccin, tenemos:
T (2) = 2T (1)
T (3) = 2T (2)
T (4) = 2T (3)
T (5) = 2T (4)
T (6) = 2T (5)

T (2) = 2a
T (3) = 4a
T (4) = 8a
T (5) = 16a
T (6) = 32
...

T(n) = 2T(n)

T(n) = 2

n-1

a
n

El orden complejidad para un algoritmo con estas caractersticas es O (2 ).

Ejemplo 4

A continuacin se muestra un algoritmo recursivo del cual se va a deducir cual es su tiempo de


ejecucin y su orden de complejidad.

Mtodo

public int recursivo4( int n )


{
int top = 4;
if (n <= 1)
{
return 1;
}

80

else
{
return n*(recursivo4(n-1) +
recursivo4(n-1) + recursivo4(n-1));
}
}
En el caso base se tiene que para el peor de los casos el tiempo de ejecucin es: T (1) = 4.
Para el caso inductivo se tienen 3 llamadas recursivas, cada una de (n-1), por lo tanto el caso
inductivo es: T(n) = 3T(n-1)+4.
Generalizando tenemos, que el caso base y el caso inductivo, quedan expresados de la
siguiente manera:
T (1) = 4
T(n) = 3T(n - 1) + 4
T (1) = a (se reemplaza por una constante)
T(n) = 3T(n - 1) + b
Reemplazando la base en la induccin, tenemos:
T (2) = 3T (1) + b
T (3) = 3T (2) + b
T (4) = 3T (3) + b
T (5) = 3T (4) + b

T (n) = 3T (n - 1) + b

T (2) = 3a + b
T (3) = 3(3a + b) + b = 9a + 4b
T (4) = 3[3(3a + b) + b] + b = 27a + 13b
T (5) = 3[3[3(3a + b) + b] + b] + b
= 81a + 27b + 9b + 3b + b
n-1

n-2

n-3

T (n) = 3 a + [3 b + 3 b +: : : + b]

Generalizando, tenemos:
T (n) = 3T (n - 1) + b

T (n) = 3

n+1

a+

)
n

El orden complejidad para un algoritmo con este caso base e inductivo es de orden O (3 ).

ACTIVIDAD

Deduzca usando recurrencias por induccin el tiempo de ejecucin y el orden de complejidad


del siguiente algoritmo recursivo.

Mtodo

public int recursivo( int n )


{
if ( n <= 1 ){
return 1;
}
else
{
for( i = 1 ; i <= n ; i++ )
{
j = j + 1;
}
return ( recursivo(n - 1) +
recursivo(n - 1) );
}
}

81

Deduzca usando recurrencias por induccin el tiempo de ejecucin y el orden de complejidad


del siguiente algoritmo recursivo.

Mtodo

public int recursivo( int n )


{
int j = 0;
if ( n <= 1 ){
return 1;
}
else
{
int k = 1
while ( k <= n)
{
j = j + 1;
k++;
}
return ( recursivo(n-1) +
recursivo(n-1) + recursivo(n-1) );
}
}

1.12.4 Resolucin de Recurrencias por sustitucin


Consiste en sustituir las recurrencias por su igualdad hasta llegar a cierta T(n) que sea
conocida. A continuacin, se muestra una serie de algoritmos recursivos que permiten deducir
su orden de complejidad.

Ejemplo 1

Este algoritmo retorna la cantidad de nmeros divisibles tanto por 2 y por 3. Se analizar su
tiempo de ejecucin y su orden de complejidad.

Mtodo

public int divisible ( int datos [ ], int n )


{
if(n==0)
{
if(datos[n] % 2==0)||datos[n] % 3==0)
{
return 1;
}
else
{
return 0;
}
}
else
{
if(datos[n] % 2==0|| datos[n] % 3==0)
{
return 1 + divisible(datos,n-1);
}
else
{
return divisible(datos, n-1);
}
}
}

82

Podemos deducir lo siguiente:


T(n) = c
T(n - 1) = c1

, si n 1
, si n > 1

T (n) = T (n - 1) + c1
= (T (n - 2) + c1) + c1 = T (n - 2) + 2c1
= (T (n - 3) + c1) + 2c1 = T (n - 3) + 3c1
= (T (n - 4) + c1) + 3c1 = T (n - 4) + 4c1
= (T (n - 5) + c1) + 4c1 = T (n - 5) + 5c1
= (T (n - q) + nc1
Cuando q = n - 1, entonces T(n) = T (1) + c1(n - 1), por lo tanto el orden de complejidad una vez
resuelta la relacin de recurrencia es O(n).

Ejemplo 2

Se va a analizar el siguiente algoritmo recursivo.

Mtodo

public int recursivo( int n )


{
if ( n <= 1 )
{
return 1;
}
else
{
return (recursivo(n-1) + recursivo(n-1));
}
}

A continuacin se analiza el orden de complejidad del algoritmo.


T(n) = 1
, si n 1
2T(n - 1) + 1 , si n > 1
T(n) = 2 * T(n - 1) + 1
2
2
= 2 * (2 * T(n - 2) + 1) + 1 = 2 * T(n - 2) + (2 - 1)
...
k
k
= 2 * T(n - k) + (2 + 1)
n-1

Para k = n - 1, T(n) = 2 , T (1) + 2

n-1

- 1 y por lo tanto T(n) es O (2 ).

Ejemplo 3

Se va a analizar el siguiente algoritmo recursivo.

Mtodo

public int recursivo( int n )


{
if ( n <= 1 ){
return 1;
}
else
{
return ( 2 * recursivo( n / 2 ) );
}
}

83

A continuacin se muestra otra relacin de recurrencia que permite encontrar el orden de


complejidad de algn algoritmo recursivo.
T(n) = 1
T(n/2) + 1

, si n 1
, si n > 1

T (n) = T (n/2) + 1
2
= T (n/2 ) + 1
3
= T (n/2 ) + 1
4
= T (n/2 ) + 1
k
= T (n/2 ) + n
k

n/2 = 1, se resuelve para k = log2(n), tenemos por lo tanto que el tiempo de ejecucin es T(n) =
T(1)+log2(n-1) y por lo tanto T(n) es O(log2(n)).

Ejemplo 4

Se va a analizar el siguiente algoritmo recursivo.


public int recursivo2 ( int n, int b )
{
if ( n <= 1)
{
return 1;
}
else
{
int i = 1
while ( i <= n)
{
b = b + 1;
i++;
}
return recursivo2 ( n - 1, b );
}
}

Mtodo

T(n) = T(n - 1) + n
= (T(n - 2) + (n - 1)) + n
= ((T(n - 3) + (n - 2)) + (n - 1) + n
...
= T (n - k) + (
)
Si k = n - 1,
T (n) = T (1) +

) = 1+ (

( ) +

( )) = 1+n (n-1)-(n-2) (n-1)/2.


2

Por lo tanto este algoritmo recursivo tiene un orden de complejidad O (n ).


Finalmente se resalta la existencia de otro mtodo para resolver recurrencias denominado el
mtodo maestro. El cual segn (Lpez, 2003) es una receta para resolver recurrencias de la
forma T(n) = aT(n/b) + f(n), a >= 1, b > 1; las cuales son tpicas de algoritmos de dividir
conquistar:

Dividir problema de tamao n en a subproblemas de tamao n/b.


Dividir y combinar = f(n)

84

ACTIVIDAD

Deduzca usando recurrencias por sustitucin el tiempo de ejecucin y el orden de complejidad


del siguiente algoritmo recursivo.

Mtodo

public int razon ( int n, int m )


{
if ( m == 0 )
{
return a;
}
return razon ( b, n/2 ) + razon ( b, n/2 );
}

Deduzca usando recurrencias por sustitucin el tiempo de ejecucin y el orden de complejidad


del siguiente algoritmo recursivo.

Mtodo

public int medio ( int a, int b )


{
if ( a == 1 )
{
return 1;
}
else
{
if ( b == 2 )
{
return a;
}
else
{
return medio ( a, b / 2 );
}
}
}

1.13 Anlisis de Mtodos de Ordenamiento


El objetivo de los algoritmos de ordenamiento es organizar una secuencia de datos, sea cual
sea su tipo. Este tema ha sido ampliamente estudiado y aplicado en diferentes contextos de las
ciencias de la computacin. Se analizarn los algoritmos de ordenamiento considerados como
clsicos, adems se estudiar la eficiencia de cada uno de ellos por medio de la deduccin de
sus rdenes de complejidad.
Algunos de ejemplos de aplicacin de estos algoritmos pueden ser:
o
o
o

Lista ordenada por apellido de estudiantes del programa de Ingeniera de


Sistemas.
Un directorio telefnico de usuarios.
Un reporte de las ventas totales para cada uno de los meses del ao.

A continuacin se muestran algunos algoritmos de ordenamiento:

85

1.13.1 Mtodo de Ordenamiento ShakerSort


Conocido como el mtodo de la Sacudida, es una mejora del mtodo de la burbuja. El
funcionamiento de este algoritmo consiste en mezclar las dos formas en que se puede realizar
el mtodo de burbuja. En este algoritmo cada pasada tiene dos etapas. En la primera etapa de
arriba hacia abajo se trasladan los elementos ms pequeos hacia la parte de arriba del
arreglo, almacenando en una variable la posicin del ltimo elemento intercambiado.
En la segunda etapa de abajo hacia arriba se trasladan los elementos ms grandes hacia la
parte de abajo del arreglo, almacenando en otra variable la posicin del ltimo elemento
intercambiado. Las pasadas sucesivas trabajan con los componentes del arreglo comprendidos
entre las posiciones almacenadas en las variables. El algoritmo termina cuando la variable que
almacena el extremo de arriba del arreglo es mayor que el contenido de la variable que
almacena el extremo de abajo.

public void shakerSort( int x[ ] )


{
int aux;
int primero = 1;
int ultimo = x.length - 1, dir = x.length - 1;
while ( ultimo >= primero )
{
for( int i = ultimo ; i >= primero ; i--)
{
if ( x [i - 1] > x[ i ] )
{
aux = x[i - 1];
x[i - 1] = x[ i ];
x[ i ] = aux;
dir = i;
}
}
primero = dir + 1;
for( int i = primero ; i <= ultimo; i++ )
{
if ( x [i - 1] > x[ i ] )
{
aux = x[i - 1];
x[i - 1] = x[ i ];
x[ i ] = aux;
dir = i;
}
}
ultimo = dir - 1;
}

Mtodo

Anlisis del
mtodo de
ordenamiento
Orden de
complejidad

El nmero de elementos comparados en la primera etapa es (n - 1)


+ (n - 2), en la segunda fase es (n-3) + (n-4), y as sucesivamente.
Sumando todas las fases se obtiene (suma desde i = 0 hasta n - 1),
i=n*(n*1)/2.
2

O(n )

86

ACTIVIDAD

Ahora que conoce algunos algoritmos de ordenamiento, ordene la siguiente secuencia


basndose en el mtodo de ordenamiento shakerSort.
148

73

485

485

202

3005

831

407

1.13.2 Mtodo de Ordenamiento Burbuja


Este mtodo consiste en la comparacin por parejas adyacentes e intercambiarlas de acuerdo
al orden que desee darse. Este proceso se repite hasta que el conjunto de datos est
completamente ordenado. Se llama burbuja, pues consiste en flotar hasta el final del arreglo el
elemento mayor en cada iteracin.

Mtodo

Anlisis del
mtodo de
ordenamiento
Orden de
complejidad

public void burbuja( int arreglo[] )


{
int temp, j, i;
for( j = 1 ; j < arreglo.length ; j++)
{
for( i = 0 ; i<arreglo.length-1 ; i++)
{
if ( arreglo[ i ] > arreglo[i+1] )
{
temp = arreglo[ i ];
arreglo[i] = arreglo[i + 1];
arreglo[i+1] = temp;
}
}
}
}
2

T(n) = 8n + 12n + 9
2

O(n )

1.13.3 Mtodo de Ordenamiento por Seleccin


Este mtodo de seleccin tiene este nombre puesto que la manera de realizar el ordenamiento
es seleccionando en cada iteracin el menor elemento del arreglo e intercambiarlo en la
posicin correspondiente. Inicialmente se encuentra el menor elemento dentro del arreglo y se
intercambia con el primer elemento del arreglo. Luego, desde la segunda posicin del arreglo
se busca el menor elemento y se intercambia con el segundo elemento del arreglo.

87

As sucesivamente se busca cada elemento de acuerdo a su ndice i y se intercambia con el


elemento que tiene el ndice k. Se ordenaran los elementos del arreglo de forma ascendente.

Mtodo

public void seleccion (int arreglo[] )


{
int i, j, k, menor;
i = 0;
while( i < arreglo.length - 1)
{
menor = arreglo [i];
k = i;
for( j = i+1; j < arreglo.length; j++)
{
if (arreglo [j] < menor )
{
menor = arreglo [j];
k = j;
}
}
arreglo [k] = arreglo[i];
arreglo [i] = menor;
i++;
}
}

Anlisis del
mtodo de
ordenamiento
Orden de
complejidad

O(n )

ACTIVIDAD

Ordene la siguiente secuencia basndose en el mtodo de ordenamiento seleccin.


748

173

8425

85

221

305

6371

5407

1.13.4 Mtodo de Ordenamiento Insercin


Este mtodo consiste en tomar cada uno de los elementos e insertarlos en una seccin del
conjunto de datos que ya se encuentra ordenado. El conjunto de datos que ya se encuentra
ordenado se inicia con el primer elemento del mismo. Este mtodo comnmente se asocia con
la forma de organizar un juego de cartas.

88

Mtodo

public void insercion( int arreglo[] )


{
int i, llave;
for ( int j = 1 ; j < arreglo.length ; j++ )
{
llave = arreglo[ j ];
i = j - 1;
while( i >= 0 && arreglo[ i ] > llave )
{
arreglo[i + 1] = arreglo[ i ];
i--;
}
arreglo[i + 1] = llave;
}
}

Anlisis del
mtodo de
ordenamiento
Orden de
complejidad

O(n )

ACTIVIDAD

Establezca tres diferencias relativas en trminos de eficiencia entre los mtodos de


ordenamiento por seleccin y por insercin.
Ordenamiento por Insercin

Ordenamiento por Seleccin

1.13.5 Mtodo de Ordenamiento QuickSort.


Es uno de los mtodos de ordenamiento ms conocidos y utilizados para el ordenamiento. Su
eficiencia es reconocida y los recursos computacionales que consume es mejor que muchos
algoritmos de ordenamiento. La descripcin del funcionamiento del algoritmo, consiste en que
inicialmente el arreglo a ordenar se divide en dos subarreglos los cuales se van ubicando de tal
forma que ellos todos los elementos del primer subarreglo son menores que los elementos del
segundo subarreglo. Se tiene un elemento pivote el cual se le asigna el valor del medio del
tamao del arreglo, este elemento pivote realiza una actividad de particin en la cual los
elementos menores estn a la izquierda y los elementos mayores esta su derecha.

89

Posteriormente, cada uno de los subarreglos es ordenado independientemente mediante


llamados recursivos. Cada subarreglo se ordena por aparte. Esta eleccin tambin puede
realizarse al inicio de la lista o arreglo, o al final del arreglo. Este algoritmo inicialmente
compara todos los elementos y despus de ello empieza a dividir recursivamente la lista o el
arreglo.
A continuacin se muestra la implementacin del mtodo QuickSort.
public void quickSort( int a[] )
{
quickSort( a, 0, a.length - 1 );
}

Mtodo

public void quickSort( int a[], int limInferior,


int limSuperior )
{
int i = limInferior;
int j = limSuperior;
int pivote = a[(limInferior+limSuperior)/2];
do
{
while( a[ i ] < pivote )
{
i++;
}
while( a[ j ] > pivote )
{
j--;
}
if (i <= j)
{
int aux = a[ i ];
a[ i ] = a[ j ];
a[ j ] = aux;
i++;
j--;
}
}
while (i <= j);
if ( j > limInferior )
{
quickSort(a, limInferior, j );
}
if ( i < limSuperior )
{
quickSort(a, i, limSuperior );
}
}
Se tienen las siguientes expresiones de base y de induccin.
T(1) = a
T(n) = 2T(n/2) + O(n)

Orden de
complejidad

Reemplazando el caso base por una constante, tenemos que:


T(1) = a
T(n) = 2T(n/2) + bn

90

Utilizando el reemplazo de la base en la induccin:


T(1) = a
T(2) = 2T(1) + 2b = 2a + 2b
T(4) = 2T(2) + 4b = 4a + 8b
T(8) = 2T(4) + 8b = 8a + 24b
T(16) = 2T(8) + 4b = 16a + 64b
Por lo tanto el tiempo de ejecucin est dado por:
T(n) = an + nlog(n)b

ACTIVIDAD

Ordene la siguiente secuencia basndose en el mtodo de ordenamiento quickSort.


884

237

183

245

122

2305

1131

212

1398

658

23

Investigue y posteriormente explique el funcionamiento y el tiempo de ejecucin para el mtodo


de ordenamiento RadixSort.

1.13.6 Mtodo de Ordenamiento ShellSort.


El mtodo de ordenamiento Shell es una versin mejorada del mtodo de Insercin directa.
Recibe su nombre en honor a su autor, Donald L. Shell, quien lo propuso en 1959. El objetivo
de este mtodo es ordenar un arreglo formado por n enteros. Inicialmente ordena subgrupos de
elementos separados k unidades (respecto de su posicin en el arreglo) del arreglo original. El
valor k es llamado incremento (Weiis, 2002).

91

Despus que los primeros k subgrupos han sido ordenados, se escoge un nuevo valor de k
ms pequeo, y el arreglo es de nuevo partido entre el nuevo conjunto de subgrupos. Cada
uno de los subgrupos mayores es ordenado y el proceso se repite de nuevo con un valor ms
pequeo de k. Eventualmente, el valor de k llega a ser 1, de tal manera que el subgrupo
consiste de todo el arreglo ya casi ordenado.

Mtodo

Anlisis del
mtodo de
ordenamiento

Orden de
complejidad

public void shellSort(int a[])


{
for (int incr = a.length/2; incr>0; incr/= 2 )
{
for (int i = incr ; i < a.length ; i++ )
{
int j = i - incr;
while (j >= 0)
{
if (a[j] > a[j + incr])
{
int T = a[ j ];
a[ j ] = a[j+incr];
a[j+incr] = T;
j -= incr;
}
else
{
j = -1;
}
}
}
}
}
Al principio del proceso se escoge la secuencia de decrecimiento
de incrementos; el ltimo valor debe ser 1. Cuando el incremento
toma un valor de 1, todos los elementos pasan a formar parte del
subgrupo y se aplica insercin directa. El mtodo se basa en tomar
como salto n/2 (siendo n el nmero de elementos) y luego se va
reduciendo a la mitad en cada repeticin hasta que el salto o
distancia vale 1.
2

O(n )

ACTIVIDAD
Ordene la siguiente secuencia basndose en el mtodo de ordenamiento shellSort.
2824

122

1832

435

128

92

3505

1599

776

1.13.7 Mtodo de Ordenamiento StoogeSort


Se trata de un algoritmo recursivo que realiza una particin en tres llamadas recursivas para
llevar a cabo el ordenamiento. A continuacin se muestra la implementacin de este mtodo de
ordenamiento.

Mtodo

Anlisis del
mtodo de
ordenamiento

Orden de
complejidad

public void stoogeSort( int a[] )


{
stoogeSort( a, 0, a.length - 1 );
}
public void stoogeSort(int a[], int i, int j )
{
int k;
if( a[ i ] > a[ j ])
{
int temp;
temp = a[ i ];
a[ i ] = a[ j ];
a[ j ] = temp;
}
if( i + 1 >= j )
{
return;
}
k = ( j - i + 1 ) / 3;
stoogeSort( a, i , j - k );
stoogeSort( a, i + k, j );
stoogeSort( a, i , j - k );
}
Si el valor del final es ms pequeo que el valor en el comienzo,
entonces, se intercambian. Si hay dos o ms elementos en la lista
actual, entonces:
Ordena los dos tercios iniciales de la lista.
Ordena los dos tercios finales de la lista.
Ordena los dos tercios iniciales de la lista nuevamente.

Este algoritmo de ordenamiento recursivo posee un orden de


2,7
complejidad de O(n ). El valor exponencial exacto es:
O(log(3)/log(1,5)

El algoritmo StoogeSort es un algoritmo de ordenamiento ineficiente que cambia los elementos


de la parte superior e inferior si es necesario, luego, recursivamente, ordena las dos terceras
partes inferiores, las dos terceras partes superiores, y nuevamente las dos terceras partes
inferiores.
Este es un algoritmo de Dividir y conquistar. El caso general para el algoritmo dividir y
conquistar tiene el siguiente principio base. Principio Base: El arreglo se clasifica en pedazos
de 2/3 de los elementos totales (primer 2/3, ltimo 2/3, primer 2/3 ) y el tamao del arreglo que
es calificado se trae abajo a dos elementos recursivamente.

1.14 Mtodos de Bsqueda


Los algoritmos de bsqueda tienen como objetivo fundamental el ubicar un objeto dentro de un
conjunto de datos existentes y determinar su ubicacin dentro de ese conjunto en caso de ser
una bsqueda satisfactoria. Se considera a la bsqueda como un problema de recuperacin de

93

datos. Existen diferentes algoritmos que resuelven el problema de la bsqueda, cada uno de
ellos tiene precondiciones que deben ser tenidas en cuenta para su aplicacin. Por ejemplo
para muchos de ellos es necesario que los elementos dentro de la estructura de datos estn
ordenados, en cambio para otros algoritmos este orden no es importante.
Estos tipos de algoritmos son de uso frecuente en contextos en los cuales se lleva a cabo
procesamiento de informacin. Estos pueden usarse para solucionar diferentes problemas,
entre algunos de ellos podemos citar:

Encontrar un cliente de una empresa teniendo en cuenta el nmero de su cdula.


Verificar si un estudiante se encuentra a paz y salvo con la universidad dando su
cdigo.
Encontrar el vendedor ms eficiente de un producto en un determinado mes.

A continuacin se tiene la implementacin de algoritmos de bsqueda. Cada uno de los


algoritmos de bsqueda ellos tendr un anlisis de su funcionamiento.

1.14.1 Bsqueda Lineal Iterativa


La bsqueda lineal consiste en encontrar un elemento sobre un conjunto de datos
comparndolos uno a uno en el orden en el que estos se encuentren. En esta primera
implementacin se recorre el arreglo de inicio a fin y se retorna verdadero si el elemento se
encuentra de lo contrario retornar falso.
La bsqueda lineal se puede realizar sobre estructuras de datos lineales (arreglos, listas,
arrays), para nuestro caso, utilizaremos un arreglo unidimensional de elementos. Pero la
bsqueda tambin puede ser extendida a otros tipos de datos, as como tambin a objetos.

Mtodo

Anlisis del
mtodo de
bsqueda

Orden de
complejidad

boolean busquedaIterativa(int arreglo[],int dato )


{
boolean estado = false;
for( int i = 0 ; i < arreglo.length ; i++ )
{
if ( arreglo [ i ] == dato )
{
estado = true;
}
}
}
Se observa que se tiene un ciclo for que recorre el arreglo desde la
posicin 0, hasta la posicin n - 1 del arreglo, por lo tanto se tiene
O(n). El condicional contiene una comparacin del elemento en la
posicin del arreglo y del elemento que se busca. El peor caso,
ser que esta comparacin sea verdadera en la ltima comparacin
o que nunca sea verdadera.
En el caso que el elemento que se esta buscando este en las
primeras posiciones del arreglo, el orden de complejidad es O(1),
pero esto nunca sucede en esta implementacin, el orden de
complejidad de la bsqueda lineal es O(n).

A continuacin, mostraremos como se busca un objeto utilizando la bsqueda lineal.


Inicialmente se tiene un arreglo con 8 elementos, y se esta buscando el nmero 21. La
siguiente figura muestra los nmeros que se encuentran dentro del arreglo.

94

Se busca el nmero 21 y se empieza por el primer elemento del arreglo, se compara el valor
que se encuentra en la posicin con el valor 21.

Se sigue realizando la bsqueda al segundo elemento del arreglo, el cual se encuentra en la


posicin 1, como an no se encuentra el elemento, continua la bsqueda.

Se sigue realizando la bsqueda al tercer elemento del arreglo, el cual se encuentra en la


posicin 2, como aun no se encuentra el elemento, continua la bsqueda.

Continua la bsqueda hacia el cuarto elemento del arreglo, el cual se encuentra en la posicin
3, al momento de realizar la comparacin, se tiene que el elemento buscado se encuentra
dentro del arreglo, lo que nos indicara que la bsqueda fue satisfactoria.

Esta implementacin tiene que aun encontrando el elemento dentro del arreglo, se contina
realizando las comparaciones que permite el tope superior del lmite del ciclo.

Existen otras implementaciones de la bsqueda lineal que pueden ser ms eficientes en


trminos de tiempos de ejecucin.

1.14.2 Bsqueda Lineal Limitada


Una segunda solucin al problema de la bsqueda, permite incorporar una mejora, la cual
consiste en una vez que se encuentre el elemento dentro del arreglo, se termine la ejecucin
del mtodo. As se garantiza que no se sigan realizando comparaciones innecesarias dado que
se encontr el dato.

95

Mtodo

boolean busquedaLineal(int arreglo[], int dato)


{
for( int i = 0 ; i < arreglo.length ; i++ )
{
if ( arreglo [ i ] == dato )
{
return true;
}
}
return false;
}

Anlisis del
mtodo de
bsqueda

Se tiene un ciclo for que recorre para el peor caso el arreglo


desde la posicin 0, hasta la posicin n - 1 del arreglo, por lo tanto
se tiene O(n).
Para el caso en el cual consideramos que el elementos que se est
buscando est en las primeras posiciones del arreglo, se tiene un
orden de complejidad O(1).

Orden de
complejidad

O(n)

1.14.3 Bsqueda Lineal Iterativa con extremos


Existen mtodos de bsqueda en los cuales es necesario que los elementos que se encuentran
dentro del arreglo, conserven un determinado orden de relacin entre ellos. Para aplicar la
bsqueda lineal analizando extremos es necesario que los elementos dentro del arreglo se
encuentren ordenados ascendentemente.
Para esta primera implementacin si el elemento que se encuentra en la primera posicin del
arreglo es mayor que el dato a buscar, el algoritmo termina y retorna falso. Lo mismo sucede si
el ltimo elemento dentro del arreglo es menor que el dato que se est buscando. Con estas
comparaciones se puede evitar realizar una comparacin si los valores se encuentran por fuera
del rango de valores.

Mtodo

boolean busquedaExtremos (int arreglo[],int dato )


{
boolean estado = false;
int n = arreglo.length;
if (dato < arreglo[0])
{
return false;
}
if ( dato > arreglo[n-1])
{
return false;
}
for(int i = 0; i < n ; i++)
{
if(arreglo[i] == dato)
{
estado = true;
break;
}
}
return estado;
}

96

Anlisis del
mtodo de
bsqueda

Orden de
complejidad

En el caso base se tienen dos condicionales que evalan si el


nmero a buscar se encuentra en el rango de los valores que se
encuentran dentro del arreglo. El orden de complejidad de ambos
condicionales es O(1)
Se tiene un ciclo for que recorre el arreglo desde la posicin 0,
hasta la posicin n-1 del arreglo, por lo tanto se tiene O(n). Lo
anterior asumiendo que el elemento se encuentra en las ltimas
posiciones del arreglo, o que no se encuentre dentro de este
O(n)

1.14.4 Bsqueda Binaria


Para este tipo de bsqueda se debe trabajar con un arreglo ordenado de elementos (nica
situacin en la que es posible, aplicar la bsqueda binaria). Este algoritmo es ms eficiente
que el de bsqueda lineal. Inicialmente se verifica si el elemento que se est buscando se
encuentra en la mitad del arreglo, en caso de no ser este dato el que se busca, entonces se
compara con el elemento que se est buscando.
Si el elemento que se busca es mayor, se redefine el lmite del arreglo desde el siguiente
elemento al del centro hasta el final del arreglo. En el caso en que el elemento que se busca
sea menor, el intervalo se toma desde la mitad del arreglo hasta el principio del arreglo.
Cada vez que se realiza una comparacin dentro del ciclo y mientras en lmite inferior no sea
mayor que el lmite superior, el reduce el conjunto de entrada a la mitad, es decir, el arreglo
descarta la mitad de los elementos que estn por fuera del rango. El ciclo se deja de ejecutar
cuando se retorna un valor ya sea falso o verdadero.
A continuacin se muestra una implementacin para la bsqueda binaria.
boolean busquedaBinaria( int arreglo[], int dato )
{
int centro;
int limSup=arreglo.length-1,;
int limInf=0;

Mtodo

while ( true )
{
centro = (limSup + limInf) / 2;
if( limInf > limSup )
{
return false;
}
else
{
if ( arreglo[ centro ] < dato )
{
limInf = centro + 1;
}
else
{
if (arreglo [centro] > dato)
{
limSup = centro - 1;

97

}
else
{
return true;
}
}
}

Anlisis del
mtodo de
bsqueda

Orden de
complejidad

}
}
Cuando se realiza una comparacin en la cual se busca el nmero,
se encuentra en caso de no ser cierto, que el conjunto de datos se
divide a la mitad. Si el tamao del arreglo es n, se va reduciendo
n
por cada comparacin a n/2, n/4, n/8,., n/2
hasta llegar a
cualquiera de los lmites del arreglo, por lo anterior se puede
deducir que el algoritmo de bsqueda lineal tiene un orden de
complejidad de orden O(log(n)).

O(log(n)).

ACTIVIDAD

Dado el siguiente algoritmo, analice si es un algoritmo correcto para realizar bsquedas, en


caso de serlo, explique la forma de funcionamiento de dicho algoritmo.

Mtodo

boolean busqueda( int arreglo [], int dato )


{
return busqueda(arreglo,dato,arreglo.length-1 );
}
boolean busqueda(int arreglo[],int dato,int pos )
{
if ( pos < 0 )
{
return false;
}
if ( arreglo [ pos ] == dato )
{
return true;
}
else
{
return busqueda(arreglo,dato,pos-1);
}
}

Anlisis del
mtodo de
bsqueda
Orden de
complejidad

1.15 Caso de Estudio: Registro de notas


Se desea crear una Aplicacin que permita a un docente llevar un registro completo de las
notas de sus estudiantes. Se requiere que se pueda agregar un estudiante. Un estudiante tiene
asociado un nombre, un apellido y dos notas parciales. Se debe permitir generar el listado de

98

estudiantes ordenado por nota 1, nota 2 o promedio. De igual forma se debe informar la nota
definitiva que ms se repite (moda) y ser posible localizar el primer estudiante que posea la
nota definitiva ingresada por el usuario.

a. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
GrupoEstudiante

DESCRIPCIN

Estudiante

b. Mtodos de la clase Estudiante.


NOMBRE
calcularDefinitiva()

DESCRIPCIN

c. Mtodos de la clase GrupoEstudiante.


IDENTIFICACIN DE MTODOS
NOMBRE
DESCRIPCIN
agregarEstudiante(String nombre,
String apellido, double nota1,
double nota2)
ordenarPorBurbuja()
ordenarPorInsercion()
ordenarPorSeleccion()
obtenerDefinitivaModa()
busquedaSecuencial(double nota)
busquedaBinaria(double nota)

99

d. Establecer las relaciones entre las clases.

e. Implementacin
A continuacin se muestra el principal mtodo de la clase Estudiante.
calcularDefinitiva()
/*Calcula la nota definitiva usando formato decimal de dos cifras */
public void calcularDefinitiva()
{
DecimalFormat formatoDecimal;
formatoDecimal = new DecimalFormat ( "0.0" );
String dato = formatoDecimal.format ( (nota1+nota2)/2 );
dato = dato.replace(',','.');
definitiva=Double.parseDouble(dato);
}

A continuacin se muestran los principales mtodos de la clase GrupoEstudiante.


Mtodo que ordena comparando elementos en
posiciones adyacentes
/**Permite ordenar por burbuja
* @return un array de String */
ordenarPorBurbuja()

public String[] ordenarPorBurbuja()


{
int i,j;
String array[]=new String[miEstudiante.size()];
Estudiante temp;
for (i=0; i<miEstudiante.size(); i++)
{
for (j=0; j<miEstudiante.size()-1;j++)
{
if (miEstudiante.get(j).getNota1() >
miEstudiante.get(j+1).getNota1())
{
temp = miEstudiante.get(j);
miEstudiante.set(j,miEstudiante.get(j+1));
miEstudiante.set(j+1,temp);
}
}
}

100

for(i=0; i<miEstudiante.size(); i++)


{
array[i]=miEstudiante.get(i).getNombre()+ " "+
miEstudiante.get(i).getApellido()+ " Nota 1: "+
miEstudiante.get(i).getNota1()+" Nota 2: "+
miEstudiante.get(i).getNota2();
}
return array;
}
ordenarPorInsercion()
/** @return un array de String */
public String[] ordenarPorInsercion()
{
String array[]=new String[miEstudiante.size()];
Estudiante temp;
int i,j;
for (i=0; i<miEstudiante.size(); i++)
{
temp = miEstudiante.get(i);
for (j=i; j>0;j--)
{
if( miEstudiante.get(j-1).getNota2()<=temp.getNota2())
{
break;
}
miEstudiante.set(j,miEstudiante.get(j-1));
}
miEstudiante.set(j,temp);
}
for(i=0; i<miEstudiante.size(); i++)
{
array[i]=miEstudiante.get(i).getNombre()+ "
"+miEstudiante.get(i).getApellido()+" Nota 1:
"+miEstudiante.get(i).getNota1()+" Nota 2:
"+miEstudiante.get(i).getNota2();
}
return array;
}
ordenarPorSeleccion()
/** @return un array de String */
public String [] ordenarPorSeleccion()
{
String array[]=new String[miEstudiante.size()];
Estudiante temp;
int i,j;
for (i=0; i<miEstudiante.size()-1; i++)
{
for (j=i+1; j<miEstudiante.size();j++)
{
if ( miEstudiante.get(i).getDefinitiva()>
miEstudiante.get(j).getDefinitiva())
{

101

temp = miEstudiante.get(i);
miEstudiante.set(i,miEstudiante.get(j));
miEstudiante.set(j,temp);
}
}
}
for(i=0; i<miEstudiante.size(); i++)
{
array[i]=miEstudiante.get(i).getNombre()+ "
"+miEstudiante.get(i).getApellido()+ " Nota 1:
"+miEstudiante.get(i).getNota1()+" Nota 2:
"+miEstudiante.get(i).getNota2()+" Def:
"+miEstudiante.get(i).getDefinitiva();
}
return array;
}
obtenerDefinitivaModa()
public double obtenerDefinitivaModa()
{
int contadorOcurrencias=0,cantidad=0,i,j,posicion=0;
for (i=0; i<miEstudiante.size(); i++)
{
for(j=0; j<miEstudiante.size(); j++)
{
if(i!=j)
{
if(miEstudiante.get(i).getDefinitiva()==
miEstudiante.get(j).getDefinitiva())
{
contadorOcurrencias++;
}
}
}
if(cantidad<contadorOcurrencias)
{
posicin =i;
cantidad=contadorOcurrencias;
}
contadorOcurrencias=0;
}
if(miEstudiante.size()>0)
{
JOptionPane.showMessageDialog(null,"La que ms se repite "
+ miEstudiante.get(posicion).getDefinitiva());
return miEstudiante.get(posicion).getDefinitiva();
}
return -1;
}
busquedaSecuencial(double nota)
public Estudiante busquedaSecuencial(double nota)
{
DecimalFormat formatoDecimal;
formatoDecimal = new DecimalFormat ( "0.0" );
String dato = formatoDecimal.format ( nota );

102

dato = dato.replace(',','.');
nota = Double.parseDouble(dato);
for (int i=0; i<miEstudiante.size(); i++)
{
if(nota==miEstudiante.get(i).getDefinitiva())
{
return miEstudiante.get(i);
}
}
return null;
}
busquedaBinaria(double nota)
public Estudiante busquedaBinaria(double nota)
{
DecimalFormat formatoDecimal;
int inicio, fin, medio;
inicio=0;
fin=miEstudiante.size()-1;
ordenarPorSeleccion();
formatoDecimal = new DecimalFormat ( "0.0" );
String dato = formatoDecimal.format ( nota );
dato=dato.replace(',','.');
nota=Double.parseDouble(dato);
while(inicio<=fin)
{
medio=(inicio+fin)/2;
if(nota==miEstudiante.get(medio).getDefinitiva())
{
return (miEstudiante.get(medio));
}
else
{
if(nota>miEstudiante.get(medio).getDefinitiva())
{
inicio=medio+1;
}
else
{
fin=medio-1;
}
}
}
return null;
}

ACTIVIDAD
Una vez escritos los mtodos para la clase GrupoEstudiante, determinar su orden de
complejidad.

103

Mtodo

Orden de Complejidad
O(
)

encontrarPersona()
ordenarPorBurbuja()
ordenarPorInsercion()
obtenerDefinitivaModa()
busquedaSecuencial(double nota)
busquedaBinaria(double nota)

O(

O(

O(

O(

O(

Para el caso de estudio Registro notas, agregar el mtodo ordenar por shellSort.

En los requerimientos que se identificaron se pudo observar que es necesario implementar un


algoritmo que permita buscar un elemento dentro de un arreglo.
Una de las opciones que tenemos disponibles partiendo del hecho de que la informacin no
est ordenada es apoyarnos en el patrn de recorrido parcial. Un ejemplo de este tipo de
patrn es el siguiente:
public boolean encontrarPersona()
{
boolean centinela = false;
for( int i = 0; i < miEmpleado.length && centinela != true ; i++)
{
if( miEmpleado[ i ].getSalario() > 430000 )
{
centinela = true;
return centinela;
}
}
return centinela;
}

104

Observe que el ciclo se rompe cuando encuentra una persona que posee un salario superior a
430000. Intente construir ahora el algoritmo que permita encontrar la informacin del primer
estudiante que posea una determinada nota definitiva.
public Estudiante busquedaSecuencial(double nota)
{
for (int i=0; i<miEstudiante.size(); i++)
{

}
}

105

106

2.1

ESTRATEGIAS DE PROGRAMACIN

Introduccin

En los captulos previos se resalt la importancia de aplicar los conceptos matemticos al


anlisis de algoritmos. Al mimo tiempo es necesario reconocer que la construccin de un
algoritmo debe planearse adecuadamente de acuerdo a los parmetros definidos para tal fin.
La seleccin de un mtodo de implementacin de forma correcta es fundamental pues de ello
depender su desempeo en cuanto a eficiencia y efectividad.
Para seguir aplicando tcnicas correctas, se muestra a continuacin tcnicas conocidas para el
diseo de algoritmos. Los temas que se trabajarn en este capitulo son las tcnicas divide y
vencers, los algoritmos voraces y la programacin dinmica. En cada uno de ellos se
realizarn aplicaciones de problemas clsicos.

2.2

Algoritmos Divide y Vencers

La tcnica divide y vencers se utiliza dentro de la algoritmia cuando es necesario


descomponer un conjunto de datos en subjconjuntos de ese mismo tamao de entrada. Cada
uno de los subconjuntos se resuelve de forma independiente y finalmente de sus resultados se
construye la solucin final del conjunto de datos original. Esta tcnica resuelve los problemas
aplicando la recursividad.
A continuacin se muestra la aplicacin de algoritmos divide y vencers en contextos de
aplicacin considerados comunes dentro del anlisis de algoritmos.

2.2.1

Bsqueda Binaria

A continuacin se muestra la implementacin de forma recursiva de la bsqueda binaria, este


mtodo recibe por parmetro el arreglo ordenado y el elemento que se desea buscar dentro del
arreglo; adems de los valores tanto del lmite inferior del arreglo como del lmite superior del
arreglo. Para analizar la bsqueda binaria usaremos tambin el nmero de comparaciones que
necesitamos, en el peor caso, que se dar cuando no encontremos el elemento buscado o
este se halle en uno de los extremos del arreglo.
boolean binariaRecursiva ( int arreglo[], int dato )
{
return binRecursiva ( arreglo, dato, 0, arreglo.length-1);
}
boolean binRecursiva (int arreglo[], int dato, int limInf, int limSup)
{
int centro = (int)( (limSup + limInf) / 2);
if ( limInf > limSup )
{
return false;
}
else
{
if ( arreglo [ centro ] > dato )
{
return binRecursiva(arreglo, dato, limInf, centro-1 );
}

107

else
{
if (arreglo [centro]<dato)
{
return binRecursiva(arreglo,dato,centro+1,limSup );
}
else
{
return true;
}
}
}
}
Cuando se realiza una comparacin en la cual se busca el nmero, se encuentra en caso de no
ser cierto, que el conjunto de datos se divide a la mitad. Si el tamao del arreglo es n, se va
reduciendo por cada comparacin a n/2,n/4,n/8 n/2n, hasta llegar a cualquiera de los lmites
del arreglo, por lo anterior se puede deducir que el algoritmo de bsqueda lineal tiene un orden
de complejidad de orden O(log(n)).
La anterior implementacin se basa en la tcnica divide y vencers ya que para solucionar el
problema necesariamente se tienen que realizar llamadas recursivas al mtodo de
implementacin.

2.2.2

Ordenamiento por el mtodo MergeSort

Este mtodo se basa en la tcnica de Divide y Vencers ya que toma el arreglo original de
datos y lo divide en dos partes del mismo tamao, lo sigue dividiendo hasta que slo se tenga
un elemento. Cada una de estas divisiones es ordenada de manera separada y posteriormente
fusionadas para formar el conjunto original ya ordenado. Este algoritmo divide inicialmente
la lista hasta su mnimo valor y luego si ordena el arreglo.

Mtodo

public void mergesort( int a[] )


{
mergesort ( a, 0, a.length -1 );
}
public void mergesort(int a[],int bajo, int alto )
{
int len, pivote, m1, m2;
if ( bajo == alto )
{
return;
}
else
{
len = alto - bajo + 1;
pivote = (bajo + alto) / 2;
mergesort( a, bajo, pivote );
mergesort( a, pivote + 1, alto );
int temp[] = new int[ len ];
for( int i = 0 ; i < len ; i++ )
{
temp[ i ] = a[bajo + i];
}
m1 = 0;
m2 = pivote - bajo + 1;

108

for( int i = 0 ; i < len ; i++ )


{
if( m2 <= alto - bajo )
{
if( m1 <= pivote - bajo )
{
if( temp[ m1 ]>temp[ m2
{
a[i+bajo]=temp[ m2++
}
else
{
a[i+bajo]=temp[ m1++
}
}
else
{
a[i+bajo] = temp[ m2++
}
}
else
{
a[i+bajo] = temp[ m1++ ];
}
}

] )
];

];

];

}
}
Se tienen las siguientes expresiones de base y de induccin.
T(1) = a
T(n) = 2T(n/2) + O(n)
Reemplazando el caso base por una constante, tenemos que:
T(1) = a
T(n) = 2T(n/2) + bn
Anlisis del
mtodo de
ordenamiento

Utilizando el reemplazo de la base en la induccin:


T(1) = a
T(2) = 2T(1) + 2b = 2a + 2b
T(4) = 2T(2) + 4b = 4a + 8b
T(8) = 2T(4) + 8b = 8a + 24b
T(16) = 2T(8) + 4b = 16a + 64b
Por lo tanto el tiempo de ejecucin est dado por:
T(n) = an + nlog(n)b

Orden de
complejidad

El trmino an es de orden O(n) y el trmino nlog(n)b es de orden


O(nlog(n)). Se observa que la funcin orden nlog(n) crece ms
rpidamente que la funcin n, por lo tanto es correcto decir que la
funcin n es de orden O(nlog(n)).

ACTIVIDAD

Dado el siguiente grfico, muestre la forma en la cual el mtodo de ordenamiento mergeSort


organiza el arreglo original. Las fechas indican que en ese momento la recursividad esta
ejecutndose.

109

Arreglo a Ordenar
5

2.2.3

Multiplicacin de Nmeros Grandes

La multiplicacin de enteros grandes es un tema de inters dentro del anlisis de algoritmos, a


priori puede parecer un tema sencillo de analizar teniendo en cuenta la gran capacidad que
poseen los tipos de datos para almacenar valores.
La siguiente tabla, muestra los rangos de valores y la cantidad de bits que cada una de estas
variables necesita para su ejecucin. Se observa que cuando se utiliza una variable de tipo int
son necesarios 32 bits.
Nombre
byte
short
int
long

Rango de valores
-128 a 127
-32768 a 32767
-2147483648 a 2147483647
-9223372036854775808 a
9223372036854775807

Tamao en bits
8
16
32
64

Declaracin
byte var1;
short var2
int
var3;
long

var4;

Mientras que si se utiliza una variable de tipo double, son necesarios 64 bits.
Nombre
float
double

Rango de valores
3,4E-38 a 3,4E38
1.7E-308 a 1.7E308

Tamao en bits
32
64

Declaracin
float num1;
double num2;

Con base en los anteriores valores es posible en determinadas situaciones que el resultado de
una multiplicacin de un nmero entero muy grande sobrepase la capacidad de un tipo de dato,
lo que puede llevar a resultados incorrectos. Cuando esta situacin sucede es posible apoyarse
en estructuras de datos dinmicas que ayudan a que una operacin de multiplicacin se realice
correctamente, esto, utilizando la tcnica de divide y vencers.
A continuacin se muestra de forma evolutiva como por medio de diferentes estrategias de
programacin se puede resolver el problema de la multiplicacin de nmeros grandes.

Algoritmo Clsico de Multiplicacin

El funcionamiento de este algoritmo esta fundamentado en la forma en que tradicionalmente


realizamos la multiplicacin, se toma cada una de las cifras del multiplicador y se multiplica

110

cada una de ellas por el multiplicando. Existe para ello las formas tradicionales de
multiplicaciones conocidas como la americana y la iglesia.
2

La complejidad computacional para el algoritmo de multiplicacin clsica es de O(n ), siendo n


el nmero de dgitos del numero mayor. Si ambos nmeros tienen dos nmero enteros n muy
2
2
grandes, el nmero de multiplicaciones es n lo que nos da el orden de complejidad O(n ).
A continuacin se muestra la implementacin con arreglos de un algoritmo clsico de
multiplicacin de nmeros enteros. En este mtodo se multiplica cada posicin de los dos
arreglos teniendo en cuenta el orden de cada uno de los elementos.
int[] Mult(int Num[],int tam1,int Num2[],int tam2)
{
int l=tam1+tam2-1,pos=tam1+tam2-1;;
int res[]= new int[tam1+tam2];

Mtodo

for(int y=0;y<=tam1+tam2-1;y++)
{
res[y]=0;
}
for( int i=tam1-1;i>=0;i--)
{
for( int j=tam2-1;j>=0;j--)
{
res[l]+=Num[i]*Num2[j];
if(res[l]>9)
{
res[l-1]+=res[l]/10;
res[l]=res[l]%10;
}
l--;
}
l=pos;
pos--;
l--;
}

Anlisis del
mtodo de
ordenamiento

return res;
}
2
El orden de complejidad de este mtodo de multiplicacin es O(n ),
esto teniendo en cuenta que los ciclos anidados iteran
completamente desde una posicin inicial hasta una posicin final.

Algoritmo Multiplicacin a la Rusa

La forma en la cual se lleva a cabo esta multiplicacin, sugiere varias metodologas. La ms


comn de ellas es crear dos columnas en la cual se escriben los dos nmeros a multiplicar. Se
debe repetir la siguiente secuencia hasta que el nmero de la columna izquierda sea un 1. Por
lo tanto la secuencia es la siguiente:
1. Dividir el nmero de la columna izquierda por dos, esta divisin es una divisin entera.
2. Una vez realizado el paso 1, duplicar el nmero de la columna derecha, sumndolo
consigo mismo.
3. Eliminar todas las filas en las cuales el nmero de la izquierda sea par.
4. Sumar los nmeros que quedan en la columna de la derecha.
A continuacin, se muestra un ejemplo que realiza una multiplicacin a la rusa.

111

Numero 1
221
110
55
27
13
6
3
1

Numero 2
1194
2388
4776
9552
19104
38208
76416
152832

Suma
1194
4776
9552
19104
76416
152832
263874

La ventaja de este algoritmo es que solo se realizan operaciones de suma y de divisin por 2. A
continuacin, se muestra una implementacin de este mtodo para multiplicar dos nmeros.

Mtodo

int multiplicacion(int m,int n)


{
int resultado = 0;
do{
if(m%2!=0)
{
resultado = resultado + n;
}
m = m/2;
n = n+n;
}while(m>=1);
return resultado;
}

Anlisis del
mtodo de
ordenamiento

Este mtodo se repite n, pero solo se realiza en los casos en los


cuales el multiplicador es un nmero impar. El orden de
2
complejidad para este caso es de O(n ).

Algoritmo Multiplicacin Divide y Vencers

Segn (Ramirez, 2003) este algoritmo cuando se aplica la tcnica de divide y vencers,
consiste de dividir tanto multiplicando como multiplicador en dos mitades, ambas con el mismo
nmero de cifras y adems que este nmero de cifras sea potencia de dos (si no los tienen, se
rellenan con ceros a la izquierda). El objetivo final de este algoritmo es efectuar a los ms 4
multiplicaciones, siguiendo los pasos que a continuacin se listan:
1. Multiplique la mitad izquierda del multiplicando (w), por la mitad izquierda del
multiplicador (y), el resultado desplcelo a la izquierda, tantas cifras tenga el
multiplicador.
2. Multiplique la mitad izquierda del multiplicando (w), por la mitad derecha del
multiplicador (z), el resultado desplcelo a la izquierda, la mitad de las cifras que tenga
el multiplicador.
3. Multiplique la mitad derecha del multiplicando (x), por la mitad izquierda del
multiplicador (y), el resultado desplcelo a la izquierda, la mitad de las cifras que tenga
el multiplicador.
4. Multiplique la mitad derecha del multiplicando (x), por la mitad derecha del multiplicador
(z), el resultado no lo desplace.
A continuacin se muestra la implementacin de la Multiplicacin divide y vencers.

112

public int[] dv1(int vec1[],int vec2[],int n)


{
int x[],y[],z[],w[],s[],t[],u[],res[],res2[],res3[],re[],auxr[],
.
auxs[], auxt[],auxu[]; int[] r;
//Caso trivial, donde se llama al algoritmo clasico
if(n==2)
{
re=new int[n+1];
re=Alg(vec1,n,vec2,n);
return re;
}
//Reserva memoria dinamica de la mitad del tamao de los arreglos
//iniciales para cada uno de los vectores componentes del algoritmo
else
{
x=new int[n/2];
y=new int[n/2];
z=new int[n/2];
w=new int[n/2];
for(int i=0;i<n/2;i++)
{
w[i]=vec1[i];
y[i]=vec2[i];
x[i]=vec1[i+(n/2)];
z[i]=vec2[i+(n/2)];
}
//r guarda la multiplicacion de los vectores "w" y "y"
r=new int[2*n];
auxr=new int[n];
//inicializamos r con ceros garantizando los ceros
iniceros(r,2*n);
auxr=dv1(w,y,n/2);
for(int i=0;i<n;i++)
{
r[i]=auxr[i];
}
//s guarda la multiplicacion de los vectores "w" y "z"
s=new int[n+(n/2)];
auxs=new int[n+(n/2)];
iniceros(s,n+(n/2));
auxs=dv1(w,z,n/2); //la formula
for(int i=0;i<n;i++)
{
s[i]=auxs[i];
}
//t guarda la multiplicacion de los vectores "x" y "y"
t=new int[(n/2)+n];
auxt=new int[n];
iniceros(t,(n/2)+n);
auxt=dv1(x,y,n/2);

113

for(int i=0;i<n;i++)
{
t[i]=auxt[i];
}
//u guarda la multiplicacin de los arreglos "x" y "z"
u=new int[n];
iniceros(u,n);
u=dv1(x,z,n/2);
res=new int[2*n];
iniceros(res,2*n);
res=suma(r,2*n,s,n+(n/2));//res guarda la suma de "r" y "s"
res2=new int[(n/2)+n];
iniceros(res2,(n/2)+n);
res2=suma(t,n+(n/2),u,n); //res2 guarda la suma de "t" y "u"
res3=new int[2*n];
iniceros(res3,2*n);
//res3 guarda la suma de "res" y "res2", es el resultado final
res3=suma(res,2*n,res2,(n/2)+n);
return res3;
}
}

La implementacin de este algoritmo se remite a la operacin de multiplicacin entre dos


nmeros de n cifras en 4 multiplicaciones de nmeros de n/2 cifras, pero su orden de
2
complejidad sigue siendo de O(n ) y por lo tanto no se evidencian mejoras en su eficiencia.

2.3

Algoritmos Devoradores

Los algoritmos devoradores son algoritmos que toman decisiones, partiendo de los datos que
tienen disponibles, ignorando las consecuencias que tales decisiones puedan tener. Esta
caracterstica los hace sencillos de disear e implementar. Poseen los siguientes elementos
caractersticos:

Conjunto de candidatos: Son los posibles elementos que pueden ser considerados.
Algunos de ellos sern seleccionados mientras que otros rechazados.
Funcin de solucin: Verifica si con los elementos seleccionados ya se ha completado
la solucin
Funcin de factibilidad: verifica si hay posibilidad de completar la solucin apoyndose
en los elementos seleccionados.
Funcin de seleccin: permite elegir el candidato ms prometedor. Generalmente
existe una relacin directa con la funcin objetivo relacionada con la funcin objetivo.
Por ejemplo si nuestra intencin es minimizar costos elegiremos el candidato ms
econmico.
Funcin objetivo: Entrega la solucin del problema

Los Algoritmos Voraces, tambin conocidos como algoritmos glotones, vidos o algoritmos
greedy, se emplean en problemas de optimizacin en los cuales se pretende maximizar o
minimizar algn valor. La codificacin de un algoritmo voraz se caracteriza por tener un bucle,
denominado bucle voraz.

114

Algunos algoritmos voraces se utilizan en problemas tales como:

2.3.1

Encontrar el rbol de recubrimiento mnimo de un grafo: por ejemplo encontrar la forma


de unir por carreteras varias ciudades.
o El algoritmo de Kruskal.
o El algoritmo de Prim.
Caminos mnimos en grafos: Consiste en encontrar la ruta ms corta posible para ir de
un lugar a otro. (Algoritmo de Dijkstra)
El problema de la mochila. Consiste en llenar una mochila de manera que el contenido
sea lo mximo posible.
El problema de planificacin de tareas. Estos problemas tratan de planificar varias
tareas o actividades de manera que se maximice la ganancia obtenida o el nmero de
tareas realizadas.
El problema del cambio de la devuelta. Consiste en conformar una tarea con el menor
nmero posible de monedas.

El Problema de la mochila

Una empresa transportadora requiere de una Aplicacin que permita llenar un camin con
objetos sin exceder el peso mximo soportado por ste, logrando una maximizacin de valor.
Cada objeto tiene un peso y un valor asociado. Es importante anotar, que los objetos pueden
fraccionarse. La aplicacin debe permitir:

Fijar el peso mximo soportado por el camin.


Agregar un nuevo objeto
Mostrar los objetos seleccionados
Mostrar el valor de la carga

a. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
Camion
Objeto

DESCRIPCIN
Es la clase principal del problema
Elemento
sobre
el
cual
se
realizan
principales operaciones

b. Relaciones entre las clases

115

las

c. Implementacin de las clases


A continuacin se muestra la implementacin de los mtodos fundamntales e la clase Objeto.
public class Objeto
{
private double peso;
private double valor;
private double cantidad;
public Objeto(double peso, double valor, double cantidad)
{
this.peso = _____________;
this.valor = ____________;
this.cantidad = _________;
}
public double getPeso()
{
return peso;
}
public void setPeso(double peso)
{
___________________
}
public double getValor()
{
_______________
}
public void setValor(double valor)
{
this.valor = valor;
}
public double getCantidad()
{
return cantidad;
}
public void setCantidad(double cantidad)
{
this.cantidad = cantidad;
}
}

A continuacin se muestran los mtodos ms importantes de la clase Camin.


import java.util.ArrayList;
import java.util.Collections;
public class Camion
{
double valor;
ArrayList <Objeto> misObjetos;
int cantidad[];
double pesoMaximo;

116

public ArrayList<Objeto> getMisObjetos()


{
return misObjetos;
}
public void setMisObjetos(ArrayList<Objeto> misObjetos)
{
this.misObjetos = misObjetos;
}
public Camion(double cambio)
{
misObjetos=new ArrayList<Objeto>();
valor=0;
this.pesoMaximo=cambio;
}
public void agregarObjeto(double peso, double valor)
{
MyComparator miComparador=new MyComparator();
misObjetos.add(new Objeto(peso,valor,0));
Collections.sort(misObjetos,miComparador);
}
public void realizarCambio()
{
double peso;
int i=-1;
peso = 0;
valor=0;
/* Los candidatos son los objetos La funcin solucin
verifica si el peso de los objetos elegidos es igual al
pesoMaximo. Un conjunto de objetos es factible si su peso
total no excede el peso mximo La funcin objetivo obtiene
el valor mximo de la mercanca del camin */
while(peso < pesoMaximo)
{
i++;
// misObjetos.get(i) Es la estrategia de seleccin se
// elige de menor a mayor peso
if(i<misObjetos.size()-1&& (peso +
misObjetos.get(i).getPeso() <= pesoMaximo))
{
misObjetos.get(i).setCantidad(1.0);
peso= peso + misObjetos.get(i).getPeso();
}
else
{
misObjetos.get(i).setCantidad((pesoMaximo-peso)/
misObjetos.get(i).getPeso());
peso=pesoMaximo;
}
}
}

117

public double obtenerValor()


{
double valor=0;
for(Objeto miObjeto:misObjetos)
{
valor+=(miObjeto.getValor()*miObjeto.getCantidad());
}
return valor;
}
public double getCambio()
{
return pesoMaximo;
}
public void setCambio(double cambio)
{
this.pesoMaximo = cambio;
}
public String[] listar() throws Exception
{
int i=0;
if(misObjetos.size()==0)
{
throw new Exception("Debe ingresar denominaciones antes
de poder realizar cambios");
}
else
{
realizarCambio();
String info[]=new String[misObjetos.size()+2];
for(i=0; i<misObjetos.size(); i++)
{
info[i]="OBJETO : "+i+" CANTIDAD:
"+misObjetos.get(i).getCantidad()+" PESO:
"+misObjetos.get(i).getPeso()+ " VALOR :
"+misObjetos.get(i).getValor();
}
return info;
}
}
public double getPesoMaximo()
{
return pesoMaximo;
}
public void setPesoMaximo(double pesoMaximo)
{
this.pesoMaximo = pesoMaximo;
}
}

118

ACTIVIDAD

Si se tienen los siguientes objetos y se desea llenar el camin de peso mximo 520 indique
cules y cuntos elementos fueron seleccionados y el valor de la carga obtenido gracias a sta
seleccin.
w
v

20
15

30
50

15
20

40
55

80
92

Se pudo observar que la estrategia de seleccin utilizada en la clase Camion fue minimizar wi.
Ahora se debe, resolver el punto anterior partiendo del hecho de que se desea maximizar v i.
Repita este procedimiento maximizando vi/wi.. Indique cul de las tres funciones de seleccin te
parece ms apropiada.

3. Construya el mtodo realizarCambio. Utilizar la funcin de seleccin minimizar wi


public void realizarCambio()
{

4. Construya el mtodo realizarCambio. Utilizar la funcin de seleccin maximizar vi/wi..

119

public void realizarCambio()


{

2.3.2

Elementos de los Algoritmos Voraces

Segn (Hernndez, 2004), los siguientes corresponden a los elementos de los algoritmos
voraces:

Los candidatos: Tenemos que resolver algn problema de forma ptima. Para construir
la solucin de nuestro problema, disponemos de un conjunto o una lista de candidatos:
las monedas disponibles.

A medida que avanza el algoritmo, vamos acumulando dos conjuntos. Uno contiene
candidatos que ya han sido considerados y seleccionados, mientras que el otro
contiene candidatos que ya han sido considerados y rechazados.

Existe una funcin que comprueba si un cierto conjunto de candidatos constituye en


una solucin de nuestro problema, ignorando si es o no optima por el momento.
Algunos ejemplos de valores devueltos por la funcin solucin en el problema de
devolver $700.
o
o
o

Solucin (200, 200,200,100) = verdadero


Solucin (200, 200, 100, 100, 100) = verdadero
Solucin (100,100) = falso.

Hay una segunda funcin que comprueba si incierto conjunto de candidatos es factible,
esto es, si es posible o no completar el conjunto aadiendo otros candidatos para
obtener al menos una solucin de nuestro problema. Una vez mas no nos preocupa
aqu si esto es optimo o no.

Normalmente, se espera que el problema tenga al menos una solucin que sea posible
obtener empleando candidatos del conjunto que estaba disponible inicialmente.

Hay otra funcin ms, la funcin de seleccin, que indica en cualquier momento cual es
el ms prometedor de los candidatos restantes, que no han sido seleccionados ni
rechazados.

Por ltimo, existe una funcin objetivo que da el valor de la solucin que hemos
hallado: el nmero de monedas que es utilizado para dar la vuelta, la longitud de la ruta
que hemos construido.

En todo algoritmo voraz existen las cuatro funciones mencionadas anteriormente, aunque
frecuentemente no estn explicitas en la codificacin del algoritmo.

120

ACTIVIDAD

Dada una coleccin de elementos enteros positivos de tamao n, los cuales se almacenarn en
un arreglo c. Cul es el conjunto solucin al aplicar el siguiente algoritmo voraz?
public boolean[] algoritmo(int p, int s)
{
int n=s-p+1,k;
boolean[] solucion=new boolean[n]; //agregado solucin vaco
for(int i=-1; i<n-1; )
{
k=i+1;
//candidato
i++;
if(par(c[k+p])) //condicin de prometedor
{
solucion[k]=true; //aadir candidato a la solucin
}
}
return solucion;
}
public boolean par(int i)
{
return (i%2==0);
}
}

2.3.3

El problema de la Devuelta.

Los algoritmos voraces son usados espontneamente por las personas en distintas ocasiones,
por ejemplo, existe un algoritmo voraz que las personas usan para conformar una devuelta. A
continuacin se muestra el conocido caso del tendero.
Supongamos que un tendero tiene que devolver $ 700 y para ello tiene las siguientes 10
monedas.
200

200

200

200

100

100

100

100

50

50

El tendero tratar de utilizar el menor nmero posible de monedas y devolver 3 monedas de


200 y una moneda de 100. Esta forma de seleccin ha utilizado un algoritmo voraz que
selecciona las monedas de mayor a menor (orden no creciente). Este algoritmo puede
detallarse de la siguiente manera:

Se selecciona la primera moneda de $200.


Se acepta esa moneda, porque sirve para conformar la devuelta de $700. Solo falta
conformar $500.
Se selecciona la segunda moneda de $200.
Se acepta esa moneda, porque sirve para conformar los $500 restantes. Falta
conformar $300.
Se selecciona la tercera moneda de $200.
Se acepta esa moneda, porque sirve para conformar los $300 restantes. Falta
conformar $100.
Se selecciona la cuarta moneda de $200.
Se rechaza esa moneda, porque solo falta conformar $100.
Se selecciona la primera moneda de $100.

121

Se acepta esa moneda, porque sirve para conformar los $100 restantes.
El algoritmo termina, porque las monedas seleccionadas y aceptadas suman $700 que
era lo que haba que devolver.

De acuerdo a lo anterior, el problema del cambio en monedas contiene los siguientes


elementos:

Se trata de devolver una cantidad con el menor nmero posible de monedas.


Se parte de:
o Un conjunto de tipos de monedas vlidas, de las que se supone que hay
cantidad suficiente para realizar el desglose.
o Un valor a retornar.

ACTIVIDAD

Dado el siguiente mtodo, explicar el funcionamiento del siguiente algoritmo voraz. El mtodo
da el cambio de n unidades utilizando el menor nmero posible de monedas.
public int [ ] devolverCambio (int n)
{
int[] denominacion = {100, 25, 10, 5, 1};
int solucion [ ];
int cantidad = 0;
int i=0, j=0;
while(cantidad!= n)
{
if( (cantidad + denominacion[ i ]) <= n)
{
solucion[ j ] = denominacion[ i ];
cantidad = cantidad + denominacion[ i ];
j++;
i--;
}
i++;
}
return solucion;
}
Considerando el caso del tendero, argumente las siguientes situaciones:
Que subconjunto se extrajo del conjunto dado?

Con que criterio se seleccionaron los elementos del concurso?

122

Segn las caractersticas de los algoritmos voraces, es posible que el tendero analice varias
para dar la devuelta?

2.4

Actividad Independiente: El problema de la Devuelta

Un tendero requiere de una Aplicacin que le permita dar a sus clientes las vueltas de tal
forma que ellos reciban la menor cantidad de monedas. Se debe permitir:

Agregar una nueva denominacin


Modificar el valor a cambiar
Dar el cambio, especificando las monedas tomadas y la cantidad de ellas.

a) Requerimientos funcionales
NOMBRE
R1
RESUMEN
ENTRADAS
RESULTADOS

NOMBRE

R2

RESUMEN
ENTRADAS
RESULTADOS

123

NOMBRE

R3

RESUMEN
ENTRADAS
RESULTADOS

b. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
Devuelta

DESCRIPCIN

c. Identificar los mtodos de la clase


NOMBRE
Devuelta(double cambio)
void
fijarDenominaciones(ArrayList<
Integer> misMonedas)
double realizarCambio()
double getCambio()
String[] listar() throws
Exception

DESCRIPCIN

A continuacin se muestra la implementacin de algunos mtodos de la clase Devuelta. Debe


completar aquellos cuyo cuerpo de implementacin esta vaco.
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.JOptionPane;
public class Devuelta
{
double suma;
ArrayList<Integer> misMonedas;
int cantidad[];
double cambio;
public Devuelta(double cambio)
{
misMonedas=new ArrayList<Integer>();
suma=0;
this.cambio=cambio;
}
public void fijarDenominaciones(ArrayList< Integer> misMonedas)
{
MyComparator miComparador=new MyComparator();
this.misMonedas=misMonedas;
Collections.sort(misMonedas,miComparador);
}

124

public double realizarCambio()


{

return suma;
}
public double getCambio()
{
return cambio;
}
public void setCambio(double cambio)
{
this.cambio = cambio;
}
public String[] listar() throws Exception
{
int i=0;
if(misMonedas.size()==0)
{
throw new Exception("Debe ingresar denominaciones
antes de poder realizar cambios");
}
else
{
double salida=realizarCambio();
String info[]=new String[misMonedas.size()+2];
for(i=0; i<misMonedas.size(); i++)
{
info[i]="Denominacion:"+misMonedas.get(i).
intValue()+" Cantidad: "+cantidad[i];
}
info[i]="Se cambio en total "+(int)salida+" pesos";
info[i+1]= "No se pudieron cambiar "+(int)(cambiosalida)+" pesos";
return info;
}
}

125

ACTIVIDAD
Identificar en la clase Devuelta los elementos caractersticos de los algoritmos devoradores.

Conjunto de candidatos:
Funcin de solucin:
Funcin de factibilidad:
Funcin de seleccin:
Funcin objetivo:

Pruebe el mtodo que construy con las siguientes denominaciones: 580, 500, 200, 100, 25,
10, 5. Los valores a cambiar son: a) 1050 b) 610 c) 602

2.5

Programacin Dinmica

La Programacin Dinmica (PD) descompone un problema en subproblemas de menor


tamao. Esta estrategia es aplicable siempre y cuando los subproblemas no sean
independientes. Se apoya en el principio de optimalidad de Bellman: Cualquier subsecuencia
de decisiones de una secuencia ptima de decisiones que resuelve un problema tambin debe
ser ptima respecto al subproblema que resuelve.
Gracias a esta estrategia es posible mejorar la eficiencia del clculo puesto que logra que cada
subproblema se resuelva sola vez (y su resultado se almacene en una tabla), logrando de sta
forma evitar tener que repetir clculos cuando el subproblema aparezca nuevamente.
Segn (Guerequeta & Vallecillo, 2000), la Programacin Dinmica no slo tiene sentido
aplicarla por razones de eficiencia, sino porque adems presenta un mtodo capaz de resolver
de manera eficiente problemas cuya solucin ha sido abordada por otras tcnicas y ha
fracasado. Donde tiene mayor aplicacin la Programacin Dinmica es en la resolucin de
problemas de optimizacin. En este tipo de problemas se pueden presentar distintas
soluciones, cada una con un valor, y lo que se desea es encontrar la solucin de valor ptimo
(mximo o mnimo).
En grandes lneas, el diseo de un algoritmo de Programacin Dinmica consta de los
siguientes pasos:
1. Planteamiento de la solucin como una sucesin de decisiones y verificacin de que
sta cumple el principio de ptimo.
2. Definicin recursiva de la solucin.
3. Clculo del valor de la solucin ptima mediante una tabla en donde se almacenan
soluciones a problemas parciales para reutilizar los clculos.
4. Construccin de la solucin ptima haciendo uso de la informacin contenida en la
tabla anterior.

2.5.1

Serie de Fibonacci

En este apartado se aplicar la serie de Fibonacci al contexto de la reproduccin de conejos.


En una granja han contratado un programador para que realice una aplicacin que permita
modelar la reproduccin de conejos mediante un esquema sencillo de suposiciones, para ello
se ha decido poner en una coneja una pareja de conejos recin nacidos. Se sabe que la
reproduccin de conejos sigue las siguientes reglas:

126

a) Cada pareja de conejos frtiles tiene una pareja de conejitos cada mes.
b) Cada pareja de conejos comienza a ser frtil a partir del segundo mes de vida
c) Ninguna pareja de conejos muere ni deja de reproducirse (Bohquez, 2006).
La aplicacin debe mostrar cuantos conejos hay al trmino del mes n, para que el granjero
pueda tomar decisiones en torno a si debe comprar ms conejeras o empezar a vender
parejas.
La siguiente tabla muestra la cantidad conejos que debe haber al cabo de 6 meses
Mes
Cantidad de conejos

0
1

1
1

2
2

3
3

4
5

5
8

Observe que se inicia con una pareja de conejos (mes 0). Esta pareja (la cual llamaremos A) al
inicio del mes dos se reproduce con lo cual quedara la pareja de padres y la pareja de hijos (la
cual llamaremos B). Al tercer mes la pareja B aun no es frtil pues solo tiene un mes de vida,
pero sus padres continuacin siendo frtiles dando lugar a una nueva pareja (pareja C). Es
decir en el mes tres ya se tendran 3 parejas. En el mes cuatro, la pareja A tiene un nuevo hijo
(D), al igual que la pareja B, mientras que la pareja C aun no es frtil pues solo tiene un mes de
vida, con lo cual quedara cinco parejas.
Esta relacin se puede expresar de la siguiente forma:
Fib(n)= Fib (n-1) + fib (n-2)
Donde Fib (0) = 1 y Fib (1) = 1
La clase Fibonacci permite resolver el problema de la reproduccin de conejos.
public class Fibonacci
{
/**
* @param n Es la cantidad de trminos de la serie. n>=0
* @return el valor en la serie fibonacci
*/
public int calcularFibonacci (int n)
{
if ((n == 0) || (n == 1))
{
return 1;
}
else
{
return calcularFibonacci(n-1) + calcularFibonacci(n-2);
}
}
}
n

El algoritmo en su versin recursiva tienen un orden de complejidad O(2 ), esto a que se


realizan dos llamadas recursivas y el caso base inductivo para este problema es: b+2T(n-1).
Es posible mejorar el orden de complejidad para este algoritmo a un orden O(n), esto se puede
conseguir utilizando un arreglo que almacene los valores almacenados para posteriormente
reutilizarlos.
Fib(0)

Fib(1)

Fib(2)

127

....

Fib(n)

El mtodo iterativo que calcula la sucesin de Fibonacci utilizando programacin dinmica es el


siguiente:
public int fibo ( int datos[], int n )
{
if (n==0)
{
return 0;
}
if (n<=1)
{
return 1;
}
else
{
datos[0]=0;
datos[1]=1;
for (int i=2; i <= n; i++)
{
datos[i] = datos[i-1] + datos[i-2];
}
}
return datos[n];
}

ACTIVIDAD

Construya la versin iterativa para el problema de la serie de Fibonacci.


public int fibo ( int n )
{

Argumente cul es ms eficiente de la versin recursiva o la versin iterativa? Halle el T(n)


para cada una de estas versiones.

128

Termine el rbol que muestra grficamente el comportamiento del algoritmo recursivo.

ACTIVIDAD

Escriba la implementacin para factorial de forma iterativa, recursiva y con programacin


dinmica.
public int factorialIterativo (
{

public int factorialRecursivo (


{

public int factorialDinamico (


{

129

Compare las anteriores implementaciones y de acuerdo a su funcionamiento y orden de


complejidad determine de cul es el ms eficiente y cul el menos eficiente.

2.5.2

Problema de la Mochila

Se va a retomar el problema del llenado del camin pero haciendo uso de programacin
dinmica. Se debe permitir fijar el peso mximo, agregar un nuevo objeto, y arrojar el valor
mximo que puede llevar el camin.
a. Identificar las entidades o clases.
ENTIDAD DEL MUNDO
Camion

DESCRIPCIN

public class Camion


{
private double valor;
prvate ArrayList <Objeto> misObjetos;
private double pesoMaximo;
public ArrayList<Objeto> getMisObjetos()
{
return misObjetos;
}
public void setMisObjetos(ArrayList<Objeto> misObjetos)
{
this.misObjetos = misObjetos;
}
public Camion(double cambio)
{
misObjetos=new ArrayList<Objeto>();
valor=0;
this.pesoMaximo=cambio;
}
public void agregarObjeto(double peso, double valor)
{
MyComparator miComparador=new MyComparator();
misObjetos.add(new Objeto(peso,valor,0));
Collections.sort(misObjetos,miComparador);
}

130

public int realizarCambio()


{
String salida="";
int g[][]=new int[misObjetos.size()][(int)pesoMaximo+2];
int j,i;
for(i=1 ; i<=(int)pesoMaximo; i++)
{
g[0][i]=1;
}
for(j=0; j<misObjetos.size(); j++)
{
g[j][0]=0;
}
for( i=1; i<misObjetos.size();i++)
{
for( j=1; j<=(int)pesoMaximo; j++)
{
if(j<misObjetos.get(i).getPeso())
{
g[i][j]=g[i-1][j];
}
else
if(g[i-1][j]>=g[i-1][j-(int)misObjetos.
get(i).getPeso()]+(int)misObjetos.get(i).
getValor())
{
g[i][j]=g[i-1][j];
}
else
{
g[i][j]=g[i-1][j-(int)misObjetos.
get(i).getPeso()]+(int)misObjetos.get(i).
getValor();
}
}
}
for( i=0; i<misObjetos.size(); i++)
{
for( j=0; j<(int)pesoMaximo+1; j++)
{
salida+=g[i][j]+" ";
}
salida+="\n";
}
return g[i-1][j-1];
}
public double getCambio()
{
return pesoMaximo;
}

131

public String[] listar() throws Exception


{
int i=0;
if(misObjetos.size()==0)
{
throw new Exception("Debe ingresar denominaciones
antes de poder realizar cambios");
}
else
{
String info[]=new String[1];
info[i]="Valor mximo "+realizarCambio();;
return info;
}
}
public double getPesoMaximo()
{
return pesoMaximo;
}
public void setPesoMaximo(double pesoMaximo)
{
this.pesoMaximo = pesoMaximo;
}
}

ACTIVIDAD
Pruebe el funcionamiento del mtodo realizarCambio() llenando la siguiente tabla. Asuma
los siguientes valores y pesos:
Limite
De peso 0
W 1=1
v1=1
W 1=3
v1=5
W 1=6
v1=17
W 1=8
v1=20
W 1=8
v1=25

2.5.3

Problema de la Devuelta

Ahora deseamos retomar el problema del cambio de moneda tratado en el captulo anterior. Se
debe permitir agregar una nueva denominacin, fijar el total a cambiar e indicar con cuantas
monedas puede ser efectuado el cambio.

132

//Las denominaciones estn ordenadas de menor a mayor


public
{

int realizarCambio()throws Exception


int n=misMonedas.size(), i, j;
int c[][]=new int[n][(int)(cambio+1)];
String mensaje="";

for(i=0; i<n; i++)


{
c[i][0]=0;
}
for(i=0; i<n; i++)
{
mensaje+=c[i][0]+" ";
for(j=1; j<=cambio; j++)
{
if(i==0 && j<misMonedas.get(i))
{
c[i][j]=999999;
}
else
{
if(i==0)
{
c[i][j]=1+c[0][j-misMonedas.get(0)];
}
else
{
if(j<misMonedas.get(i))
{
c[i][j]=c[i-1][j];
}
else
{
if(c[i-1][j]<1+c[i][j-misMonedas.get(i)])
{
c[i][j]=c[i-1][j];
}
else
{
c[i][j]=1+c[i][j-misMonedas.get(i)];
}
}
mensaje+=c[i][j]+"
";
}
mensaje+=" \n";
}
if( c[n-1][(int)(cambio)]>999999)
{
throw new Exception("No se pudo completar el cambio
solicitado");
}
return c[n-1][(int)(cambio)];
}

133

ACTIVIDAD

Llene la siguiente tabla basndose en el mtodo anterior.


Cantidad
d1= 1

d2= 3
d3= 6
Modifique el mtodo para que adems de indicar la totalidad de monedas requeridas para
efectuar el cambio, especifique cuales y cuntas de cada una.

Cul es el orden de complejidad del mtodo realizarCambio, justifique su respuesta.

2.5.4

Algoritmo de Dijkstra

El algoritmo de Dijkstra, tambin llamado algoritmo de caminos mnimos, es un algoritmo para


la determinacin del camino ms corto dado un vrtice origen al resto de vrtices en un grafo
con pesos en cada arista. Su nombre se refiere a Edsger Dijkstra, quien lo describi por
primera vez en 1959.
La idea fundamental en este algoritmo consiste en ir explorando todos los caminos ms cortos
que parten del vrtice origen y que llevan a todos los dems vrtices; cuando se obtiene el
camino ms corto desde el vrtice origen, al resto de vrtices que componen el grafo, el
algoritmo se detiene.
A continuacin se muestra la implementacin del algoritmo de Dijkstra en programacin
dinmica. Se consideran tres mtodos para la solucin del problema.

134

Mtodo

public void Dijkstra()


{
Sol=new boolean [n];
Def=new float [n-1];
for (int i=0;i<(n-1);i++)
{
Sol[i+1]=false;
Def[i]=L[0][i+1];
}
Sol[0]=true;
for (int i=0;i<(n-2);i++)
{
Sol[pos]=true;
for (int j=0;j<(n-1);j++)
{
if (!Sol[j+1])
{
Def[j]=Math.min(Def[j],
Def[pos-1]+L[pos][j+1]);
}
}
}
}

Anlisis del
mtodo
Orden de
complejidad

2.5.5

Hoja de trabajo: Lotera

Se desea crear una Aplicacin que permita generar combinaciones ganadoras. La lotera debe
proporcionarnos 6 nmeros para jugar a la lotera Primitiva. Evitando con ello evitar tener que
rellenar las columnas para conseguir un ahorro de tiempo.
Se debe permitir generar combinaciones aleatorias de seis nmeros para la lotera Primitiva,
resetear la lotera y mostrar el total de combinaciones existentes.

135

a) Requerimientos funcionales
NOMBRE
R1
RESUMEN
ENTRADAS
RESULTADOS

R2
NOMBRE
RESUMEN
ENTRADAS
RESULTADOS

NOMBRE
R3
RESUMEN
ENTRADAS
RESULTADOS
b. Identificar las entidades o clases.
ENTIDAD DEL MUNDO
Loteria

DESCRIPCIN

c. Implementacin de mtodos
public class Loteria
{
prvate int arreglo[];
prvate final int TOTAL=6;
public Loteria()
{
arreglo=new int[TOTAL];
}
public int[] generarCombinacionGanadora()
{
int dato;
for(int i=0; i<TOTAL; i++)
{

}
return arreglo;
}

136

public void resetear()


{
for(int i=0; i<TOTAL; i++)
{

}
}
//Esta es la version recursiva
public double calcularCombinatorias(int n, int k)
{
if(k==0||k==n)
{
return 1;
}
else
{
if(k>0&&k<n)
{
return calcularCombinatorias(n-1, k-1)+
calcularCombinatorias(n-1, k);
}
else
{
return 0;
}
}
}

ACTIVIDAD

1. Construya un rbol que permita obtener el valor de calcularCombinatorias(6,4) y halle el T(n)


de dicha funcin.

2. Construya una tabla con los valores obtenidos de la prueba anterior


Por ejemplo si se tiene C(6,2)=15, puesto que hay 15 formas de escoger 2 objetos a partir de
un conjunto con 6 elementos.

137

Por ejemplo si se calcula C(5,3) cual sera el resultado.

C(5,3) =

Complete la siguiente tabla con base en los valores establecidos.


0

0
1
2
3
4
5
6
.
.
n

Con base en dicha tabla construya un algoritmo ms eficiente, que haga uso de programacin
dinmica.
public double calcularCombinatorias(int n, int k)
{

138

Verifique si la siguiente implementacin corresponde a la solucin del problema de los


coeficientes binomiales.
public int[] binomial (int n, int k)
{
int matriz [][]= new int [ n ] [ k ];
for (int i=0; i<n;i++)
{
matriz [i][0] = 1;
}
for (int i=1; i<n;i++)
{
matriz [i][1] = i;
}
for (int i=2; i<k;i++)
{
matriz [i][i] = 1;
}
for (int i=3; i<n;i++)
{
for (int j=2; j<i;j++)
{
if (j<k)
{
matriz [i][j] = matriz[i-1][j-1]+matriz[i-1][j];
}
}
}
return matriz;
}

139

140

3.1

ALGORITMOS APLICADOS A GRAFOS Y ARBOLES

Introduccin

Los rboles son frecuentemente utilizados en: la programacin, en analizadores sintcticos, en


sistemas de organizacin, en los sistemas operativos, en la bsqueda de datos. En aspectos
del mundo real se trabaja a diario con rboles, como por ejemplo como organigramas
organizacionales. Dentro del contexto del anlisis de algoritmos, los grafos permiten la solucin
de problemas relacionados con caminos cortos, planeacin de tareas.
Inicialmente se analizaran los conceptos fundamentales de los arboles y despus se analizar
lo relacionado con los grafos.
3.2

Arboles Binarios

Los rboles son estructuras de datos dinmicas con una naturaleza diferente en cuanto a las
estructuras de datos anteriormente analizadas, esta naturaleza esta determinada directamente
con su estructura, la cual impone una jerarqua sobre una coleccin de objetos. Un rbol es
una coleccin de elementos llamados nodos, uno de los cuales se distingue como raz. Sobre
ellos se encuentra definida una relacin de paternidad que define la estructura jerrquica a la
que nos referimos anteriormente. Los nodos de un rbol contienen dos o ms enlaces. El nodo
raz es el primer nodo de un rbol. Cada enlace del nodo raz hace referencia a un hijo. el hijo
izquierdo es el primer nodo del subrbol izquierdo, y el hijo derecho es el primer hijo del
subrbol derecho.
Este captulo se basar fundamentalmente es los rboles binarios. Este tipo de rbol se
caracteriza por que tiene un nodo llamado padre y el cual puede tener cero, uno o dos hijos
como mximo. A su vez cada hijo puede verse como otro rbol.
Una definicin recursiva de rbol es que un rbol puede verse como una estructura formada
por un nodo (la raz de dicho rbol) y una lista de rboles. Este nodo (raz) es el padre de las
races de los rboles que componen la lista, a partir de lo cual, se establece la relacin de
paternidad entre ellos. La siguiente figura muestra una representacin de un rbol binario.
Raiz

nodo
o

nodo

nodo

A continuacin se exponen las definiciones ms relevantes asociadas a los rboles binarios.

Nodo: Un rbol se compone de cero, uno o ms nodos. Un nodo tiene


fundamentalmente informacin que almacena en su campo de datos, una referencia al
hijo izquierdo y una referencia al hijo derecho. Cuando se crea un nodo, se almacena
su dato y sus referencias son nulas. En la figura xx se muestra una abstraccin de lo
que representa un nodo.

141

nodo
o

Nodo de un rbol

Raz: En un rbol se identifica el nodo raz como el nodo principal en la jerarqua, as


como tambin se pueden distinguir la raz otros subrboles. Por ejemplo en la siguiente
figura, la raz principal es el nodo cuya informacin es 20. Podemos distinguir tambin
el rbol 60 90, cuya raz es el nodo con informacin 60. El rbol de la derecha tiene
como raz el nodo cuya informacin es 90.
20

10

60

90

Padre: Es un nodo que puede o no tener hijos. El nodo con informacin 34, tiene 2
hijos, el hijo izquierdo, cuya que contiene el valor 1, y el hijo derecho, cuya informacin
es 44.
34

44

99

Hoja: Una hoja es un nodo que no tiene hijo derecho ni izquierdo. Por ejemplo, en la
siguiente figura, el nodo 2 es una hoja mientras que el nodo con informacin 1 no se
puede considerar como hoja por que tiene ramificacin por la izquierda.
1

20

30

40

20

142

Nivel: Cada nodo tiene un nivel dentro de un rbol binario. El nodo raz tiene un nivel 0
y los dems nodos tienen el nivel de su padre mas 1. Por esto los nodos que son hijos
del nodo raz tienen nivel 1. Segn la figura: El nodo 34 tiene nivel 0, los nodos 2 y 44
tienen nivel 1 y los nodos 2 y 98, tienen nivel 2.
10

60

90

34

99

44

Niveles de un rbol

Altura: La altura de un rbol binario es el nivel de la hoja o de las hojas que estn ms
distantes de la raz. Basndonos en la siguiente figura, la altura del rbol cuya raz es
A, corresponde a la longitud del camino para llegar a la hoja mas distante de la raz.
En este caso ser 3.
2

34

44

99

44

3.2.1

34

Arbol Binario Completo: Un rbol binario completo es aquel en el que todo nodo no
terminal tiene sus dos hijos. Todos los nodos no terminales tienen sus dos hijos. El
mximo numero de nodos que puede tener un rbol de nivel n es: 20 + 21 + 22 + 23...
+ 2n . Si n es 3 entonces : 20 + 21 + 22 + 23 = 15

rboles Binarios de Expresin

Los rboles binarios de expresin, tienen aplicaciones en diferentes contextos, entre las ms
aplicadas en el contexto de las ciencias de la computacin se encuentran la representacin de
expresiones proposicionales y expresiones aritmticas con las cuales se pueden realizar
diferentes clculos matemticos.

143

Inicialmente se muestra su aplicacin en el contexto de expresiones proposicionales. Por


ejemplo si se tiene la expresin: (p v q) (q ^ r), el rbol de representacin ser el
siguiente:

En este caso el operador principal de la expresin es la implicacin y por ello se ubica como el
padre del rbol, este rbol tiene dos subexpresiones que se pueden denominar hijo izquierdo e
hijo derecho, cada uno de los cuales se compone de una expresin.
La expresin: ((p v q) ^ (st)) (s v t), tiene el siguiente rbol de representacin:

En este caso el operador principal de la expresin es l bicondicional y por ello se ubica como
el padre del rbol, este rbol tiene dos subexpresiones que se pueden denominar hijo izquierdo
e hijo derecho, cada uno de los cuales se compone de una expresin. El hijo izquierdo tiene a
su vez una expresin compuesta de hijo izquierdo e hijo derecho, cuyo operador principal es la
conjuncin. El hijo derecho tiene como operador principal es la disyuncin.

ACTIVIDAD

Dadas las siguientes frmulas proposicionales, dibuje su rbol de formacin:

p q r ^ q q p
q (p ^p) r v s p
(p q) ^ (p q) ^ p ^ s q
(p ^ r) (p ^ q) q

144

Con el objetivo de evitar las ambigedades en la interpretacin de las frmulas proposicionales,


se debe establecer un orden de precedencia para aplicar los operadores de una frmula
proposicional.
El siguiente ser el orden en el cual se aplicar la precedencia de los conectivos lgicos:
1.
2.
3.
4.
5.

Operador
Operador
Operador
Operador
Operador

de negacin
de conjuncin ^
de disyuncin v
condicional
bicondicional

Dado el siguiente rbol de formacin, escriba su correspondiente frmula proposicional.

Dado el siguiente rbol de formacin, escriba su correspondiente frmula proposicional.

Otra aplicacin muy conocida de los rboles es la de los Arboles de expresin, en la cual cada
expresin (compuesta por dos operandos y un operador) se representa a travs de esta
estructura. La raz del rbol contiene un operador, mientras que los subrboles izquierdo y
derecho son los operandos izquierdo y derecho respectivamente. En la hojas solo pueden ir
operandos. Por ejemplo la expresin ((3+5)*(6-2)) puede expresarse tal como se muestra a
continuacin.

145

9
8
2

7
7

9
9
1

9
7

Algunas calculadoras comerciales se apoyan en este tipo de rboles y especficamente en la


notacin postfijo para poder resolver las operaciones planteadas. Por ejemplo la expresin 3 5
+ 6 2 * (equivalente al rbol anterior) se resolvera como 8 4 * dando como resultado 32.

3.2.2

Implementacin de Arboles Binarios

Un rbol binario es un conjunto de elementos que est vaco o dividido en tres subconjuntos. El
primer subconjunto es la raz, los otros dos son los subrboles izquierdo y derecho. Cada nodo
puede tener cero, uno o mximo dos hijos.
Para la implementacin de rboles Binarios, se utilizar una clase Nodo de la cual se pueden
utilizar tantos objetos como sean necesarios. A continuacin, se puede apreciar la
implementacin esta clase en Java:
public class
{
public
public
public
Mtodo

Anlisis del
Orden de
Complejidad

Nodo
Comparable info;
Nodo derecho;
Nodo izquierdo;

public Nodo (Comparable dato)


{
info = dato;
derecho = null;
izquierdo = null;
}
}
Inicialmente se tiene la clase Nodo, la cual es define dos
referencias las cuales determinan sus hijos tanto derecho como
izquierdo y un campo llamado dato el cual la almacenar la
informacin del nodo. Esta clase tiene un mtodo constructor y si se
analiza su orden de complejidad se puede deducir que es
constante, es decir, O(1).

Tambin es necesario para la implementacin de rboles Binarios crear una clase Arbol. Esta
clase es la que contiene la implementacin de las principales operaciones que se pueden llevar
a cabo con los rboles, estas operaciones son fundamentalmente de creacin, bsqueda,
eliminacin. A continuacin, se puede apreciar la implementacin esta clase en Java, as como
una explicacin de los mtodos ms utilizados en los rboles, posteriormente, se analizar el
orden de complejidad de cada uno de los mtodos.

146

public class Arbol


{
public Nodo raiz;
public int cantidad;
Arbol()
{
raiz = null;
cantidad = 0;
}

Mtodo

Anlisis del
Orden de
Complejidad

}
El constructor de rbol, tiene en sus implementacin nicamente
operaciones elementales, por lo tanto el orden de complejidad de
este mtodo es O(1).

Se declara una instancia de la Clase Nodo y una variable entera que servir para contar los
nodos que se tienen en el rbol.
A continuacin se analizar el orden de complejidad de cada uno de los mtodos que
implementan la estructura de datos rbol.

Buscar en un Arbol

El mtodo buscar(Comparable dato, Nodo nodo), es un mtodo iterativo,


fundamentalmente el conjunto de datos de entrada para este algoritmo est determinada
por la referencia de los nodos del rbol. Para nuestro anlisis se debe tener en cuenta el
peor de los casos, y este es que el nodo que se est buscando es un nodo hoja. El mtodo
retorna un valor booleano, recibe por parmetro el valor del nodo a buscar y el nodo, este
mtodo divide el rbol a la mitad cada vez que realiza una comparacin.

Mtodo

Anlisis del
Orden de
Complejidad

public boolean buscar(Comparable dato, Nodo nodo)


{
boolean salida;
salida = false;
while (nodo != null && !salida)
{
if (dato.compareTo(nodo.info) < 0)
{
nodo = nodo.izquierdo;
}
else if (dato.compareTo(nodo.info)> 0)
{
nodo = nodo.derecho;
}
else
{
salida = true;
}
}
return salida;
}
Cuando se hace cada comparacin dentro del while, se est
ignorando la mitad el rbol (ya sea la izquierda o la derecha), lo que
implica que el conjunto de datos de entrada se esta reduciendo a la
mitad y se puede afirmar que el orden de complejidad de este
mtodo es O(log n). Esta deduccin es posible hacerla, pues en el
capitulo anterior por el mtodo de sustitucin ya se demostr.

147

La figura muestra un caso cuando se desea buscar un elemento cuyo valor en su campo de
datos es 3. La eficiencia de las operaciones sobre los rboles binarios estas asociada en la
mayora de casos a la mitad del conjunto de datos de entrada.

34

44

98

97

99

Cuando se realiza la primera comparacin, se encuentra que el valor que se busca es menor al
valor de la raz y se debe realizar una bsqueda por el subrbol izquierdo.

34

44

98

97

99

La bsqueda continua por la mitad del subrbol, en este caso caso el subrbol derecho en
donde se encuentra el elemento que se esta buscando.

Insertar en un rbol

El mtodo insertar, fundamentalmente inserta un nodo en el rbol, recibe como parmetro


un valor, ese valor en caso de ser mayor al valor del nodo en el que se encuentre, seguir
comparando con el siguiente nodo del subrbol derecho, en caso contrario lo har en el
subrbol izquierdo.

148

Mtodo

public Nodo insertarNodo(Comparable dato, Nodo nodo)


{
if (nodo == null)
{
return new Nodo(dato);
}
else
{
if (dato.compareTo(nodo.info) <= 0)
{
nodo.izquierdo =
insertar_nodo(dato,nodo.izquierdo);
}
else
{
nodo.derecho =
insertar_nodo(dato, nodo.derecho);
}
}
return nodo;

Anlisis del
Orden de
Complejidad

}
Cuando se hace cada comparacin dentro del while, se est ignorando
la mitad el rbol (ya sea la izquierda o la derecha), lo que implica que
el conjunto de datos de entrada se esta reduciendo a la mitad y se
puede afirmar que el orden de complejidad de este mtodo es
O(log n). Esta deduccin es posible hacerla, pues en el capitulo
anterior por el mtodo de sustitucin ya se demostr.

Dado que se no est haciendo balanceo del rbol, es decir, se inserta el nodo donde le
corresponda sin tener en cuenta la proporcin de las ramas del rbol, todo nodo que se inserte
se convertir en una hoja del rbol. Esto implica que como mximo el recorrido que se hace es
la altura misma del rbol y por lo tanto la relacin entre la altura y el nmero de nodos es del
orden de O(logn).

34

44

98

97

La operacin de insercin contina por el subrbol izquierdo.

149

99

34

44

98

97

99

Finalmente se puede insertar el nodo garantizando siempre la propiedad del rbol binario
ordenado.

34

44

98

97

99

Eliminar en un rbol

El mtodo eliminar permite remover un nodo del rbol, recibe como parmetro un valor que
servir para encontrar el nodo a eliminar. Una vez se halla este nodo se procede a eliminarlo
del rbol y se realizan los correspondientes cambios en el rbol.
El mtodo eliminar(Comparable dato, Nodo nodo), es un mtodo recursivo. Para nuestro
anlisis se debe tener en cuenta el peor de los casos, y este es que el nodo que se est
buscando es un nodo hoja. En el caso de eliminar un nodo de un rbol, se deben tener en
cuenta los siguientes casos:

El aspecto mas sencillo, si el nodo que deseamos remover es un nodo hoja, Se elimina
la referencia que tiene el nodo padre del nodo. Lo anterior colocando una referencia
nula.

En el caso en el cual el nodo a eliminar, solo tenga un descendiente, entonces se


substituye por dicho descendiente.

150

Los siguientes son aspectos que deben ser tenidos en cuenta si el nodo que se desea eliminar
posee dos subrboles, estas se detallan a continuacin:

Se puede sustituir el nodo a eliminar por el mayor elemento (nodo ms a la derecha) del
subrbol izquierdo del nodo a eliminar.

Es similar al caso anterior. Se puede sustituir el nodo a eliminar por el menor elemento
(nodo ms a la izquierda) del subrbol derecho del nodo a eliminar.

Se puede colocar el subrbol izquierdo a la izquierda del menor elemento (nodo ms a


la izquierda) del subrbol derecho del nodo a eliminar.

Es similar al caso anterior. Se puede colocar el subrbol derecho a la derecha del mayor
elemento (nodo ms a la derecha) del subrbol izquierdo del nodo a eliminar.

Mtodo

Anlisis del
Orden de
Complejidad

public Nodo eliminar_nodo(Comparable dato, Nodo nodo)


{
if (nodo == null)
{
return null;
}
if (dato.compareTo(nodo.info) < 0)
{
nodo.izquierdo =
eliminar_nodo(dato, nodo.izquierdo);
}
else if (dato.compareTo(nodo.info) > 0)
{
nodo.derecho =
eliminar_nodo(dato,nodo.derecho);
}
else if(nodo.izquierdo!=null && nodo.dercho!= null)
{
nodo.info = buscarMenor(nodo.derecho).info;
nodo.derecho = eliminarMenor(nodo.derecho);
}
else
{
nodo =
(nodo.izquierdo!=null)?nodo.izquierdo:nodo.derecho;
}
cantidad--;
return nodo;
}
Para el peor de los casos, el elemento menor ser la hoja del rbol, en
consecuencia el orden es O(log n).

Buscar el menor elemento del rbol

Realiza un recorrido por todo el subrbol izquierdo de acuerdo a un nodo especfico, con el
objetivo de encontrar el menor elemento del subrbol.

151

Mtodo

Anlisis del
Orden de
Complejidad

public Nodo buscarMenor(Nodo nodo)


{
if (nodo == null)
{
return null;
}
while(nodo.izquierdo != null)
{
nodo = nodo.izquierdo;
}
return nodo;
}
Cuando se hace cada comparacin en la lnea, se est ignorando la mitad el
rbol, ms exactamente todo el subrbol derecho, por lo tanto el rbol se
reduce a la mitad y se puede afirmar que el orden de complejidad de este
mtodo es O(logn).

En el siguiente grafico se observa que buscar el menor elemento, corresponde a recorrer el


rbol siempre por el subrbol izquierdo, hasta encontrar su hoja izquierda.

88

89

90

Eliminar el menor elemento del rbol

Mtodo recursivo que elimina el menor elemento del rbol, realiza siempre un recorrido por el
subrbol izquierdo.

Mtodo

public Nodo eliminarMenor(Nodo nodo)


{
if (nodo == null)
{
return null;
}
if (nodo.izquierdo != null)
{
nodo.izquierdo = eliminarMenor (nodo.izquierdo);
else
{
nodo = nodo.derecho;
}
return nodo;
}

152

Anlisis del
Orden de
Complejidad

Cuando se hace cada comparacin en la lnea, se est ignorando la mitad el


rbol, ya sea el subrbol izquierdo o el derecho, por lo tanto el rbol se
reduce a la mitad y se puede afirmar que el orden de complejidad de este
mtodo es O(logn).

ACTIVIDAD

Analice el siguiente mtodo de implementacin de la altura del rbol y determine su orden de


complejidad.

Mtodo

public static int altura(Nodo nodo)


{
if (nodo == null)
{
return -1;
}
else
{
return 1+ Math.max(altura(nodo.izquierdo),
altura(nodo.derecho));
}
}

Anlisis del
Orden de
Complejidad

3.2.3

Recorridos en Arboles Binarios

Existen tres tipos de recorridos que pueden realizarse a un rbol. El primero de ellos es el
preorden, el cual consiste en recorrer primero la raz, luego el subrbol izquierdo y finalmente el
derecho. El recorrido inorden a su vez procesa primero el subrbol izquierdo, despus el raz y
a continuacin el subrbol derecho. Finalmente el recorrido postorden procesa primero los
subrboles izquierdo y derecho y finalmente procesa la raz.
Para el siguiente rbol los recorridos son:
ballena

ratn

pez

tit

tigre

ballena

cocodrilo

153

Inorden: ballena, cocodrilo, pantera, pez, ratn, tigre, titi


Preorden: ratn, cocodrilo, ballena, pantera, pez, tigre, tit
Postorden: ballena, pez, pantera, cocodrilo, titi, tigre, ratn
El mtodo recursivo que realiza el recorrido en inorden para el caso de estudio tratado es:
public String imprimirInorder(Nodo n)
{
if (n != null)
{
imprimirInorder(n.obtenerIzquierda());
salida+=((Persona) n.getData()).getNombre() + "
"+((Persona) n.getData()).getDireccion()+" "+((Persona)
n.getData()).getTelefono()+" \n ";
imprimirInorder(n.obtenerDerecha());
}
return salida;
}

ACTIVIDAD

Construya los mtodos para los recorridos faltantes


public String imprimirPostorder(Nodo n)
{

public String imprimirPreorder(Nodo n)


{

}
Determine el orden de complejidad para los mtodos:
imprimirInorder(Nodo n)
imprimirPostorder(Nodo n)
imprimirPreorder(Nodo n)

O(
O(
O(

154

)
)
)

3.3

Caso de estudio: Agenda Telefnica

Se desea crear una Aplicacin que permita manejar la informacin de una agenda telefnica.
Cada Contacto tiene un nombre, una direccin y un telfono. Se debe permitir:

Agregar un contacto.
Eliminar la informacin de toda la agenda
Listar la informacin en preorden
Listar la informacin en inorden
Listar la informacin en postorden

Para la implementacin se har uso de un rbol binario de bsqueda

a. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
ArbolBinario

DESCRIPCIN

Nodo
Persona

b. Implementacin de las clases


Para nuestro caso de estudio la implementacin tendr la siguiente forma:
public class
{
private
private
private

Nodo
Nodo izquierda;
Nodo derecha;
Object dato;

155

/** Constructor del nodo */


public Nodo(Nodo izquierda, Nodo derecha, Object dato)
{
fijarALaIzquierda(izquierda);
fijarALaDerecha( derecha);
setData( dato );
}
public Nodo obtenerIzquierda()
{
return this.izquierda;
}
public Nodo obtenerDerecha()
{
return this.derecha;
}
public Object getDato()
{
return this.dato;
}
public void fijarALaIzquierda(Nodo n)
{
this.izquierda = n;
}
public void fijarALaDerecha(Nodo n)
{
this.derecha = n;
}
public void setDato(Object d)
{
this.dato = d;
}
}
La siguiente es la implementacin para la clase rbol Binario.
public class
{
private
private
private

ArbolBinario
Nodo raiz;
String salida=" ";
boolean esta=false;

public void adicionarNodo( String nombre, String direccion, String


telefono ) throws RepetidoException
{
Persona dato=new Persona(nombre, direccion,telefono);
// Crear nodo
Nodo temp = new Nodo(null, null, dato);
if ( raiz == null )
{
raiz = temp;
}
else
{
verificarRepetido(nombre,raiz);
if(esta==false)
{
insertarNodo( raiz, temp );
}

156

else
{
esta=false;
throw new RepetidoException("elemento ya est");
}
}
}
public void verificar( String nombre, Nodo n)
{
esta=false;
verificarRepetido( nombre, n);
}
public void verificarRepetido( String nombre, Nodo n)
{
if (n != null)
{
verificarRepetido(nombre,n.obtenerIzquierda());
if(((Persona) n.getData()).getNombre().equals(nombre))
{
esta=true;
}
verificarRepetido(nombre,n.obtenerDerecha());
}
}
public void insertarNodo(Nodo n, Nodo temp)
{
String nInfoNombre, tInfoNombre;
nInfoNombre = ((Persona)n.getDato()).getNombre();
tInfoNombre = ((Persona)temp.getDato()).getNombre();
if (tInfoNombre.compareTo(nInfoNombre) > 0)
{
// Prueba para adicionar a la derecha
if (n.obtenerDerecha() == null)
{
// halla un espacio para colocar el nodo
n.fijarALaDerecha(temp);
}
else
{
//intenta nuevamente hacia abajo
insertarNodo( n.obtenerDerecha(), temp);
}
}
else{
// prueba a la izquierda
if (n.obtenerIzquierda() == null)
{
n.fijarALaIzquierda(temp);
}
else
{
insertarNodo( n.obtenerIzquierda(), temp);
}
}
}

157

public void postorden(Nodo nodo)


{
if (nodo != null)
{
postorden(nodo.izquierdo);
postorden(nodo.derecho);
System.out.print(nodo.info + " ");
}
}
public void preorden(Nodo nodo)
{
if(nodo != null)
{
System.out.print(nodo.info + " ");
preorden(nodo.izquierdo);
preorden(nodo.derecho);
}
}
}

3.4

Hoja de trabajo: Graficador de rboles

Se desea crear una aplicacin que permita almacenar los nombres de varias personas, acorde
a las reglas seguidas en un rbol binario de bsqueda.
La aplicacin debe permitir agregar un nombre, eliminar un nombre, eliminar el mnimo
elemento, hallar el nombre de mximo elemento y borrar el rbol. De igual forma el rbol
deber poder mostrarse grficamente.

158

a) Requerimientos funcionales
NOMBRE

R1

RESUMEN
ENTRADAS
RESULTADOS

NOMBRE
R2
RESUMEN
ENTRADAS
RESULTADOS

NOMBRE
R3
RESUMEN
ENTRADAS
RESULTADOS
b. Identificar las entidades o clases.
ENTIDAD DEL MUNDO
ArbolBinarioDeBusqueda
Nodo

DESCRIPCIN

ElementoDuplicadoException
ElementoNoEncontradoException

c. Las Relaciones entre las clases

159

d. Implementacin de los mtodos


A continuacin se muestran los mtodos de la clase Nodo.
public class Nodo
{
private Comparable informacion;
private int posicionX, posicionY;
private Nodo izquierdo,derecho;
public Nodo(Comparable informacion, Nodo izquierdo, Nodo derecho)
{
this.izquierdo = izquierdo;
this.derecho = derecho;
this.informacion = (Comparable ) informacion;
}
public Nodo getDerecho()
{
return derecho;
}
public Nodo getIzquierdo()
{
return izquierdo;
}
public int getPosicionY()
{
return posicionY;
}
public int getPosicionX()
{
return posicionX;
}
public Comparable getInformacion()
{
return informacion;
}
}
A continuacin se muestra la implementacin de los mtodos mas importantes del rbol binario
de bsqueda.
public class ArbolBinarioDeBusqueda
{
private int totalNodosHorizontalEscala = 0; int alturaMaxima=0;
private String cadenaIngresada;
private Nodo root;
public ArbolBinarioDeBusqueda( )
{
root = null;
cadenaIngresada= new String();
}

160

public void recorrerEnInorder(Nodo t, int yCoordenada)


{
if (t != null)
{
recorrerEnInorder(t.getIzquierdo(), yCoordenada + 1);
//Agrega 1 a la coordenada en y
t.setPosicionX ( totalNodosHorizontalEscala++);
//Coordenada en x es el nmero de nodos contados con inorder
t.setPosicionY ( yCoordenada);
// se fija la coordenada en y con el valor de y
recorrerEnInorder(t.getDerecho(), yCoordenada + 1);
}
}
/**Permite hallar la altura del rbol
* @param t el nodo donde inicia */
public int hallarAlturaDelArbol(Nodo t)
{
if(t==null)
{
return -1;
}
else
{
return 1 +
obtenerMaximo(hallarAlturaDelArbol(t.getIzquierdo()),
hallarAlturaDelArbol(t.getDerecho()));
}
}
/** Halla el mximo de dos valores
*/
public int obtenerMaximo(int a, int b)
{
if(a>b)
{
return a;
}
else
{
return b;
}
}
public void calcularPosicionesDeNodos()
{
int contador = 1;
recorrerEnInorder(root, contador);
}
/** Permite insertar un elemento en el rbol
* @param x Es el elemento
*/
public void insertar( Comparable x ) throws Exception
{
root = insertarElemento( x, root );
}

161

/** Permite remover un elemento del rbol


* @param x el elemento que se va a eliminar

*/

public void remover( Comparable x ) throws


ElementoNoEncontradoException
{
root = eliminarElemento( x, root );
}
/** Permite eliminar el menor elemento del rbol */
public void removerElMenor( ) throws Exception
{
root = eliminarElMinimo( root );
}
/** Devuelve el menor elemento */
public Comparable hallarElMenor( )
{
return elementAt( hallarElMinimo( root ) );
}
/** Permite halla el mayor elemento del rbol
public Comparable hallarMaximo( )
{
return elementAt( hallarElMaximo( root ) );
}
public Comparable hallarElemento( Comparable x )
{
return elementAt( hallar( x, root ) );
}
/** Permite borrar el rbol*/
public void vaciarArbol( )
{
root = null;
}
/** Indica si el rbol est vaco */
public boolean estaVacio( )
{
return root == null;
}
private Comparable elementAt( Nodo t )
{
if(t==null)
{
return null;
}
else
{
return t.getInformacion();
}
}

162

*/

/** Inserta un nodo al rbol si es posible */


public Nodo insertarElemento( Comparable x, Nodo t ) throws
ElementoDuplicadoException
{
if( t == null )
{
t = new Nodo(x,null,null);
}
else if( x.compareTo( t.getInformacion() ) < 0 )
{
t.setIzquierdo(insertarElemento(x,t.getIzquierdo() ));
}
else if( x.compareTo( t.getInformacion() ) > 0 )
{
t.setDerecho(insertarElemento( x, t.getDerecho() ));
}
else
{
throw new ElementoDuplicadoException( x.toString( )+
" ya se encuentra en el rbol" );
}
return t;
}
/** Elimina un nodo del rbol */
public Nodo eliminarElemento( Comparable x, Nodo t )throws
ElementoNoEncontradoException
{
if( t == null )
{
throw new ElementoNoEncontradoException(x.toString( )+
" no fue encontrado" );
}
if( x.compareTo( t.getInformacion() ) < 0 )
{
t.setIzquierdo(eliminarElemento(x,t.getIzquierdo() ));
}
else if( x.compareTo( t.getInformacion ()) > 0 )
{
t.setDerecho (eliminarElemento( x, t.getDerecho()));
}
else if( t.getIzquierdo()!=null && t.getDerecho()!=null )
{
t.setInformacion(hallarElMinimo(t.getDerecho() ).
getInformacion() );
t.setDerecho (eliminarElMinimo( t.getDerecho() ));
}
else
{
t = ( t.getIzquierdo() != null ) ? t.getIzquierdo() :
t.getDerecho();
}
return t;
}

163

/** Elimina el menor elemento dentro del rbol */


public Nodo eliminarElMinimo( Nodo t )throws
ElementoNoEncontradoException
{
if( t == null )
{
throw new ElementoNoEncontradoException("No Hallado");
}
else if( t.getIzquierdo() != null )
{
t.setIzquierdo (eliminarElMinimo( t.getIzquierdo ()));
return t;
}
else{
return t.getDerecho();
}
}
/** Permite encontrar un elemento dentro del rbol */
private Nodo hallar( Comparable x, Nodo t )
{
while( t != null )
{
if( x.compareTo( t.getInformacion() ) < 0 )
{
t = t.getIzquierdo();
}
else if( x.compareTo( t.getInformacion() ) > 0 )
{
t = t.getDerecho();
}
else {
return t;
// Coincide
}
}
return null;
// No coincide
}
/** Encuentra el menor elemento dentro del rbol*/
public Nodo hallarElMinimo( Nodo t )
{
if( t != null ){
while( t.getIzquierdo ()!= null ){
t = t.getIzquierdo();
}
}
return t;
}
/** Encuentra el mximo elemento dentro del rbol */
private Nodo hallarElMaximo( Nodo t )
{
if( t != null ){
while( t.getDerecho() != null )
{
t = t.getDerecho();
}
}
return t;
}

164

// Metodos set y get


public Nodo getRoot()
{
return root;
}
public void setRoot(Nodo root)
{
this.root = root;
}
public int getAlturaMaxima()
{
return alturaMaxima;
}
public int getTotalNodosHorizontalEscala()
{
return totalNodosHorizontalEscala;
}
}

Determine el orden de complejidad para los mtodos:


recorrerEnInorder(Nodo t, int yCoordenada)

O(

hallarAlturaDelArbol(Nodo t)

O(

obtenerMaximo(int a, int b)

O(

calcularPosicionesDeNodos()

O(

insertarElemento( Comparable x, Nodo t )

O(

eliminarElemento( Comparable x, Nodo t )

O(

eliminarElMinimo( Nodo t )

O(

hallar( Comparable x, Nodo t )

O(

hallarElMinimo( Nodo t )

O(

hallarElMaximo( Nodo t )

O(

Escriba la versin iterativa para el mtodo hallarElMaximo( Nodo t )

165

Escriba la versin iterativa para el mtodo hallarElMaximo( Nodo t )

3.5

Grafos

Las estructuras de datos no lineales se caracterizan por no existir una relacin de adyacencia,
entre sus elementos, es decir, un elemento puede estar relacionado con cero, uno o ms
elementos.
Entre las mltiples aplicaciones que tienen estas estructuras podemos mencionar (Besembel,
2006) :

Los grafos son estructuras que se utilizan para modelar diversas situaciones tales
como: sistemas de aeropuertos, flujo de trfico.
Los grafos tambin son utilizados para realizar planificaciones de actividades, tareas
del computador, planificar operaciones en lenguaje de mquinas para minimizar tiempo
de ejecucin.
Los grafos pueden ser utilizados como la estructura bsica para mltiples aplicaciones
en el rea de la Computacin.

Existe una gran cantidad de problemas que se puede formular en trminos de grafos. Para la
solucin de estos es posible que sea necesario examinar todos los nodos o todas las aristas
del grafo. En algunos casos se imponen el orden en que deben ser examinados (recorridos)
estos nodos.

3.5.1

Conceptos fundamentales de los Grafos

Segn (Hernndez G. , 2004), un grafo es un par G = (V,A), donde V es un conjunto finito no


vaco (a cuyos elementos llamaremos vrtices) y A es una familia finita de pares no ordenados
de vrtices de V (a cuyos elementos llamaremos aristas o arcos). Un grafo simple es un par
G=(V,A) donde V es un conjunto finito no vaco y A es un conjunto finito de pares no ordenados
de vrtices distintos de V.
Con base en lo anterior, un grafo es un conjunto compuesto por nodos y los cuales estn
conectados por aristas. Notacionalmente hablando si las aristas no tienen direccin
especificada mediante una flecha terminal, se afirma que es un grafo no dirigido. En caso
contrario se trata de un grafo dirigido.
A continuacin se muestran los conceptos bsicos de los grafos dirigidos y no dirigidos.

3.5.2

Grafos Dirigidos

Segn (Universidad Autnoma Metropolitana , 2010), Un grafo dirigido, o dgrafo, es un par


(
) donde V es un conjunto cuyos elementos se llaman vrtices y E es un conjunto de
pares no ordenados de elementos distintos de V. Los vrtices tambin suelen llamarse nodos.

166

Los elementos de E se llaman aristas, o aristas dirigidas, o arcos. Para la arista dirigida
(
) en E, v es su cola y w es su cabeza; (
) se representa en los diagramas con la flecha
.
Grficamente se muestra la estructura de un grafo dirigido.

Una alternativa respecto a la representacin con matriz de adyacencia es un arreglo indizado


por nmero de vrtice que contiene listas ligadas llamadas listas de adyacencia. La siguiente
es la representacin grfica.

En los casos para los que los grafos representan a nivel computacional la situacin real,
comnmente se tiene en cuenta otro tipo de variable dada por el peso de cada uno de los
arcos, este pero puede representar costo, distancia, tiempo u cualquier otra medida de
conexin entre los vrtices.

3.5.3

Grafos No Dirigidos

Segn (Universidad Autnoma Metropolitana , 2010), un grafo no dirigido es un par


(
)
donde V es un conjunto cuyos elementos se llaman vrtices y E es un conjuntos de pares no
ordenados de elementos distintos de V. Los vrtices tambin suelen llamarse nodos. Los
elementales de E se llaman aristas, o aristas no dirigidas, para hacer hincapi. Cada arista se
puede considerar como un subconjunto de V que contiene dos elementos; as *
+ denota
una arista no dirigida. En los diagramas esta arista es la lnea
. En el texto escribiremos
simplemente
.

167

Asociados a los grafos se adiciona una terminologa frecuentemente utilizada, a continuacin


se definen los ms comunes.

Camino: Se define como camino a una secuencia de vrtices y que se encuentra


conectados por arcos. La longitud de este camino es n-1.

Camino simple: Es un camino en el que ningn vrtice es igual a otro.

Ciclo: Es un camino en el que el primer nodo es igual al ltimo, este ciclo ser simple
si el camino tambin lo es, para grafos dirigidos. En grafos no dirigidos es necesario
que los arcos sean distintos.

Puesto que el nmero de posibles aristas es


en un grafo dirigido, o de n (n-1)/2 en un grafo
no dirigido, la complejidad de tales algoritmos estar en ( ).

3.6

Arboles de Recubrimiento Mnimo

Un rbol de recubrimiento mnimo de un grafo, se identifica como un conjunto de aristas que


permiten la conexin de todos los vrtices del mismo, y cuya suma de los pesos de las aristas
es menor en relacin con la suma de cualquier otro conjunto de aristas que conecten a todos
los vrtices del grafo.
Entre los algoritmos mas comunes de recubrimiento mnimo se encuentra el algoritmo de Prim
y el de Kruskal. A continuacin se analiza cada uno de ellos.

3.6.1

Algoritmo de Prim

Es un algoritmo usado en la teora de grafos y tienen como objetivo encontrar un rbol de


recubrimiento mnimo. El algoritmo analiza el grafo y encuentra una serie de aristas en la cual
el peso total de las mismas es el mnimo posible.
A continuacin se muestra una implementacin para el algoritmo de Prim.
Descripcin

Esta implementacin permite encontrar el camino mnimo de un


grafo.

Mtodo

public int[] caminoMinimo(int[][] graf, int valor)


{
int nodos = graf.length;
int Solucion[] = new int[nodos];
int minimo = 0, menor = 0, nodoact = valor;
Solucion[0] = valor;

168

while ( minimo < ( nodos - 1 ) )


{
int i = 1;
while (i < nodos)
{
if ( graf[nodoact][i] <
graf[nodoact][menor]
&& !estaEn( Solucion, i ) )
{
menor = i;
}
i++;
}
minimo++;
nodoact = menor;
Solucion[minimo] = nodoact;
}
return Solucion;
}

Anlisis del
Orden de
complejidad

3.6.2

public boolean estaEn(int[] arreglo, int i)


{
int j;
for ( j = 0; j < arreglo.length; ++j)
{
if ( arreglo[j] == i )
{
break;
}
}
if ( j < arreglo.length )
{
return true;
}
else
{
return false;
}
}
El bucle principal se ejecuta n - 1 veces; en cada iteracin, el bucle
para anidado requiere un tiempo que est en O(n). Por lo tanto, el
2
algoritmo de Prim requiere un tiempo que est en O(n ).

Algoritmo de Kruskal

Segn (Colmenares, 2006), sea G=(V,A) un grafo ponderado y conexo; el conjunto de aristas
de candidatas es A. Sea G=(V,T) ; donde T es un conjunto de aristas que comienza vaco.
Luego se selecciona en cada etapa la arista de menor peso que todava no haya sido
inspeccionada (seleccionada o rechazada) independientemente de donde se encuentra dicha
arista. Inicialmente el conjunto de aristas T est vaco y a medida que progresa el algoritmo se
van aadiendo aristas a T. Mientras no se haya encontrado la solucin, el grafo parcial que
contiene los nodos de G y las aristas de T consta de varias componentes conexas. Si una
arista une a dos vrtices de componentes conexas distintas, entonces la aadimos a T, y ahora
forman una sola componente conexa. En caso contrario se rechaza la arista. El algoritmo se
detiene cuando queda una sola componente conexa.

169

ACTIVIDAD

Implemente algoritmo de Kruskal de forma tal que pueda encontrar en camino mnimo en un
grafo.

Mtodo

Anlisis del
Orden de
complejidad

3.6.3

Algoritmo de Dijkstra

Como se mencion en captulos anteriores, el Algoritmo de Dijkstra, determina el camino ms


corto entre un determinado vrtice y el resto de vrtices de un grafo, en donde cada una de las
aristas contiene un peso.
Para el anlisis del algoritmo de Dijkstra se va a contextualizar en una aplicacin en una
empresa de telecomunicaciones la cual requiere tender fibra ptica entre dos ciudades. Por ello
debe elegir el camino a seguir de tal forma que los costos se reduzcan.
Se debe permitir:

Ingresar el grafo que corresponde a como las ciudades estn comunicadas


Borrar el grafo ingresado
Arrojar el camino menos costoso, haciendo uso del algoritmo de Dikjstra

170

a. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
Dijkstra

DESCRIPCIN
Es la clase principal del mundo

b. Diagrama de clase

c. Implementacin
A continuacin se muestra la implementacin de los principales mtodos del algoritmo de
Dijkstra en el contexto de la empresa de comunicaciones.
import java.util.Stack;
public class Dijkstra
{
//Constante para indicar que no hay camino
private final int INF=Integer.MAX_VALUE; //Infinito
private int noExiste=8;
public int numVertices, primerNodo=0, ultimoVertice;
private int[] predecessor, []distancia;
private String[] misNombres;
private boolean arregloAuxiliar[];
private int pesos[][];
/* Constructor de la clase Dijkstra*/
public Dijkstra(int pesos[][],int numVertices,String misNombres[])
{
this.pesos=pesos;
this.numVertices=numVertices;
this.misNombres=misNombres;
distancia=new int[numVertices];
arregloAuxiliar=new boolean[numVertices];
predecesor=new int[numVertices];
ultimoVertice=numVertices-1;
}

171

/*Mtodo principal del algoritmo de Dijkstra */


public void aplicarDijkstra(int origen)
{
int cont,posicion;
int i;
distancia[origen]=0;
for(cont=primerNodo; cont<=ultimoVertice; cont++)
{
if(cont!=origen) distancia[cont]=INF;
arregloAuxiliar[cont]=false;
predecesor[cont]=noExiste;
}
for(i=0; i<numVertices; i++)
{
posicion=encontrarMinima();
arregloAuxiliar[posicion]=true;
if(distancia[posicion]==INF)
{
continue;
}
for(cont=primerNodo; cont<=ultimoVertice; cont++)
{
if(devolverSucesor(posicion,cont) &&
!arregloAuxiliar[cont] && (distancia[posicion]+
pesos[posicion][cont]<distancia[cont]))
{
distancia[cont]=distancia[posicion]+
pesos[posicion][cont];
predecesor[contador]=posicion;
}
}
}
}
private int encontrarMinima()
{
int j,posicion;
for(j=primerNodo; j<ultimoVertice; j++)
{
if(!arregloAuxiliar[j])
{
break;
}
}
assert(j<=ultimoVertice);
posicion=j;
for(j++; j<=ultimoVertice; j++)
{
if(!arregloAuxiliar[j]&&distancia[j]<distancia[posicion])
{
posicion=j;
}
return posicion;
}

172

public String devolverRutaMasCorta(int origen, int llegada)


{
String salida="";
assert(origen!=noExiste && llegada!=noExiste);
aplicarDijkstra(origen);
salida+=" El camino menos costoso de "+misNombres[origen]+
" a "+misNombres[llegada]+" es:\n";
Stack<Integer> miStack=new Stack<Integer>();
for(int v=llegada; v!=origen; v=predecesor[v])
{
if(v==noExiste)
{
salida=("No existe camino,el grafo no est conectado");
return salida;
}
else
{
miStack.push(v);
}
}
miStack.push(origen);
while(!miStack.empty())
{
salida+=misNombres[miStack.pop()]+" -> ";
}
salida=salida.substring(0,salida.length()-3);
return salida;
}
private boolean devolverSucesor(int x, int y)
{
return ((pesos[x][y]!=INF) && x!=y);
}
}

ACTIVIDAD

Analice la clase anterior y escriba un comentario en torno a su funcionamiento.

173

3.7

Arboles n-arios

Un rbol n-rio es un rbol cuyos nodos tienen n o menos hijos. Es de anotar que n puede ser
cualquier valor entero positivo. En n-rio no es importante el orden, a diferencia de lo que
ocurre en el rbol binario de bsqueda.

tit

tigre

titi

cocodrilo

pez

pantera

Este tipo de rboles puede ser recorrido mediante preorden, logrando de esta forma que cada
nodo aparezca una sola vez al realizar el listado de los elementos del rbol. En este tipo de
rbol se requiere que se indique al momento de realizar una insercin, el sitio en el cual deber
ser agregado.
Las definiciones expuestas para rboles binarios pueden perfectamente ajustarse a este tipo
de rboles.
Para iniciar la construccin de nuestra aplicacin empezaremos con una porcin de la clase
ArbolNArio. A continuacin debe analizar el mtodo recorrer y escribir una interpretacin en
torno a la labor que realiza
public class ArbolNArio<T>
{
private Nodo<T> raiz;
private boolean asignado=false;
private Puesto miPuesto=null;
public String recorrer()
{
ArrayList <Nodo<T>> misNodos = new ArrayList<Nodo<T>>();
recorrerEnInorden(raiz, misNodos);
return misNodos.toString();
}
private void recorrerEnInorden(Nodo<T> miNodo,List<Nodo<T>>miLista)
{
miLista.add(miNodo);
for (Nodo<T> info : miNodo.getHijos())
{
recorrerEnInorden(info, miLista);
}
}

174

public void asignar(String cedula, String nombre, String


direccion, String puesto)
{
asignado=false;
if((((Puesto)(raiz.getInformacion())).getNombre()).
equals(puesto))
{
raiz.asignarPersona(cedula, nombre, direccion, puesto);
if(raiz.isAsignado()==true)
{
asignado=true;
}
}
else
{
ArrayList <Nodo<T>> misNodos= new ArrayList<Nodo<T>>();
recorrerParaAsignar(raiz, misNodos,cedula, nombre,
direccion, puesto);
}
if(asignado==true)
{
asignado=false;
}
}
private void recorrerParaAsignar(Nodo<T> miNodo, List<Nodo<T>>
miLista,String cedula, String nombre, String direccion,
String puesto)
{
miLista.add(miNodo);
for (Nodo<T> info : miNodo.getHijos())
{
info.asignarPersona(cedula, nombre, direccion, puesto);
if(info.isAsignado()==true)
{
asignado=true;
}
recorrerParaAsignar(info, miLista,cedula, nombre,
direccion, puesto);
}
}
public void setRaiz(Nodo<T> rootElement)
{
this.raiz = rootElement;
}
public Nodo<T> getRaiz()
{
return this.raiz;
}
}

Un fragmento de la clase Nodo se presenta a continuacin, se debe completar la clase.


public class Nodo<Tipo>
{
public Tipo informacion;
public ArrayList<Nodo<Tipo>> hijos;

175

boolean asignado=false;
public Nodo(Tipo informacion)
{
this.informacion =________________________;
}
public boolean isAsignado()
{
return asignado;
}
public void setAsignado(boolean asignado)
{
this.asignado = asignado;
}
public void setHijos(ArrayList<Nodo<Tipo>> hijos)
{
this.hijos = hijos;
}
public Tipo getInformacion()
{
return informacion;
}
public void setInformacion(Tipo informacion)
{
this.informacion = informacion;
}
public ArrayList <Nodo<Tipo>> getHijos()
{
if (hijos != null)
{
return hijos;
}
else
{
return new ArrayList<Nodo<Tipo>>();
}
}
public void agregarHijo(Nodo<Tipo> hijo)
{
if (hijos == null)
{
________= new ArrayList<Nodo<Tipo>>();
}
hijos.add(hijo);
}
public void eliminarHijoPosicion(int posicion) throws
IndexOutOfBoundsException
{
hijos.remove(posicion);
}

176

public String toString()


{
StringBuilder concatenador = new StringBuilder();
concatenador.append("{").append(((Puesto)informacion).
getMiPersona().getNombre().toString()).append(",[");
int i = 0;
if(hijos==null)
{
hijos=new ArrayList<Nodo<Tipo>>();
}
for (Nodo<Tipo> miNodo : hijos)
{
if (i > 0)
{
concatenador.append(",");
}
concatenador.append(((Puesto)miNodo.getInformacion()).
getMiPersona().getNombre().toString());
i++;
}
concatenador.append("]").append("}");
return concatenador.toString();
}
public void asignarPersona(String cedula, String nombre, String
direccion, String puesto)
{
asignado = false;

}
}

3.8

Actividad Independiente: Organigrama de empresa

Se desea crear una aplicacin que permita asignar personas de acuerdo al cargo que ocuparn
dentro del organigrama de una empresa.
La aplicacin debe permitir crear un el organigrama por defecto (para ello se deben agregar los
cargos especificados en el grfico anterior), asignar un puesto (un puesto tiene un nombre y
una persona asignada), consultar que persona labora en un puesto determinado y mostrar el
recorrido en preorden del organigrama. Es de anotar que una persona tiene una cedula, un
nombre y una direccin asociada.

177

Se puede ver que el organigrama que debemos trabajar no corresponde a un rbol binario, por
el contrario estamos trabajando un rbol n-ario.

3.9

Arboles AVL

La solucin de este caso de estudio requiere de la comprensin del concepto de rbol AVL. Se
le denomin AVL por los nombres de sus creadores Adelson-Velskii y Landis. Es un rbol que
adems de ser binario de bsqueda cumple con la condicin de que para cada uno sus nodos,
la diferencia entre las alturas de sus subrboles es como mximo 1.
Gracias al equilibrio logrado es posible asegurarnos de que la profundidad del rbol sea
O(log(n)), logrando de esta forma que el tiempo de ejecucin de las operaciones que se
ejecutan sobre dichos rboles sea como mximo (log(n)) en el peor caso, siendo n la cantidad
de elementos. No obstante, es de resaltar que mantener esta propiedad de equilibrio agregue
una dificultad adicional a la hora de insertar y eliminar datos.
Es importante que tengas claro que el factor de equilibrio (FE) es igual a restar a la altura del
subrbol derecho la altura del subrbol izquierdo, en caso de que de un valor diferente de 1, -1
0 podemos concluir que no es AVL

3.9.1

Elementos de los rboles AVL

Los rboles AVL son fundamentalmente arboles de bsqueda con una condicin particular de
equilibrio entre sus subrboles. Esa condicin se fundamenta en que la altura de cada uno de
sus subrboles (izquierdo y derecho) no pueden diferenciarse en a los sumo una unidad.
Lo condicin de equilibrio entre los subrboles esta dado por un rango comprendido entre -1, 0
y 1. Si en determinado momento este rango no corresponde, se debe aplicar un procedimiento
de rotacin que se muestra posteriormente en este libro.

178

El siguiente corresponde a un rbol AVL, ya que la condicin de equilibrio se encuentra entre 1, 0 y 1.

El siguiente rbol muestra que no es un rbol AVL, el motivo esta sustentado en que la
condicin de equilibrio no se encuentra entre -1, 0 y 1. La diferencia entre la altura de los
subrboles derecho e izquierdo en de 2 unidades.

A continuacin se muestran las operaciones fundamentales de los arboles AVL.

3.9.2

Operaciones de los rboles AVL

A continuacin se muestran las operaciones fundamentales que se pueden realizar con los
arboles AVL.

Insercin en un rbol AVL

Pueden surgir cuatro problemas al momento de realizar una insercin, a continuacin se


presentan cuatro posibles casos:
1) Se inserta un nodo en el subrbol izquierdo del hijo izquierdo de Y, siendo Y el nodo que
ha perdido el equilibrio
2) Se inserta un nodo en el subrbol derecho del hijo izquierdo de Y
3) Se inserta un nodo en el subrbol izquierdo del hijo derecho de Y
4) Se inserta un nodo en el subrbol derecho del hijo derecho de Y.
Para resolver los casos 1 y 4 se utiliza una rotacin simple, mientras que para los casos 2 y 3
(que son los ms complejos, puesto que la insercin se realiza en el interior del rbol) se
resuelven mediante rotaciones dobles.

179

Caso 1: Para este caso se usa una rotacin simple a la izquierda (RSI). Recuerda que el
problema surgi porque se insert un nodo en el subrbol izquierdo del hijo izquierdo de Y,
siendo Y el nodo que ha perdido el equilibrio.

El esquema de rotacin ha usar se muestra a continuacin:

rbol antes de la rotacin

rbol despus de la rotacin simple

Para nuestro ejemplo particular: y= 13, puesto que fue quien perdi el equilibrio, x=12, A=10.
Entonces el rbol quedara con la siguiente estructura luego de la rotacin.

180

ACTIVIDAD

Inserte 1 al rbol e identifique si ha perdido su condicin de equilibrio. Si es as, entonces


realice una rotacin para restaurarla.
151
7

17

53

18

20

Caso 4: para este caso particular se usa una rotacin simple a la derecha (RSD). Recuerda
que el problema surgi porque se insert un nodo en el subrbol derecho del hijo derecho de Y.
A continuacin se presenta un rbol en el cual se aplica este tipo de rotacin.

rbol antes de la rotacin

rbol despus de la rotacin simple del 7

Este problema fue resuelto siguiendo el esquema de rotacin presente a continuacin:

Se puede observar que el nodo que ha quedado desequilibrado es 7. De acuerdo a nuestro


grfico anterior entonces los valores de los nodos son: y= 7, x=8 y C=9.

181

ACTIVIDAD

Plantee un rbol y una posible insercin sobre ste que implique la prdida de equilibrio y para
su solucin requiera una rotacin simple a la derecha. Ilustre el proceso seguido.

Caso 2: Para este caso particular se usa una rotacin doble izquierda derecha. Recuerde que
el problema surgi porque se insert un nodo en el subrbol derecho del hijo izquierdo de Y.

El rbol original

El rbol luego de insertar 4

Se puede observar que la insercin ha afectado al nodo 6, ya que se insert un nodo en el


subrbol derecho del hijo izquierdo de Y. Por lo tanto debemos seguir el siguiente esquema
que corresponde a una rotacin doble:

Rotacin doble izquierda derecha que soluciona el caso dos

182

Para este caso particular, se muestra a continuacin el rbol despus la insercin y


posteriormente la rotacin.

rbol luego de la insercin


rbol despus de la rotacin
En este caso el los valores del rbol despus de la rotacin son: y = 6, z= 3 y x= 4
Caso 3: Para este caso particular se usa una rotacin doble. Recuerde que el problema surgi
porque se insert un nodo en el subrbol izquierdo del hijo derecho de Y.

Rotacin doble derecha izquierda para solucionar el caso 3


A continuacin se muestra la secuencia desde el rbol original hasta el rbol con el nodo
insertado.

rbol original

rbol despus de la insercin de 19

183

Se observa que en este caso se ha perdido el equilibrio en el elemento 15. Por lo tanto: y=15,
z=20 y x=19. Con lo cual nuestro rbol resultante sera:

ACTIVIDAD

Dada la secuencia de valores enteros 18, 10, 31, 5, 22, 12, 3, 37, 24, 11, 6, 2, representa
grficamente el rbol AVL que surge e indica los momentos en los cuales debiste efectuar la
rotacin.
Identificar cules de los siguientes rboles binarios de bsqueda son AVL. Los que no lo sea
deben marcarse e indicar todos los nodos que violen el equilibrio.

3.10 Caso de estudio Grupo de estudiantes


Se desea crear una aplicacin que permita manejar el registro de notas de un grupo de 10
estudiantes. Cada estudiante tiene un cdigo y dos notas parciales. Se debe permitir:

Permitir calcular la nota definitiva de cada estudiante


Listar de forma ordenada por cdigo las notas definitivas del grupo
Calcular el porcentaje de estudiantes que aprobaron el curso

184

Es de anotar que el objetivo de este caso de estudio es introducir el concepto de rbol AVL.

a. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
Persona

DESCRIPCIN

Nodo
ArbolAvl
Luego de haber entendido los conceptos referentes a rboles AVL, podemos proceder a la
construccin de la aplicacin
b. Implementacin de las clases
public class Persona
{
private int codigoEstudiante;
private double nota1, nota2, notaDefinitiva;
/* Mtodo constructor de la clase Estudiante
*/
public Persona(int codigoEstudiante, double nota1, double nota2 )
{
this.codigoEstudiante=codigoEstudiante;
this.nota1=nota1;
this.nota2=nota2;
}
/**Permite calcular la nota definitiva*/
public double calcularDefinitiva()
{
notaDefinitiva=(nota1+nota2)/2;
return notaDefinitiva;
}

185

/**Devuelve nota definitiva*/


public double getNotaDefinitiva()
{
return notaDefinitiva;
}
/**Devuelve un String con la informacin del estudiante
* @return String Puede tomar los valores de "Perdi" o "Gan" */
public String determinarEstado()
{
if(notaDefinitiva<3)
{
return "Perdi";
}
else
{
return "Gan";
}
}
public int getCodigoEstudiante(){
return codigoEstudiante;
}
}
La siguiente es la implementacin de la clase Nodo para los arboles AVL.
public class Nodo<T>
{
private Persona miInformacion;
private Nodo<T> izquierdo, derecho;
private int
altura;
public Nodo( Persona informacion ){
this( informacion, null, null );
}
public Nodo( Persona informacion, Nodo<T> izquierdo,
Nodo<T> derecho )
{
this.miInformacion = informacion;
this.izquierdo
= izquierdo;
this.derecho
= derecho;
this.altura
= 0;
}
public Persona getInformacion()
{
return miInformacion;
}
public Nodo<T> getIzquierdo()
{
return izquierdo;
}
public Nodo<T> getDerecho()
{
return derecho;
}

186

public int getAltura()


{
return altura;
}
}
A continuacin se muestra la implementacin de los principales mtodos de la clase Arbol AVL.
public class ArbolAvl<T extends Comparable<? super T>>
{
private Nodo<T> raiz;
private String salida, array[];
private int contador,i;
public ArbolAvl( )
{
salida="";
raiz = null;
contador=0;
i=0;
}
/** Inserta un nuevo elemento en el rbol.
* @param nuevo es el elemento que se desea insertar */
public void insertarElemento( Persona nuevo ) throws Exception
{
raiz = insertar( nuevo, raiz );
contador++;
}
/**Metodo para insertar un dato en un subrbol.
* @param nuevo La informacin a insertar.
* @param miNodo El nodo que es la raiz en el subrbol.
* @return La nueva raz en el subrbol.
*/
private Nodo<T> insertar( Persona nuevo, Nodo<T> miNodo )
throws Exception
{
if( miNodo == null )
{
return new Nodo<T>( nuevo, null, null );
}
int resultadoComparacion = nuevo.compareTo( miNodo.
getInformacion() );
if( resultadoComparacion < 0 )
{
miNodo.setIzquierdo ( insertar( nuevo,
miNodo.getIzquierdo() ));
if( darAltura( miNodo.getIzquierdo ()) - darAltura( miNodo.
getDerecho() ) == 2 )
{
if(nuevo.compareTo(miNodo.getIzquierdo().
getInformacion())< 0 )
{
miNodo = rotarConHijosALaIzquierda( miNodo );
}

187

else
{
miNodo = rotarDoubleHijosIzquierda( miNodo );
}
}
}
else if( resultadoComparacion > 0 )
{
miNodo.setDerecho(insertar(nuevo, miNodo.getDerecho ()));
if( darAltura( miNodo.getDerecho() ) - darAltura( miNodo.
getIzquierdo ()) == 2 )
{
if(nuevo.compareTo(miNodo.getDerecho().
getInformacion())>0)
{
miNodo = rotarConHijosALaDerecha( miNodo );
}
else
{
miNodo = rotarDobleHijosDerecha( miNodo );
}
}
}
}
else
{
//Se descartan los duplicados
throw new Exception("El elemento est duplicado: "+nuevo.
getCodigoEstudiante());
}
miNodo.setAltura (Math.max( darAltura( miNodo.getIzquierdo() ),
darAltura( miNodo.getDerecho() ) ) + 1);
return miNodo;
}
/** Halla el elemento ms pequeo del rbol
* @throws Exception cuando el rbol esta vaco */
public Persona hallarMinimo( ) throws Exception
{
if( !estaVacio( ) ){
return hallarMinimo( raiz ).getInformacion();
}
else
{
throw new Exception("El rbol est vaco");
}
}
/** Halla el dato ms pequeo en un subrbol
* @param miNodo El nodo raz en el subrbol */
private Nodo<T> hallarMinimo( Nodo<T> miNodo )
{
if( miNodo == null ){
return miNodo;
}
while( miNodo.getIzquierdo() != null ){
miNodo = miNodo.getIzquierdo();
}
return miNodo;
}

188

/**Halla el elemento ms grande del rbol


* @throws Exception Se genera cuando el rbol est vaco */
public Persona hallarMaximo( ) throws Exception
{
if(! estaVacio( ) )
{
return hallarMaximo( raiz ).getInformacion();
}
else
{
throw new Exception( "El arbol est vaco ");
}
}
/**Metodo para hallar el dato ms grande en un subrbol
* @param miNodo El nodo raz en el subarbol*/
private Nodo<T> hallarMaximo( Nodo<T> miNodo )
{
if( miNodo == null )
{
return miNodo;
}
while( miNodo.getDerecho() != null )
{
miNodo = miNodo.getDerecho();
}
return miNodo;
}
/** Halla un elemento en el rbol, true si lo encuentra.
* @param elemento Es el elemento buscado */
public boolean estaContenido( T elemento )
{
return contener( elemento, raiz );
}
/**Halla un item en el subrbol, true si es hallado
* @param buscado es el elemento que se quiere buscar.
* @param miNodo el nodo que es la raz en el rbol*/
private boolean contener( T buscado, Nodo<T> miNodo )
{
while( miNodo != null )
{
int compareResult =
buscado.compareTo((T)miNodo.getInformacion());
if( compareResult < 0 )
{
miNodo = miNodo.getIzquierdo();
}
else if( compareResult > 0 )
{
miNodo = miNodo.getDerecho();
}
else{
return true;
// Coincide
}
}
return false;
// No coincide
}

189

/** Elimina el rbol */


public void vaciarArbol( )
{
raiz = null;
}
/** Indica si el rbol est vaco
* @return Si est vaco devuelve true, sino false */
public boolean estaVacio( )
{
return raiz == null;
}
/** Imprime el contenido del rbol */
public void imprimirArbol( ) throws Exception
{
if( !estaVacio( ) )
{
imprimir( (raiz) );
}
else
{
throw new Exception("El rbol est vacio");
}
}
/** Permite imprimir el rbol en inorder
* @param miNodo Es la raz del rbol */
private void imprimir( Nodo<T> miNodo )
{
if( miNodo != null ){
imprimir( miNodo.getIzquierdo() );
System.out.println( "Codigo "+ miNodo.getInformacion().
getCodigoEstudiante()+" Nombre "+ miNodo.getInformacion().
getNotaDefinitiva() );
imprimir( miNodo.getDerecho() );
}
}
private int devolverCantidadNodos( Nodo<T> miNodo )
{
return contador;
}
public String [] listarConDefinitivas( ) throws Exception
{
i=0;
array=new String[devolverCantidadNodos(raiz)];
if( !estaVacio( ) )
{
imprimirPorDefinitiva( raiz );
return array;
}
else
{
throw new Exception("El rbol est vacio");
}
}

190

/** Imprime la nota definitiva */


private void imprimirPorDefinitiva( Nodo<T> miNodo )
{
if( miNodo != null )
{
imprimirPorDefinitiva( miNodo.getIzquierdo() );
array[i] ="Codigo "+ miNodo.getInformacion().
getCodigoEstudiante() +
"Definitiva"+miNodo.getInformacion().getNotaDefinitiva() ;
i++;
imprimirPorDefinitiva( miNodo.getDerecho());
}
}
/** Retorna el porcentaje de estudiantes que perdieron */
public double obtenerPorcentajePerdieron()throws Exception
{
return 100-obtenerPorcentajeGanaron();
}
/** Retorna el porcentaje de estudiantes que ganaron. */
public double obtenerPorcentajeGanaron( ) throws Exception
{
double porcentaje=0;
i=0;
if( !estaVacio( ) )
{
devolverPorcentajeGanan( raiz );
return (i*100)/devolverCantidadNodos(raiz);
}
else
{
throw new Exception("El rbol est vacio");
}
}
private void devolverPorcentajeGanan( Nodo<T> miNodo)
{
if( miNodo != null )
{
devolverPorcentajeGanan( miNodo.getIzquierdo() );
if(miNodo.getInformacion().getNotaDefinitiva()>=3)
{
i++;
}
devolverPorcentajeGanan( miNodo.getDerecho());
}
}
/** Devuelve la altura del nodo, si es null entonces manda -1 */
private int darAltura( Nodo<T> miNodo )
{
if(miNodo == null )
{
return -1;
}

191

else
{
return
}

miNodo.getAltura();

}
/** Rotacin simple caso 1 */
private Nodo<T> rotarConHijosALaIzquierda( Nodo<T> miNodo )
{
Nodo<T> k1 = miNodo.getIzquierdo();
miNodo.setIzquierdo ( k1.getDerecho());
k1.setDerecho ( miNodo);
miNodo.setAltura(Math.max( darAltura( miNodo.getIzquierdo()),
darAltura( miNodo.getDerecho()))+1);
k1.setAltura ( Math.max( darAltura( k1.getIzquierdo() ),
miNodo.getAltura() ) + 1);
return k1;
}
/** Rotacion simple caso 4 */
private Nodo<T> rotarConHijosALaDerecha( Nodo<T> miNodo )
{
Nodo<T> k2 = miNodo.getDerecho();
miNodo.setDerecho (k2.getIzquierdo());
k2.setIzquierdo( miNodo);
miNodo.setAltura(Math.max( darAltura( miNodo.getIzquierdo() ),
darAltura( miNodo.getDerecho() ) ) + 1);
k2.setAltura ( Math.max( darAltura( k2.getDerecho() ),
miNodo.getAltura())+1);
return k2;
}
/** Rotacin doble caso 2 */
private Nodo<T> rotarDoubleHijosIzquierda( Nodo<T> miNodo )
{
miNodo.setIzquierdo(rotarConHijosALaDerecha(
miNodo.getIzquierdo() ));
return rotarConHijosALaIzquierda( miNodo );
}
/** Rotacion doble caso 3 */
private Nodo<T> rotarDobleHijosDerecha( Nodo<T> miNodo )
{
miNodo.setDerecho ( rotarConHijosALaIzquierda(
miNodo.getDerecho() ));
return rotarConHijosALaDerecha( miNodo );
}
}

ACTIVIDAD

Determine el orden de complejidad para los mtodos:


hallarMinimo( )

O(

192

hallarMaximo( )

O(

estaContenido( T elemento )

O(

listarConDefinitivas( )

O(

imprimirPorDefinitiva( Nodo<T> miNodo )

O(

obtenerPorcentajeGanaron( )

O(

darAltura( Nodo<T> miNodo )

O(

rotarConHijosALaIzquierda( Nodo<T> miNodo )

O(

rotarConHijosALaDerecha( Nodo<T> miNodo )

O(

rotarDoubleHijosIzquierda( Nodo<T> miNodo )

O(

rotarDobleHijosDerecha( Nodo<T> miNodo )

O(

Analice cual debe ser siempre el orden de complejidad de la altura de un rbol AVL.

Analice cual debe ser siempre el orden de complejidad de la insercin de un rbol AVL.

3.11 Backtracking
El backtracking (conocido tambin como vuelta atrs) permite encontrar soluciones a
problemas. Su funcionamiento podra verse como una bsqueda sistemtica, en la cual
bsicamente lo que se hace es probar todo lo posible hasta encontrar la solucin o determinar
que no existe solucin para dicho problema. Este tipo de algoritmos presentan naturaleza
recursiva. Se caracterizan adems porque en caso de no hallar solucin a una subtarea se
vuelva a la subtarea original y se ensaya por otra ruta diferente a las que ya se han probado.

Ejemplos tpicos de juegos en los cuales se aplica backtracking son el laberinto, la ubicacin de
8 reinas en un tablero de ajedrez de forma que no se ataquen, pacman, juegos de carros, entre
otros.

193

3.12 Caso de estudio: Reinas


Se desea realizar una Aplicacin que permita jugar Ubicacin de Reinas. Dicho juego
pretende que el usuario coloque 8 reinas de forma que ellas no se ataquen entre s.
La aplicacin deber examinar si las reinas puestas por el usuario se atacan, en cuyo caso
mostrar las posiciones de ataque. De igual forma se deber brindar la opcin de que el
computador solucione el juego.

a. Identificar las entidades o clases.


ENTIDAD DEL MUNDO
Reinas

DESCRIPCIN
Es la clase principal del mundo

b. Diagrama de Clases

194

c. Implementacin de los mtodos


A continuacin se muestra la implementacin de los principales mtodos de la clase Reinas.
public class Reinas
{
private int resultado[], contador;
private String salida;
/** Retorna verdadero si la ubicacin no causa conflicto
* @param q[]. Arreglo que contiene las posiciones q!=null,n>=0
* @param n. Posicin a examinar para determinar si es
* consistente */
public
{

boolean determinarSiEsConsistente(int[] q, int n)


for (int i = 0; i < n; i++)
{
if (q[i] == q[n])
{
salida= q[i]+","+i+ ","+q[n] +","+n;
return false;
// igual columna
}
if ((q[i] - q[n]) == (n - i))
{
salida= q[i]+","+i+ ","+q[n] +","+n;
return false;
// igual diagonal principal
}
if ((q[n] - q[i]) == (n - i))
{
salida= q[i]+","+i+ ","+q[n] +","+n;
return false;
// igual diagonal secundaria
}
}
return true;

}
/** Permite llenar el array con la ubicacin de las reinas
* @param q: arreglo que contiene las posiciones q!=null */
public void obtenerUbicacionReinas(int[] q)
{
resultado=new int[8];
for (int i = 0; i <q.length; i++)
{
resultado[i]=q[i];
}
}
/** Considera las permutaciones posibles usando backtracking
* @param n. Es el tamao del arreglo n>0 */
public void enumerarPosibilidar(int n)
{
int[] a = new int[n];
resultado=new int[8];
contador=0;
salida="";
enumerarPosibilidad(a, 0,(int)(Math.random()*8));
}

195

/** Permite enumerar las posibilidades


* @param q. Es el arreglo q!=null
* @param n es la posicin a obtener n>=0
* @param aleatorio. Para ubicar las reinas */
public void enumerarPosibilidad(int[] q, int n,
int aleatorio)
{
int N = q.length;
if (n == N)
{
contador++;
if(contador==aleatorio)
{
obtenerUbicacionReinas(q);
}
}
else
{
for (int i = 0; i < N; i++)
{
q[n] = i;
if (determinarSiEsConsistente(q, n))
{
enumerarPosibilidad(q, n+1,aleatorio);
}
}
}
}
/** Determinar si un arreglo lleno es consistente: se debe
examinar cada una de las posiciones y en caso de que una
falle entonces se devuelve falso.
* @param q es el arreglo. q>=0
* @param n es el tope del arreglo n>=0 */
public boolean determinarConsistencia (int[] q, int n)
{
for(int i=0; i<n; i++)
{
if(!determinarSiEsConsistente(q, i))
{
return false;
}
}
return true;
}
/** Devuelve la ruta con las posiciones errneas */
public String getSalida()
{
return salida;
}
/**Permite fijar la ruta con las posiciones errneas */
public void setSalida(String salida)
{
this.salida = salida;
llamarVerificarInvariante();
}

196

/**Permite obtener el array con las posiciones */


public int[] getResultado()
{
return resultado;
}
/**Permite fijar el array con el resultado*/
public void setResultado(int[] resultado)
{
this.resultado = resultado;
llamarVerificarInvariante();
}
}

ACTIVIDAD

Analice el mtodo enumerarPosibilidar


public void enumerarPosibilidar(int n)
{
int[] a ;
resultado=new int[8];
contador=0;
salida="";
a=new int[n];
enumerarPosibilidad(a, 0,(int)(Math.random()*8));
}
Si el nmero generado por la funcin random es 0, indique los valores con los cuales queda el
array a.

Si el nmero generado por la funcin random es 2, indique los valores con los cuales queda el
array a.

3.13 Actividad Independiente: Laberinto.


Se desea realizar una Aplicacin para el juego del Laberinto. Cada vez que se inicie el juego
se deber crear un laberinto diferente.
Cuando inicia el juego el usuario est ubicado en la posicin 0,0. Es de anotar, que si l est
ubicado en una posicin especifica podr moverse hacia arriba, hacia abajo, hacia los lados
haciendo uso de las Flechas. El jugador deber recorrer el laberinto en mximo 10 segundos,
de lo contrario perder. En cuyo caso podr obtener la solucin del laberinto, obtenida a travs
de backtracking, proporcionada por el computador

197

a. Clases o entidades
ENTIDAD DEL MUNDO
Laberinto

DESCRIPCIN
Es la clase principal del problema

Posicion

b. Diagrama de Clases

c. Implementacin de mtodos
public class Laberinto
{
private char[][] matriz;
private ArrayList<Posicion> rutaSolucion;
private final char noSePuede = '*';
private final char vacio = '_';
private final char ruta = 'c';
private final char muro = 'X';
public Laberinto()
{
matriz=new char[12][12];
rutaSolucion=new ArrayList<Posicion>();
}
/** Devuelve la matriz de caracteres*/
public char[][] getMatriz()
{
return matriz;
}

198

/** Permite fijar la matriz */


public void setMatriz(char[][] matriz)
{
this.matriz = matriz;
}
/** Este mtodo permite crear el laberinto
public void crearLaberinto()
{

}
}

199

*/

200

4.1

ANLISIS DE ESTRUCTURAS DE DATOS

Introduccin

Una estructura de datos es un conjunto de objetos que facilitan el uso dinmico de la memoria,
en consecuencia se logra una mejora en el desempeo de los algoritmos que las usan. La
mayora de los algoritmos tratados aqu pueden ser implementados usando arreglos; sin
embargo, pueden no ser tan eficientes o requerir mayor cantidad de lneas de cdigo para
lograr el mismo efecto.
Las estructuras de datos que analizaremos en este captulo son las listas, las pilas, las colas y
los rboles. Para cada una de estas estructuras se mostraran algunas de las muchas
implementaciones posibles de estas, as como tambin el anlisis del orden de complejidad de
los mtodos que las implementan. Comenzaremos con la estructura de datos dinmica lista
4.2

Estructura de Datos Lista

Las listas son las estructuras de datos lineales ms generales. Permiten generalmente el
acceso para consulta o modificacin en cualquiera de los extremos de la estructura, e incluso
en un punto medio, es frecuente recorrer una lista una lista buscando cierto elemento y, una
vez hallado, eliminarlo, modificar el contenido o insertar un elemento a su izquierda o a su
derecha.
Cuando se habla del concepto de lista, existen casos especiales lo cuales presentan problemas
en el diseo de algoritmos, y estos ocasionan con frecuencia errores en el cdigo. Por lo tanto
se debe procurar escribir cdigo que evite esos casos especiales. Uno de estos casos es
implementar lo que se conoce como nodo cabecera. Un nodo cabecera es un nodo adicional
en la lista que no guarda ningn dato, pero que sirve para satisfacer el requerimiento de que
cada nodo que contenga un elemento tenga un nodo anterior.
Para nuestras implementaciones de cada una de las listas, tendremos siempre en cuenta la
implementacin y el nodo cabecera.

4.2.1

Lista Sencillamente Enlazada Implementacin 1.

Una lista enlazada tambin recibe el nombre de "lista concatenada", "lista eslabonada" o "lista
lineal". Es una coleccin de elementos llamados nodos, que en su conjunto forman un
ordenamiento lineal. Comnmente cada nodo contiene un dato y una referencia al siguiente
nodo. Una lista enlazada es una estructura de datos dinmica que permite almacenar cualquier
cantidad de nodos (Bedoya, 2008).
La siguiente figura muestra una lista sencillamente enlazada, esta lista esta conformada por
nodos, los cuales tienen un campo que contiene el dato y una referencia al siguiente nodo de la
lista.

A continuacin se mostrar la implementacin de los mtodos ms relevantes asociados a las


listas sencillamente enlazadas, con sus respectivos rdenes de complejidad.

201

Inicialmente debemos partir del elemento fundamental de la lista: el nodo, a continuacin, se


muestra la implementacin de la clase Nodo, cada nodo se compone de un dato y una
referencia al siguiente nodo de la lista.

Mtodo
(Clase
Completa)

Anlisis del
Orden de
Complejidad

public class Nodo


{
Object info;
Nodo siguiente;
Nodo (Object dato)
{
info = dato;
siguiente = null;
}
}
La clase Nodo, tiene un mtodo constructor llamado Nodo(Object
dato), cuyo orden de complejidad es O(1).

A continuacin se muestra una primera implementacin para una lista sencillamente enlazada, se
analizar para cada uno de los mtodos de la clase lista su orden de complejidad, siempre teniendo en
cuenta y considerando el peor de los casos.

public class Lista


{
Nodo cabecera;
int cantidad;
Mtodo
(Clase
Completa)

Anlisis del
Orden de
Complejidad

Lista()
{
cabecera = new Nodo(null);
cantidad = 0;
}
}
La clase Lista, tiene un mtodo constructor llamado Lista( ), cuyo
orden de complejidad es O(1).

El mtodo eliminar(), permite remover el primer nodo de la lista, verifica inicialmente si la lista
esta vaca, si se cumple el condicional se muestra un mensaje indicado que no hay nodos en la
lista. Si existen nodos en la lista, se actualiza la referencia siguiente del nodo cabecera, a la
referencia que tiene el nodo siguiente del siguiente de cabecera.

Mtodo

Anlisis del
Orden de
Complejidad

public void eliminar()


{
if (cabecera.siguiente == null)
{
System.out.print("Lista esta vacia");
}
else
{
cabecera.siguiente =
cabecera.siguiente.siguiente;
}
}
El orden de complejidad para este mtodo es O(1), puesto que solo
se tienen operaciones elementales en su implementacin.

202

El mtodo remover(Object elemento), permite eliminar uno o ms nodos de la lista sencilla, de


acuerdo al valor enviado como parmetro, este mtodo eliminar todos los nodos de la lista
que correspondan con el campo de dato que llega por parmetro al mtodo. Para ello recorre la
lista desde el primer nodo de la lista, hasta el final de la misma.
Public void remover(Object elemento)
{
Nodo p = new Nodo();
Nodo q = new Nodo();
p = cabecera.siguiente;
q = cabecera.siguiente;

Mtodo

Anlisis del
Orden de
Complejidad

while (p != null)
{
if (elemento.equals(p.info))
{
if (p == cabecera.siguiente)
{
eliminar();
p=q= cabecera.siguiente;
}
else
{
q.siguiente = p.siguiente;
p = p.siguiente;
}
cantidad--;
}
else
{
q = p;
p = p.siguiente;
}
}
}
Para el anlisis del orden de complejidad de este mtodo se debe
tener en cuenta el peor de los casos, asumiendo que se tiene una
lista con n nodos, si se desea remover todos los nodos con igual
campo de dato, se debe realizar un recorrido lineal de toda la
estructura y nodo por nodo, por lo tanto el orden de complejidad
para este mtodo es O(n).

El mtodo buscar(Object elemento), busca un nodo especfico en la lista de acuerdo al valor


enviado por parmetro, realiza un recorrido de la lista desde el primer nodo de la lista hasta el
ltimo nodo de la lista.

Mtodo

public boolean buscar(Object elemento)


{
Nodo p = new Nodo();
p = cabecera.siguiente;
while(p!= null && !p.info.equals(elemento))
{
p = p.siguiente;
if (p == null)
{
return false;
}
return true;
}

203

Anlisis del
Orden de
Complejidad

Asumiendo que la lista tiene n nodos, el orden de complejidad de


este mtodo es O(n), asumiendo para el peor caso, que el elemento
que se esta buscando esta en la ltima posicin o en su defecto no
se encuentra el la lista sencillamente enlazada.

El mtodo insertar(Object elemento), permite insertar un nodo siguiente al nodo cabecera.


public void insertar(Object elemento)
{
Nodo n = new Nodo(elemento);
if (cabecera.siguiente == null)
{
cabecera.siguiente = n;
}
else
{
n.siguiente = cabecera.siguiente;
cabecera.siguiente = n;
}
cantidad++;

Mtodo

}
Anlisis del
Orden de
Complejidad

El orden de complejidad de este mtodo es O(1), por que solo se


tienen operaciones elementales en su implementacin tanto la
sentencia if como la sentencia else son de O(1) y por la regla de la
suma se puede afirmar que su orden es constante.

El mtodo Nodos(), retorna la cantidad de nodos que tenga la lista hasta ese momento.

Mtodo
Anlisis del
Orden de
Complejidad

4.2.2

public int Nodos()


{
return cantidad;
}
El orden de complejidad para este mtodo es O(1), solo posee un
retorno de un valor, se considera una operacin elemental.

Lista Sencillamente Enlazada Implementacin 2.

A continuacin, se mostrar otra la implementacin de la clase nodo a partir de la cual se


puede empezar a construir la lista sencillamente enlazada. Un nodo es la estructura bsica a
partir de la cual podemos construir una lista.

Mtodo

public class Nodo


{
Object dato;
Nodo siguiente;
public Nodo(Object O){
dato = O;
siguiente = null;
}
}

204

Anlisis del
Orden de
Complejidad

La clase Nodo tiene un mtodo constructor el cual recibe un objeto


por parmetro, el orden de complejidad del mtodo es O(1), y el
orden de complejidad de la clase tambin es O(1), esta clase posee
nicamente
operaciones
elementales,
mas
exactamente
operaciones de asignacin.

Para nuestro caso, la siguiente figura permite realizar una abstraccin de un nodo.
null

null

El mtodo constructor de la lista sencilla permite crear una lista vaca.

Mtodo

Anlisis del
Orden de
Complejidad

ListaSencilla()
{
cabecera = new Nodo(null);
ultimo = cabecera;
actual = cabecera;
}
El orden de complejidad para el mtodo lista sencilla es O(1), pues
solo tiene operaciones de asignacin las cuales son todas de orden
constante.

El mtodo nuevo, permite inicializa un nodo, este nuevo nodo es el nodo cabecera el cual
contiene un campo de datos null y una referencia siguiente null, adicionalmente se inicializan
las referencias ltimo y actual a cabecera.

Mtodo

Anlisis del
Orden de
Complejidad

public void nuevo()


{
cabecera.siguiente=null;
ultimo=cabecera;
actual=cabecera;
}
El orden de complejidad de este mtodo es O(1), ya que todas las
instrucciones que la implementan son constantes.

El mtodo estaVacia(), retorna un valor booleano, si la referencia siguiente de la cabecera es


null quiere decir que la lista no tiene mas nodos y retorna verdadero, de lo contrario la lista
tiene nodos y retorna falso

Mtodo

Anlisis del
Orden de
Complejidad

public boolean estaVacia()


{
if ( cabecera.siguiente==null)
{
return true;
}
else
{
return false;
}
}
El orden de complejidad de este mtodo es O(1), ya que todas las
instrucciones que la implementan son constantes.

205

Para el caso en la cual la lista solo tenga el nodo cabecera, el mtodo estavacia() retorna
verdadero. La figura siguiente muestra una lista con esta precondicin.
null
null
Lista Vacia.

Si la lista posee nodos, el mtodo estavacia(), retorna falso. La figura muestra esta situacin.
3

null

12
null
null

Lista con dos nodos.

El mtodo insertarInicio(Object element) permite insertar un nodo como siguiente al nodo


cabecera, inicialmente verifica si la lista esta vaca, si esta vaca inserta un nodo como
siguiente al cabecera, de lo contrario, el nodo se inserta al lado del nodo cabecera.

Mtodo

Anlisis del
Orden de
Complejidad

public void insertarInicio(Object elemento)


{
Nodo nuevo = new Nodo(elemento);
if( estaVacia()){
cabecera.siguiente = nuevo;
ultimo=nuevo;
actual= nuevo;
}
else
{
nuevo.siguiente=cabecera.siguiente;
cabecera.siguiente=nuevo;
actual = nuevo ;
}
}
El orden de complejidad de este mtodo es O(1), el bloque
constituido por el condicional if es de orden O(1) y el bloque
constituido por la sentencia else tambin es de O(1), por lo tanto
O(1)+O(1) por la regla de la suma tenemos O(1).

A continuacin se muestra en la figura una situacin cuando la lista est vaca, es decir, solo
est el nodo cabecera.
cabecera
null
null

Lista Vacia.

Una vez que se invoque el mtodo insertarInicio(Object element), la lista queda con un nodo y
ese nodo tiene un nombre el cual llega por parmetro al mismo. La figura muestra como queda
la lista una vez se inserta un nodo siguiente al nodo cabecera. Para este caso el valor que llega
por parmetro al mtodo es dat.
cabecera
null

dat
null

insertarInicio.

206

En caso de que ya existan nodos en la lista, la referencia al siguiente nodo de la lista se


actualiza al nodo que se inserta. La siguiente figura muestra una precondicin de una lista de
objetos, la lista tiene tres nodos.
3

null

12
null

Lista con dos nodos.

La figura muestra la insercin de un nodo cuando ya existen nodos en la lista, el objeto que
llega por parmetro es el valor 5, por lo tanto la lista queda con tres nodos.

null

insertarInicio.

12
null

El mtodo insertarUltimo(Object elemento) permite insertar un nodo como el ultimo nodo de la


lista, inicialmente verifica si la lista esta vaca, si esta vaca inserta un nodo como siguiente al
cabecera, de lo contrario si ya existen nodos en la lista lo inserta al final de la lista.

Mtodo

Anlisis del
Orden de
Complejidad

public void insertarUltimo(Object elemento)


{
Nodo nuevo = new Nodo (elemento);
if(estaVacia())
{
ultimo.siguiente=nuevo;
ultimo= nuevo;
actual = nuevo;
}
else
{
ultimo.siguiente=nuevo;
ultimo = nuevo;
actual = nuevo;
}
}
El orden de complejidad de este mtodo es O(1), el bloque
constituido por el condicional if es de orden O(1) y el bloque
constituido por la sentencia else tambin es de O(1), por lo tanto
tenemos O(1).

En la figura se muestra una situacin cuando la lista est vaca, es decir solo esta el nodo
cabecera en la lista.
cabecera
null
null

Lista Vacia.

Una vez que se invoque el mtodo insertarUltimo(Object elemento, la lista queda con un nodo
y ese nodo tiene un nombre el cual llega por parmetro al mismo. La siguiente figura muestra
como se inserta un nodo siguiente al nodo cabecera.

207

cabecera
null

10
null

InsertarUltimo

En caso de que ya existan nodos en la lista, como se sabe mediante la referencia al ltimo
nodo, cual es el ltimo nodo, se procede a insertar el nuevo nodo. La siguiente figura muestra
una precondicin de una lista de objetos, la lista tiene dos nodos.
12
null

null

null

A continuacin se muestra la insercin de un nodo cuando ya existen nodos en la lista, el


parmetro para este ejemplo es 10, por lo tanto la lista queda con tres nodos.

null

12

10
null

La siguiente figura muestra la insercin de un nodo cuando ya existen nodos en la lista, el


parmetro para este ejemplo es la cadena Tel, por lo tanto la lista queda con tres nodos.

null

12
insertarUltimo.

null

null

El mtodo eliminarInicio() permite eliminar el nodo siguiente al nodo cabecera, inicialmente


verifica si la lista esta vaca, si esta vaca muestra un mensaje que advierte que la lista esta
vaca, de lo contrario ya existen nodos en la lista y lo que hace es eliminar el primer nodo de la
lista.

Mtodo

public void eliminarInicio()


{
Nodo borrar = cabecera.siguiente;
if(estaVacia())
{
JOptionPane.showMessageDialog("Lista vacia");
}
else
{
if(borrar==ultimo)
{
cabecera.siguiente=null;
actual=cabecera;
ultimo=actual;
}
else
{
cabecera.siguiente=borrar.siguiente;
actual=cabecera.siguiente;
}
}
}

208

Anlisis del
Orden de
Complejidad

El orden de complejidad de este mtodo es O(1) ya que todas sus


instrucciones son constantes.

La siguiente figura, muestra una lista de con tres nodos, el mtodo eliminar inicio, eliminar el
nodo siguiente al nodo cabecera, el nodo a ser eliminado es el nodo sealado por el valo.

null

null

null

sun

null
Lista con tres nodos.

Una vez se elimine el nodo, la lista sencillamente enlazada quedar con dos nodos, la siguiente
figura, muestra el estado final de la lista.
12
null

null

null
EliminarInicio.

4.2.3

Lista Sencilla Circular

Una lista sencilla circular es una coleccin de elementos llamados nodos, organizados de tal
manera que el ultimo nodo de la lista apunta al nodo cabecera. La figura, muestra una lista
circular. Se puede observar que lista contiene un nodo cabecera, el cual nos sirve para
referenciar el inicio de la lista. Una lista sencilla circular es una lista en la cual el ltimo nodo
apunta al primer nodo de la lista, para nuestro caso al nodo cabecera.

null

33

A continuacin, se mostrar la clase nodo a partir de la cual se puede empezar a construir la


lista sencillamente enlazada circular.
public class Nodo
{
Object dato;
Nodo siguiente;
Mtodo

Anlisis del
Orden de
Complejidad

public Nodo(Object O)
{
dato = O;
siguiente = null;
}
}
El orden de complejidad para la clase Nodo es O(1).

El mtodo constructor de la lista sencilla se crea con una lista vaca.

209

Mtodo

Orden de
Complejidad

ListaSencillaCircular()
{
cabecera = new Nodo(null);
ultimo = cabecera;
actual = cabecera;
}
El orden de complejidad para el mtodo lista sencilla es O(1).

El mtodo nuevo, permite crear un nuevo nodo a la lista, este nuevo nodo es el nodo cabecera
el cual contiene un campo de datos null y una referencia siguiente null. Se inicializa una
variable tamao en cero pues el nodo cabecera no se cuenta dentro de la lista.

Mtodo

Orden de
Complejidad

public void nuevo()


{
cabecera.siguiente=null;
ultimo=cabecera;
actual=cabecera;
}
El orden de complejidad de este mtodo es O(1).

El mtodo estaVacia(), retorna un valor booleano, si la referencia siguiente de la cabecera es


null, quiere decir que la lista no tiene mas nodos y retorna verdadero.

Mtodo

Orden de
Complejidad

public boolean estaVacia()


{
if ( cabecera.siguiente==null)
{
return true;
}
else{
return false;
}
}
El orden de complejidad para este mtodo es O(1) .

La figura, muestra una lista sencilla circular vaca, esta lista posee nicamente el nodo
cabecera. Por lo tanto el mtodo estaVacia() para este caso retornara trae.

null
Lista circular vaca.

Caso contrario al anterior se presenta cuando la lista tiene uno o mas nodos, la figura muestra
una lista sencilla circular con 3 nodos, para este caso el mtodo estaVacia() retorna falso,
puesto que existen nodos en la lista.

null

234

21

Lista circular con tres nodos.

210

312

El mtodo insertarInicio() permite insertar un nodo como siguiente al nodo cabecera,


inicialmente verifica si la lista esta vaca, si esta vaca inserta un nodo como siguiente al
cabecera, de lo contrario ya existen nodos en la lista y lo inserta.
public void insertarInicio(Object elemento )
{
Nodo nuevo = new Nodo(elemento);
if( estaVacia()){
cabecera.siguiente = nuevo;
nuevo.siguiente=cabecera;
ultimo=nuevo;
actual= nuevo;
}
else
{
nuevo.siguiente=cabecera.siguiente;
cabecera.siguiente=nuevo;
actual = nuevo ;
}

Mtodo

Orden de
Complejidad

}
El orden de complejidad de este mtodo es O(1) ya que todas sus
instrucciones son constantes.

La siguiente figura muestra una precondicin en la cual se tiene una lista vaca, a esta lista se
le adicionara un nuevo nodo, el cual ser el siguiente del nodo cabecera.

null
Lista circular vaca.

Una vez se inserte el nodo en la lista, esta quedar como se muestra en la figura a
continuacin.
null

15
insertarInicio.

Tambin existe el caso en el cual la lista circular tenga nodos, la figura a continuacin muestra
esta situacin.

null

11

15

insertarInicio.

Una vez se invoque el mtodo insertarInicio(), se adiciona un nuevo nodo, el cual quedar
como siguiente del nodo cabecera. Para este caso se inserto un nuevo nodo cuyo contenido en
el campo info es 5.

null

11

insertarInicio.

211

15

El mtodo insertarUltimo() permite insertar un nodo como el ultimo nodo de la lista, inicialmente
verifica si la lista esta vaca, si esta vaca inserta un nodo como siguiente al cabecera, de lo
contrario ya existen nodos en la lista y lo inserta al final.

Mtodo

Anlisis del
Orden de
Complejidad

public void insertarUltimo(Object elemento)


{
Nodo nuevo = new Nodo (elemento);
if(estaVacia())
{
ultimo.siguiente=nuevo;
nuevo.siguiente=ultimo;
ultimo= nuevo;
actual = nuevo;
}
else
{
ultimo.siguiente=nuevo;
nuevo.siguiente=cabecera;
ultimo = nuevo;
actual = nuevo;
}
}
El orden de complejidad de este mtodo es O(1) ya que todas sus
instrucciones son constantes.

La siguiente figura muestra lista con un nodo, a esta lista se le adicionar un nuevo nodo el
cual se convertir en el ultimo nodo de las lista despus de su insercin.

null

pl
insertarUltimo.

Una vez se inserte este nodo en la lista, esta quedara como se muestra en la figura.

null

pl

ca

Figura 20. insertarUltimo.

El mtodo eliminarInicio() permite eliminar el nodo siguiente al nodo cabecera, inicialmente


verifica si la lista esta vaca, si esta vaca muestra un mensaje que advierte que la lista esta
vaca, de lo contrario ya existen nodos en la lista y lo que hace es eliminar el primer nodo de la
lista.
public void eliminarInicio()
{

Mtodo

Nodo borrar = cabecera.siguiente;


if(estaVacia())
{
JOptionPane.showMessageDialog("Lista
Vacia");
}

212

else
{
if(borrar==ultimo)
{
cabecera.siguiente=null;
actual=cabecera;
ultimo=actual;
}
else
{
cabecera.siguiente=borrar.siguiente;
actual=cabecera.siguiente;
}
}
}
Anlisis del
Orden de
Complejidad

El orden de complejidad de este mtodo es O(1) ya que todas sus


instrucciones son constantes.

La siguiente figura, muestra una lista sencilla circular con tres nodos. El ovalo rojo muestra cual
es el nodo que ser removido de la lista.

null

Lista sencilla circular.

Una vez se invoque el mtodo eliminarInicio(), se elimina el nodo siguiente al nodo cabecera y
la lista queda como se muestra en la figura a continuacin.
null

eliminarInicio.

4.2.4

Lista Doblemente Enlazada

Una lista doblemente enlazada es una coleccin de elementos llamados nodos, los cuales
tienen generalmente tres campos: un campo izquierda, un campo dato y un campo derecha.
Los campos izquierda y derecha son referencias a los nodos ubicados en cada nodo. Tiene la
ventaja de que estando en cualquier nodo se puede acceder al nodo que est tanto a la
izquierda como a la derecha. El tiempo para todas las operaciones es constante, excepto para
las operaciones que requieran un tiempo proporcional a la longitud de la lista.
La siguiente figura muestra una lista doblemente enlazada.
null
null

10

33

85
null

Lista Doblemente enlazada.

A continuacin, se mostrar a clase nodo a partir de la cual se puede empezar a construir la


lista doblemente enlazada.

213

public class
{
NodoDE
Object
NodoDE
Mtodo

Orden de
Complejidad

NodoDE
izquierda;
dato;
derecha;

public NodoDE(Object elemento)


{
izquierda = null;
dato = elemento;
derecha = null;
}
}
El orden de complejidad del mtodo constructor es O(1)

El mtodo constructor de la lista doble se crea con una lista vaca.

Mtodo

Orden de
Complejidad

ListaDoble()
{
cabecera = new NodoDE(null);
ultimo = cabecera;
actual = cabecera;
}
El orden de complejidad para el mtodo lista doble es O(1).

El mtodo nuevo, permite crear un nuevo nodo a la lista, este nuevo nodo es el nodo cabecera
el cual contiene un campo de datos null y una referencia siguiente null. Se inicializa una
variable tamao en cero pues el nodo cabecera no se cuenta dentro de la lista.

Mtodo

Orden de
Complejidad

public void nuevo()


{
cabecera.derecha=null;
ultimo=cabecera;
actual=cabecera;
tamao=0;
}
El orden de complejidad de este mtodo es O(1), ya que todas las
instrucciones que la implementan son constantes.

El mtodo estaVacia(), retorna un valor booleano, si la referencia siguiente de la cabecera es


null, quiere decir que la lista no tiene mas nodos y retorna verdadero.

Mtodo

Orden de
Complejidad

public boolean estaVacia()


{
if ( cabecera.derecha==null)
{
return true;
}
else
{
return false;
}
}
El orden de complejidad para este mtodo es O(1) .

214

En la grafica a continuacin se observa que la lista esta vacia, pues solo se tiene el nodo
cabecera con sus respectivas referencias y por lo tanto el mtodo para este caso retorna
verdadero.
null
null

null

Lista Doblemente enlazada.

En este caso la lista contiene un nodo cuyo contenido es el nmero 5 y por lo tanto el mtodo
retorna falso.
null
null

5
null

Lista Doblemente enlazada.

El mtodo insertarInicio() permite insertar un nodo como siguiente al nodo cabecera,


inicialmente verifica si la lista esta vaca, si esta vaca inserta un nodo como siguiente al
cabecera, de lo contrario ya existen nodos en la lista y lo inserta.
public void insertarInicio(Object elemento)
{
NodoDE nuevo = new NodoDE(elemento);
NodoDE temporal = cabecera.derecha;
if (estaVacia())
{
cabecera.derecha=nuevo;
nuevo.izquierda=cabecera;
ultimo = nuevo;
actual = nuevo;
}
else
{
nuevo.derecha=cabecera.derecha;
nuevo.izquierda=cabecera;
temporal.izquierda=nuevo;
cabecera.derecha=nuevo;
actual = nuevo;
}

Mtodo

Orden de
Complejidad

}
El orden de complejidad de este mtodo es O(1) ya que todas sus
instrucciones son constantes.

Para este caso se observa que inicialmente la lista esta vaca y posteriormente se inserta un
nodo, actualizndose la referencia derecha del nodo cabecera.
null
null

null

Lista Doblemente enlazada.

El mtodo siempre insertar un nodo a la derecha del nodo cabecera.


null
null

96

Lista Doblemente enlazada.

215

null

El mtodo insertarUltimo() permite insertar un nodo como el ultimo nodo de la lista, inicialmente
verifica si la lista esta vaca, si esta vaca inserta un nodo como siguiente al cabecera, de lo
contrario ya existen nodos en la lista y lo inserta al final.
public void insertarUltimo(Object elemento)
{
NodoDE nuevo = new NodoDE (elemento);
if(estaVacia())
{
ultimo.derecha=nuevo;
nuevo.izquierda=ultimo;
ultimo= nuevo;
actual = nuevo;
}
else
{
ultimo.derecha=nuevo;
nuevo.izquierda=ultimo;
ultimo = nuevo;
actual = nuevo;
}

Mtodo

Anlisis del
Orden de
Complejidad

}
El orden de complejidad de este mtodo es O(1), ya que todas sus
instrucciones son constantes.

Este mtodo inserta un nodo al final de la lista, para este caso se pretende insertar el nodo con
valor 3.
null

2
null

null

Lista Doblemente enlazada.

La lista una vez insertado el nodo queda de la siguiente manera.

null
null

Lista Doblemente enlazada.

null

El mtodo eliminarInicio() permite eliminar el nodo siguiente al nodo cabecera, inicialmente


verifica si la lista esta vaca, si esta vaca muestra un mensaje que advierte que la lista esta
vaca, de lo contrario ya existen nodos en la lista y lo que hace es eliminar el primer nodo de la
lista.
public void eliminarInicio()
{
NodoDE borrar = cabecera.derecha;
Mtodo

if(estaVacia())
{
JOptionPane.showMessageDialog(null,"Lista
Esta Vacia");
}

216

else
{
if(borrar==ultimo)
{
cabecera.derecha=null;
borrar.izquierda=null;
actual=cabecera;
ultimo=actual;
}
else{
NodoDE temporal=borrar.derecha;
cabecera.derecha=temporal;
temporal.izquierda=cabecera;
actual=cabecera.derecha;
}
}
Anlisis del
Orden de
Complejidad

}
El orden de complejidad de este mtodo es O(1) ya que todas sus
instrucciones son constantes.

El mtodo elimina el primer nodo de la lista, para ello es necesaria la actualizacin de las
correspondientes referencias. Para es caso el nodo a eliminar es el que contiene el nmero 73.
null

73

233

69
null

null
Lista Doblemente enlazada

Posterior a la eliminacin del nodo, la estructura de la lista queda de la siguiente manera.


null
null

4.2.5

233

69
null

Lista Doblemente enlazada.

Lista Circular Doblemente Enlazada

Una lista doblemente enlazada es una coleccin de elementos llamados nodos, los cuales
tienen generalmente tres campos: un campo izquierda, un campo dato y un campo derecha.
Los campos izquierda y derecha son referencias a los nodos ubicados en cada nodo. Tiene la
ventaja de que estando en cualquier nodo se puede acceder al nodo que esta tanto a la
izquierda como a la derecha. El tiempo para todas las operaciones es constante, excepto para
las operaciones que requieran un tiempo proporcional a la longitud de la lista.
La siguiente figura muestra una lista doblemente enlazada.

null

34

21

Lista Circular Doblemente Enlazada

217

78

A continuacin, se mostrar la clase nodo a partir de la cual se puede empezar a construir la


lista doblemente enlazada circular.
public class
{
NodoDE
Object
NodoDE

NodoDE
izquierda;
dato;
derecha;

public NodoDE(Object elemento)


{
izquierda = null;
dato = elemento;
derecha = null;
}

Mtodo

}
Orden de
Complejidad

El orden de complejidad para el mtodo nodo doblemente enlazada


es O(1).

El mtodo constructor de la lista doble se crea con una lista vaca.

Mtodo

Orden de
Complejidad

ListaDoble()
{
cabecera = new NodoDE(null);
ultimo = cabecera;
actual = cabecera;
}
El orden de complejidad para el mtodo lista doble es O(1).

El mtodo nuevo, permite crear un nuevo nodo a la lista, este nuevo nodo es el nodo cabecera
el cual contiene un campo de datos null y una referencia siguiente null. Se inicializa una
variable tamao en cero pues el nodo cabecera no se cuenta dentro de la lista.

Mtodo

Orden de
Complejidad

public void nuevo()


{
cabecera.derecha=null;
ultimo=cabecera;
actual=cabecera;
tamao=0;
}
El orden de complejidad de este mtodo es O(1), ya que todas las
instrucciones que la implementan son constantes.

El mtodo estaVacia, retorna un valor booleano, si la referencia siguiente de la cabecera es


null, quiere decir que la lista no tiene ms nodos y retorna verdadero.

Mtodo

public boolean estaVacia()


{
if ( cabecera.derecha==null){
return true;
}
else
{
return false;
}
}

218

Orden de
Complejidad

El orden de complejidad para este mtodo es O(1) .

Para este primer caso el mtodo retornar falso, teniendo en cuenta que nicamente la lista
tiene el nodo cabecera.
null
Lista vacia doblemente enlazada circular

Para este segundo caso el mtodo retornar verdadero, teniendo en cuenta que la lista se
compone de varios nodos.

null

500

21

64

Lista doblemente enlazada circular

El mtodo insertarInicio permite insertar un nodo como siguiente al nodo cabecera, inicialmente
verifica si la lista esta vaca, si esta vaca inserta un nodo como siguiente al cabecera, de lo
contrario ya existen nodos en la lista y lo inserta.
public void insertarInicio(Object elemento)
{
NodoDE nuevo = new NodoDE(elemento);
NodoDE temporal = cabecera.derecha;

Mtodo

Anlisis del
Orden de
Complejidad

if (estaVacia())
{
cabecera.derecha=nuevo;
nuevo.izquierda=cabecera;
ultimo = nuevo;
actual = nuevo;
}
else
{
nuevo.derecha=cabecera.derecha;
nuevo.izquierda=cabecera;
temporal.izquierda=nuevo;
cabecera.derecha=nuevo;
actual = nuevo;
}
}
El orden de complejidad de este mtodo es O(1) ya que todas sus
instrucciones son constantes.

Para este caso si se parte de una lista circular doblemente enlazada que se encuentra vaca, el
proceso de insertar un nodo se muestra a continuacin.

null
Lista doblemente enlazada circular

219

Si se desea insertar un nodo, la lista queda de la siguiente manera si se va a insertar un nodo


con el valor 58.

Lista doblemente enlazada circular

El mtodo insertarUltimo permite insertar un nodo como el ultimo nodo de la lista, inicialmente
verifica si la lista esta vaca, si esta vaca inserta un nodo como siguiente al cabecera, de lo
contrario ya existen nodos en la lista y lo inserta al final.
public void insertarUltimo(Object elemento)
{
NodoDE nuevo = new NodoDE (elemento);

Mtodo

Orden de
Complejidad

if(estaVacia())
{
ultimo.derecha=nuevo;
nuevo.izquierda=ultimo;
ultimo= nuevo;
actual = nuevo;
}
else
{
ultimo.derecha=nuevo;
nuevo.izquierda=ultimo;
ultimo = nuevo;
actual = nuevo;
}
}
El orden de complejidad de este mtodo es O(1) ya que todas sus
instrucciones son constantes.

El mtodo eliminarInicio permite eliminar el nodo siguiente al nodo cabecera, inicialmente


verifica si la lista esta vaca, si esta vaca muestra un mensaje que advierte que la lista esta
vaca, de lo contrario ya existen nodos en la lista y lo que hace es eliminar el primer nodo de la
lista.
public void eliminarInicio()
{
NodoDE borrar = cabecera.derecha;

Mtodo

if(estaVacia())
{
JOptionPane.showMessageDialog("Lista Vacia");
}
else
{
if(borrar==ultimo)
{
cabecera.derecha=null;
borrar.izquierda=null;
actual=cabecera;
ultimo=actual;
}

220

else
{
NodoDE temporal=borrar.derecha;
cabecera.derecha=temporal;
temporal.izquierda=cabecera;
actual=cabecera.derecha;
}
}
Anlisis del
Orden de
Complejidad

}
El orden de complejidad de este mtodo es O(1) ya que todas sus
instrucciones son constantes.

Para este caso se quiere eliminar el nodo que se encuentra al inicio de la lista. En este caso se
desea eliminar el nodo con valor 80.

null

80

21

98

Lista doblemente enlazada circular

Posterior a la eliminacin del nodo, la lista queda configurada de la siguiente manera.

Lista doblemente enlazada circular

4.3

Actividad Independiente: Ciudades

La oficina de Planeacin Nacional necesita una Aplicacin que le permita manejar la


informacin fundamental de n ciudades del pas. Cada una de las ciudades tiene un nombre,
una extensin en metros cuadrados y un nmero de habitantes.
Desde la aplicacin se debe poder:

Determinar cul es la ciudad que tiene la menor cantidad de habitantes.


Determinar cul ciudad tiene la mayor cantidad de extensin en metros cuadrados.
Buscar una ciudad por nombre.
Determinar cuntas ciudades tienen ms de 100000 habitantes.

221

a) Requerimientos funcionales

NOMBRE

R1 Mostrar Ciudad con ms extensin en metros cuadrados.

RESUMEN
ENTRADAS
RESULTADOS

NOMBRE
RESUMEN
ENTRADAS

R2 Mostrar Ciudad con menos cantidad de estudiantes.

RESULTADOS

NOMBRE

R3 Buscar Ciudad por cdigo

RESUMEN
ENTRADAS
RESULTADOS
NOMBRE

R3 Contar la cantidad de ciudades que tienen mas de 100000


habitantes

RESUMEN
ENTRADAS
RESULTADOS

222

b. Identificar las entidades o clases.


ENTIDAD

DESCRIPCIN

Ciudad
GrupoCiudades

c. Las relaciones entre las clases

d. Implementacin
Se deben implementar las clases del dominio del problema asociadas con la estructura de lista
que considere ms apropiada.
public class GrupoCiudades
{

223

4.4

Estructura de Datos Pila

Uno de los conceptos ms tiles en las ciencias de la computacin es el de la pila. En este


apartado examinaremos esta estructura de datos engaosamente simple y veremos por qu
tiene una funcin tan importante en las reas de programacin y lenguajes de programacin.
Una pila es un contenedor de objetos que se insertan y se eliminan siguiendo el principio
ltimo en entrar, primero en salir (LIFO: Last In First Out). Una pila es un conjunto ordenado
de elementos (los llamaremos nodos a partir de ahora), en el cual se pueden agregar y eliminar
elementos en un extremos que es llamado el tope de la pila. A continuacin se muestra en la
figura una abstraccin de de una pila.
tope

2
7
0
6
9
8
5

Pila con elementos

A diferencia del arreglo, la definicin de la pila considera la insercin y eliminacin de


elementos, por lo que una pila es un objeto dinmico en constante cambio. La definicin
especifica que solo un extremo de la pila se designa como el tope. Pueden colocarse nuevos
elementos en el tope de la pila (en este caso el tope de la pila sube para corresponder al nuevo
elemento mas alto), o se pueden quitar elementos (en este caso el tope de la pila baja para
corresponder al nuevo elemento mas alto). Debemos decidir cual extremo de la pila se designa
como el tope es decir, en cual extremo se agregan o suprimen elementos.

4.4.1

Implementacin Basada en un arreglo

Se debe especificar algn tamao mximo N para la pila; EJ= 1000 elementos. Entonces la pila
consiste en un arreglo S de N elementos mas una variable entera t, que expresa ndice del
elemento superior del arreglo S.

N-1

Al considerar que los arreglos comienzan en java con el ndice 0, se inicializa t en 1, y se usa
este valor de t para indicar cuando est vaca la pila. De igual forma, se puede usar esta
variable para determinar la cantidad de elementos (t+1) en una pila. Se introduce la excepcin
llamada StackFullException para indicar la condicin de error que se produce si se trata de
insertar un elemento nuevo y la pila S se encuentra llena (Goodrich & Tamassia, 2002).
A continuacin se muestra la implementacin de una pila mediante un arreglo, mediante los
siguientes mtodos fundamentales:

Mtodo

public class Stack


{
public static final int CAPACIDAD = 2000;
public int capacidad, top = -1;
private Object s[];

224

public Stack (){


this(CAPACIDAD);
}
public Stack (int cap)
{
capacidad = cap;
s= new Object [capacidad];
}
Orden de
Complejidad

}
El orden de complejidad de los mtodos constructores tiene
nicamente instrucciones constantes y por lo tanto el orden de
complejidad es O(1).

Retorna si la pila esta vaca, como se esta trabajando con un ndice que controla el tope de la
pila, cuando el valor del ndice es menor que cero, retorna que la pila no tiene elementos,
como nicamente se esta retornando en el mtodo un valor.

Mtodo
Orden de
Complejidad

public boolean isEmpty()


{
return(top<0);
}
El orden de complejidad para el mtodo isEmpty() es constante,
O(1).

Inserta un objeto en la pila, primero verifica si existe disponibilidad de espacio en la estructura


esttica, si es cierta esta condicin, se enva una excepcin la cual muestra un mensaje de
advertencia, en caso de existir espacio en la estructura, se actualiza el ndice del vector
mediante un incremento y se asigna en la posicin el elemento que llega por parmetro en el
mtodo.

Mtodo

Orden de
Complejidad

public void push (Object obj)


throws StackFullException
{
Object elem;
if(top+1==capacidad)
{
throw new StackFullException ("Pila
Llena");
}
else{
s[++top]=obj;
}
}
El orden de complejidad para este mtodo es O(1), complejidad
constante.

El mtodo top retorna un objeto de la pila, el objeto que retorna en caso de encontrarse es el
elemento que esta en el tope de la pila. Si la pila se encuentra vaca, se muestra un mensaje
de advertencia indicando esta situacin.

Mtodo

public Object top () throws StackEmptyException


{
if(isEmpty())
{
throw new StackEmptyException ("Pila
Vacia");
}

225

else
{
return s[top];
}
}
Anlisis del
Orden de
Complejidad

Por la regla de la suma podemos deducir que el orden de


complejidad de este mtodo es O(1).

El mtodo pop elimina el ultimo elemento de la pila y lo retorna, inicialmente verifica si la pila
contiene o no elementos, en caso de estar la pila vaca, muestra un mensaje de advertencia
indicando la situacin, en caso contrario, actualiza la posicin del ndice mediante un
decremento y se retorna el elemento de la posicin en la cual se encontraba el ndice
inicialmente.
public Object pop () throws StackEmptyException
{
Object elem;

Mtodo

Anlisis del
Orden de
Complejidad

if (isEmpty())
{
throw new StackEmptyException
Vacia");
}
else
{
elem=s[top];
s[top]=null;
top--;
return elem;
}

("Pila

}
Por la regla de la suma podemos deducir que el orden de
complejidad de este mtodo es constante O(1).

Las siguientes dos clases son las que contienen las excepciones que se mencionan en la
implementacin de los mtodos de pila.
public class StackEmptyException extends Exception
{
StackEmptyException (String cadena)
{
System.out.println (cadena);
}
}
Mtodo

Anlisis del
Orden de
Complejidad

public class StackFullException extends Exception


{
StackFullException (String cadena)
{
System.out.println(cadena);
}
}
Estas clases tienen orden de complejidad constantes ya que
contienen llamados a mtodos propios de lenguaje y por lo tanto su
orden de complejidad es O(1).

226

4.4.2

Implementacin utilizando Listas

La implementacin de una pila tambin es posible utilizando nodos, lo importante en este caso
es que se respete el principio FIFO. El constructor de la pila permite crear una pila vaca.
public class Pila
{
Nodo tope;
Mtodo

Anlisis del
Orden de
Complejidad

Pila()
{
tope = new Nodo(null);
}
}
El orden de complejidad para este mtodo es O(1), complejidad
constante.

El mtodo pop() elimina el ultimo elemento de la pila y lo retorna, inicialmente verifica si la pila
contiene o no elementos, en caso de estar la pila vaca, muestra un mensaje de advertencia
indicando la situacin, en caso contrario, actualiza la posicin del ndice mediante un
decremento y se retorna el elemento de la posicin en la cual se encontraba el ndice
inicialmente.

Mtodo

Orden de
Complejidad

Object pop()
{
Object dato;
if (tope == null)
{
System.out.println("Pila esta Vacia");
dato = null;
}
else
{
dato = tope.siguiente.info;
tope.siguiente =
tope.siguiente.siguiente;
}
return dato;
}
Por la regla de la suma podemos deducir que el orden de
complejidad de este mtodo es constante O(1).

El mtodo push(elemento) Inserta un objeto en la pila, como es una implementacin diferente


basada en nodos, ya no se verifica la disponibilidad de espacio en memoria, pues esta
implementacin esta basada en el concepto de estructura dinmica. Este mtodo agrega un
nodo a la pila, y este nodo queda referenciado como el tope de la pila.

Mtodo

public void push(Object e)


{
Nodo n = new Nodo(e);
if (tope != null)
{
n.siguiente = tope.siguiente;
tope.siguiente = n;
}
}

227

Anlisis del
Orden de
Complejidad

El orden de complejidad para este mtodo es O(1), complejidad


constante.

El mtodo mostrar permite realizar un recorrido de todos los elementos de la pila, cuando
realiza este recorrido, va mostrando el contenido de cada uno de los elementos de la pila, se
realiza el recorrido mediante la instruccin p = p.siguiente.

Mtodo

Anlisis del
Orden de
Complejidad

void mostrar()
{
Nodo p = new Nodo();
p = tope.siguiente;
while (p != null)
{
System.out.print(" " + p.info + "\n");
p = p.siguiente;
}
}
El anlisis de este mtodo para el peor de los casos asumiendo
que se tienen n nodos en la pila es de orden O(n) lineal, pues
recorre la pila desde el primer nodo hasta el ltimo.

Este mtodo retorna si la pila esta vaca, como se esta trabajando con un ndice que controla el
tope de la pila, cuando el valor del ndice es menor que cero, retorna que la pila no tiene
elementos, como nicamente se esta retornando en el mtodo un valor.

Mtodo

Anlisis del
Orden de
Complejidad

boolean pilaVacia()
{
return tope.siguiente == null
}
El orden de complejidad para el mtodo isEmpty() es constante,
O(1)

El mtodo top() retorna un objeto de la pila, el objeto que retorna en caso de encontrarse es el
elemento que esta en el tope de la pila. Si la pila se encuentra vaca, se muestra un mensaje
de advertencia indicando esta situacin.

Mtodo
Anlisis del
Orden de
Complejidad

4.5

Nodo cima()
{
return tope.siguiente;
}
Por la regla de la suma podemos deducir que el orden de
complejidad de este mtodo es O(1).

Estructura de Datos Cola

La estructura de datos cola pertenece tambin a las estructuras lineales, y se caracteriza


porque las inserciones de nuevos elementos solo se permiten en uno de los extremos de la
estructura, que tradicionalmente se llama final de la cola, mientras que las consultas y

228

eliminacin solo se permiten en el extremo opuesto de la cola, que tradicionalmente


llamaremos el frente de la cola.
Una cola es un contenedor de objetos que se insertan y se eliminan siguiendo el principio
Primero en entrar, primero en salir (FIFO: First In First Out). Al elemento de ms tiempo en la
cola se le denomina frente de la cola.

4.5.1

Implementacin Basada en un arreglo

Inicialmente se muestra una implementacin de una cola utilizando como estructura base una
arreglo unidimensional de elementos. Es necesario al momento de trabajar con colas definir
cual ser el frente y cual ser el final de la cola, lo anterior es fundamental para establecer por
donde insertarn elementos a la cola y por donde se eliminarn los elementos de la cola.
Para definir el frente y el final de la cola es necesario definir dos variables, frente y final. Donde
frente es un ndice dentro del arreglo que guarda el primer elemento de la cola y final es un
ndice dentro del arreglo que indica la siguiente celda disponible de arreglo para insertar un
elemento.
Al principio se asignar frente = final = 0, para indicar que la cola se encuentra vaca. Cuando
se saca un elemento del frente de la cola, solo incrementar frente para sealar la siguiente
celda. De igual manera, cuando se agrega un elemento, tan solo se incrementa final para
sealar la siguiente celda disponible en la cola. Sin embargo de acuerdo a lo anterior existe un
problema con este mtodo.
Por ejemplo, considrese lo que sucede cuando se coloca y se quita de la cola N veces
distintas a un solo elemento. Se obtendra frente = final = N. Si a continuacin se tratara de
insertar el elemento solo una vez mas, se obtendra un error de arreglo fuera de lmites, aun
cuando haya suficiente lugar en la cola para ese caso. Para evitar ese problema, se har que
los ndices frente y final se repitan. Esto es, se considerar ahora que la cola es un arreglo
circular.
Uso del arreglo Q en forma circular, la configuracin normal con f r.

N-1

Uso del arreglo Q en forma circular, la configuracin envuelta con r < f. Se resaltan las celdas
que guardan los elementos de la cola (Goodrich & Tamassia, 2002).

N-1

Cada vez que se incrementa frente o final, es posible calcular este incremento en la forma:
(frente+1) %n (final+1) % n, respectivamente, siendo n el tamao total del arreglo
unidimensional. Lo anterior garantiza el manejo circular de la cola.
Se examinar ahora el caso que se presenta si se forman N objetos en la cola Q, sin sacar
ninguno de ellos. Se tendra que f=r, que es la misma condicin que se presenta cuando la cola
esta vaca. Por consiguiente, no se podra decir cual es la diferencia entre una cola llena y una
vaca en este caso. La solucin es insistir en que Q nunca puede contener mas de N-1 objetos
(Goodrich & Tamassia, 2002).

229

A continuacin se muestra la implementacin de los mtodos de la cola. En esta


implementacin se utilizaran dos excepciones las cuales indican cuando la cola esta llena o
cuando la cola esta vaca.
class queue
{
private Object Q[];
public static final int CAPACIDAD =50;
public int n, f =0, r =0;

Mtodo

Orden de
Complejidad

public
queue ()
{
this (CAPACIDAD);
}
public queue (int cap)
{
n = cap;
Q = new Object [n];
}
}
El orden de complejidad para este mtodo es O(1), complejidad
constante.

El siguiente mtodo llamado isEmpty() retorna si la cola esta vaca, como se esta trabajando
con un ndice que controla el tope de la pila y el final de la pila, cuando estos valores son
iguales, se determina que la cola esta llena.

Mtodo

Orden de
Complejidad

public boolean isEmpty ()


{
boolean lleno = false;
if(f==r)
{
lleno= true;
return lleno;
}
return lleno;
}
nicamente se esta retornando en el mtodo un valor booleano, el
orden de complejidad para el mtodo isEmpty() es constante, O(1).

El mtodo enqueue(Object nuevoElemento) inserta un objeto en la cola, primero verifica si


existe disponibilidad de espacio en la estructura esttica, si es cierta esta condicin, se enva
una excepcin la cual muestra un mensaje de advertencia, en caso de existir espacio en la
estructura, se asigna en la posicin el elemento que llega por parmetro en el mtodo y se
actualiza el ndice siempre garantizando que el vector se trabaje manera circular.

Mtodo

public void enqueue (Object nuevoElemento)


throws QueueFullException
{
if (size() == n-1)
{
throw new QueueFullException ("No se
puede insertar");
}
Q[r] = nuevoElemento;
r = (r+1) % n;
}

230

Orden de
Complejidad

El orden de complejidad para este mtodo es O(1), complejidad


constante.

El mtodo front() retorna un objeto de la cola, el objeto que retorna en caso de encontrarse es
el elemento que esta en el frente de la cola. Si la cola se encuentra vaca, se muestra un
mensaje de advertencia indicando esta situacin.

Mtodo

Anlisis del
Orden de
Complejidad

public Object front ()throws QueueFullException


{
if (isEmpty ())
{
throw new QueveFullException ("Cola
Vacia");
}
else
{
return Q[f];
}
}
Por la regla de la suma podemos deducir que el orden de
complejidad de este mtodo es O(1) y por lo tanto es constante.

El mtodo size() retorna un valor entero, este valor entero determina la cantidad de elementos
que tiene la cola.

Mtodo
Anlisis del
Orden de
Complejidad

public int size ()


{
return (n-f+r) % n;
}
El orden de complejidad de este mtodo es O(1) y por lo tanto es
constante.

El mtodo dequeue() elimina el ultimo elemento del frente de la cola y lo retorna, inicialmente
verifica si la cola contiene o no elementos, en caso de estar la cola vaca, muestra un mensaje
de advertencia indicando la situacin, en caso contrario, actualiza la posicin del ndice
mediante f = (f+1) % n y se retorna el elemento de la posicin en la cual se encontraba el ndice
inicialmente.

Mtodo

public Object dequeue ()throws QueueFullException


{
Object temp;
if (isEmpty ())
{
throw new QueueFullException ("Pila
Vacia");
}
else
{
temp = Q[f];
Q[f] = null;
f = (f+1) % n ;
return temp;
}
}
}

231

Orden de
Complejidad

4.5.2

Por la regla de la suma podemos deducir que el orden de


complejidad de este mtodo es constante O(1).

Implementacin utilizando Listas

A continuacin se muestra una implementacin del concepto cola basndose en nodos,


inicialmente se explicar la funcionalidad de cada uno de los mtodos de implementacin, para
posteriormente entrar en detalle con el anlisis y deduccin del orden de complejidad.
Inicialmente se declara la clase Cola con su respectivo mtodo constructor. Se declaran tres
referencias con las cuales es posible realizar las diferentes operaciones sobre las colas
utilizando listas. El mtodo inicializa dos referencias en null.
public class Cola{
Nodo frente;
Nodo ultimo;
int cantidad;
Mtodo

Orden de
Complejidad

Cola()
{
frente = new Nodo(null);
ultimo = new Nodo(null);
cantidad = 0;
}
El orden de complejidad para este mtodo es O(1).

El mtodo dequeue, elimina el prximo nodo candidato a salir de la cola, si la cola est vaca,
muestra un mensaje de advertencia indicando esta situacin, este mtodo retorna el dato del
nodo removido.

Mtodo

Orden de
Complejidad

Object dequeue()
{
Object dato = null;
if (frente.siguiente != null)
{
dato = frente.siguiente.info;
frente.siguiente =
frente.siguiente.siguiente;
cantidad--;
}
else
{
System.out.println("Cola vacia !!!");
}
return dato;
}
El orden de complejidad para este mtodo es O(1).

El mtodo queue agrega un nuevo nodo a la cola, recibe por parmetro el valor que ser
asignado al nodo.

232

Mtodo

Orden de
Complejidad

void queue(Object e)
{
Nodo n = new Nodo(e);
Nodo p = new Nodo();
if (frente.siguiente == null)
{
frente.siguiente = n;
ultimo.siguiente = n;}
else
{
ultimo.siguiente.siguiente = n;
ultimo.siguiente = n;
}
catidad++;
}
El orden de complejidad para este mtodo es O(1).

El mtodo top retorna el frente de la cola.

Mtodo
Orden de
Complejidad

Nodo top()
{
return frente.siguiente;
}
El orden de complejidad para este mtodo es O(1).

El mtodo ltimo retorna el elemento que se encuentra al final de la cola.

Mtodo
Orden de
Complejidad

Nodo ultimo()
{
return ultimo.siguiente;
}
El orden de complejidad para este mtodo es O(1).

Mtodo que retorna la cantidad de elementos que tiene la cola.

Mtodo
Orden de
Complejidad

4.6

int longitud()
{
return cantidad;
}
El orden de complejidad para este mtodo es O(1).

Estructura ArrayList

En las estructuras contenedoras de tamao fijo, de antemano se conoce cul es la capacidad


que tendr la estructura para almacenar elementos, es decir que cuando se est ejecutando la
aplicacin si se necesita agregar un elemento y ya no exista capacidad, se genera un error.
Para solucionar este tipo de problemas es necesario utilizar una estructura contenedora flexible
que se pueda modificar durante la ejecucin de la aplicacin (Cardona, Jaramillo, & Villegas,
2008).

233

Los ArrayList, son una de las muchas estructuras que crecen o disminuyen dinmicamente.
En los ArrayList nicamente se pueden almacenar objetos, no es posible almacenar en el
tipos primitivos de datos.
Algunos mtodos de los mtodos predefinidos de la clase ArrayList se muestran a
continuacin:

size(): Devuelve el tamao de la estructura, es decir, la cantidad de elementos que


contiene
isEmpty(): Indica si hay o no elementos en el ArrayList.
add(elemento): permite insertar un nuevo elemento al final de la estructura
contenedora.
set(posicin, elemento): remplaza el elemento que se hay en la posicin indicada por
uno nuevo.
contains(elemento): Si el elemento est contenido se devuelve true, de lo contrario
false.
toArray(): Copia los elementos de la estructura a un arreglo de objetos.
add(posicin, elemento): permite insertar un elemento en la posicin indicada. Si ya
haba un elemento en esa posicin, el elemento que ya existe y todos los que se
encuentran a su derecha se corrern una posicin hacia la derecha.
remove (posicin): borra el elemento que est en la posicin indicada, esto implica
que los elementos que estaban a la derecha del elemento eliminado se corrern hacia
la izquierda para ocupar el lugar eliminado. Esta operacin hace que el tamao de la
estructura se reduzca en 1.
remove(elemento): en este caso se enva el objeto que se desea eliminar. Es
importante aclarar, que si se crea un nuevo elemento con los datos del objeto que se
desea eliminar no indica que sea el mismo elemento. Para que sean iguales deben
ocupar la misma posicin de memoria, es decir, deben ser la mismas instancias.

Teniendo en cuenta que los anteriores son mtodos predefinidos por el lenguaje de
programacin, la implementacin y anlisis de los mismos se realizar en un contexto de
aplicacin que se muestra en la siguiente seccin.

4.7

Caso de estudio Universidad

Se quiere construir una Aplicacin que maneje la informacin de los programas acadmicos de
una Universidad. Cada programa acadmico tiene nombre, cdigo, telfono y un nmero de
estudiantes. El cdigo de cada programa debe ser nico es por defecto la palabra clave para
su bsqueda.
En el programa de la Universidad se debe poder: (1) agregar un nuevo programa acadmico,
(2) mostrar la lista completa de los programas acadmicos, (3) buscar programa acadmico
por el cdigo, (4) mostrar el nombre de los programas que tengan ms de 350 estudiantes.

234

a. Identificar las clases.


ENTIDAD
Universidad
Programa

DESCRIPCIN
Es la clase principal del problema.
Contiene la informacin de cada programa acadmico.

b. Diagrama de clases.
A continuacin se muestra el diagrama de clases para el caso de estudio.

235

Un ArrayList es una clase contenedora de objetos en java la forma en la cual se declara un


arreglo es similar a la que se realizo con la declaracin de los arreglos. Para el caso de estudio
se tiene que la clase Universidad tiene una coleccin de programas, por lo tanto debemos
declarar un ArrayList de Programa y posteriormente crearlo.
La sintaxis para la declaracin y creacin de un ArrayList es la siguiente:
import java.util.ArrayList;
public class Universidad
{
private ArrayList <Programa> misProgramas ;
}
Es necesario para la declaracin del ArrayList determinar que tipo de informacin se
almacenar en el, para este caso se tiene <Programa> y misProgramas es de tipo
ArrayList.
La construccin del ArrayList se debe hacer dentro del mtodo constructor, para el caso de
estudio la inicializacin se realizara de la siguiente manera:
public Universidad()
{
misProgramas = new ArrayList<Programa>();
}
A continuacin se mostraran los mtodos ms comunes para la clase ArrayList.
Es necesario una vez se crea el ArrayList, es posible realizar varios tipos de operaciones,
inicialmente tenemos el mtodo add(objeto), el cual permite adicionar un elementos a la
estructura contenedora. Para el caso de estudio, si deseamos agregar un programa es posible
adicionarlo al ArrayList misProgramas.
public void agregarPrograma( String nombre, String codigo,
String telefono, int estudiantes)
{
Programa miPrograma=new Programa(nombre,codigo,telefono,
estudiantes);
misProgramas.add(miPrograma);
}
En muchas ocasiones es necesario obtener un elemento de un ArrayList, para este caso se
utiliza el mtodo get(i) , donde i indica la posicin del elemento que se desea obtener. La
instruccin (misProgramas.get(i).getCodigo())obtiene el objeto subindicado i, y de
este objeto se obtiene el cdigo. Tambin es necesario obtener el tamao del ArrayList por
ejemplo para establecer recorridos mediante ciclos, esta mtodo es size(). Por ejemplo para
el ArrayList la instruccin misProgramas.size() retorna el tamao de la estructura
contenedora.

c. Implementacin de los mtodos


La siguiente es la implementacin de los mtodos asociados a los requerimientos definidos
para el caso de estudio. Se analizar su orden de complejidad.

236

Inicialmente si tiene el mtodo Constructor de la clase Universidad. En l se inicializa el arreglo


de programas acadmicos.
public class Universidad
{
private ArrayList <Programa> misProgramas ;
Mtodo

Orden de
Complejidad

public Universidad()
{
misProgramas = new ArrayList<Programa>();
}
El orden de complejidad para este mtodo es O(1).

Permite crear un programa acadmico al ArrayList y posteriormente adicionarlo a la coleccin


de programas mediante la operacin add.

Mtodo

Orden de
Complejidad

public void agregarPrograma( String nombre, String


codigo,String telefono, int estudiantes)
{
Programa miPrograma = new Programa(nombre,
codigo, telefono, estudiantes);
misProgramas.add(miPrograma);
}
El orden de complejidad para este mtodo es O(1).

Este mtodo una arreglo de cadenas con la informacin de todos los programas acadmicos.
Para esta operacin utiliza el mtodo predefinido get(i), en el cual i es el ndice por medio del
cual se accede a cada programa.

Mtodo

public String[] getInfoProgramas()


{
String []info= new String
[misProgramas.size()];
for(int i=0; i<misProgramas.size(); i++)
{
info[i]=misProgramas.get(i).toString();
}
return info;

Orden de
Complejidad

}
El orden de complejidad del mtodo se analiza asumiendo que el
ArrayList contiene n elementos. Por lo tanto se necesita de un ciclo
que permita recorrer la totalidad del mismo, y su orden de
complejidad es O(n).

El mtodo buscarPrograma permite buscar un programa acadmico por el nombre. La clave


para realizar la bsqueda es el cdigo del programa. En caso que exista el cdigo en el
ArrayList, se retorna la referencia al programa con toda su informacin, en caso contrario, se
retorna una referencia nula.

237

Mtodo

Orden de
Complejidad

public Programa buscarPrograma(String codigo)


{
for (int i=0; i < misProgramas.size(); i++)
{
if((misProgramas.get(i).getCodigo()).
equals(codigo))
{
return (misProgramas.get(i));
}
}
return null;
}
Para el caso de la bsqueda se tiene el mejor y el peor caso. Si el
elemento se encuentra en las primeras posiciones del arreglo, el
orden de complejidad del mtodo es O(1), pero si el elemento a
buscar se encuentra en las ltimas posiciones del arreglo, el orden
de complejidad del mtodo es O(n).

Este programa retorna el nombre del programa con ms de 350 alumnos.

Mtodo

Orden de
Complejidad

public String ProgramasConMasAlumnos()


{
String cadena = " ";
int i = 0;
while(i < misProgramas.size())
{
if((misProgramas.get(i).
getEstudiantes()>350))
{
cadena+=misProgramas.get(i).
getNombre();
}
i++;
}
return cadena;
}
En este caso se debe realizar un recorrido por cada uno de los
programas acadmicos, entonces si se tienen n programas
acadmicos, el orden de complejidad es O(n).

ACTIVIDAD
El mtodo remove(objeto) permite eliminar un objeto de la estructura contenedora. Escriba
un mtodo que permita eliminar todos los programas acadmicos cuya cantidad de estudiantes
sea inferior a 98.

238

Investigue en que consiste el mtodo set(objeto), el cual permita modificar la informacin


de un Programa Acadmico.

239

240

5.1

OPTIMIZACIN, PRUEBAS Y LMITES DE LA LGICA

Introduccin

En este captulo se mostrarn las tcnicas bsicas que pueden ser usadas para la optimizacin
de cdigo. En captulos anteriores se han mostrado algunas tcnicas que son de utilizada para
el anlisis y solucin de problemas de programacin. Adicional a estas tcnicas se incorpora la
optimizacin de cdigo es cual es un tema que correctamente aplicado puede ayudar a
maximizar la eficiencia en trminos de tiempo de ejecucin de un programa.
Con el propsito de analizar los aspectos relacionados con las pruebas al cdigo. Java dispone
del Framework JUnit para la realizacin y automatizacin de pruebas y el cual se explicar
mediante un ejemplo de aplicacin.
Finalmente en este captulo se mostrarn los conceptos bsicos de los problemas asociados a
los lmites de la lgica, los cuales son conocidos como los problemas clase P y clase NP.

5.2

Tcnicas de Optimizacin

El objetivo de las tcnicas de optimizacin es mejorar el cdigo fuente para que nos d un
rendimiento mayor. Muchas de estas tcnicas vienen a compensar ciertas ineficiencias que
aparecen en el cdigo fuente. Las optimizaciones en realidad proporcionan mejoras, pero no
aseguran el xito de la aplicacin. Algunas optimizaciones que se pueden aplicar a los ciclos
son:
Desenvolvimiento de ciclos (loop unrolling).
Reduccin de esfuerzo.
Tipos de variables
Fusin de ciclos (loop jamming).

5.2.1

Desenvolvimiento de ciclos

Esta tcnica de optimizacin consiste en desenvolver el cuerpo del ciclo dos o ms veces
incrementando los saltos de ciclo, con el fin de mejorar el reso de registros, minimizar
recurrencias y de exponer ms paralelismo a nivel instruccin. El nmero de desenvolvimiento
es determinado automticamente por el compilador o bien por el programador mediante el
empleo de directivas (Garca, Delgado, & Castaeda, 2000).
Cuando se encuentra que la cantidad de iteraciones del ciclo es pequea y constante, esta
tcnica se puede utilizar. Por ejemplo, si se tiene el siguiente cdigo.
Para este caso inicialmente se muestra un mtodo el cual contiene un cdigo que se va a
optimizar. En estos casos no se esta resolviendo un problema especifico se quiere hacer
nfasis en la aplicacin de la tcnica.

Mtodo

public void ejemplo()


{
int prueba[]= new int [2000];
int solucion [] = new int [6];
int k = 0;
for (int i = 0; i < prueba.length; i++)
{
k++;
prueba[i]= (i*3) + 25;

241

if (k % 13 == 0)
{
k = 0;
for (int j = 0 ; j < 6; j++)
{
solucion[j]= prueba[j]+5;
}
}
}
}

La aplicacin de la tcnica de desenvolvimiento, permitira modificar el cdigo de la siguiente


manera:

Mtodo

public void Desenvolvimiento1()


{
int prueba[]= new int [2000];
int solucion [] = new int [6];
int k = 0;
for (int i = 0; i < prueba.length; i++)
{
k++;
prueba[i]= (i*3) + 25;
if (k % 13 == 0)
{
k=0;
solucion[0] = prueba[0] + 5;
solucion[1] = prueba[1] + 5;
solucion[2] = prueba[2] + 5;
solucion[3] = prueba[3] + 5;
solucion[4] = prueba[4] + 5;
solucion[5] = prueba[5] + 5;
}
}
}

Se observa que el ciclo mas externo del mtodo ejemplo(), se elimina por el equivalente en el
cdigo, de forma tal que el acceso a cada ndice del arreglo se realiza directamente. El
algoritmo primer mtodo es menos eficiente que el que aparece optimizado. De forma
experimental, se puede afirmar que la correcta aplicacin de la tcnica mejora el tiempo de
ejecucin.
A continuacin se muestra otro ejemplo para mostrar la aplicacin de la tcnica de
desenvolvimiento. Por ejemplo si se tiene el siguiente fragmento de cdigo:

Mtodo

public void ejemplo1()


{
arreglo= new int [5000000];
temp = new int[5000000];
tam= arreglo.length;
tam1 = temp.length;
for(int j=0; j<=tam1; j++)
{
temp [j]= j+=5;
}
int j =0;

242

for (int i=0;i<tam;i++)


{
arreglo[i]=temp[j]*temp[j++]+2000;
}
}
La optimizacin del cdigo mediante la tcnica de desenvolvimiento se muestra a continuacin.
public void desenvolvimiento1()
{
arreglo= new int [5000000];
temp = new int[5000000];
tam= arreglo.length;
tam1 = temp.length;
for(int j=0; j<=tam1; j++)
{
temp [j]= j+=5;
}
int j=0;
for (int i=0;i<tam;i+=5)
{
arreglo[i] = temp[j]*temp[j++]+2000;
arreglo[i+1]= temp[j]*temp[j++]+2000;
arreglo[i+2]= temp[j]*temp[j++]+2000;
arreglo[i+3]= temp[j]*temp[j++]+2000;
arreglo[i+4]= temp[j]*temp[j++]+2000;
}

Mtodo

5.2.2

Reduccin de Esfuerzo

La tcnica de reduccin de esfuerzo (Strength Reduction), sugiere que una expresin puede
remplazarse por otra, siempre y cuando cumpla la misma funcin, siempre garantizando una
mejora en la eficiencia de su ejecucin. A continuacin se muestra un caso en el cual se aplica
esta tcnica.

Mtodo

public void ejemplo2 ()


{
int prueba [] = new int [5000];
int solucion [] = new int [5000];
for (int i = 0; i < prueba.length; i++)
{
prueba[i] = (Math.random () * 101);
}
for (int j = 0; j < solucion.length; j++)
{
solucion [j] = Math.pow(prueba[j],3);
}
}

La tcnica de optimizacin de reduccin de esfuerzo, sugiere entonces, eliminar los llamados a


mtodos predefinidos de clase, y escribir directamente el cdigo.

243

Mtodo

public void reduccionEsfuerzo()


{
int prueba [] = new int [5000];
int solucion [] = new int [5000];
for (int i = 0; i < prueba.length; i++)
{
prueba[i] = (int)(Math.random()* 101);
}
for (int j = 0; j < solucion.length; j++)
{
solucin[j] = prueba [j] * prueba[j] *
prueba[j];
}
}

La instruccin solucion[j] = Math:pow(prueba[j]; 3) del mtodo ejemplo2, se reemplaza por el


cdigo equivalente, pero sin llamados a mtodos predefinidos, y quedando as: solucion[j] =
prueba[j] * prueba[j] * prueba[j].
Tambin, para este caso el mtodo denominado ejemplo2 es menos eficiente denominado
reduccin de esfuerzo. Lo anterior se confirma mediante la experimentacin en tiempo de
ejecucin. En este caso se puede afirmar que la operacin de potencia mediante un mtodo de
clase predefinida, es mucho ms lenta que la operacin de producto.

5.2.3

Tipos de Variables

Se ejecuta la misma operacin pero con datos de tipo entero y reales. Adicionalmente el
concepto de tipo de dato se utiliza, entre otras cosas, para realizar las operaciones entre los
datos. As, para un mismo procesador, efectuar el producto de dos nmeros es mucho ms
lento si estos nmeros son de tipo real que si son de tipo entero; sin embargo ambos casos son
similares desde el punto de vista algortmico. A continuacin se muestra la implementacin
usando tipos de datos double.

Mtodo

public void ejemplo3()


{
double prueba[]= new double [2500000];
double solucion [] = new double [2500000];
for (int i=0; i < prueba.length; i++)
{
prueba[i] = (i * 5) + 345;
}
for (int j = 0; j < solucion.length; j++)
{
solucion[j] = prueba[j] * j;
}
}

La siguiente implementacin, corresponde a la optimizacin utilizando tipo de variable int.

Mtodo

public void tiposVariables()


{
int prueba1[]= new int [2500000];
int solucion1 [] = new int [2500000];
for (int i=0; i < prueba1.length; i++)
{
prueba1[i] = (i * 5) + 345;
}

244

for (int j = 0; j < solucion1.length; j++)


{
solucion1[j] = prueba1[j] * j;
}
}
Si se ejecutan ambos algoritmos en un computador que posee un procesador de 32 bits,
necesariamente debe existir mayor carga computacional para el procesamiento de los tipos de
datos double. Lo anterior nos muestra que tambin la velocidad de procesamiento, incide en el
tiempo de ejecucin de una aplicacin.

ACTIVIDAD

Aplique la tcnica de fusin de ciclos al siguiente mtodo.

Mtodo

5.2.4

public void tiposVariables1()


{
double arreglo1[]= new double [5000000];
double operacion1[]= {1,2,3,4,5,6};
int longi=arreglo1.length , i, j=0;
for (i=0;i<longi;i++)
{
arreglo1[i]= operacion[j++] +i*i+ 150;
if (j==6){
j=0;
}
}
}

Fusin de ciclos

Cuando en una implementacin se tienen ciclos que iteran la misma cantidad de veces, tal
como se muestra en el siguiente cuadro Dada esta situacin, se puede optimizar el cdigo,
aplicando la tcnica de la fusin de ciclos. Para este ejemplo, se puede observar que el tamao
de los tres arreglos definidos es igual. La operacin de lectura de los datos en cada uno de
ellos se realiza por medio de un ciclo que itera desde cero hasta 2500000.
Cuando se puede verificar que los ciclos iteran la misma cantidad de veces y estos se
encuentran concatenados, es posible unir todas las instrucciones en un solo ciclo. Esta
operacin de fusin mejora la ejecucin de las instrucciones en cuanto a su tiempo.

Mtodo

public void ejemplo4()


{
int prueba [] = new int [2500000];
int prueba1 [] = new int [2500000];
int solucion [] = new int [2500000];
for (int i = 0; i < prueba.length; i++)
{
prueba[i] = (i * 2) * (35 - i);
}
for (int j = 0; j < solucion.length; j++)
{
solucion[j] = prueba[j] * j;
}

245

for (int k = 0; k < prueba1.length; k++)


{
prueba1[k] = prueba[k] - k;
}
}
El anterior cdigo, se puede optimizar de forma tal que estos se unan en un solo ciclo. Es
necesario verificar previamente la relacin entre las operaciones que se realizan en este nico
ciclo. Lo anterior para evitar resultados errneos.

Mtodo

public void fusionCiclos()


{
int prueba [] = new int [2500000];
int prueba1 [] = new int [2500000];
int solucion [] = new int [2500000];
for (int i = 0; i < prueba.length; i++)
{
prueba[i] = (i * 2) * (35 - i);
solucion[i] = prueba[i] * i;
prueba1[i] = prueba[i] - i;
}
}

Otro ejemplo en el cual se muestra la fusin de ciclos. Para este caso se escribe un fragmento
de cdigo que permite realizar la suma de los elementos que se encuentran en la diagonal
secundaria de una matriz.

Cdigo

for(int i=0; i<tam;i++)


{
for(int j=0;j<tam;j++)
if(i==j)
{
resul=arreglo[i][j];
}
}

El cdigo optimizado aplicando la tcnica de fusin de ciclos se muestra a continuacin.

Cdigo

5.2.5

int s;
for(int i=0; i<tam;i++)
{
s=i;
resul=arreglo[i][s];
}

Expresiones redundantes

Otras de las tcnicas ms comunes de optimizacin en compiladores es la eliminacin de


operaciones que son redundantes en sub-expresiones, logrndose una reduccin de cdigo.
As, muchas de las veces a una variable se le asigna un valor y se reutiliza varias veces, ya
que dicha variable es comn en otras operaciones (Garca, Delgado, & Castaeda, 2000).

246

public void ejemplo5()


{
int lon,l,m;
double j;
lon = arreglo1.length;
for (int i=0;i<lon;i++)
{
j=arreglo1[i]*Math.pow(i,3) +
Math.pow(i,3) ;
}

Mtodo

}
El mtodo optimizado aplicando la tcnica de expresiones redundantes se muestra a
continuacin.

Mtodo

5.2.6

public void redundantes()


{
int lon,l,m;
double j;
lon = arreglo1.length;
for (int i=0;i<lon;i++)
{
j=Math.pow(i,3)*(arreglo1[i] + 1 );
}
}

Folding

Es una de las tcnicas de optimizacin ms y de uso mas sencillo, la cual hace referencia a
que en muchas ocasiones las expresiones pueden se simplificadas. Por ejemplo si se tiene la
expresin:
int i = 20 + 58 c;
El compilador de java directamente reemplaza los nmeros 20 y 58 por 78. Este caso se
extiende a las expresiones que impliquen todo tipo de operacin aritmtica.
Para aplicar esta tcnica es necesario que se apliquen conceptos fundamentales de
factorizacin y leyes aplicables a expresiones tales como la ley conmutativa y la ley asociativa.
Estas tcnicas se recomienda que se apliquen a los mtodos que contienen valores locales.
A continuacin se muestra un ejemplo al cual se le aplicar la tcnica de Folding.
public void ejemplo6()
{
int arreglo1[]= new int [3000000];
int i,k,lon,l,m,j;
lon = arreglo1.length;
Mtodo
for (i=0;i<lon;i++)
{
arreglo1[i]=(int)(Math.random()* 101);
}

247

k=8500;
l=15250;
m=450;
for (i=0;i<lon;i++)
{
j=arreglo1[i]*((k+l)*m)/150;
}
}
Del mtodo anterior se reemplazaron las expresiones k=8500; l=15250 y m=450, de
forma tal que se sustituy directamente por el valor resultante de aplicarlos en la formula. De
esa manera la tcnica muestra como queda el mtodo.
public void folding()
{
int arreglo1[]= new int [3000000];
int i,k,lon,l,m,j;
lon = arreglo1.length;
for (i=0;i<lon;i++)
{
arreglo1[i]=(int)(Math.random()* 101);
}
for (i=0;i<lon;i++)
{
j=arreglo1[i]*71250;
}

Mtodo

ACTIVIDAD

Dado el siguiente fragmento de cdigo, aplique todas las tcnicas posibles para optimizar el
cdigo.
int arreglo[]= new int [5000000];
int arreglo1[]= new int [5000000];
int a[]= {10,20,30,40,50,60};
int longi=arreglo.length, i, m=0,w, k=50,l=25,m=50;
for (i=0;i<longi;i++)
{
arreglo[i]= a[m++]+arreglo1[i]*Math.pow(i,2)+
Math.pow(i,2)+1;
if(m==6)
{
m=0;
}
}
for (i=0;i<longi;i++)
{
w=Math.pow(a[i],6)+ 500;
}

248

for (i=0;i<long;i++)
{
arreglo1[i]= arreglo1[i]+m+k/m*l;

}
Dado el siguiente fragmento de cdigo, aplique todas las tcnicas posibles para optimizar el
cdigo.
a=16541;
b=24415;
c=86414;
for(int i=0; i<tam;i++)
{
arreglo[i]=(54*i)+((b+((int)Math.sqrt(b*b)+(4*a*c))/(2*a)));
}
Dado el siguiente fragmento de cdigo, aplique todas las tcnicas posibles para optimizar el
cdigo.
public void ejercicio()
{
int prueba[]= new int [900];
int solucion [] = new int [8];
int k = 0;
for (int i = 2; i < prueba.length; i++)
{
k++;
prueba[i]= (i*3) + 25;
if (k % 2 == 0)
{
k = 13;
for (int j = 4 ; j < 11; j++)
{
solucion[j]= prueba[j]+Math.pow(prueba[j],2);
}
}
}
}

5.3

El diseo por contrato

El Diseo por contrato es una metodologa que se basa en la idea de tener claramente definido
lo que se tiene en la entrada y lo que se debe proporcionar como resultado. A este conjunto de
trminos lo denominaremos contrato entre las partes. En general, puede afirmarse que el
contrato es un acuerdo formal, en el que se expresan las obligaciones y derechos de los
participantes. Por lo tanto si ocurre algn error nos remitiremos al contrato para poder
determinar las responsabilidades. Tenga en cuenta que para asignar responsabilidades se
puede apoyar en la tcnica del experto (el dueo de la informacin es el responsable de ella) o
la tcnica de descomposicin por requerimientos (descomponer un requerimiento funcional en
subproblemas para poder satisfacer el requerimiento completo) (Villalobos & Casallas, 2006).
Considere el siguiente mtodo:
public void
direccion)

agregarEstudiante(String

249

cdigo,

String

nombre,

String

En este caso, las condiciones previas (que de ahora en adelante denominaremos


precondiciones) que deben cumplirse para que este mtodo se ejecute son:

El array de estudiantes ya se declar y se le reserv memoria


El cdigo no es null, ni vacio
El nombre no es null, ni vacio
La direccin no es null, ni vacio
No se sabe si hay un estudiante con este cdigo

La precondicin expresa las restricciones necesarias para que la operacin funcione de forma
adecuada.
El resultado de la ejecucin del mtodo (denominado postcondicin) debe ser:

Se agreg al estudiante.
Se produjo un error y no se pudo efectuar la operacin de agregar.

La postcondicin se define como las condiciones que deben ser ciertas cuando termina la
ejecucin del mtodo.
Se considera entonces que la precondicin son las condiciones impuestas para que se d el
desarrollo del mtodo, mientras que la postcondicin se considera como los compromisos
aceptados.
Es muy importante realizar una adecuada documentacin a los contratos, esto se logra
haciendo uso de javadoc. Tenga en cuenta que en general, se deben agregar los comentarios
a cada uno de los mtodos, que componen un programa.
/**
* Estos son comentarios javadoc
*/
Cuando se usan comentarios javadoc, se puede especificar la versin, fecha de creacin,
autor.
* @ version 1.0
* @ created Nov-2007
* @ author Carlos Perez
Para que eclipse nos ahorre un poco de trabajo con la generacin de la documentacin javadoc
se debe digitar /** y presionar la tecla enter.
Ya enfocndonos en el contrato, la documentacin debe tener la siguiente estructura:
/**
* Este mtodo permite agregar un nuevo estudiante a un curso de
* programacin
* <b>pre: </b> El array donde se guardan los estudiantes no es null.
* <b>post: </b> Se ha agregado un nuevo estudiante.
* @ param cdigo. Cdigo estudiante. codigo!=null, !codigo.equals("");
* @ param nombre. Nombre estudiante. nombre!=null, !nombre.equals("");
* @ param direccin. Direccin estudiante direccion!=null,!direccion.
* equals("");
* @throws Exception si un estudiante tiene el mismo cdigo genera una
* excepcin
*/

250

public void agregarEstudiante(String codigo, String nombre, String


direccion) throws Exception
{
}
Cuando ya se han documentado todos los mtodos, se puede generar la documentacin
Javadoc. Para ello es necesario dar clic en la barra de men principal Generar Javadoc.
El diseo por contratos requiere tambin la incorporacin del concepto de invariante. La
Invariante indica las condiciones que deben cumplirse en todo momento para las instancias de
una clase. Por lo tanto se considera como una asercin que expresa restricciones generales
que se cumplen para toda la clase. Algunas reglas referentes al uso de excepciones son las
siguientes:
No se deben usar a la entrada de mtodos pblicos (solo a la entrada de mtodos privados) y
pueden usarse a la salida de mtodos pblicos o privados que modifican el estado del objeto.
La clase Fibonacci permite resolver el problema de la reproduccin de conejos.
public class Fibonacci
{
int n;
/**
* Este mtodo permite calcular la serie Fibonacci
* @param n Es la cantidad de trminos de la serie. n>=0
* @return el valor en la serie fibonacci
*/
public int calcularFibonacci (int n)
{
this.n=n;
if ((n == 0) || (n == 1))
{
return 1;
}
else
{
llamarVerificarInvariante();
return calcularFibonacci(n-1) + calcularFibonacci(n-2);
}
}
/**
* Llama al mtodo verificar invariante
*/
public void llamarVerificarInvariante()
{
try
{
verificarInvariante();
}
catch(AssertionError ae)
{
imprimirErrorInvariante(ae);
}
}

251

/** Este mtodo permite verificar la invariante de la clase */


public void verificarInvariante( )
{
assert (n>=0) : "n debe ser mayor o igual a cero";
}
/**Este mtodo permite imprimir el error que ha ocurrido al
* momento de verificar la invariante
* @param ae assetionError es el error ocurrido */
public static void imprimirErrorInvariante( AssertionError ae )
{
StackTraceElement[] stack = ae.getStackTrace();
StackTraceElement stackTraceElement = stack[ 0 ];
System.err.println( "Error en el assert" );
System.err.println( "
Clase=
" +
stackTraceElement.getClassName() );
System.err.println( "
Metodo= " +
stackTraceElement.getMethodName() );
System.err.println( "
Mensaje= " + ae.getMessage() );
}
}

5.4

Pruebas con JUnit

Java dispone del Framework JUnit para la realizacin y automatizacin de pruebas. Las
pruebas permiten verificar el funcionamiento de los mtodos de la clase, para determinar si
funcionan como se espera. Es decir, permiten para identificar posibles errores y revelar el
grado de cumplimiento en relacin a las especificaciones que inicialmente se plantearon para el
sistema.
La realizacin de pruebas es fundamental a la hora de garantizar la calidad del software
construido. Para crear una clase de prueba es necesario ubicarse en el paquete test, el cual
debi crear previamente, luego dar clic sobre l y seleccionar JUnit Test Case.
Para mostrar el uso de JUnit se tendr en consideracin el caso de estudio para un estudiante.
A continuacin se muestra la descripcin del problema.
Se desea crear una aplicacin para manejar la informacin de un estudiante. Un estudiante
tiene un cdigo, un nombre y una nota definitiva. Se debe permitir crear un nuevo estudiante,
dar una bonificacin de una dcima en la nota definitiva y devolver el cdigo del estudiante.
El diagrama de clases correspondiente es:

252

A continuacin se procede a crear en eclipse el ambiente de pruebas. Se debe crear un caso


de prueba seleccionando JUnit Test Case.

En Class under Test se debe presionar el botn Browse y luego se debe escribir el nombre de
la clase a la que se le va a hacer el test. A continuacin dar clic en Ok.

Luego, en el campo Name, es necesario escribir el nombre que se le va a poner a la prueba,


por ejemplo EstudianteTest. El nombre del test debe iniciar con el nombre de la clase a la que
se le va a realizar la prueba seguida de la palabra Test.
Algunas de las opciones disponibles respeto a etiquetas para realizar las pruebas son las que
se muestran a continuacin:

253

setUpBeforeClass: Este mtodo ser ejecutado al inicio del lanzamiento de todas las
pruebas. nicamente puede tenerse un mtodo con esta opcin. Se utiliza en general
para inicializar atributos que son comunes a todas las pruebas.
tearDownAfterClass: nicamente puede haber un mtodo con esta marca. Se ejecuta
una sla vez cuando ha finalizado la ejecucin de todas las pruebas.
tearDown: Se ejecuta despus de ejecutar todas las pruebas de esta clase. Este
mtodo se sustituye por la anotacin @After. Esta etiqueta efecta todo lo contrario de
la etiqueta @Before.
setUp: Se invoca antes de ejecutar cada prueba. Este mtodo se sustituy por la
anotacin @Before
Test: Se le coloca @Test a la prueba que se va a ejecutar
Ignore: El mtodo que tenga esta marca no ser ejecutado.

Continuando con la creacin de la clase de prueba para Estudiante de clic en Next.


Posteriormente hay que seleccionar los mtodos donde se cambia el estado del objeto: set,
constructores o mtodos que realicen clculos.

Para este caso se seleccionarn los siguientes mtodos:


o
o
o
o

setCodigo(String)
setNombre(String)
setNota(double)
calcularBonificacion()

A continuacin es necesario dar clic en Finish. La siguiente ventana aparecer:

Posteriormente aparece el siguiente cdigo de implementacin.


import static org.junit.Assert.*;
import org.junit.Test;

Mtodo

public class EstudianteTest


{
@Test
public void testSetCodigo()
{
fail("Not yet implemented");
}
@Test
public void testSetNombre()

254

{
fail("Not yet implemented");
}
@Test
public void testSetNota()
{
fail("Not yet implemented");
}
@Test
public void testCalcularBonificacion()
{
fail("Not yet implemented");
}
}
A continuacin se debe usar la siguiente clase para verificar que los mtodos de la clase
Estudiante estn correctamente implementados. Es necesario modificar el cdigo para que
quede de la siguiente forma:
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class EstudianteTest
{
private Estudiante miEstudiante;
/** Construye un nuevo estudiante*/
@Before
public void setupEscenario1( )
{
miEstudiante=new Estudiante();
miEstudiante.setCodigo("123");
miEstudiante.setNombre("Juan");
miEstudiante.setNota(3);
}
/** Prueba de los mtodos setCodigo*/
Mtodo
@Test
public void testSetCodigo(){
assertEquals( "El codigo es
invlido.", "123",
miEstudiante.getCodigo() );
}
@Test
public void testSetNombre() {
assertEquals( "El nombre es
invlido.", "Juan",
miEstudiante.getNombre());
}
@Test
public void testSetNota() {
assertEquals( "La nota es
incorrecta.", 3.0,
miEstudiante.getNota(),1 );
}

255

@Test
public void testCalcularBonifiacion()
{
assertEquals( "La nota definitiva con
bonificacin es incorrecta.", 3.1,
miEstudiante.calcularBonifcacion(),1 )
}
}

5.5

Lmites de la Lgica

Como ltima seccin de este libro se plantea una clasificacin de los problemas segn el tipo
de algoritmo. Se describirn los conceptos fundamentales de los algoritmos de clase P, clase
NP y NP completos.

5.5.1

Clase P

Los algoritmos de la clase P son conocidos como aquellos algoritmos cuyo tiempo de ejecucin
y complejidad computacional se encuentra en un orden polinmico. Con base en lo analizado
en este libro se puede afirmar que los algoritmos con tiempo de ejecucin polinmico son aun
considerados como eficientes y por lo tanto son tratables y manejables en la prctica.
Algunos ejemplos de problemas y soluciones que se encuentran en la clase P, son los
siguientes:

5.5.2

Multiplicacin de nmero grandes


Multiplicacin de Matrices
Resolucin de Sistemas de Ecuaciones Lineales
Procedimientos de ordenamiento y bsqueda.
Aplicaciones tpicas empresariales
Sistemas de informacin para organizaciones.

Clase NP

Los algoritmos de la Clase NP estn asociados a problemas complejos y de ndoles


enumerativo, en donde su espectro de solucin esta asociada a una bsqueda sistemtica
dentro de un rbol; la caracterstica fundamental del rbol es que es de profundidad limitada y
su anchura es a lo sumo exponencial.
Algunos ejemplos de problemas y soluciones que se encuentran en la clase NP, son los
siguientes:

Problema de aprendizaje
Problemas complejos con Grafos
Coloreado de Grafos
El problema del Agente Viajero
Programacin automtica de Tareas

Generalmente estos problemas que se consideran como intratables, desembocan en mtodos


no determinsticos en los cuales se planean mtodos que no necesariamente llevan a una
solucin esperada y por el contrario llegan a soluciones hipotticas. Los problemas de esta
clase se denominan NP (la N de no-deterministas y la P de polinmicos).

256

BIBLIOGRAFA

Aho, A., & Ullman, J. (1995). Foundations of Computer Science. Computer Science Press,
1995.
Aho, A., Ullman, J., & Hopcrof, J. (1994). The Design and Analysis of Computer. AddisonWesley.
Baldwin, D., & Scragg, G. (2004). Algorithms & Data Structures. Massachusetts: Computer
Engineering Series.
Bedoya, O. (2008). Estructura de Datos y Algoritmia. Cali, Valle, Colombia.
Besembel, I. (2006). Diseo y Anlisis de Algoritmos. Recuperado el 10 de 10 de 2011
Bohquez, J. (2006). Diseo Efectivo de Programas Correctos. Bogot: Lemoine Editores.
Brassard, G., & Bratley, P. (1997). Fundamentos de Algoritmia. Madrid: Prentice Hall.
Cardona, S., Jaramillo, S., & Carmona, P. (2007). Anlisis de Algoritmos en Java. Armenia:
Elizcom.
Cardona, S., Jaramillo, S., & Villegas, M. (2008). Introduccin a la Programacin en Java .
Armenia: Elizcom.
Colmenares, J. (2006). Grafos. Recuperado el 10 de 11 de 2011, de
http://www.ica.luz.ve/juancol/eda/grafos/grafos.html
Cormen, T., Leiserson, C., Rivest, R., & Stein, C. (2001). Introduction to Algorithms.
Dunfermline, FIF, United Kingdom: The MIT Press.
Di Mare, A. (1998). Convenciones de Programacin para Pascal. Recuperado el 1 de 10 de
2011, de http://www.di-mare.com/adolfo/p/convpas.htm
Garca, A., Delgado, J., & Castaeda, S. (2000). Metodologas de Optimizacin en
CICESE2000. Recuperado el 1 de 9 de 2011, de
http://telematica.cicese.mx/computo/super/cicese2000/optimiza/
Goodrich, M. T., & Tamassia, R. (2002). Estructuras de datos y algoritmos. Cecsa.
Grimaldi, R. (1998). Matemtica discreta y combinatoria. Addison Wesley Longman.
Guerequeta, R., & Vallecillo, A. (2000). Tcnicas de Diseo de Algoritmos . Mlaga: Servicio de
Publicaciones de la Universidad de Mlaga.
Hernndez, G. (2004). Grafos. Madrid, Espaa.
Hernndez, L. (2004). Anlisis de Algoritmos y Complejidad Computacional. Recuperado el 10
de 11 de 2011, de http://www.geocities.ws/leoher314/algorit.htm
Iparraguirre, J. (2009). Algoritmos para caminos mnimos. Lima, Per.

257

Lpez, J. (2003). Ecuaciones de Recurrencia. Recuperado el 02 de 02 de 2010, de


http://ocw.univalle.edu.co/ocw/ingenieria-de-sistemas-telematica-y-afines/fundamentosde-analisis-y-diseno-de-algoritmos/material/Unidad1-4.pdf
Maas, j. (1997). Anlisis de Algoritmos: Complejidad. Recuperado el 15 de 1 de 2010, de
http://www.lab.dit.upm.es/~lprg/material/apuntes/o/index.html#s5
McConnell, J. J. (2007). Analysis of Algorithms: An Active Learning Approach. Sudbury: Jones
and Bartlett Publishers.
Molpeceres, A. (2001). Convenciones de Cdigo para el lenguaje de programacin.
Recuperado el 1 de 5 de 2011, de is-g7b2011.googlecode.com/files/EstndarCdigoJava.pdf
Preiss, B. (1998). Data Structures and Algorithms with Object-Oriented. Addison Wesley
Publishing.
Ramirez, M. (2003). Anlisis de Algoritmos. Cali, Colombia.
Universidad Autnoma Metropolitana . (2010). Anlsis de Algoritmo. Recuperado el 1 de 5 de
2011, de http://aniei.org.mx/paginas/uam/CursoAA/index.html
Valenzuela, V. (2003). Manual de Anlisis y Diseo de Algoritmos. Santiago, Chile.
Villalobos, J., & Casallas, R. (2006). Fundamentos de Programacin aprendizaje activo basado
en casos. Bogot: Pearson Prentice Hall.
Weiis, M. (2002). Estructuras de datos. Florida: Addison Wesley.

258

Potrebbero piacerti anche