en Java
Análisis de
Algoritmos en Java
Derechos reservados
c Noviembre de 2007
°
ISBN: 978-958-44-2384-9
Objetivos
Este libro pretende ser flexible en la forma como puede impartirse a las
personas interesadas. La comprensión de los temas, depende fundamental-
mente de la preparación de los estudiantes. Se presentan conceptos basicos
fundamentales e intermedios, los cuales se pueden aplicar en la práctica,
ası́ como también realizar un análisis riguroso de los conceptos teoricos que
se imparten.
Metodologı́a de trabajo
Nuevas caracterı́sticas
Presentación III
1. Conceptos básicos 1
1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.7. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.8. Autoevaluación . . . . . . . . . . . . . . . . . . . . . . . . . . 10
vii
viii ÍNDICE GENERAL
2. Análisis de Algoritmos 21
2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.4. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.5. Autoevaluación . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3. Tiempo de ejecución 43
3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4. Complejidad Computacional 65
4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.5. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
4.6. Autoevaluación . . . . . . . . . . . . . . . . . . . . . . . . . . 93
4.6.2. Complejidad . . . . . . . . . . . . . . . . . . . . . . . 93
5. Recursividad 103
5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
5.2. Concepto de recursividad . . . . . . . . . . . . . . . . . . . . 104
5.3. Algoritmos recursivos . . . . . . . . . . . . . . . . . . . . . . 105
5.3.1. Algoritmo recursivo: sumatoria . . . . . . . . . . . . . 106
5.3.2. Algoritmo recursivo: multiplicación . . . . . . . . . . . 107
5.3.3. Algoritmo recursivo: suma de cifras de un número . . 109
5.3.4. Algoritmo recursivo: potencia . . . . . . . . . . . . . . 111
5.3.5. Algoritmo recursivo: cantidad de cifras de un número 112
5.3.6. Algoritmo recursivo: máximo común divisor . . . . . . 114
5.3.7. Algoritmo recursivo: números armónicos . . . . . . . . 115
5.3.8. Algoritmo recursivo: módulo . . . . . . . . . . . . . . 117
5.3.9. Algoritmo recursivo: Contar ceros arreglo . . . . . . . 118
5.3.10. Algoritmo recursivo: Número menor arreglo . . . . . . 119
5.3.11. Algoritmo recursivo: Número de apariciones . . . . . . 120
5.3.12. Algoritmo recursivo número mayor arreglo . . . . . . . 121
5.3.13. Algoritmo recursivo suma elementos de un arreglo . . 122
5.4. Análisis de algoritmos recursivos . . . . . . . . . . . . . . . . 123
5.4.1. Análisis del algoritmo recursivo: factorial . . . . . . . 124
5.4.2. Análisis del algoritmo recursivo: multiplicación . . . . 127
5.4.3. Análisis del algoritmo recursivo: Fibonacci . . . . . . . 128
5.5. Resolver recurrencias por inducción . . . . . . . . . . . . . . . 130
5.5.1. Ejemplo 1 . . . . . . . . . . . . . . . . . . . . . . . . . 131
5.5.2. Ejemplo 2 . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.5.3. Ejemplo 3 . . . . . . . . . . . . . . . . . . . . . . . . . 133
5.5.4. Ejemplo 4 . . . . . . . . . . . . . . . . . . . . . . . . . 134
ÍNDICE GENERAL xi
6. Ordenamiento 159
6.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
6.2. Método de la Burbuja . . . . . . . . . . . . . . . . . . . . . . 160
6.3. Método de la burbuja bidireccional . . . . . . . . . . . . . . . 162
6.4. Ordenamiento por selección . . . . . . . . . . . . . . . . . . . 167
6.5. Método de Inserción . . . . . . . . . . . . . . . . . . . . . . . 171
6.6. ShellSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
6.7. MergeSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
6.8. QuickSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
6.9. StoogeSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
6.10. RadixSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
6.11. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
6.12. Autoevaluación . . . . . . . . . . . . . . . . . . . . . . . . . . 194
6.12.1. Algoritmos de ordenamiento . . . . . . . . . . . . . . . 194
6.12.2. Lecturas Complementarias . . . . . . . . . . . . . . . 197
xii ÍNDICE GENERAL
Bibliografı́a 247
1
Conceptos básicos
1.1. Introducción
Un algoritmo puede ser caracterizado por una función lo cual asocia una
salida: s = f(E) a cada entrada E 1 .
1
MSc Vı́ctor Alfonso Valenzuela Ruz - Universidad de Chile - Facultad de Ingenierı́a
1.2. DEFINICIÓN DE ALGORITMO 3
Cuadro 1: Es primo 1
Cuadro 2: Es primo 2
¿Hace el algoritmo uso adecuado de los recursos? Si, pues para algunos
casos no itera toda la cantidad de veces permitida por el ciclo.
Descripción de la solución.
8 CAPÍTULO 1. CONCEPTOS BÁSICOS
1.7. Conclusiones
La construcción de algoritmos es una destreza que se debe adquirir,
y es de un gran significado para los profesionales inmersos en esta área.
Computadoras con mayor capacidad de procesamiento, no necesariamente
significa mayor velocidad de los algoritmos, en muchos proyectos de desarro-
llo de software se puede encontrar que el hardware no es el mayor problema
como si lo puede ser las aplicaciones inefectivas.
El desarrollo de una aplicación de software involucra múltiples fases que
van desde la especificación del problema, hasta la implementación de la solu-
ción, es en este punto en donde merece especial atención analizar el impacto
de la eficiencia de los algoritmos que implementamos, dado que lo que nece-
sitamos son tiempos de respuesta rápidos de acuerdo a los requerimientos
de los usuarios finales.
De acuerdo a lo anterior, gran parte del trabajo de los ingenieros es
saber qué problema se va a resolver, la otra parte corresponde a la forma
como se va solucionar, y es en este punto en donde el análisis de algoritmos
es fundamental para diseñar e implementar algoritmos correctos y eficientes.
La algoritmia es uno de los pilares de la programación y su relevancia
se muestra en el desarrollo de cualquier aplicación, más allá de la mera
construcción de programas.
10 CAPÍTULO 1. CONCEPTOS BÁSICOS
1.8. Autoevaluación
Algoritmos Voraces.
Algoritmos Paralelos.
Algoritmos Probabilistas.
Algoritmos Cuánticos.
imprimir("El numero armonico de " + numero +" " + "es: " + result);
}
imprimir("El numero armonico de " + numero +" " + "es: " + result);
}
1.8. AUTOEVALUACIÓN 13
if(estado == false)
{
JOptionPane.showMessageDialog(null," "+cad+" NO es palindroma");
}
else
{
JOptionPane.showMessageDialog(null," "+cad+" SI es Palindroma");
}
}
n Ln n Ln
0 2 5 11
1 1 6 18
2 3 7 29
3 4 8 47
4 7 9 76
x = 2;
y = 1;
i = 1;
while( i <= n )
{
z = x + y;
x = y;
y = z;
i++;
}
return x;
}
x = 2;
y = 1;
for ( i = 1 ; i <= n ; i++ )
{
z = x + y;
x = y;
y = z;
}
return x;
}
16 CAPÍTULO 1. CONCEPTOS BÁSICOS
i=1;
while(i<num1)
{
prox= penult+ult;
penult=ult;
ult=prox;
i++;
}
JOptionPane.showMessageDialog(null,"Resultado es: " + prox);
}
temp = num1;
}
18 CAPÍTULO 1. CONCEPTOS BÁSICOS
12. Dado el siguiente algoritmo, verifique que este suma cada una de las
cifras de un número entero positivo. Determine de acuerdo a los crite-
rios dados, si el algoritmo es bueno. Ver Cuadro 15
Cuadro 15: suma cifras
if(num==0)
{
JOptionPane.showMessageDialog ( null, "Resultado es : " + num);
}
for(i=10;num!=0;i+=10)
{
contador+= (num%i);
num/=i;
i-=10;
}
if(contador!=0)
{
JOptionPane.showMessageDialog ( null,"Resultado es : "+contador);
}
}
}
4. El artı́culo The Best of the 20th Century: Editors Name Top 10 Algo-
rithms. SIAM News, Volume 33, Number 4. Escrito por Barry Arthur
Cipra Ph.D. Mathematics. University of Maryland College Park. Cita
los algoritmos que bajo su perspectiva, son los más relevantes en la
historia de las ciencias de la computación. http : //www.siam.org
2.1. Introducción
while (valora!=valorb)
{
if(valora<valorb)
{
valorb = valorb - valora;
}
else
{
valora = valora - valorb;
}
}
imprimir("El MCD de: \n "+ a +" y "+ b +" es: "+valora);
}
acum i i >= 1
1 3 3 >= 1
5 2 2 >= 1
25 1 1 >= 1
125 0 0 >= 1
√
El ciclo for itera numero − 1 veces, una vez se cumple el condicional
if, se rompe el ciclo a través de la instrucción break y retorna falso.
resultado = 0;
for ( i = 2 ; i <= numero / 2 ; i++ )
{
if ( numero % i == 0 )
{
resultado = 1;
}
}
if ( resultado == 0 )
{
return true;
}
else
{
return false;
}
}
numero
El ciclo for itera 2 − 1 veces.
n Fn n Fn
0 0 6 8
1 1 7 13
2 1 8 21
3 2 9 34
4 3 10 55
5 5 11 89
i = 1;
while(i < n)
{
sig = penult + ult;
penult = ult;
ult = sig;
i++;
}
imprimir("el número "+n+ " en la serie es : "+ sig);
}
public ejemplo1()
{
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;
for (int j = 0 ; j < 6; j++)
{
solucion[j] = prueba[j] + 5;
}
}
}
}
2.3. TÉCNICAS DE OPTIMIZACIÓN 31
public 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;
}
}
}
public ejemplo2 ()
{
int prueba [] = new int [5000];
int solucion [] = new int [5000];
public reduccionEsfuerzo ()
{
int prueba [] = new int [5000];
int solucion [] = new int [5000];
public 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;
}
}
public tiposVariables()
{
int prueba1[]= new int [2500000];
int solucion1 [] = new int [2500000];
for (int i=0; i < prueba1.length; i++){
prueba1[i] = (i * 5) + 345;
}
for (int j = 0; j < solucion1.length; j++)
{
solucion1[j] = prueba1[j] * j;
}
}
34 CAPÍTULO 2. ANÁLISIS DE ALGORITMOS
La figura 2.1, muestra los rango de valores y la cantidad de bits que cada
una de estas variables necesita para su ejecución. Se observa que cuando se
utiliza una variable de tipo int son necesarios 32 bits, mientras que si se
utiliza una variable de tipo double, son necesarios 64 bits.
public ejemplo4 ()
{
int prueba [] = new int [2500000];
int prueba1 [] = new int [2500000];
int solucion [] = new int [2500000];
public fusionCiclos()
{
int prueba [] = new int [2500000];
int prueba1 [] = new int [2500000];
int solucion [] = new int [2500000];
2.4. Conclusiones
2.5. Autoevaluación
i = 0;
while (aux >= numero2)
{
i++;
aux = aux - numero2;
}
imprimir("La operación entre "+numero1+" y "+ numero2+ " es : "+i);
}
2.5. AUTOEVALUACIÓN 37
m = 1;
if (numero==2 || numero==1)
{
imprimir("El numero " + numero +" " + "es primo");
}
else if (numero%2 == 0)
{
imprimir("El numero " + numero +" " + "es primo");
}
else
{
for (int i=3; i<=Math.sqrt(numero); i+=2)
{
if (numero%i==0)
{
auxiliar = 1;
break;
}
}
}
if(auxiliar==1)
{
imprimir("El numero " + numero +" " + "no es primo");
}
else
{
imprimir("El numero " + numero +" " + "es primo");
}
}
if (a%b==0)
{
aux = a;
}
else
{
for (int i=a;num==2;i+=a)
{
for (int n=b; (n<=i && num==2);n+=b)
{
if(i==n)
{
num = 3;
aux = i;
}
}
}
}
JOptionPane.showMessageDialog(null,"El m.c.m es: " + aux);
}
1. Dado el siguiente programa, Ver Cuadro 38, aplique las técnicas nece-
sarias para optimizarlo. Adicionalmente, describa su funcionamiento e
indique que problema resuelve.
Cuadro 38: Manejo de ciclos 1
}
return matriz;
}
public ejercicio ()
{
int prueba [] = new int [2500];
int prueba1 [] = new int [2000];
int solucion [] = new int [2500];
for (int i = 1; i < prueba.length; i++){
prueba[i] = i * 5;
}
for (int j = 0; j < solucion.length; j++){
solucion[j] = j * 4;
}
for (int k = 10; k < prueba1.length - 1 ; k++){
prueba1[k] = prueba[k] - k;
}
}
3. Dado el siguiente código java, Ver Cuadro 40, aplique las técnicas
necesarias para optimizarlo.
Cuadro 40: Manejo de ciclos 1
public 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);
}
}
}
}
Tiempo de ejecución
3
3.1. Introducción
Para este libro, sólo tendremos en cuenta el análisis para el peor de los
casos.
Asignaciones a variables.
1
Vı́ctor Valenzuela Ruz, Universidad de Chile - Facultad de Ingenierı́a
46 CAPÍTULO 3. TIEMPO DE EJECUCIÓN
Ejemplo 1
Vamos a calcular el tiempo de ejecución del método que aparece en el
Cuadro 41:
Cuadro 41: Ejemplo 1
x += 2;
y = x + 3;
z = x + y + 2;
w = x + y + z;
}
Ejemplo 2
i = 0;
while ( i < n )
{
x = i + 3;
y = x + 2;
i = i + 1;
}
}
T (0) = 3
T (1) = 7
T (2) = 11
Ejemplo 3
w = 2;
for ( int i = 3 ; i <= n ; i++ )
{
x = i * j;
y = w * x;
}
}
Ejemplo 4
Ejemplo 5
Tint (n) = 3n + 2
En este ejemplo nos permite afirmar que cuando se tienen dos ciclos
anidados que inician en 0 e iteran hasta n, encontramos que la función T (n)
tiene un orden cuadrático. Para verificar el correcto cálculo del tiempo de
ejecución , se recomienda asignar algún valor entero a la variable n y realizar
un conteo de las instrucciones.
52 CAPÍTULO 3. TIEMPO DE EJECUCIÓN
Ejemplo 6
El método del Cuadro 46 tiene tres ciclos anidados, cada uno de los
cuales se analizará por separado para posteriormente estimar el tiempo de
ejecución total. Cada uno de los ciclos itera desde 0 hasta n, por lo que el
orden de la función de tiempo de ejecución debe ser cúbico.
Cuadro 46: Ejemplo 6
x = 0;
for ( i = 0 ; i < n ; i++ )
{
for ( j = 0 ; j < n; j++ )
{
for ( k = 0 ; k < n ; k++ )
{
x++;
}
}
}
}
Tint (n) = 1 + n + n + n + 1 = 3n + 2
A continuación, analizaremos el ciclo del medio, éste lo llamaremos
Tmedio (n).
Tmedio (n) = 1 + n + n + 1 = 2n + 2
Vamos a calcular el tiempo de ejecución de los dos ciclos más internos,
al cual llamaremos Tparcial (n).
Ejemplo 7
k = 0;
if else
if (a[i] < a[i + 1]) if (a[i] < a[i + 1])
temp = a[i] temp = a[i + 1]
a[i + 1] = temp x = a[i]
- k++
Ejemplo 8
i = 32;
while( i > 0 )
{
x++;
temp++;
y = y * 1;
i = i / 2;
}
}
Ejemplo 9
Ejemplo 10
T (n) = 4n + 1
Ejemplo 11
Ejemplo 12
Ejemplo 13
Ejemplo 14
T (n) = (n + 1) ∗ n + 2n + 2 = n2 + 3n + 2
Ejemplo 15
{
switch (0)
{
case 0 : metodo1();
case 1 : metodo2();
case 2 : metodo3();
}
if(m==x+2)
{
metodo4();
}
}
Ejemplo 16
{
switch (2)
{
case 0 : metodo1();
case 1 : metodo2();
case 2 : metodo3();
}
metodo4();
}
Ejemplo 17
{
switch (0)
{
case 0 : metodo1();
break;
case 1 : metodo2();
case 2 : metodo3();
case 3 : metodo4();
}
}
4.1. Introducción
Ejemplo 1
Ejemplo 2
Ejemplo 3
Ejemplo 4
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
función T (n), la cual determina la cantidad de operaciones que efectúa un
algoritmo. De acuerdo a la forma como se calcula el T (n) es importante
hacer dos presiciones:
1
René Mac Kinney- Romero, Ph.D. in Computer Science
4.3. COMPLEJIDAD COMPUTACIONAL 71
Notación Omega Ω
Dada una función 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 ningún 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)). (Ver Figura
4.6).
f (n)
lı́m ∈ IR+ ⇒ f (n) ∈ O(g(n)) y g(n) ∈ O(f (n))
n→∞ g(n)
f (n)
lı́m =0 ⇒ f (n) ∈ O(g(n)) y g(n) ∈
/ O(f (n))
n→∞ g(n)
f (n)
lı́m = +∞ ⇒ f (n) ∈
/ O(g(n)) y g(n) ∈ O(f (n))
n→∞ g(n)
Ejemplo 1
2n3 − 1
lı́m , por L’Hôpital
n→∞ n2 + 2n + 1
6n2
= lı́m , por L’Hôpital
n→∞ 2n + 2
12n
= lı́m
n→∞ 2
= +∞
Ejemplo 2
Solución
n3 − 1
lı́m , por L’Hôpital
n→∞ log2 (n) + 3n
3n2
= lı́m , simplificando
n→∞ 1
+3
n ln(2)
4.3. COMPLEJIDAD COMPUTACIONAL 75
3n2
= lı́m , simplificando
n→∞ 1 + 3n ln(2)
n ln(2)
3n3 ln(2)
= lı́m , por L’Hôpital
n→∞ 1 + 3n ln(2)
9n2 ln(2)
= lı́m , simplificando
n→∞ 3 ln(2)
= lı́m 3n2
n→∞
= +∞
Ejemplo 3
3n2
= lı́m , simplificando
n→∞ 3n
+ log2 (3n)
3n ln(2)
3n2
= lı́m , simplificando
n→∞ 1
+ log2 (3n)
ln(2)
3n2
= lı́m , simplificando
n→∞ 1 + ln(2) log2 (3n)
ln(2)
3 ln(2)n2
= lı́m , por L’Hôpital
n→∞ 1 + ln(2) log2 (3n)
76 CAPÍTULO 4. COMPLEJIDAD COMPUTACIONAL
6n ln(2)
= lı́m , simplificando
n→∞ 3
ln(2)
3n ln(2)
= lı́m 6 ln(2)n2
n→∞
= +∞
Ejemplo 4
La función n3 − 1 es O(2n−5 )
Solución
n3 − 1
lı́m , por L’Hôpital
n→∞ 2n−5
3n2
= lı́m , por L’Hôpital
n→∞ 2n−5 ln(2)
6n
= lı́m , por L’Hôpital
n→∞ 2n−5 ln(2)2
6
= lı́m
n→∞ 2n−5 ln(2)3
= 0
Ejemplo 5
Solución
n2 + 2n + 1
lı́m , por L’Hôpital
n→∞ log2 (n) + 3n
2n + 2
= lı́m , simplificando
n→∞ 1
+3
n ln(2)
4n ln(2) + 2 ln(2)
= lı́m , simplificando
n→∞ 3 ln(2)
4n + 2
= lı́m , simplificando
n→∞ 3
= +∞
Ejemplo
Dado el método del Cuadro 58, vamos a identificar las instrucciones que
tengan un orden de complejidad constante.
Cuadro 58: Constante
if ( a[n-1] == a[n-2] )
{
cantidad++;
}
else
{
cantidad--;
}
}
instruccion Orden
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)
10
2 + log(n)
Complejidad logarı́tmica O(log(n)) 500 + log(n)
2
10 + log(4n)
n + log(n)
Complejidad lineal O(n) 5n + log(n)
3n + 400
Se tiene un ciclo for, en este ciclo se inicializa con una variable i que
empieza en uno, se incrementa de uno en uno y el ciclo termina cuando
el valor se llega al tamaño del arreglo, por lo tanto se tiene un orden
de complejidad O(n).
n log(n) + 89n
log(n) + n log(n)
Complejidad O(n log(n))
2n + 3n log(n)
9n + n log(n) + 1
x = 0;
for ( i = 1 ; i < n ; i++ )
{
for ( j = 1 ; j < n ; j*=2 )
{
x = x+2;
}
}
}
sumatoria += prueba[i][j];
}
return sumatoria;
}
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;
}
}
}
}
m = 1;
x = 1;
return m;
}
Transitividad
Esta regla está basada en la transitividad de la relación menor que
“<”, ya que si A ≤ B y B ≤ C se puede concluir que A ≤ C.
Asociando ésto a la función 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)).
Términos de Orden Inferior Suponga que T (n) es de la forma
polinomial: ak nk + ak−1 nk−1 + ak−2 nk−2 + . . . + a1 n1 + a0 , entonces
es posible, eliminar todos los términos con exponente inferior k. Por
la regla anterior ak nk es O(nk )[14].
Regla de la Suma Suponga que un algoritmo está formado por dos
secciones, una de ellas con O(n2 ) y la otra con O(n3 ). Entonces es
posible sumar estos dos órdenes de complejidad para obtener la com-
plejidad total del algoritmo. La regla es la siguiente:
Suponga que para T 1(n) se sabe que es O(f 1(n)) y T 2(n) es O(f 2(n))
y suponga, además, que f 1 crece más rápido que f 2. Esto se traduce en
que f 2(n) es O(f 1(n)). En consecuencia se puede concluı́r que T 1(n)+
T 2(n) es O(f 1(n))[15].
4.4. ORDENES DE COMPLEJIDAD 87
if ( expresionlógica )
{
// se ejecuta si la expresionalógica es verdadera con T(n) = f(n)
}
else
{
// se ejecuta si la expresionalógica es falsa con T(n) = g(n)
}
Son cuatro las situaciones que deben ser analizadas cuando se estudia
la complejidad para los condicionales.
Ejemplo
Se observa que no existe un cuerpo para una sentencia else, por lo tanto
g(n) = 0. A continuación se explica el orden de complejidad del condicional:
Ejemplo
En los Cuadros 68 y 69 se muestra un primer ejemplo, en el cual se hace
un llamado a métodos. Todo este ejemplo se encuentra implementando una
clase en java.
Cuadro 68: Sumatoria 1
Ejemplo
class pruebas
{
int matriz [ ][ ];
int suma = 0;
crearMatriz ( entero );
mostrarMatriz ( entero );
}
return value;
}
iteran desde cero hasta el mismo valor. De acuerdo a ello, podemos afirmar
que el orden de complejidad para los ambos métodos es O(n2 ).
El método mostrarMatriz() tiene dos ciclos for anidados los cuales it-
eran hasta el mismo valor, de acuerdo a lo anterior, podemos afirmar que el
orden de complejidad para el método ingresar es O(n2 ). Las otras lı́neas de
implementación tienen un orden de complejidad O(1), los cuales se descar-
tan, esto por la regla de la suma. Por la regla de la suma, el programa
anterior tiene un orden de complejidad O(n2 ).
92 CAPÍTULO 4. COMPLEJIDAD COMPUTACIONAL
4.5. Conclusiones
Orden N ombre
O(1) Constante
O(log(n)) Logaritmica
O(n) Lineal
O(n log(n)) n log(n)
O(n2 ) Cuadratica
O(n3 ) Cúbica
O(nn ) Exponencial
4.6. Autoevaluación
a) 3n3
b) 10n + 2
c) n2 + 50n + 2
a) n2 + 2n− + 10
b) n3 + 100
c) 2n4 + 5n + 10
d ) 5n5 + +1500
4.6.2. Complejidad
i = 128;
while( i > 0 )
{
x++;
temp++;
y = y * 1;
z = x + temp;
i = i / 2;
}
}
y = 0;
for ( i = 0 ; i < n ; i++ )
{
j = 1;
while (j < n )
{
x = x+2;
j *=2
}
}
}
public void ejercicio ( int binar[], int tam, int n, int a[] )
{
int i, k;
i = 64;
while( i > 1 )
{
x++;
y = y * 1;
i = i / 2;
}
}
switch (2)
{
case 0 : metodo1();
break;
case 1 : metodo2();
case 2 : metodo3();
}
for (int i=2; i<n+2 ; i+=2)
{
Metodo1();
Metodo2();
}
}
}
5.1. Introducción
Se crea una pila para cada variable local, una pila para cada argumento
y una pila para la dirección de retorno.
En cada llamada recursiva del procedimiento:
• Insertar en cada pila de variable local el valor actual
• Insertar en cada pila de argumento el valor actual.
• Insertar en la pila de direcciones, la siguiente sentencia a la lla-
mada.
Al acabar cada ejecucion recursiva del procedimiento:
• Si la pila de direcciones esta vacia, devolver el control al programa
de llamada.
• Sacar de las pilas de variables locales y parámetros los valores de
las cimas.
• Devolver la ejecución a la sentencia extraı́da de la pila de dire-
cciones.
sumatoria(5) = 5 + sumatoria(4)
= 5 + (4 + (sumatoria(3)))
= 5 + (4 + (3 + sumatoria(2)))
= 5 + (4 + (3 + (2 + sumatoria(1))))
= 5 + (4 + (3 + (2 + 1)))
= 5 + (4 + (3 + 3))
= 5 + (4 + 6)
= 5 + 10
= 15
5.3. ALGORITMOS RECURSIVOS 107
Obtener 4 * 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
108 CAPÍTULO 5. RECURSIVIDAD
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 Figura 5.2 se muestra una de las formas como se puede representar
este algoritmo recursivo.
5.3. ALGORITMOS RECURSIVOS 109
potencia(3, 4) = 3 ∗ potencia(3, 3)
= 3 ∗ (3 ∗ (potencia(3, 2)))
= 3 ∗ (3 ∗ (3 ∗ potencia(3, 1))
= 3 ∗ (3 ∗ (3 ∗ 3))
= 3 ∗ (3 ∗ 9)
= 3 ∗ 27
= 81
112 CAPÍTULO 5. RECURSIVIDAD
H1 = 1
1
H2 = 1 +
2
1 1
H3 = 1+ +
2 3
1 1 1
Y, en general, Hn = 1 + 2 + 3 + ... + n para cualquier n → Z+ .
H1 = 1, n=1
µ ¶
1
Hn+1 = Hn + , n≥1
n+1
116 CAPÍTULO 5. RECURSIVIDAD
if(inicio == fin)
{
return vector[inicio];
}
else
{
int menor = numeroMenor(vector,inicio+1,fin);
if(menor<vector[inicio])
{
return menor;
}
else
{
return vector[inicio];
}
}
}
Inducción
Repeticiones sucesivas
0! = 1
1! = 1
2! = 2 ∗ 1 = 2 ∗ 1!
3! = 3 ∗ 2 ∗ 1 = 3 ∗ 2!
4! = 4 ∗ 3 ∗ 2 ∗ 1 = 4 ∗ 3!
···
n! = n ∗ (n − 1)!
f actorial(3) = 3 ∗ (f actorial(2))
= 3 ∗ (2 ∗ f actorial(1))
= 3 ∗ (2 ∗ 1)
= 3∗2
= 6
if ((n == 1) || (n == 0))
{
return 1;
}
126 CAPÍTULO 5. RECURSIVIDAD
if ((n == 1) || n == 0))
{
return 1;
}
else
{
return n * factorial(n - 1);
}
T (2) = b + T (1) = b + a
T (3) = b + T (2) = b + (b + a) = 2b + a
T (4) = b + T (3) = b + (2b + a) = 3b + a
T (5) = b + T (4) = b + (3b + a) = 4b + a
T (6) = b + T (5) = b + (4b + a) = 5b + a
···
T (n) = b + T (n − 1) = (n − 1)b + a
5.4. ANÁLISIS DE ALGORITMOS RECURSIVOS 127
Si n = 2, T (2) = b + T (1) = 1b + a
Si n = 4, T (4) = b + T (2) = b + b + a = 2b + a
Si n = 8, T (8) = b + T (4) = b + 2b + a = 3b + a
Si n = 16, T (16) = b + T (8) = b + 3b + a = 4b + a
T (n) = b log(n) + a.
f ib(n) = 0 , n = 0
f ib(n) = 1 , n = 1
f ib(n) = f ib(n − 1) + f ib(n − 2) , n ≥ 2
n Fn n Fn
0 0 6 8
1 1 7 13
2 1 8 21
3 2 9 34
4 3 10 55
5 5 11 89
= 1 + (1 + (f ibo(0) + f ibo(1))
= 1 + (1 + (0 + f ibo(1)))
= 1 + (1 + (0 + 1))
= 1 + (1 + 1)
= 1+2
= 3
Análisis del algoritmo recursivo
Base: T (1) = a
Inducción: T (n) = b + T (n − 2) + T (n − 1)
T (n) = b + 2T (n − 1)
Se asignan valores a n:
T (1) = a
T (2) = b + 2T (1) = b + 2a
T (3) = b + 2T (2) = b + 2(b + 2a) = (22 − 1)b + 22 a
T (4) = b + 2T (3) = b + 2(3b + 4a) = (23 − 1)b + 23 a
T (5) = b + 2T (4) = b + 2(7b + 8a) = (24 − 1)b + 24 a
T (6) = b + 2T (5) = b + 2(15b + 16a) = (25 − 1)b + 25 a
Se concluye entonces que:
T (n) = (2n−1 − 1)b + 2n−1 a, como a y b son de orden O(1) esta ecuación
se puede volver de la forma: T (n) = 2 ∗ 2n−1 − 1 = 2n − 1, en consecuencia
es de orden O(2n ).
5.5.1. Ejemplo 1
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:
5.5.2. Ejemplo 2
En el análisis del caso base se tiene que para el peor de los casos el
tiempo de ejecución 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).
T (1) = 8
T (n) = T (n/2) + 8
5.5.3. Ejemplo 3
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 comparación se puede
garantizar una cota superior para este problema.
T (1) = a
T (n) = 2T (n − 1)
5.5.4. Ejemplo 4
if (n <= 1)
{
return 1;
}
else
{
return n*(recursivo4(n-1) + recursivo4(n-1) + recursivo4(n-1));
}
}
Generalizando, tenemos:
n
X
T (n) = 3T (n − 1) + b ⇒ T (n) = 3n+1 a + (3n−i b)
i=2
5.5.5. Ejemplo 5
T (1) = O(1)
T (n) = 3T (n − 1) + O(1)
136 CAPÍTULO 5. RECURSIVIDAD
T (1) = a
T (n) = 3T (n − 1) + b
Generalizando, tenemos:
3n−1 − 1
T (n) = 3T (n − 1) + b ⇒ T (n) = 3n−1 a + b
2
5.5.6. Ejemplo 6
T (1) = O(1)
T (n) = 3T (n − 1) + O(n)
T (1) = a
T (n) = 3T (n − 1) + bn
138 CAPÍTULO 5. RECURSIVIDAD
Generalizando, tenemos:
T (n) = 3T (n − 1) + bn ⇒
n−4
X ¡ i ¢
T (n) = 3n−1 a + 3n−1 b + 3 (n − i)b , ∀n ≥ 4
i=0
5.5.7. Ejemplo 7
T (1) = O(1)
T (n) = T (n − 1) + T (n − 1) + O(n)
T (1) = a
T (n) = 2T (n − 1) + bn
T (1) = a
T (2) = 2T (1) + 2b ⇒ T (2) = 2a + 2b
T (3) = 2T (2) + 4b ⇒ T (3) = 2(2a + 2b) + 3b = 4a + 7b
T (4) = 2T (3) + 4b ⇒ T (4) = 2(4a + 7b) + 4b = 8a + 14b + 4b = 8a + 18b
···
Generalizando, tenemos:
n−1
X
n−1
¡ n−1−i ¢
T (n) = 2T (n − 1) + bn ⇒ T (n) = 2 a+ 2 (i + 1) b + 2n−1 b
i=2
5.5.8. Ejemplo 8
T (1) = O(1)
T (n) = 4T (n/2) + 2n2
T (1) = O(1) = a
T (n) = 4T (n/2) + bn2
Generalizando, tenemos:
T (n) = c , si n ≥ 1
T (n − 1) = c1 , 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
142 CAPÍTULO 5. RECURSIVIDAD
= (T (n − 5) + c1 ) + 4c1 = T (n − 5) + 5c1
= (T (n − 6) + c1 ) + 5c1 = T (n − 6) + 6c1
···
= (T (n − q) + nc1
T (n) = 1 , si n ≤ 1
2T (n − 1) + 1 , si n > 1
T (n) = 2 ∗ T (n − 1) + 1
= 2 ∗ (2 ∗ T (n − 2) + 1) + 1 = 22 ∗ T (n − 2) + (22 − 1)
···
= 2k ∗ T (n − k) + (2k + 1)
T (n) = 1 , si n ≤ 1
T (n/2) + 1 , si n > 1
T (n) = T (n/2) + 1
= T (n/22 ) + 1
= T (n/23 ) + 1
= T (n/24 ) + 1
= T (n/2k ) + n
n/2k = 1, se resuelve para k = log2 (n), tenemos por lo tanto que el tiempo
de ejecución es T (n) = T (1) + log2 (n − 1) y por lo tanto T (n) es O(log2 (n)).
T (n) = 1, si n ≤ 1
T (n − 1) + n, si n > 1
144 CAPÍTULO 5. RECURSIVIDAD
return recursivo2 ( n - 1, b );
}
}
T (n) = T (n − 1) + n
= (T (n − 2) + (n − 1)) + n
= ((T (n − 3) + (n − 2)) + (n − 1) + n
···
k−1
X
= T (n − k) + (n − i)
i=0
Si k = n − 1,
n−2
X n−2
X n−2
X
T (n) = T (1)+ (n−i) = 1+( (n)+ (i)) = 1+n(n−1)−(n−2)(n−1)/2.
i=0 i=0 i=0
El Cuadro 5.2, generaliza las soluciones más comunes que aplican a las
funciones recursivas. Se asume para todos los casos que la ecuación base es
T (1) = a y que k ≥ 0. Para todos los casos bnk es reemplazado por cualquier
polinomio de grado k [17].
5.8. Conclusiones
5.9. Autoevaluación
return razon ( b, a - 1 );
}
154 CAPÍTULO 5. RECURSIVIDAD
if (n==0 || n==1)
{
arreglo[n] = 1;
return arreglo[n];
}
else
{
arreglo[n] = misterio(n-1,arreglo) * n;
return arreglo[n];
}
}
C(5,3)= C(4,2)+C(4,3) =
C(3,1)+C(3,2)+C(3,2)+C(3,3)=
C(2,0)+C(2,1)+C(2,1)+C(2,2)+C(2,1)+C(2,2)+C(2,2)+C(2,3)=
1+3C(2,1)+3C(2,2)+C(1,2)+C(1,3)=
1+3(C(1,0)+C(1,1))+3(C(1,1)+C(1,2))+C(1,2)+C(0,2)+C(0,3)=
1+3C(1,0)+3C(1,1)+3C(1,1)+3C(1,2)+C(1,2)+C(0,2)+C(0,3)=
1+3+6C(1,1)+4C(1,2)+C(0,2)+C(0,3)=
1+3+6(C(0,0)+C(0,1))+4(C(0,1)+C(0,2))+C(0,2)+C(0,3)=
1+3+6+10C(0,1)+5C(0,2)+C(0,3)=
10+10C(-1,0)+10C(-1,1)+5C(-1,1)+5C(-1,2)+C(-1,2)+C(-1,3)=10
158 CAPÍTULO 5. RECURSIVIDAD
i = inicio;
while(i <= fin)
{
if ( arreglo [i - 1] > arreglo[ i ] )
{
temp = arreglo[i - 1];
arreglo[i - 1] = arreglo[ i ];
arreglo[ i ] = temp;
estado = i;
}
i++;
}
}
}
Este método de selección tiene este nombre puesto que la manera de rea-
lizar el ordenamiento es seleccionando en cada iteración el menor elemento
del arreglo e intercambiarlo en la posición correspondiente. Inicialemte se en-
cuentra el menor elemento dentro del arreglo y se intercambia con el primer
168 CAPÍTULO 6. ORDENAMIENTO
elemento del arreglo. Luego, desde la segunda posición del arreglo se busca
el menor elemento y se intercambia con el segundo elemento del arreglo.
Ası́ sucesivamente se busca cada elemento de acuerdo a su ı́ndice i y se in-
tercambia con el elemento que tiene el ı́ndice k. Se ordenarán los elementos
del arreglo de forma asecendente.
i = 0;
while( i < arreglo.length - 1)
{
menor = arreglo [i];
k = i;
arreglo[i + 1] = llave;
}
}
6.6. ShellSort
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;
}
}
}
}
}
1
PhD (Computer Science) from Carleton University (http://www.cccg.ca/ morin/)
176 CAPÍTULO 6. ORDENAMIENTO
Estas posiciones estan dadas por las variables j y j + salto (Ver Figura
6.35).
El arreglo una vez terminada esta primera iteración completa queda con
la siguiente configuración. (Ver Figura 6.36)
6.7. MergeSort
2
PhD Computer Science - Carleton University). (http://www.cccg.ca/ morin/
6.7. MERGESORT 181
m1 = 0;
m2 = pivote - bajo + 1;
Las asignaciones del Cuadro 140 son asignaciones cuyo orden de com-
plejidad es O(1). Estas asignaciones representan el tamaño del arreglo
y un elemento pivote el cual almacena la mitad del tamaño del arreglo.
T (1) = a
T (n) = 2T (n/2) + O(n).
T (1) = a
T (n) = 2T (n/2) + bn.
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 ejecución está dado por: T (n) = an + n log2 (n)b
El término an es de orden O(n) y el término n log2 (n)b es de orden
O(n log2 (n)). Se observa que la función orden n log(n) crece más rápida-
mente que la función n, por lo tanto es correcto decir que la función n es de
orden O(n log2 (n)).
6.8. QUICKSORT 183
6.8. QuickSort
que el elemento dado, se ponen en una segunda lista. Por lo tanto el método
elige un elemento denominado pivote y pone en una lista o arreglo, todos los
elementos mayores que el pivote y los elementos menores en otra lista. Esta
elección también puede realizarse al inicio de la lista o arreglo, o al final del
arreglo. Este algoritmo inicialmente compara todos los elementos y después
de ello empieza a dividir recursivamente la lista o el arreglo. (Cuadro 142).
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);
6.9. StoogeSort
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 );
}
3
Sanchit Karve
6.10. RADIXSORT 187
6.10. RadixSort
El método de radix sort es un método eficiente de ordenamiento, que en
su esencia no lleva a cabo comparaciones de posiciones adyacentes dentro
de una arreglo. Esta solución propuesta se compone de tres clases de imple-
mentación. La clase radixSort que contiene la logica del problema, la clase
cola enlazada y la clase nodo. Cada una de ellas se describira más a fondo.
A continuación se muestra un ejemplo de cómo se realiza el ordenamien-
to por medio del radix sort, el arreglo que se desea ordenar se puede ver en
la Figura 6.44.
ColaEnlazada[] Q =
{
new ColaEnlazada(), // Radical 0
new ColaEnlazada(), // Radical 1
new ColaEnlazada(), // Radical 2
new ColaEnlazada(), // Radical 3
new ColaEnlazada(), // Radical 4
new ColaEnlazada(), // Radical 5
new ColaEnlazada(), // Radical 6
new ColaEnlazada(), // Radical 7
new ColaEnlazada(), // Radical 8
new ColaEnlazada() // Radical 9
};
El método sort(int a[] ) recibe el arreglo que contiene todos los elementos
que se desean ordenar. Este método tiene como responsabilidad, encontrar
el máximo elemento dentro del arreglo, y posteriormente determinar la can-
tidad de digitos que tiene este elemento mayor. (Ver Cuadro 145). El ciclo
f or de este método permite identificar el elemento mayor del arreglo.
El ciclo while para el peor caso, se asume que se construye una lista de
n elementos para cada una de las colas. Por lo tanto, por la regla de la suma,
se tiene que el orden de complejidad para este ciclo es O(n) + O(1). Para el
análisis del ciclo for mas externo, asumiendo que el tamaño del número mas
grande del arreglo es n, el orden de complejidad del método es n, supuesto
que no es práctico en la vida real.
ColaEnlazada()
{
inicio = null;
fin = null;
tamano = 0;
}
tamano++;
nodoEntero temp = new nodoEntero(num);
if (inicio == null)
{
inicio = temp;
fin = inicio;
}
else
{
fin.siguiente = temp;
fin = temp;
}
temp = null;
}
int temp;
tamano--;
temp = inicio.valor;
nodoEntero nodoTemp;
nodoTemp = inicio;
inicio = inicio.siguiente;
nodoTemp = null;
return temp;
}
6.10. RADIXSORT 193
nodoEntero(int a)
{
valor = a;
siguiente = null;
anterior = null;
}
nodoEntero()
{
siguiente = null;
anterior = null;
valor = 0;
}
}
194 CAPÍTULO 6. ORDENAMIENTO
6.11. Conclusiones
M etodo Orden
Burbuja O(n2 )
Seleccion O(n2 )
Inserción O(n2 )
ShellSort O(n2 )
Sharker O(n2 )
MergeSort n log(n)
QuickSort O(n log(n))
RadixSort O(n)
StoogrSort O(n2,7 )
6.12. Autoevaluación
a) InsertionSort
b) ShellSort
c) ShakerSort
d ) BubbleSort
e) StoogeSort
f ) MergeSort
g) QuickSort
4
Magister En Ciencias Computacionales
6.12. AUTOEVALUACIÓN 197
La búsqueda del número 21, empieza por el primer elemento del arreglo,
al momento de comparar sus valores, se encuentra que no son iguales. (Ver
Figura 7.9)
Se tiene un ciclo for que recorre para el peor caso el arreglo desde la
posición 0, hasta la posición n − 1 del arreglo, por lo tanto se tiene
O(n).
Este método por lo tanto se puede considerar más eficiente que el ante-
rior, dado que en caso de encontrarse el elemento no se realizan más itera-
ciones, retorna verdadero indicando que si esta el dato.
7.3. BÚSQUEDA LINEAL ITERATIVA CON EXTREMOS 205
Parea el análisis de este algoritmo se tiene: el caso base tiene que ambos
condicionales solo tienen instrucciones constantes, por lo tanto el orden es
O(1). Cuando no se cumple las condiciones del caso base, se puede deducir
para el caso inductivo: T (n) = O(1) + T (n − 1),para n > 1. Lo anterior
debido a que la variable pos se decrementa de uno en uno. Se tiene por lo
tanto:
T (2) = b + T (1) = b + a
T (3) = b + T (2) = b + (b + a) = 2b + a
T (4) = b + T (3) = b + (2b + a) = 3b + a
T (5) = b + T (4) = b + (3b + a) = 4b + a
···
T (n) = b + T (n − 1) = (n − 1)b + a
Cada vez que se realiza una comparación dentro del ciclo y mientras en
lı́mite inferior no sea mayor que el lı́mite superior, el reduce el conjunto de
entrada a la mitad, es decir, el arreglo descarta la mitad de los elementos
que estan por fuera del rango. El ciclo se deja de ejecutar cuando se retorna
un valor ya sea falso o verdadero.
while ( true )
{
centro = (limSup + limInf) / 2;
boolean binRecursiva (int arreglo[], int dato, int limInf, int limSup)
{
int centro = (int)( (limSup + limInf) / 2);
7.8. Conclusiones
7.9. Autoevaluación
2. Suponga que se tiene una lista de enteros (3, 5, 13, 21, 73, 81, 93).
Dibuje paso a paso la búsqueda en esta lista de los números 81 y 75
usando:
Listas
Pilas
Colas
8.1. Listas
Las listas son las estructuras de datos lineales más comunes. Permiten
generalmente el acceso para consulta o modificación en cualquiera de los
extremos de la estructura, e incluso en un punto medio, es frecuente recorrer
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 los cuales presentan
problemas en el diseño de algoritmos, y estos ocasionan con frecuencia erro-
res en el código. Por lo tanto se debe procurar escribir código 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 ningún dato, pero que sirve para satisfacer el requerimiento de que
cada nodo que contenga un elemento tenga un nodo anterior.
Para la implementación de cada tipo de lista, se considerará el nodo
cabecera.
listaSencilla()
{
cabecera = new Nodo(null);
ultimo = cabecera;
tama~
no = 0;
}
// Metodos de la clase
if(estaVacia())
{
JOptionPane.showMessageDialog(null,"Lista Vacı́a");
}
else
{
if(temp==ultimo)
{
cabecera.siguiente=null;
tama~
no--;
}
else
{
cabecera.siguiente=temp.siguiente;
tama~
no--;
}
}
}
Object info;
String cadena = "";
for(int i=0;i<tama~
no();i++ )
{
if(movil.info!=null)
{
info = movil.info;
cadena += info +" ";
movil = movil.siguiente;
}
}
return cadena;
}
listaCircular()
{
cabecera = new Nodo(null);
ultimo = cabecera;
tama~
no=0;
}
//Implementación de métodos
if( estaVacia())
{
cabecera.siguiente = nuevo;
nuevo.siguiente=cabecera;
ultimo=nuevo;
tama~
no++;
}
else
{
nuevo.siguiente=cabecera.siguiente;
cabecera.siguiente=nuevo;
tama~
no++;
}
}
222 CAPÍTULO 8. ANÁLISIS DE ESTRUCTURA DE DATOS
if(estaVacia())
{
JOptionPane.showMessageDialog(null,"Lista Vacia");
}
else
{
if(temp==ultimo)
{
cabecera.siguiente=null;
tama~
no--;
}
else
{
cabecera.siguiente=temp.siguiente;
tama~
no--;
}
}
}
Para este tipo de lista es necesario definir la clase nodo. Se definen dos
variables de tipo Nodo que sirven como refencias para cada nodo. Estas
referencias son las que permiten que la lista se pueda recorrer de izquierda
a derecha, o que se pueda insertar un nodo tambien a la derecha o a la
224 CAPÍTULO 8. ANÁLISIS DE ESTRUCTURA DE DATOS
public Nodo(Object o)
{
izquierda = null;
derecha = null;
dato = o;
}
}
listaDoble()
{
cabecera = new Nodo(null);
ultimo = cabecera;
actual = cabecera;
tama~
no = 0;
}
// Implementación de métodos
if(estaVacia())
{
actual.derecha=nuevo;
nuevo.izquierda=actual;
ultimo= nuevo;
actual = nuevo;
tama~no++;
}
else
{
if(actual == ultimo)
{
actual.derecha=nuevo;
nuevo.izquierda=actual;
actual = nuevo;
ultimo = nuevo;
tama~
no++;
}
else
{
nuevo.derecha=actual.derecha;
nuevo.izquierda=actual;
temporal.izquierda=nuevo;
actual.derecha=nuevo;
actual = nuevo;
tama~
no++;
}
}
}
if(estaVacia())
{
actual.derecha=nuevo;
nuevo.izquierda=actual;
ultimo= nuevo;
actual = nuevo;
tama~no++;
}
else
{
if(actual == ultimo)
{
nuevo.derecha=actual;
nuevo.izquierda=temporal;
temporal.derecha = nuevo;
actual.izquierda = nuevo;
actual = nuevo;
tama~
no++;
}
else
{
nuevo.derecha=actual;
nuevo.izquierda=temporal;
temporal.derecha=nuevo;
actual.izquierda=nuevo;
actual = nuevo;
tama~
no++;
}
}
}
if (estaVacia())
{
cabecera.derecha=nuevo;
nuevo.izquierda=cabecera;
ultimo = nuevo;
actual = nuevo;
tama~
no++;
}
else
{
nuevo.derecha=cabecera.derecha;
nuevo.izquierda=cabecera;
temporal.izquierda=nuevo;
cabecera.derecha=nuevo;
actual = nuevo;
tama~
no++;
}
}
El método del Cuadro 178, muestra el método insertarU ltimo, dado que
se tiene una referencia ultimo, esta permitirá insertar un elemento al final
de la lista con un orden de complejidad O(1). En ningún caso es necesario
recorrer toda la lista para realizar esta inserción.
if(estaVacia())
{
ultimo.derecha=nuevo;
nuevo.izquierda=ultimo;
ultimo= nuevo;
actual = nuevo;
tama~
no++;
}
else
{
ultimo.derecha=nuevo;
nuevo.izquierda=ultimo;
ultimo = nuevo;
actual = nuevo;
tama~
no++;
}
}
if(estaVacia())
{
JOptionPane.showMessageDialog(null,"Lista Vacia");
}
else
{
if(borrar==ultimo)
{
cabecera.derecha=null;
borrar.izquierda=null;
tama~
no--;
actual=cabecera;
ultimo=actual;
}
else
{
Nodo temporal=borrar.derecha;
cabecera.derecha=temporal;
temporal.izquierda=cabecera;
tama~
no--;
actual=cabecera.derecha;
}
}
}
8.2. Pila
public Stack ()
{
this (TAMA~
NO);
}
public Stack (int cap)
{
capacidad = cap;
Arreglo = new Object [capacidad] ;
}
1
Estructuras de datos y algoritmos en JAVA, segunda edición
232 CAPÍTULO 8. ANÁLISIS DE ESTRUCTURA DE DATOS
if (isEmpty ())
{
JOptionPane.showMessageDialog(null,"Pila Vacı́a ");
}
elemento = Arreglo [tope];
Arreglo[tope] = null;
tope --;
cantidad--;
return elemento;
}
pila()
{
tope = new Nodo(null);
}
// implementación de métodos
if (tope.siguiente != null)
{
n.siguiente = tope.siguiente;
}
else
{
tope.siguiente = n;
}
}
236 CAPÍTULO 8. ANÁLISIS DE ESTRUCTURA DE DATOS
if (tope.siguiente == null)
{
JOptionPane.showMessageDialog(null,"Pila Vacı́a ");
}
else{
dato = tope.siguiente.info;
}
return dato;
}
if (tope.siguiente == null)
{
JOptionPane.showMessageDialog(null,"Pila Vacı́a ");
}
else
{
dato = tope.siguiente.info;
tope.siguiente = tope.siguiente.siguiente;
}
return dato;
}
8.3. COLA 237
8.3. Cola
Es necesario para trabajar con colas definir cual será el frente y cual
será el final de la cola, lo anterior es fundamental para establecer por donde
insertarán elementos a la cola y por donde se eliminarán los elementos de la
cola.
class cola
{
static final int CAPACIDAD =10;
Object arreglo[];
int n, f =0, r =0;
public cola ()
{
this (CAPACIDAD);
}
//implementación de métodos
2
Estructuras de datos y algoritmos en JAVA, segunda edición
8.3. COLA 239
if(f==r)
{
lleno= true;
return lleno;
}
return lleno;
}
if (isEmpty ())
{
temp = null;
JOptionPane.showMessageDialog(null,"Cola Vacı́a ");
}
else
{
temp = arreglo[f];
arreglo[f] = null;
f = (f+1) % n ;
}
return temp;
}
8.4. CONCLUSIONES 241
8.4. Conclusiones
Los siguientes son los ordenes de complejidad de cada uno de los métodos
que se implementaron. Para la clase Lista sencillamente enlazada, se tienen
los siguientes: Ver Cuadro 8.1.
M etodo Orden
estaVacia() O(1)
insertar() O(1)
eliminar() O(1)
imprimir() O(n)
Los siguientes son los ordenes de complejidad de cada uno de los métodos
que se implementaron. Para la clase Lista Sencilla Circular, se tienen los
siguientes: Ver Cuadro 8.2.
M etodo Orden
insertarPrimero() O(1)
eliminar() O(1)
insertarUltimo() O(1)
imprimir() O(n)
Los siguientes son los ordenes de complejidad de cada uno de los métodos
que se implementaron. Para la clase Lista Doblemente Enlazada, se tienen
los siguientes: Ver Cuadro 8.3.
242 CAPÍTULO 8. ANÁLISIS DE ESTRUCTURA DE DATOS
M etodo Orden
insertarDerecha() O(1)
insertarIzquierda() O(1)
insertarPrimero() O(1)
insertarUltimo() O(1)
eliminarInicio() O(1)
Los siguientes son los ordenes de complejidad de cada uno de los métodos
que se implementaron. Para la clase Pila (con arreglos y listas), se tienen
los siguientes: Ver Cuadro 8.4.
M etodo Orden
isEmpty () O(1)
push () O(1)
top () O(1)
size () O(1)
pop () O(1)
Los siguientes son los ordenes de complejidad de cada uno de los méto-
dos que se implementaron. Para la clase Cola, se tienen los siguientes: Ver
Cuadro 8.5.
M etodo Orden
enqueue() O(1)
dequeue() O(1)
front() O(1)
isEmpty() O(1)
8.5. Autoevaluación
public Nodo ()
{
}
public Nodo (Object info)
{
this.info = info;
siguiente = null;
}
}
244 CAPÍTULO 8. ANÁLISIS DE ESTRUCTURA DE DATOS
Cola()
{
inicio = new Nodo();
fin = new Nodo();
nodos = 0;
}
if (inicio.siguiente == null)
{
inicio.siguiente = n;
fin.siguiente = n;
}
else
{
fin.siguiente.siguiente = n;
fin.siguiente = n;
}
nodos++;
}
Nodo top()
{
return inicio.siguiente;
}
int cantidad()
{
return nodos;
}
247
248 BIBLIOGRAFÍA
249
250 ÍNDICE ALFABÉTICO
lineal, 82 n log(n), 66
llamada a métodos, 88 n2 , 66
logarı́tmica, 80 n3 , 66
ordenamiento
eficiencia burbuja, 160
espacio, 70 burbuja bidireccional, 162
Estructuras gnomeSort, 195
cola, 237 inserción, 171
pila, 229 mergeSort, 180
quickSort, 183
instrucción radixSort, 187
break, 56 selección, 167
Math. pow, 37 shellSort, 174
stoogeSort, 186
Lista
circular, 220 Pilas
doble, 223 arreglos, 230
sencilla, 216 pilas con listas, 234
Listas potencia número, 25
sencillamente enlazada, 216 propiedades
logaritmo, 55, 81 orden inferior, 86
regla de la suma, 86
mcd, 22
transitividad, 86
número recursividad
primos, 3 caso base, 103
números directa, 104
armónicos, 11, 115 indirecta, 104
notación recursión, 103
asintótica, 71 regla del lı́mite, 73
Big Oh, 71
Omega, 73 Serie Fibonacci, 14