Algoritmia II
Algoritmia II
Algoritmia II
ANÁLISIS DE ALGORITMOS
Cuando se dice que un problema tiene solución, significa que existe un algoritmo
susceptible de implantarse en una computadora, capaz de producir la respuesta correcta
para cualquier instancia del problema en cuestión. Para ciertos problemas es posible
encontrar más de un algoritmo para resolverlos, lo que conlleva a tener que seleccionar
el mejor de ellos, de acuerdo a ciertos criterios.
Entre los principales criterios que se deben de tener en cuenta cuando se evalúa una
solución algorítmica, se encuentran los siguientes:
1. Que sea fácil de entender, implementar, depurar y mantener
2. Que se encuentre bien documentado
3. Que pueda ser usado por otros algoritmos
4. Que sea eficiente con respecto a los recursos que utiliza. La eficiencia
computacional de un algoritmo está relacionada principalmente con el tiempo
que demora su ejecución y el espacio que gasta en memoria, siendo el primero,
por lo general, el más importante.
Cuando se diseña una solución algorítmica para un programa que se va a usar con poca
frecuencia, no se justifica escoger entre varias soluciones, la más eficiente con respecto
a los recursos, sólo se tendrían en cuenta los primeros tres criterios. En caso contrario,
si el programa se va a utilizar con periodicidad, se justifica escoger entre varias
soluciones encontradas la que sea más eficiente con respecto al uso de los recursos. De
todos modos, ninguno de los criterios de evaluación debe eliminarse por completo. Por
ejemplo, si se selecciona una solución muy eficiente pero muy difícil de entender, puede
suceder que después no sea posible darle el mantenimiento adecuado.
Se han definido tres enfoques para seleccionar la mejor solución algorítmica para un
mismo problema:
- El enfoque empírico (a posteriori): consiste en implementar cada una de las
soluciones algorítmicas y medir los tiempos reales obtenidos de cada una de
ellas.
- El enfoque teórico (a priori): consiste en encontrar en forma matemática la
cantidad de recursos necesarios para cada una de las soluciones algorítmicas
como una función del tamaño de los casos considerados.
- El enfoque híbrido: consiste en determinar en forma teórica las funciones que
describen la eficiencia de las diferentes soluciones algorítmicas para luego
encontrar en forma empírica aquellos parámetros numéricos que sean
específicos a cada una soluciones.
O(1) > O(logbn) > O(n) > O(n logbn) > O(n2) > O(n3) > O(cn) > O(n!)
Siendo los algoritmos O(1) los más eficientes y los O(n!) los menos eficientes.
2 1 2 2 4 8 4 2
4 2 4 8 16 64 16 24
8 3 8 24 64 512 256 40320
16 4 16 64 256 4096 65536 16!
32 5 32 160 1024 32768 232 32!
64 6 64 384 4096 262144 264 64!
Propiedades de la notación O:
- c O(f(n)) = O(f(n))
- O(f(n) + g(n)) = max{ O(f(n)), O(g(n)) } regla de la suma
- O(f(n)) * O(g(n)) = O(f(n) * g(n)) regla del producto
- O(O(f(n))) = O(f(n))
Ejemplos:
Si f(n)=4N2 + 6log2N + 8
Debido a que parte de la ecuación es cuadrática y parte es logarítmica, al aplicar la
regla de la suma, el mayor orden entre O(N2) y O(log2N) es O(N2). Por lo tanto, el orden
de magnitud que se asigna a f(n) es O(N2).
Ejemplo 1
f(n)
I=2 -------------------------------------------- 1
J=1 -------------------------------------------- 1
Para K = 1 hasta N -------------------------- N+1
{
I = I + 3 -------------------------------------- N
J= J + 4 -------------------------------------- N
Muestre I, J, K ------------------------------- N
}
-----------------
f(n)= 4N + 3 = O(N)
Ejemplo 2
f(n)
A=2 -------------------------------------------- 1
B=1 -------------------------------------------- 1
Para I = 1 hasta N ---------------------------- N+1
{
Para J = 1 hasta N -------------------------- (N + 1)*N=N2 + N
{
A = A + 3 -------------------------------------- N2
B = B * 4 -------------------------------------- N2
Muestre A, B, K ------------------------------- N2
}
} -----------------
f(n)= 4N2 + 2N + 3 = O(N2)
Ejemplo 3
f(n)
A=2 -------------------------------------------- 1
B=1 -------------------------------------------- 1
Para I = 1 hasta N { ---------------------------- N+1
Para J = 1 hasta I -------------------------- N*(N+1)/2 + N
{
A = A + 3 -------------------------------------- N*(N+1)/2
B = B * 4 -------------------------------------- N*(N+1)/2
Muestre A, B, K ------------------------------- N*(N+1)/2
} ----------------------
} f(n)= 2N2 + 4N + 3 = O(N2)
El número de veces que se ejecuta el ciclo interno J, depende de los valores que
tome la variable I en cada iteración. Para cuando:
I =1, la instrucción del ciclo interno se ejecuta 1 vez, más chequeo fin ciclo
I =2, la instrucción del ciclo interno se ejecuta 2 veces, más chequeo fin ciclo
I=3, la instrucción del ciclo interno se ejecuta 3 veces, más chequeo fin ciclo
Cuando,
I = N, la instrucción del ciclo interno se ejecuta N veces.
Ejemplo 4
f(n)
A=2 -------------------------------------------- 1
B=1 -------------------------------------------- 1
Para I = 1 hasta N { ---------------------------- N+1
Para J = 1 hasta I{ -------------------------- N*(N+1)/2 + N
Para K = 1 hasta J{---------------------------
6
5
A = A + 3 -------------------------------------- (1)
B = B * 4 -------------------------------------- (1)
Muestre A, B, K ------------------------------- (1)
} -------------------------------
} f(n)=
3 13 3=O(N3)
1 1
2 2
" " 1 #
$!
%
" " 1 # $! &" " 12" 4( (1)
Al resultado de la ecuación (1) se le debe sumar los fines de ciclo K. En este caso,
))
1 + 2 + 3 +…+N =
(2)
En el ciclo Mientras, para cada uno de los valores de J igual desde 1 hasta N, la I se ejecuta
desde 1 hasta N. Luego el número de veces que se ejecuta el ciclo Mientras, es igual a N2 +
N.
Ejemplo 6:
f(n)
A=1 ------------------------------------------------- 1
C=1 -------------------------------------------------- 1
Mientras (C < N) haga ---------------------------------- log2N +1
{
A= A + 1 ----------------------------------------------- log2N
C = C * 2 ----------------------------------------------- log2N
Muestre A, C ------------------------------------------ log2N
} ----------------------
f(n)= 4log2N + 3 = O(log2N)
Ejemplo 7:
f(n)
A=1 ------------------------------------------------- 1
C=N -------------------------------------------------- 1
Mientras (C > 1) haga ---------------------------------- log2N +1
{
A= A + 1 ----------------------------------------------- log2N
C = C / 2 ----------------------------------------------- log2N
Muestre A, C ------------------------------------------ log2N
} ----------------------
f(n)= 4log2N + 3 = O(log2N)
Como en el ejemplo 4, el número de veces que se ejecuta la instrucción del
ciclo va a depender de los valores de C. Si por ejemplo, N=128 (27), el ciclo
se ejecuta para C = 128, 64, 32, 16, 8, 4, 2 y 1, es decir, 8 veces. Si N=256
(28), el ciclo se ejecuta 9, veces. Luego, el número de veces que se ejecuta el
ciclo es igual a log2N +1 (el entero inmediato superior).
Ejemplo 8:
f(n)
j=1 ------------------------------------------------ 1
z=0 ------------------------------------------------ 1
Haga{
A = 1 ------------------------------------------------- N
Mientras (A≤ N) haga ---------------------------------- N*(log2N +1) = Nlog2N + N
{
A= A * 2 ----------------------------------------------- N* log2N
z = z + 1 ----------------------------------------------- N*log2N
Muestre A, z ------------------------------------------ N* log2N
}
j=j+1 ------------------------------------------N
} Mientras (j ≤ N) N+1
---------------------
f(n)= 4N + 4Nlog2N + 3 = O(Nlog2N)
Ejercicio 1:
Determinar el f(n) y el Orden de Magnitud de un algoritmo que encuentre la suma de los
N primeros números naturales.
Valores de entrada:
N: Variable numérica entera. Número de valores a sumar
Resultado:
Suma: Variable numérica entera que contiene la suma de los N primeros números
naturales.
Solución 1:
Programa Suma1 f(n)
{
entero N, Suma = 0 ----------- 1
Entre N ----------------- 1
Para I= 1 hasta N ---------- N+1
{
Suma = Suma + I -------- N
}
Muestre Suma ------------ 1
} ------------
f(n) = 2N + 4 = O(N)
Solución 2:
Dado que la suma de 1+ 2 + 3 + …...+ (N - 1) + N = N*(N+1) / 2, es posible diseñar otra
solución utilizando la fórmula anterior.
Ejercicio 2:
Determinar el f(n) y el Orden de Magnitud de un método que encuentre la suma de los
N primeros números de una serie aritmética con valor inicial a1 y una diferencia
(distancia) d.
Valores de entrada:
a1: Variable numérico que contiene el valor inicial de la serie
d: Variable numérica. Diferencia entre los términos de la serie
N: Variable numérica entera. Número de valores a sumar
Salida:
Suma: Variable numérica que contiene la suma de los N primeros números de la serie
aritmética
Solución 1:
Solución 2:
En una serie aritmética el término N-ésimo de la serie es igual a:
an = a1 + (N – 1)*d
Ejercicio 3:
Elaborar dos métodos uno con orden de magnitud O(N) y el otro O(1), que encuentre la
suma de los N primeros números de una serie geométrica con valor inicial a1 y una
razón r.
an = a1*rN – 1
Referencias
- Estructuras de Datos y Algoritmos. Alfred V.Aho, John E. Hopcroft, Jeffrey D.
Ullman. Addison-Wesley, Iberoamericana 1988.
1. a) Elaborar un método que tiene como entrada dos matrices A y B de orden N*M y
que retorne la matriz C con el resultado de la suma de A + B
b) Encontrar el contador de frecuencia y el orden de magnitud del método anterior
2. a) Elaborar un método que tiene como entrada dos matrices A y B de orden N*M y
que retorne la matriz C con el resultado de la multiplicación de A * B
b) Encontrar el contador de frecuencia y el orden de magnitud del método anterior
a) entero p1 (entero n)
{
entero i, j, numero
entero suma = 0
Para i=1 hasta n-1
{
Para j= i+1 hasta n
{
Entre numero
suma = suma + numero
}
}
retorne( suma )
}
b) entero p2 (entero n)
{
entero i = 1, x= 0
Mientras i ≤ n haga
{
x =x+1
i=i+3
}
retorne( x )
}
c) entero p3 (entero n)
{
entero i = 1, x= 0
Mientras i < n haga
{
x =x+1
i=i*3
}
retorne( x )
}
d) entero p4 (entero n)
{
entero i = 1, x= 0, j
Haga
{
j=1
Mientras j ≤ n haga
{
x =x+2
j = j*2
}
i = i +1
} Mientras ( i ≤ n)
retorne( x )
}
e) entero p5(entero n)
{
entero i, j, k , x = 0
Para i=1 hasta n
{
Para j=1 hasta n
{
Para k =1 hasta j
{
x=x+2
} //fin ciclo k
} // fin ciclo j
}// fin ciclo i
retorne( x )
}
f ) entero P6(entero n, entero m)
{
entero i, j, k , tmp = 0
Para i=1 hasta n
{
Para j=1 hasta n
{
Es (j = = m) Si: {
Para k= 1 hasta n inc 2
tmp= tmp + (i+j+k)
}
No: {
Para k= 1 hasta n
tmp= tmp + (i+j+k)
}
retorne (tmp)
}
h) entero p8 (entero n, entero m)
{
entero resultado = 1
Es(n > m) Si:
Para i=1 hasta n {
resultado = resultado + 1}
No:
resultado = resultado * 2
retorne(resultado)
}
j) entero p9 (entero n)
{
entero j= 0, L= 0
Para i=1 hasta n {
j = j + i*i
Para k=1 hasta j {
L=L+1
}// fin ciclo k
}// fin ciclo i
Muestre L
}
BÚSQUEDA Y ORDENAMIENTO
BÚSQUEDA
La búsqueda es una de las operaciones que se realiza con mayor frecuencia sobre un
conjunto de elementos.
Búsqueda Lineal: El método de la búsqueda lineal compara cada uno de los elementos
del conjunto con el valor a buscar y termina cuando: encuentra el valor, en este caso el
método retornará el mensaje “existe el valor”, o la posición en donde se encuentra dicho
valor. Cuando después de comparar todos los elementos del conjunto, con el valor a
buscar, ninguno coincide, el método retornará el mensaje “no existe el valor” o la
posición -1.
Entrada:
A: Arreglo unidimensional de N valores numéricos enteros
N: Tamaño del arreglo A. Variable numérica entera.
V: Valor a buscar en el arreglo A. Variable numérica entera.
Resultado:
S: Variable tipo hilera en donde se retorna el resultado de la búsqueda: “existe el valor”
o, “no existe el valor”.
La instrucción del ciclo Mientras, se ejecuta N+1 veces porque en el peor de los casos (cuando
no existe V), la primera condición (L < = N) es falsa cuando L > N.
A
9 15 28 35 42 57 76 125 224 275 360 612
1 2 3 4 5 6 7 8 9 10 11 12
Resultado:
S: Variable tipo hilera en donde se retorna el resultado de la búsqueda: “ existe el valor”
o, “no existe el valor”.
hilera BÚSQUEDA_BINARIA(entero V) f(n)
{
hilera S = ”no existe el valor” ----------------------------------------------- 1
entero LI = 1, LS = N, Medio ----------------------------------------------- 2
Mientras (LI < = LS) ^ (S = = ”no existe el valor”) ----------------------- log2 N + 2
{
Medio = (LI + LS) / 2 --------------------------------------------------- log2 N + 1
Es (V < A[ Medio ] ) Si: LS = Medio - 1 -------------------------------- 3 *(log2 N + 1)
No: Es (V > A[Medio] ) Si: LI = Medio +1
No: S = ”existe el valor”
}
Retorne S
} ---------------------
f(n) = 5log2 N + 9 = O( log2 N )
La instrucción del ciclo Mientras se ejecuta log2 N + 2, ya que para el peor de los casos,
que se presenta cuando no existe V en el arreglo, el ciclo termina cuando LI >LS. Por
ejemplo, si N = 64, la segunda vez que ingresa al ciclo, se han descartado de la
búsqueda la mitad de los valores, es decir 32, luego se descartan 16, después 8, 4, 2, 1 y
termina porque LI > LS. Es decir, que para N = 64 el número de veces que se ejecuta el
ciclo es log264 + 2 = 6 + 2 = 8. Si N = 128, el número de veces que se ejecuta el ciclo
es: log2128 + 2= 7 + 2 = 9 veces.
Comparando el orden de magnitud del método de la Búsqueda Lineal, que es O(N), con
el orden de magnitud del método de la Búsqueda Binaria O(log2N), significa que la
Búsqueda Binaria es más eficiente.
ORDENAMIENTO
Ejemplos de ordenamientos:
El directorio telefónico, una lista de estudiantes, bibliotecas y diccionarios, etc.
El ordenar un grupo de datos significa mover los datos o sus referencias, para que
queden en una secuencia tal que represente un orden, el cual puede ser numérico,
alfabético o incluso alfanumérico, en forma ascendente o descendente.
Internos: Son aquellos en los que los valores a ordenar están en memoria principal, por
lo que se asume que el tiempo que se requiere para acceder a cualquier elemento sea el
mismo. Son usados cuando el número de valores a ordenar no es muy grande.
Externos: Son aquellos en los que los valores a ordenar están en memoria secundaria,
por lo que se asume que el tiempo que se requiere para acceder a cualquier elemento
depende de la última posición accesada (posición 1, posición 500, etc).
Resultado:
El método retorna el arreglo ordenado
Procedimiento:
En este método se realizan N - 1 pasadas.
En la primera pasada, se compara V[1] con V[2] y si V[1] > V[2], se intercambian,
luego se compara V[2] con V[3] y si V[2] > V[3], se intercambian, y así se
continua hasta comparar V[N-1] con V[N]. Al finalizar la primera pasada, el mayor
de todos los elementos del arreglo queda ubicado en la última posición del arreglo.
En la segunda pasada, se compara V[1] con V[2] y si V[1] > V[2], se intercambian,
luego se compara V[2] con V[3] y si V[2] > V[3], se intercambian, y así se
continua hasta comparar V[N-2] con V[N-1]. Al finalizar la segunda pasada, el
siguiente mayor de todos los elementos del arreglo queda ubicado en la penúltima
posición del arreglo
En la N-1 pasada se compara V[1] con V[2] y si V[1] > V[2], se intercambian.
BURBUJA( ) f(n)
{
entero aux
Para I = 1 hasta N - 1 haga --------------------------------- N
Para J = 1 hasta N - I haga ----------------------------- ------ + (N - 1)
Es ( V[ J ] > V[ J + 1]) Si: { aux =V[ J ] --------------------------- 4
V[ J ] = V[J +1]
V[ J +1] = aux
}
Fin ciclo J
Fin ciclo I
Retorne
} ---------------------------------
- 1
Para el último valor que toma I que es N – 1, el ciclo J se ejecuta 1 vez, más chequeo
fin ciclo, por lo tanto el número de veces que se ejecuta el ciclo J es igual a:
(N – 1) + (N – 2) + … + 2 + 1= 1
Ejemplo:
Sea el arreglo a ordenar V = [40, 12, 78, 65, 8]
En la primera pasada:
Se compara V[1]:V[2], como V[1] > V[2] se intercambian y queda V igual a:
V = [12, 40, 78, 8, 65]
Se compara V[2]:V[3], V[2] < V[3] , el arreglo permanece igual
V = [12, 40,78, 8, 65]
Se compara V[3]:V[4], como V[3] > V[4] se intercambian y queda V igual a:
V = [12, 40, 8, 78, 65]
Se compara V[4]:V[5], como V[4] > V[5] se intercambian y queda V igual a:
V = [12, 40, 8, 65, 78 ]
Al finalizar la primera pasada queda ubicado en la última posición del arreglo, el
mayor valor del arreglo V.
En la segunda pasada:
Se compara V[1]:V[2], como V[1] < V[2], el arreglo permanece igual
V = [12, 40, 8, 65, 78 ]
Se compara V[2]:V[3], V[2] > V[3] se intercambian y queda V igual a:
V = [12, 8, 40, 65, 78 ]
En la tercera pasada:
Se compara V[1]:V[2], como V[1] < V[2] no se realiza intercambio.
Se compara V[2]:V[3], V[2] < V[3] no se realiza intercambio.
Al finalizar la tercera pasada queda ubicado en la tercera posición del arreglo, el
siguiente mayor de V
BURBUJA_MEJORADO( ) f(n)
{
entero aux, I = 1 ----------------------------------------------------------- 1
booleano Bandera = falso -------------------------------------------------------- 1
Resultado
El método retorna el arreglo ordenado
SELECCION( ) f(n)
{
entero aux, menor
Ejemplo:
En el mismo arreglo del ejemplo del método de la burbuja
V = [40, 12, 78, 65, 8]
Se realizan 4 pasadas.
En la primera pasada, se busca el menor de los elementos del arreglo y se
intercambia con el de la primera posición, quedando el arreglo:
V = [8, 12, 78, 65, 40]
Resultado
El método retorna el arreglo ordenado
INSERCION( ) f(n)
{
booleano Bandera
entero J, aux
Para I = 2 hasta N haga -------------------------------------------------- N
{ Bandera = falso -------------------------------------------------------- N-1
J= I -------------------------------------------------------------- N-1
Mientras (J > 1) ∧ (Bandera = = falso) -------------------------- N 1
{ Es ( V[ J ] < V[J – 1]) Si:{ aux = V[ J ] -------------------------- 5
V[ J ] = V[J – 1]
V[ J – 1] = aux
J= J - 1
}
No: Bandera = verdadero
}Fin ciclo J
} Fin ciclo I
} ------------------------------
3 3
Ejemplo:
En el arreglo del ejemplo
V = [40, 12, 78, 65, 8]
INSERCION_BINARIA( ) f(n)
{
entero J, aux, izq, der, medio
Para I = 2 hasta N haga -------------------------------------------------- N
{
aux = V[I] -------------------------------------------------- N-1
izq= 1 -------------------------------------------------- N-1
der= I -1 -------------------------------------------------- N-1
Mientras (izq < = der) haga --------------------------------- (N – 1)(log2N +2)
{
medio =((izq + der)/2 --------------------------------- (N – 1)(log2N + 1)
Es (aux < V[medio])Si: der = medio - 1 -------------- 2(N – 1)(log2N +1)
No: izq = medio + 1
}
J=I–1 -------------------------------------------------- N-1
Mientras (J > = izq) haga ----------------------------------------- N*( N + 1)/2 + N - 2
{
V[J + 1] = V[J] ----------------------------------------- N*( N + 1)/2 - 1
J=J–1 -------------------------------------- N*( N + 1)/2 - 1
}
V[izq] = aux -------------------------------------------------- N-1
} //fin ciclo I
}
a) O(N2)
b) O(N)
c) O(log 3N)
d) O(Nlog 2N)
e) O(N3)
f) O(N3)
g) O(1)
h) O(N)
i) O(log 2N)
j) O(N4)
Listas Simples Enlazadas
La lista simple enlazada es una estructura de datos que permite almacenar datos de una
forma organizada, al igual que los arreglos pero, a diferencia de éstos, una lista enlazada
es una estructura dinámica, por lo que no se tiene que conocer de antemano el número
de elementos que se van a almacenar
En una lista simple enlazada, cada uno de los elementos de la lista, almacena la
referencia del siguiente elemento de la lista, excepto el último que no tiene sucesor y
contiene null que significa fin de la lista.
La principal ventaja de las listas enlazadas con respecto a los arreglos, es que no se
necesita conocer de antemano el número de elementos que se van a almacenar, ya que a
medida que se necesite almacenar un nuevo elemento, se solicita el espacio para él. Otra
ventaja de las listas enlazadas es que para realizar operaciones de inserción y/o borrado
no hay que mover todos los elementos, sólo se actualizan los enlaces de los nodos
afectados por dichas operaciones, mientras que en un arreglo si se va a insertar un nuevo
elemento se deben mover hacia la derecha de él todos los elementos que se encuentren a
partir de la posición de la cual se va a realizar la inserción. De igual manera si se va a
realizar una operación de eliminación de un elemento sobre el arreglo.
Una desventaja de las listas enlazadas es que no se puede acceder directamente sobre un
elemento si no se conoce la referencia en donde se encuentra ubicado. En un arreglo se
puede ir directamente a la posición en donde se encuentra cualquier elemento.
5 9 3 12 null
dato sig
Primero Ultimo
Los datos a almacenar en cada nodo pueden ser de cualquier tipo. En este ejemplo la
lista se compone de un conjunto de números enteros.
Las operaciones básicas que se definen sobre una lista simple enlazada son las
siguientes:
- Construir la lista
- Encontrar si la lista es vacía, o no.
- Encontrar primer elemento de la lista
- Encontrar último elemento de la lista
- Insertar: inserta un nuevo nodo en la lista, La inserción en una lista enlazada
puede ser al principio, al final de la lista o en orden.
- Eliminar: elimina un nodo de la lista
- Buscar: busca la referencia del nodo en donde se encuentra un determinado dato
- Anterior: retorna la referencia del nodo anterior a otro nodo dado
Clase Lista_Simple{
Nodo primero, ultimo;
Es (EsVacia( ))Si:{Insertar_principio(nuevoDato);
retorne;
}
x = primero( );
Mientras (x!=null) ∧ ( x.Retorne_Dato() < nuevoDato)
x=x.Retorne_Sig();
Es(x = =null) Si{Insertar_fin(nuevoDato);
retorne;
}
Es(x = =primero())Si: Insertar_principio(nuevoDato);
No:{
nuevoNodo = new Nodo(nuevoDato);
ant=Anterior(x);
ant.Asigna_Sig(nuevoNodo);
nuevoNodo.Asigna_Sig(x);
}
}
Programa Crear_Lista{
entero dato
Lista_Simple miLista= new Lista_Simple()
Entre dato
Mientras (dato != 0){
miLista.insertar_Ordenada(d)
Entre dato
}
miLista.Mostrar()
}
Listas Doblemente Enlazadas
Una lista doblemente enlazada se compone de un conjunto de elementos. Cada elemento
de la lista doblemente enlazada contiene un conjunto de campos de información y dos
enlaces, en donde un enlace contiene la referencia al anterior elemento a él en la lista y
el otro enlace contiene la referencia al siguiente elemento de la lista.
La ventaja de lista doble enlazada, con respecto a la lista simple enlazada, es que se
puede recorrer la lista en ambos sentidos, ya que partiendo del primer elemento se
puede recorrer la lista hasta el último de ella, o viceversa, partiendo del último elemento
se puede llegar al primero.
null 5 9 3 12 null
Primero Ultimo
Las operaciones básicas que se definen sobre una lista doblemente enlazada son las
siguientes:
- Construir la lista
- Encontrar si la lista es vacía, o no.
- Encontrar primer elemento de la lista doble
- Encontrar último elemento de la lista doble
- Insertar: inserta un nuevo nodo en la lista, La inserción en una lista doble
enlazada puede ser al principio, al final de la lista o en orden.
- Eliminar: elimina un nodo de la lista doble enlazada
- Buscar: busca la referencia del nodo en donde se encuentra un determinado dato
- Mostrar lista doble de izquierda a derecha
- Mostrar lista doble de derecha a izquierda
Clase Lista_Doble{
Nodo_D primero, ultimo
}
Nodo_D Buscar_Nodo(entero d){ // Buscar nodo de un determinado dato
Nodo_D x
x=primero( )
Mientras(x!= null ∧ x.RetorneDato( )!=d)
x = x.RetorneSig()
retorne(x) //si no existe dato retorna x =null
}
5 9 3 12
dato sig
Primero Ultimo
La Clase Lista Simple Enlazada Circular hereda de la clase Lista Simple Enlazada.
//Mostrar la Lista
mostrar( ){
Nodo x
x=primero()
Haga{
Muestre x.Retorne_Dato()
x= x.Retorne_Sig();
} Mientras (x!=primero())
}
} // fin clase lista circular
Listas Doblemente Enlazadas y Circulares
Una lista doblemente enlazada y Circular es una lista doblemente enlazada en donde el
primer elemento en su campo de anterior almacena la referencia del último elemento de
la lista y el último elemento en su campo de siguiente almacena la referencia del primer
elemento de la lista
5 9 3 12
Primero Ultimo
insertar_ordenadaDCir(entero nuevoDato){
Nodo-D x, , nuevoNodo
entero sw=0
Es(EsVacia( ))Si:{insertar_principioDCir(nuevoDato)
retorne
}
x = primero( )
Haga {
Es(nuevoDato > x.Retorne_Dato()) Si: x = x.Retorne_Sig()
No: sw=1
} Mientras ((x!=primero()) && (sw= =0))
9 3 12 null
dato sig
primero Ultimo
Lista Simple Enlazada Con Cabeza (LSC) Vacía se representa de la siguiente manera:
null
primero ultimo
Clase LSC{
Nodo primero, ultimo
//Mostrar la Lista
mostrar( ){
Nodo x
x = primero() //método de LSC
Mientras(x!=null) {
Muestre x.Retorne_Dato()
x = x.Retorne_Sig()
}
}
} // fin clase lista circular
primero ultimo
null
null
primero ultimo
primero ultimo
Ejercicios
Implementar las operaciones básicas sobre los diferentes tipos de listas con Cabeza
EJERCICIOS LISTAS ENLAZADAS
1. Dada una Lista Simple Encadenada retornar la lista al revés, es decir que el
primer elemento quede de último, el segundo de penúltimo y así sucesivamente.
Nota: No se puede crear una nueva lista.
2. Dadas dos listas simplemente encadenadas y circulares L1 y L2 ordenadas por
el campo de dato, intercale los elementos de L2 en L1 de tal forma que L1
permanezca ordenada.
3. Dada una Lista Simple Encadenada con registro cabeza L1 y un valor V1, crear
las listas L2 y L3 simplemente encadenadas y circulares, de tal forma que L2
contenga los valores menores o iguales a V1 en L1 y L3 contenga los valores
mayores a V1.
4. Dada una Lista Simple Encadenada y circular, eliminar de la lista un
determinado dato, todas las veces que se presente.
5. Dada una Lista Doblemente Encadenada y circular L1, encontrar los elementos
que poseen el valor mayor de la lista y el valor menor de la lista y luego,
intercambie dichos elementos.
6. Dada una Lista Doblemente Encadenada y circular ordenada con valores
repetidos, eliminar los valores repetidos de la lista.
7. Dada una Lista Simple Encadenada, circular y con cabeza, encontrar el
promedio de los valores contenidos en la lista.
8. Dada una Lista Doblemente Encadenada, circular y con cabeza, encontrar el
menor valor de la lista, y eliminarlo de ella.
9. Dadas las listas L1 y L2 Simplemente Encadenadas circulares y con cabeza,
encontrar si dichas listas son Semejantes, es decir si cada valor de la lista L1 se
presenta al menos una vez en la lista L2.
10. Dadas las listas L1 y L2 Simplemente Encadenadas circulares, encontrar si
dichas listas son Iguales, es decir si cada valor de la lista L1 se presenta
exactamente una vez en la lista L2.
11. Dadas las listas L1 y L2 Simplemente Encadenadas y circulares ordenadas y sin
valores repetidos dentro de cada una de ellas, crear las listas L3 y L4, en donde
L3 contenga los valores que se encuentran tanto en L1 como en L2 y L4
contenga el resto de valores.
12. Dada una Lista Simple Encadenada y circular, elaborar un método que retorne la
suma de los valores contenidos en las posiciones pares de la lista.
13. Dada una Lista Simplemente Encadenada, eliminar I elementos de la lista a
partir del elemento ubicado en la k-ésima posición de la lista.
14. Dada una Lista Simplemente encadenada, insertar I elementos en la lista a partir
de la k-ésima posición de la lista.
TALLER DE LISTAS ENCADENADAS
1. Dada una Lista Simple Encadenada retornar la lista al revés, es decir que el
primer elemento quede de último, el segundo de penúltimo y así sucesivamente.
2. Dadas dos listas simplemente encadenadas y circulares L1 y L2 ordenadas por
el campo de dato, intercale los elementos de L2 en L1 de tal forma que L1
permanezca ordenada.
3. Dada una Lista Simple Encadenada con registro cabeza L1 y un valor V1, crear
las listas L2 y L3 simplemente encadenadas y circulares, de tal forma que L2
contenga los valores menores o iguales a V1 en L1 y L3 contenga los valores
mayores a V1.
4. Dada una Lista Doblemente Encadenada y circular L1, encontrar los elementos
que poseen el valor mayor de la lista y el valor menor de la lista y luego,
intercambie dichos elementos.
5. Dada una Lista Doblemente Encadenada y circular ordenada con valores
repetidos, eliminar los valores repetidos de la lista.
6. Dada una Lista Simple y circular, encontrar el promedio de los valores
contenidos en la lista.
7. Dadas las listas L1 y L2 Simplemente Encadenadas y circulares ordenadas y sin
valores repetidos dentro de cada una de ellas, crear las listas L3 y L4, en donde
L3 contenga los valores que se encuentran tanto en L1 como en L2 y L4
contenga el resto de valores.
8. Dada una Lista Simplemente Encadenada, eliminar I elementos de la lista a
partir del elemento ubicado en la k-ésima posición de la lista.
9. Dada una Lista Simplemente encadenada, insertar I elementos en la lista a partir
de la k-ésima posición de la lista.
Manejo de Caracteres
Los lenguajes de programación utilizan juegos a alfabetos para representar información
de tipo carácter. Se han definido diferentes alfabetos, entre ellos se encuentran:
Las principales operaciones que se definen sobre la clase Hilera son las siguientes:
Clase Hilera
{
Hilera( ) //constructor
entero LONG(hilera) // retorna la longitud de una hilera
CONCAT (hilera) //concatena una subhilera en una hilera
hilera SUB(entero, entero) //extrae una subhilera de una hilera
entero POS(hilera) // retorna la posición a partir de la cual se encuentra una subhilera
en una hilera
ELIMINAR(entero, entero) //elimina una subhilera de una hilera
INSERTAR(entero, hilera) //inserta una subhilera en una hilera a partir de una
posición dada
REM(entero, entero, hilera) //reemplaza una subhilera por otra subhilera
}
- Concatenación de hileras
Sean H, H1dos variables de tipo hileras. La operación:
H.CONCAT( H1)
Concatena los caracteres contenidos en la hilera H1 en la hilera H. La hilera H1 no se
modifica.
H.CONCAT( H1)
Luego de la operación la hilera H = “lluevehoy”
H = “no” + “ “ + H + “ “ + H1
En la hilera H se almacena:
H = “no llueve hoy”
- Extracción de hileras
Sean H, H1dos variables de tipo hileras y las variables I, K de tipo entero. La
operación:
H1 = H.SUB(I, K)
Casos Especiales:
. Si se omite el segundo parámetro, se extrae hasta el final de la hilera H.
H1 = H.SUB(3) H1 = “cdefghijk”
. Si la posición I no existe en la hilera, el resultado es la hilera vacía. Por ejemplo,
H1 = H.SUB(14) H1 = “”
H1 = H.SUB(-1) H1 = “”
- Posición
Sean H, H1dos variables de tipo hileras y la variable I de tipo entero. La operación:
I = H.POS(H1)
retorna la posición en donde comienza la primera ocurrencia de la hilera H1 en la
hilera H. Si H1 no existe en H, retorna un valor de 0.
I = H.POS(H1)
retorna un valor de I = 8
Si H1 = “xyw”
I = H.POS(H1)
retorna I = 0.
I = H.POS(H1, 5)
retorna I = 7.
Ejercicio 1:
Elaborar un algoritmo que cuente el número de espacios en blanco que se presentan
en un texto representado en una hilera.
Entrada:
TEXTO: variable hilera que contiene el texto
Resultado:
Cont: variable numérica entera. Número es espacios en blanco en el texto.
Programa Contar_Espacios
{
hilera Texto
entero k, Cont = 0
Entre Texto
k = Texto.POS(“ “) //busca la primera ocurrencia del espacio en blanco
Mientras ( k != 0) haga
{ Cont = Cont + 1
k = Texto.POS(“ “, k+1)
}
Muestre “El espacio en blanco se presenta: “ + Cont + “veces en el texto”
}
Ejercicio 2:
Elaborar un algoritmo que cuente el número de veces que se presentan cada una de
las letras del alfabeto en un texto representado en una hilera.
Entrada:
TEXTO: variable hilera que contiene el texto
Resultado:
CONT[26]: arreglo de valores numéricos que contiene el número de veces que se
presenta cada letra.
Si se define una hilera con las letras del alfabeto, como por ejemplo:
ALFA = “abcdefghijklmnopqrstuvwxyz”
Programa Contar_Letras
{
Hilera TEXTO, ALFA = “abcdefghijklmnopqrstuvwxyz”
entero CONT[ 26], j
Para i = 1 hasta 26
CONT[i] = 0
Entre TEXTO
Para i = 1 hasta TEXTO.LONG( ) {
j= ALFA.POS(TEXTO.SUB(i, 1))
Es (j != 0 ) Si: CONT[ j ] = CONT[ j] + 1
}
Para i = 1 hasta 26
Muestre “La letra” + ALFA.SUB(i, 1) + “se presenta: ” + CONT[i] +” veces”
}
Ejercicio 3:
Elaborar un algoritmo que cuente el número de veces que se presenta una
determinada palabra en un texto representado en una hilera.
Entrada:
TEXTO: variable hilera que contiene el texto
PAL: variable hilera que contiene la palabra a contar
Resultado:
Cont: variable numérica que contiene el número de veces que se encuentra la
palabra.
Programa Contar_Palabra
{
Hilera TEXTO, PAL
entero Cont, k
Entre TEXTO, PAL
TEXTO = “ “ + TEXTO + “ “
PAL = “ “ + PAL + “ “
k = Texto.POS(PAL) //busca la primera ocurrencia de la palabra
Mientras ( k != 0) haga
{ Cont = Cont + 1
k = Texto.POS(PAL, k + PAL.LONG( ) – 1 )
}
Muestre “La palabra ” + PAL + se presenta: “ + Cont + “veces en el texto”
}
- Eliminación en Hileras
La operación para eliminar un conjunto de caracteres de una hilera H, se define de la
siguiente manera:
H.ELIMINAR(I, K)
Elimina de la hilera H a partir del carácter ubicado en la I-ésima posición de H, K
caracteres.
H.ELIMINAR(4,3)
H.ELIM(4,3)
Es equivalente a:
H = H.SUB(1, 3) + H.SUB(7)
H = “abc” + “fxyzijxyz”
H = “abcfxyzijxyz”
Ejercicio 1:
Elaborar un algoritmo que elimine los espacios en blanco si entre cada una de las
palabras de un texto se presenta más de uno.
Entrada:
TEXTO: variable hilera que contiene el texto
Resultado:
TEXTO: texto que contiene un sólo espacio en blanco entre palabras
Programa Eliminar_Espacios
{
hilera Texto
entero k
Entre Texto
k = Texto.POS(“ “) //busca la primera ocurrencia de 2 espacios en blanco
Mientras ( k != 0) haga
{ Texto.ELIMINAR( k, 1)
k = Texto.POS(“ “, k) //busca las siguientes ocurrencias de 2 espacios en
blanco
}
Muestre Texto
}
Ejercicio 2:
Elaborar un algoritmo que elimine los signos de puntuación en un texto
Entrada:
TEXTO: variable hilera que contiene el texto con signos de puntuación
Resultado:
TEXTO: texto sin signos de puntuación
Ejercicio 3:
Elaborar un algoritmo que elimine en un texto una determinada palabra, todas las
veces que se presente dicha palabra.
Entrada:
TEXTO: variable hilera que contiene el texto
PAL: variable hilera que contiene la palabra eliminar del texto
Resultado:
TEXTO: texto sin la palabra que se eliminó
Programa Eliminar_Palabra
{
hilera Texto, PAL
entero k
Entre Texto, PAL
TEXTO = “ “ + TEXTO + “ “
PAL = “ “ + PAL + “ “
k = Texto.POS(PAL) //busca la primera ocurrencia de la palabra
Mientras ( k != 0) haga
{ Texto.ELIMINAR(k, PAL.LONG( ) -1)
k = Texto.POS(PAL, k )
}
Muestre Texto
}
- Inserción en Hileras
La operación para insertar un conjunto de caracteres en una hilera H, se define de la
siguiente manera:
H.INSERTAR( I, H1)
Inserta en la hilera H a partir de la I-ésima posición, la hilera H1.
H.INSERTAR(4, H1)
H = H.SUB(1, I - 1) + H1 + H.SUB( I)
Ejercicio 1:
Elaborar un algoritmo que dado un texto y dos palabras, PAL1 Y PAL2, inserte en el
texto la palabra PAL2 a continuación de la palabra PAL1, todas las veces que PAL1
se presente en el texto.
Entrada:
TEXTO: variable hilera que contiene el texto
PAL1, PAL2: variables hileras
Salida:
TEXTO: texto con la palabra PAL2
Programa Insertar_Palabra
{
hilera Texto, PAL1, PAL2
entero l
Entre Texto, PAL1, PAL2
TEXTO = “ “ + TEXTO + “ “
PAL1 = “ “ + PAL1 + “ “
PAL2 = PAL2 + “ “
l = Texto.POS(PAL1) //busca la primera ocurrencia de PAL1
Mientras ( l != 0) haga
{ Texto.INSERTAR(l + PAL1.LONG( ), PAL2)
l = Texto.POS(PAL1, l + PAL1.LONG( ) + PAL2.LONG( ) - 1)
}
Muestre Texto
}
- Reemplazo en Hileras
La operación para reemplazar un conjunto de caracteres en una hilera H, por otro
conjunto de caracteres, se define de la siguiente manera:
H.REEMPLAZAR( I, J, H1)
H.REEMPLAZAR(3, 2, H1)
1. H.ELIMINAR(3, 2)
Luego de la operación H= “abefgh”
2. H.INSERTAR(3, H1)
H = “abxxxxxxefgh”
Ejercicio 1:
Elaborar un algoritmo que dado un texto y dos palabras, PAL1 Y PAL2, reemplace
en el texto la palabra PAL1 por la palabra PAL2, todas las veces que PAL1 se
presente en el texto.
Entrada:
TEXTO: variable hilera que contiene el texto
PAL1, PAL2: variables hileras
Salida:
TEXTO: texto con la palabra reemplazada
Programa Reemplazar_Palabra
{
hilera Texto, PAL1, PAL2
entero l
Entre Texto, PAL1, PAL2
TEXTO = “ “ + TEXTO + “ “
PAL1 = “ “ + PAL1 + “ “
PAL2 = “ “ + PAL2 + “ “
l = Texto.POS(PAL1) //busca la primera ocurrencia de PAL1
Mientras ( l != 0) haga
{ Texto.REEMPLAZAR(l, PAL1.LONG( ), PAL2)
l = Texto.POS(PAL1, l + PAL2.LONG( ) - 1)
}
Muestre Texto
}
TALLER SOBRE MANEJO DE CARACTERES
1. Dado un texto y una palabra, elaborar un método que reemplace la palabra por
un ‘*’, todas las veces que se presente dicha palabra en el texto.
2. Elaborar un método que reciba como entrada un caracter y que retorne un valor
booleano, en donde un valor de verdadero indica que el carácter de entrada es
una letra.
3. Dado un texto, mostrar las palabras que contienen una sola vocal.
4. Elaborar un método que dado un texto y una palabra, retorne la posición en que
se encuentra la palabra en el texto. Si la palabra no existe, retorna 0.
Nota: No utilizar la operación POS.
5. Elaborar un método que reciba como entrada un texto y retorne la palabra del
texto que tenga la mayor longitud.
6. Elaborar un método que dado un texto, retorne el texto al revés.
7. Elaborar un método que reciba como entrada un caracter y que retorne un valor
booleano, en donde un valor de verdadero indica que el carácter de entrada es un
signo de puntuación.
8. Dado un texto, contar las veces que se presentan cada una de las vocales.
9. Dado un texto, encontrar las veces que se presentan cada una de las palabras del
texto. Se debe mostrar cada una palabras ordenadas alfabéticamente y las veces
que se presentan.
10. Un palíndromo es una palabra, número o frase que se lee igual hacia adelante
que hacia atrás. Si se trata de un número, se llama capicúa. Como por ejemplo,
oso, radar, anilina, etc. O las frases, “Dábale arroz a la zorra el abad”, “La ruta
nos aportó otro paso natural”. Cuando se trata de una frase, se ignoran los
espacios en blanco. Elaborar un método que retorne verdadero, si una hilera H,
dada es un palíndromo, de lo contrario retorna falso.
11. Un anagrama es una palabra o frase que resulta de la transposición de letras de
otra palabra o frase. Por ejemplo,
amor - roma
miedo - medio
salta - atlas
monja - jamon
lamina - animal
14. Dadas dos hileras caracteres H1 y H2, en donde cada una ellas contiene una
lista de nombres separados por espacios en blanco. Crear una nueva hilera que
contenga los nombres que no son comunes a H1 y a H2.
Por ejemplo:
H1= “juan pedro carlos gloria lina mario marta”
H2= “mario andres alberto carlos monica lucia pedro”
15. Elaborar un método que reciba como entrada una hilera H1 y retorne una nueva
hilera H2 codificada de la siguiente manera: Si un carácter de la hilera H1 es una
letra o un dígito, éste será reemplazado por el siguiente carácter correspondiente
de la tabla ASCII, excepto la letra ´Z’ que será reemplazada por la ‘A’ y el
dígito ‘9’ por el ‘0’. O sea que el dígito ‘1’ se reemplaza por el ‘2’ y la letra ‘P’
por la letra ‘Q’. Cualquier carácter que no sea letra o dígito se reemplaza por un
punto (‘.’)
16. Dada una lista encadenada H1 que contiene un caracter por elemento, elaborar
un método que inserte a partir de la posición I-ésima de la lista, los caracteres
contenidos en una lista simple enlazada H2.
17. Dada una lista encadenada H1 que contiene un carácter por elemento, elaborar
un método que elimine a partir del elemento ubicado en la posición I-ésima de la
lista, LONG caracteres.
18. Dada una lista encadenada H1 que contiene un caracter por elemento y una
hilera H2, elaborar un método que elimine de la lista H1, los caracteres
contenidos en H2.
Nota: Un caracter de H2 se puede presentar varias veces en H1.
19. Dada una lista encadenada H1 que contiene una palabra por elemento, y una
palabra PAL1, elaborar un método que elimine los elementos de la lista que
contienen la palabra PAL1.
20. Dada una lista encadenada H1 que contiene una palabra por elemento, y dos
palabras PAL1 y PAL2, elaborar un método que inserte en la lista, la palabra
PAL2, a continuación del elemento que contiene la palabra PAL1.
Colas
Una cola es una lista ordenada de elementos en donde las operaciones de inserción se
realizan por un extremo denominado ultimo (final) y las operaciones de borrado por el
otro extremo denominado principio(frente). Una cola se considera una estructura FIFO
(primero en entrar primero en salir).
Las operaciones básicas que se definen sobre una Cola son las siguientes:
- Construir la cola
- Determinar si la cola es vacía, o no.
- Encolar: inserta un elemento al final de la cola
- Desencolar: retira el primer elemento de la cola
- Primero: retorna el valor del primer elemento de la cola
- Representación en arreglos:
Se define el arreglo en donde se van a almacenar los elementos de la cola.
Por ejemplo,
Entero COLA[N]
1 2 3 4 5 6 7 8 9 10
35 20 15 8
primero = 0 ultimo = 4
Se definen las variables que van a manejar las posiciones de Primero y Ultimo
de la cola. La variable primero contiene la posición anterior al primer elemento
de la cola. La variable ultimo contiene la posición del ultimo elemento que
ingresó a la cola.
Colas Circulares
0 1 2 3 4 5
20 15 8
primero = 0 ultimo = 3
0 1 2 3 4 5
8 6 30
primero = 2 ultimo = 5
Para manejar la cola en forma circular se utiliza la operación módulo, para que
cuando se presente el caso de que se llegue a la última posición del arreglo y la
cola no se encuentre llena, se comience a utilizar la posición 0 del arreglo, sin
tener que desplazar los elementos hacia la parte izquierda de él.
ultimo = (ultimo + 1) % N
primero = (primero + 1) % N
Condición de Cola Llena: En una cola circular, la cola se encuentra llena cuando
0 1 2 3 4 5
15 20 8 6 30
ultimo = 1 primero = 2
ultimo = (ultimo + 1) % N
Cola[ ultimo] = dato
}
Entero Desencolar( )
{
Es (Esvacia()) Si; Muestre “Cola Vacia”
retorne (-1)
primero = (primero + 1)% N
retorne( Cola[primero]
}
0 1 2 3 4 5
15
ultimo = 0 primero = 5
Entero Primero( )
{
Es (Esvacia()) Si: Muestre “Cola Vacia”
retorne (-1)
retorne( Cola[primero + 1)% N])
}
Inicialmente cada una de las colas contiene igual número de elementos, es decir,
M=N/2.
Si N = 10, M =10/2=5
M-1 M N-1
0 1 2 3 4 5 6 7 8 9
15 8 56 35 40
p1 u1 p2 u2
Se definen las variables que contienen el primero y el último de cada una de las 2
colas, en donde inicialmente contendrán los valores:
La cola 1 está llena y la cola 2 tiene espacio, en este caso, en la cola 2 se pueden
presentar las siguientes situaciones:
M-1 M N-1
0 1 2 3 4 5 6 7 8 9
20 15 8 56 35 40
p1 u1 p2 u2
- p2 > u2:
M-1 M N-1
0 1 2 3 4 5 6 7 8 9
20 15 8 56 24 35 40
p1 u1 u2 p2
En este caso, se desplazan una posición hacia la derecha los elementos que se
encuentran desde la posición u2 hasta la posición M y se incrementan las
variables u2 y M.
M-1 M N-1
0 1 2 3 4 5 6 7 8 9
30 20 15 56 35 25
u1 p1 u2 p2
- u1 < p1:
M-1 M N-1
0 1 2 3 4 5 6 7 8 9
30 32 56 30 35 38 41
u1 p1 u2 p2
se desplazan un lugar hacia la izquierda los elementos que se encuentran
desde la posición p1 +1 hasta la M – 1 y se decrementan en 1, p1 y M.
M -1 M N-1
0 1 2 3 4 5 6 7 8 9
30 32 30 35 38 41
u1 p1 u2 p2
Cola _Llena(entero c)
{
Es(c= =1)Si:{ Es(EsLlenaCola2( )) Si: Muestre "Las dos colas estan llenas "
Terminar
Es (p2= = M) Si: {p2= N -1
Es (u2 = = M) Si: u2 = N – 1
}
No: Es (p2 > u2) Si:{Para k=u2 hasta M, k- -
cola[k+1] = cola[k]
u2 = u2 + 1
}
M= M + 1
Es(u1 < p1)Si: {Para k=M – 1hasta p1+2, k- -
cola[k] = cola[k-1]
p1 = p1 + 1
}
}// fin si c es cola 1
M=M-1
Es(u2 < p2)Si: {Para k=M hasta u2 – 1
cola[k] = cola[k+1];
u2=u2 - 1;
}
} //fin si c es cola 2
}
Desencolar:
El método para desencolar tiene como parámetro de entrada la cola en la que se va
a encolar (1 o 2) y retorna el primer valor que se encuentra en la cola (-1, si la cola
está vacía).
Entero desencolar(entero c)
{
Es (c = = 1) Si: Es(EsVaciaCola1( )) Si: Muestre "cola 1 vacia"
retorne(-1)
No: p1 = (p1+1)%M
retorne( cola[p1] )
}
Representación de una Cola en Lista Simple Enlazada
5 9 3 12 null
Primero Ultimo
Clase Cola{
nodo primero, ultimo
Constructor:
Si la cola se encuentra vacía, las variables primero y ultimo contienen null.
En el constructor se inicializan estas dos variables en null.
Cola( )
{ primer = ultimo = null
}
d = primero. Retorne_Dato( )
primero = primero.Retorne_Sig( )
Es (primero = null) Si: ultimo = null
retorne ( d )
}
retorne( primero.Retorne_Dato( ) )
}
Primero Ultimo
1 2
2 1 5 7
4 8
d = Primero[ I ]. Retorne_Dato( )
Primero [ I] = Primero[ I ].Retorne_Sig( )
retorne ( d )
}
Entero Primero(entero I )
{
Es( EsVacia( I))Si: Muestre “Cola” + I + “ Vacia”
retorne (-1)
Las pilas tienen varias aplicaciones en las ciencias de la computación, entre ellas se encuentran
el manejo de las llamadas a métodos y la evaluación de expresiones en un compilador.
Las operaciones básicas que se definen sobre una Pila son las siguientes:
- Construir la Pila
- Determinar si la Pila es vacía, o no.
- Determinar si la Pila se encuentra llena, o no.
- Desapilar: inserta un elemento en el tope de la pila
- Desapilar: retira el elemento que se encuentra en el tope de la pila y retorna el valor.
- Tope: retorna el valor del tope de la pila
- Representación en arreglos:
Se define el arreglo en donde se van a almacenar los elementos de la pila y una variable
que contiene la posición del Tope de la Pila.
Inicialmente Tope = 0
1 2 3 4 5 6 7 8 9 10
45 80 12 30
Tope
booleano EsLlena( ){
retorne(Tope = = N)
}
Apilar(entero dato)
{
Es (EsLlena()) Si: Muestre “Pila llena”
Terminar
Tope = Tope + 1
Pila[ Tope] = dato
}
entero Desapilar( )
{ entero d
Es (EsVacia()) Si: Muestre “Pila Vacia”
retorne (-1)
d = Pila[ Tope ]
Tope = Tope - 1
retorne ( d)
}
Tope: Retorna el dato que se encuentra en el tope de la pila. Si la pila está vacía, retorna -
1
entero Tope( ){
Es (EsVacia()) Si: Muestre “Pila Vacia”
Retorne (-1)
retorne Pila[Tope]
}
Manejo de 2 pilas en arreglos
Se utiliza cuando en una aplicación se necesita manejar simultáneamente dos pilas.
Se define un sólo arreglo de tamaño N en donde se van a almacenar los valores de las dos
pilas.
A continuación se presentan dos formas de manejar las dos pilas en un sólo arreglo:
Se definen las variables que van a contener los topes de las pilas:
Tope1 = tope pila 1
Tope2 = tope pila 2
M N-1
1 2 3 4 5 6 7 8 9 10
4 27 30 42 35
Tope 1 Tope 2
Pila 1 Pila2
En esta forma de almacenar los valores, si la pila 1 está llena, se revisa si la pila 2
tiene espacio disponible y en este caso se mueven los elementos de la pila 2 una
posición a la derecha. Si la pila 2 es la que se encuentra llena, y la pila 1 tiene espacio
disponible, se desplazan los elementos de la pila 2 una posición hacia la izquierda. En
ambos casos se actualizan los valores de las variables Tope2 y M:
2) Inicialmente:
Tope1 = 0 y Tope2 = N + 1
1 2 3 4 5 6 7 8 9 10
4 27 30 75 15
Tope 1 Tope 2
5 9 3 12 null
Tope
Clase Pila{
nodo Tope
Cola( ) // constructor de la cola
Booleano EsVacia( )
Apilar( elemento) //inserta elemento en el tope de la pila
elemento Desapilar( ) //elimina elemento del tope de la pila y retorna su valor
elemento Tope( ) //retorna valor del tope de la pila
}
Cola( ){
Tope = null
}
d = Tope. Retorne_Dato( )
Tope = Tope.Retorne_Sig( )
retorne ( d )
}
retorne( Tope.Retorne_Dato( ) )
}
Se define un sólo arreglo, por ejemplo el arreglo PILAS, de tamaño M en donde se van a
almacenar los valores de las N pilas. Inicialmente todas las pilas contienen el mismo
número de elementos T = M/N.
PILAS
M
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
4 8 12 20 30 40 60 70 22 23
Inicialmente los arreglos BASES y TOPES, en este ejemplo, contienen los valores:
BASES
1 2 3 4 5
0 5 10 15 20
TOPES
1 2 3 4
0 5 10 15
Después de haber apilado los valores que se encuentran en el arreglo de PILAS los arreglos
BASES y TOPES contienen:
BASES
1 2 3 4 5
0 5 10 15 20
TOPES
1 2 3 4
3 10 10 17
Busca primero cuál de las pilas desde la I + 1 hasta la N, tiene espacio disponible. Si la pila
J es la pila con espacio disponible, mueve un lugar hacia derecha todos los elementos del
arreglo Pilas desde el tope de la pila J hasta la base de la pila [I+1] +1. Luego actualiza las
bases y los topes desde la pila I+1 hasta la pila J. Si todas estas pilas están llenas, busca
desde la pila I - 1 hasta la 1 la pila con espacio disponible. Si la pila J es la pila con
espacio, desplaza los elementos en el arreglo de pilas, desde la base de la pila J +1 hasta el
tope de la pila I, un lugar hacia la izquierda. Luego actualiza las bases y los topes de las
pilas J +1 hasta la pila I. Después de asignar el espacio a la pila I que se encontraba llena,
retorna para apilar el nuevo elemento. Si todas las pilas están llenas, termina el método de
apilar.
Desapilar: Elimina el elemento que se encuentra en la posición del tope de la pila I y
retorna el dato que se encuentra en dicha posición. Retorna -1 si la pila se encuentra vacía
Tope: Retorna el dato que se encuentra en el tope de la pila I. Si la pila está vacía, retorna -
1
entero Tope( I ){
Es (EsVacia(I)) Si: Muestre “Pila” + I +” Vacia”
Retorne (-1)
retorne Pilas[Tope[ I ]]
}
Se define un arreglo de tamaño N (número de Pilas) de tipo nodo que contiene el tope de
cada una de las pilas. Si por ejemplo se tiene N =4 pilas, la representación es la siguiente:
Topes
1 2
2 1 5 7
4 8
Apilar: Tiene como entrada la pila I en donde se va a apilar y el dato que se va a apilar.
Apilar(entero I, entero d) //inserta un elemento en el tope de la pila
{
nodo nuevo = new nodo(d)
Es( EsVacia(I )) Si: Topes[ I] = nuevo
No. {nuevo.Asigna_Sig(Topes[I])
Topes[ I ] = nuevo
}
}
Entero Desapilar( entero I) //retira el elemento que se encuentra en la posición del Tope de
{ la pila I y retorna valor
entero d
Es( EsVacia( I))Si: Muestre “Pila”+ I + “Vacia”
retorne (-1)
d = Topes[ I ]. Retorne_Dato( )
Topes [ I] = Topes[ I ].Retorne_Sig( )
retorne ( d )
}
Un método recursivo consta de dos partes: una caso base, en el cual se especifica el
valor de la función para uno o varios de los valores que se encuentran en el dominio de
dicha función y un caso general, en donde la función se especifica en términos de la
misma función.
Para determinar el f(n), o la complejidad de una solución algorítmica recursiva, se han empleado
tres técnicas de resolución de ecuaciones de recurrencia:
- Expansión de Recurrencias
- Método de la Ecuación característica
- Fórmulas Maestras
Expansión de Recurrencias
Un sistema recurrente consiste de un conjunto de valores iniciales, o valores de la
función en unos puntos específicos y una ecuación recurrente en donde el valor la
función en el punto n está dado en términos de otro punto m, siendo n > m.
El método de expansión de recurrencias consiste en aplicar la función general a cada uno de los
valores de n hasta llegar a la condición base (inicial) la cual es reemplazada por su valor.
Dependiendo del tipo de ecuación obtenida en t(n), se encuentra el Orden de Magnitud del
método recursivo.
Ejemplo 1
Calcular el factorial de un número dado N.
1, Si n= 0
Fact( n ) =
n * Fact(n – 1), Si n > 0
Parámetros de entrada:
N: Número al que se le desea calcular el factorial
Salida:
retorna el factorial del número N.
1, Si n = 0
t( n) =
1 + t(n – 1), Si n >0
Si n = 4
t(4) = 1 + t(3)
= 1 + 1+ t(2)
= 1 + 1 + 1+ t(1)
= 1 + 1 + 1 + 1 +t(0)
Ejemplo 2
Dada la siguiente función recursiva
1, Si n = 1
t( n) =
1 + 2 t(n - 1), Si n > 1
para k = n - 1
t(n) = (2n-1 - 1) + 2n-1t(1)
Si n = 4
t(4) = 1 + 2t(3)
= 3 + 4 t( 2)
= 7 + 8 t( 1)
Ejemplo 3
Dada la siguiente función recursiva
1, Si n = 0
t( n) =
1 + t(n/2), Si n >0
Si n = 16
t(16) = 1 + t(8)
= 1 + 1+ t(4)
= 1 + 1+1 + t(2)
= 1 + 1 + 1 + 1 + t(1)
= 1 + 1 + 1 + 1 + 1 + t(0) = 6
Si n= 16, log216 + 2 = 4 + 2
Ejemplo 4
Dada la siguiente función recursiva
1, Si n = 0
t( n) =
n + t(n -1), Si n >0
Ejemplo 1
Para el ejemplo del factorial de un número, en donde el sistema recurrente es:
1, Si n = 0
t( n) =
1 + t(n – 1), Si n >0
Encontrar la solución para la ERLH:
La ecuación característica de la relación de recurrencia es: r - 1 = 0, o sea que r = 1
1
Encontrar la solución :
Como f(n) =A(1)n y se presenta multiplicidad de raíz, cada término de la ecuación es de
la forma, An*1n
An – A(n – 1) = 1
A=1
indica que:
n + c
1, Si n = 1
t( n) =
1 + 2 t(n-1), Si n >1
Encontrar la solución :
Como f(n) =A(1)n
2 1
Con base en la forma de la solución general, se obtiene que el orden de magnitud será
igual a O(2 ).
Ejemplo 3
Para la función F3 en donde el sistema de recurrencias es:
1, Si n = 0
t( n) =
1 + t(n/2), Si n >0
ti(h) = ti - ti-1 = 0
Ecuación característica:
r - 1 = 0, r = 1
ti(h) = c*(1)i
La forma del f(n) para ti(P) es (1)i, pero como se presenta multiplicidad, f(n) es igual a:
Ai(1)i
Reemplazando en la ecuación:
Ai – A(i – 1)= 1
A=1
o sea que
ti = ( c + i) (1)i
ti = ( 1 + i)
Ejemplo 4
Para la función F4 el sistema recurrente es
1, Si n = 0
t( n) =
n + t(n -1), Si n >0
Encontrar la solución :
Como f(n) = (An + B)(1)n y se presenta multiplicidad de raíz, f(n) = n(An + B)(1)n =
An2 + Bn
En n: 2A = 1, A = ½
-A + B = 0, B = 1/2
1 1
2 2
Si t0 =1, c =1
Con base en la forma de la solución general, se obtiene que el orden de magnitud será
igual a O(n2)
Referencias
- Estructuras de Datos y Algoritmos. Alfred V.Aho, John E. Hopcroft, Jeffrey D.
Ullman. Addison-Wesley, Iberoamericana 1988.
- Fundamentos de Algoritmia. Brassard, Gilles, Bratley, Paul. Prentice-Hall. 1997.
- Estructura de Datos. Libro de Problemas. Luis Joyanes Aguilar, Ignacio Zahonero
Martínez, Matilde Fernández A y Lucas Sánchez G. Ed. Mc. Graw Hill. 1999
TALLER DE RECURSIÓN
sp1(v, n, i)
{
Es (i <= n) Si:{ sp1(v, n, 2*i)
Muestre(v[i])
sp1(v, n, 2*i+1)
}
}
sp2(v, n, i)
{
Es (i <= n) Si:{ Muestre(v[i])
sp2(v, n, 2*i)
sp2(v, n, 2*i+1)
)
}
sp3(v, n, i)
{
Es (i <= n) Si: {sp3(v, n, 2*i)
sp3(v, n, 2*i+1)
Muestre(v[i])
}
)
sp4(v, n, i)
{
Es (i <= n) Si:{Muestre(v[i])
sp4(v, n, i+1)
Muestre(v[i])
}
}
4. Elaborar un método recursivo que reciba como entrada una palabra y la muestre
en orden inverso. Es decir, que primero muestre la ultima letra de la palabra,
luego la penúltima, y asi sucesivamente hasta la primera letra de la palabra de
entrada.
5. Elaborar un método recursivo que recorra y muestre los datos de una lista
simple enlazada de izquierda a derecha y de derecha a izquierda.
B + 1, A=0
- Simple (Lineal): Cuando en una condición sólo existe una llamada recursiva.
- Múltiple: Cuando en una condición se presentan varias llamadas recursivas.
Ejemplo 1
Calcular el factorial de un número dado N.
1, N = 0
Fact( N ) =
N*Fact(N – 1), N > 0
En este caso, el modelo tiene 2 condiciones, en donde la condición base se presenta
cuando N = 0.
Entrada:
N: número entero al que se le va a calcular el factorial
Salida:
El método retorna un número entero que contiene el factorial de N.
Programa Factorial
{ entero factorial, N
entre N
L0 factorial = Fact(N) //invoca el método Fact
Muestre “El factorial de N es”: + factorial
}
L1 0 1
L1 1 1*1=1
L1 2 2*1=2
L1 3 3*2=6
L1 4 4*6=24
L0 5 5*24=120
Dr N Fact
4*Fact(3) = 4 * 6 = 24
3*Fact(2) = 3*2= 6
2* Fact(1) = 2* 1 = 2
1*Fact(0) = 1*1 = 1
retorna 1
Ejemplo 2
Calcular el N-ésimo término de la serie de Fibonacci.
0, N = 1
Fib( N ) = 1, N = 2
Entrada:
N: número entero que contiene cuál es el N-ésismo término a calcular
Salida:
El método retorna el valor del N-ésismo término de la serie
L2 1 0
L1 2 1
L2 3 1 +0 = 1
L2 2 1
L2 1 0
L1 2 1
L1 3 1+0 = 1
L1 4 1+ 1= 2
L0 5 2+1= 3
Dr N Fib
fib(5) = 2 + 1 = 3
+
fib(4) =2 fib(3) = 1
+ +
fib(2) fib(1) 1 1 0
1 0
Ejemplo 3
Encontrar el Máximo Común Divisor entre dos enteros N y M
MCD(N, M ) = N, M = 0
Entrada:
N, M: números enteros
Salida:
El método retorna el Máximo Común Divisor entre N y M
L2 4 0 4
L2 8 4 4
L1 12 8 4
L0 8 12 4
Dr N M MCD
Si N = 7 y M = 5:
L2 1 0 1
L2 2 1 1
L2 5 2 1
L0 7 5 1
Dr N M MCD
Ejemplo 4
Sea Q(M, N) el número de formas diferentes en que un entero M puede expresarse
como una suma de números menores o iguales que N.
1, M =1 o N = 1
Entrada:
M, N: números enteros
Salida:
El método retorna el número de formas
L2 2 1 1
L1 2 2 1+1=2
L4 2 3 2
L4 1 2 1
L3 3 1 1
L4 3 2 1 +1=2
L3 5 1 1
L3 5 2 1 + 2=3
L0 5 3 3 + 2 =5
Dr M N Q
siendo Q(5, 3) = 5.
Ejemplo 5
Calcular la raíz cuadrada de un número entero dado N, mediante el siguiente método
A, | | < E
Entrada:
N, A, E: números reales
Salida:
Raiz Cuadrada de N.
Cuando se encuentra que A2= (7.000000013)2 - 49 es menor que 0.00001, retorna que
la raíz cuadrada de 49 es 7.000000013. Como L1 es retornar el valor de A a la llamada
anterior, éste valor se retorna hasta cuando se llegue a que la pila se encuentre vacía.
Ejemplo 6
Un método para evaluar un polinomio de la forma
A[0], N = 0
Entrada:
A: arreglo de coeficientes (Números reales)
N: grado de polinomio. Entero
X: punto a evaluar. Real
Salida:
Resultado de evaluar el polinomio de acuerdo al valor de X.
0 1 2 3 4 5
2 5 -3 0 4 -6
N=5 y X=1.0
L1 (*) 0 1 2
L1 (*) 1 1 1*2 + 5 = 7
L1 (*) 2 1 1*7 +(-3) = 4
L1 (*) 3 1 1*4 + 0 = 4
L1 (*) 4 1 1*4 + 4 = 8
L0 (*) 5 1 1*8 + (-6) = 2
Dr A N X Eval
(*): Se almacena el arreglo A
En los ejemplos anteriores todos los métodos retornan un valor y en éste caso cuando se
realiza el seguimiento, se asigna una etiqueta en el punto en donde se invoca la llamada
recursiva, ya que en una misma instrucción se pueden tener varias veces llamadas
recursivas, o se puede presentar que luego de regresar de la llamada se realice alguna
operación.
Entrada:
A: arreglo unidimensional
N: tamaño del arreglo A
Mostrar(arreglo A, entero N)
{
Es(N> 0) Si:{Muestre A[N]
Mostrar(A, N-1)
}
}L1
L1 (*) 0
L1 (*) 1 10
L1 (*) 2 35
L1 (*) 3 8
L1 (*) 4 12
L1 (*) 5 43
L0 (*) 6 64
Dr A N Muestra
Ejemplo 8
Elaborar un método recursivo que muestre las permutaciones generadas con N valores
lógicos.
Si N= 4 y se ini
cializa en VVVV
VVVV VVVF VVFV VVFF VFVV VFVF VFFV VFFF
FVVV FVVF FVFV FVFF FFVV FFVF FFFV FFFF
Entrada:
A: arreglo unidimensional de valores lógicos
N: tamaño del arreglo
Pos: Posición a partir de la cual se realiza la permutación. Inicialmente P = 1.
Ejemplo 9
Elaborar un método recursivo que muestre las permutaciones generadas con N símbolos
dados.
Si por ejemplo N=3 y los símbolos son ABC, las permutaciones obtenidas son:
ABC ACB BAC BCA CBA CAB
Entrada:
A: arreglo unidimensional de símbolos de tipo hilera
N: tamaño del arreglo
Pos: Posición a partir de la cual se realiza la permutación. Inicialmente P = 1.
Una técnica muy utilizada para el diseño de algoritmos es "Dividir para conquistar".
Los algoritmos de este tipo se caracterizan por estar diseñados siguiendo estrictamente
las siguientes fases:
Datos de Entrada:
A: arreglo unidimensional de valores numéricos enteros
LI: Numérico entero. Inicialmente igual a 1
LS: Numérico entero. Inicialmente igual a N(Tamaño del arreglo)
Resultado
El método retorna el arreglo ordenado
Ejemplo:
12 35 6 20 43 68 9 3 40 70
1 2 3 4 5 6 7 8 9 10
L2 10 10
L1 9 9
L2 9 10 9
L2 8 8
L2 7 7
L1 6 6
L1 6 7 6
L1 6 8 7
L2 6 10 8
L2 5 5
L1 4 4 L3 1 5 10
L2 4 5 4 L3 6 8 10
L2 3 3 L3 9 9 10
L2 2 2 L3 6 7 8
L1 1 1 L3 6 6 7
L1 1 2 1 L3 1 3 5
L1 1 3 2 L3 4 4 5
L1 1 5 3 L3 1 2 3
L1 1 10 5 L3 1 1 2
Dr LI LS M Dr INF M SUP
MergeSort Merge
Análisis de la complejidad del método de Ordenamiento MergeSort
1, n =1
T(n) =
El cual resulta de tener dos llamadas recursivas de MergeSort con n/2 elementos y O(n)
es el orden de magnitud del método Merge.
Ecuación característica:
r - 2 = 0, r = 2,
ti(H) = c*(2)i
La forma del f(n) para ti(P) es 2i, pero como se presenta multiplicidad, f(n) es igual a:
Ai2i
Reemplazando en la ecuación:
Ai2i – 2[A(i – 1)2i-1] = 2i
Ai2i – Ai2i + A2i = 2i
A2i = 2i, o sea que A = 1
ti = ( c + i) 2i
O sea que el MergeSort es más eficiente que los métodos de Burbuja, Selección e
Inserción que son de orden de magnitud O(n2).
Método de Ordenamiento QuickSort
El procedimiento es el siguiente:
- Elegir un elemento de la lista a ordenar, al que se denomina pivote.
- Resituar los demás elementos de la lista a cada lado del pivote, de manera que a un
lado queden todos los menores que él, y al otro los mayores. Los elementos iguales al
pivote pueden ser colocados tanto a su derecha como a su izquierda, dependiendo de
la implementación deseada. En este momento, el pivote ocupa exactamente el lugar
que le corresponderá en la lista ordenada.
- La lista queda separada en dos sublistas, una formada por los elementos a la
izquierda del pivote, y otra por los elementos a su derecha.
- Repetir este proceso de forma recursiva para cada sublista mientras éstas contengan
uno o más elementos. Una vez terminado este proceso todos los elementos estarán
ordenados.
Datos de Entrada:
A: arreglo unidimensional de valores numéricos enteros
IZQ: Numérico entero. Inicialmente igual a 1
DER: Numérico entero. Inicialmente igual a N(Tamaño del arreglo)
Resultado
El método retorna el arreglo ordenado