Apuntes

Anuncio
Lección 11 – Estructuras dinámicas lineales (ii)
Estructuras dinámicas lineales (ii)
Introducción
Continúa la exposición de los distintos algoritmos existentes para manipular listas simplemente enlazadas; sin
el lección anterior se presentaban algoritmos iterativos, en ésta se verán en su versión recursiva.
Implementación de listas simplemente enlazadas en la notación algorítmica (ii)
Operaciones sobre listas (simplemente enlazadas) implementadas recursivamente
Recorrido de una lista simplemente enlazada (de forma recursiva)
acción recorrer (cabezaLista ∈ puntero a nodo)
inicio
si cabezaLista ≠ NIL entonces
escribir cabezaLista↑.numero
llamar recorrer (cabezaLista↑.siguiente)
fin si
fin acción
En el algoritmo que se presenta el caso base podría
decirse que es elíptico1 puesto que ninguna acción se debe
realizar para recorrer una lista vacía; así, sólo aparece el caso
recursivo que se limita a imprimir el contenido de la cabeza de
la lista recibida y procede a recorrer el resto de la lista (una
lista cuya cabeza es el elemento que sigue al actual).
A continuación se muestra de forma gráfica el recorrido recursivo de una lista con los números 1, 2 y 3:
llamar recorrer (lista)
lista
inicio
si cabezaLista ≠ NIL entonces
escribir cabezaLista↑
↑ .numero
llamar recorrer (cabezaLista↑.siguiente)
fin si
fin acción
1
2
3
lista
2
3
lista
lista
NIL
3
NIL
NIL
NIL
La primera invocación de la acción imprime el contenido de la cabeza de la lista
(1) y invoca de nuevo la acción para el resto de la lista.
inicio
si cabezaLista ≠ NIL entonces
escribir cabezaLista↑
↑ .numero
llamar recorrer (cabezaLista↑.siguiente)
fin si
fin acción
La segunda invocación imprime el contenido de la cabeza de la lista, en este caso
el resto de la lista anterior, con lo cual aparece por pantalla el número 2 y se
invoca nuevamente la acción para el resto de la lista.
inicio
si cabezaLista ≠ NIL entonces
escribir cabezaLista↑
↑ .numero
llamar recorrer (cabezaLista↑.siguiente)
fin si
fin acción
La tercera invocación imprime el contenido de la cabeza de la lista, en este caso el
resto del resto de la lista anterior, mostrándose el número 3 e invocando la acción
para el resto de la lista.
inicio
si cabezaLista ≠ NIL entonces
escribir cabezaLista↑.numero
llamar recorrer (cabezaLista↑.siguiente)
fin si
fin acción
Esta cuarta invocación recibe una lista vacía, por tanto, no hace nada y retorna a
la tercera invocación que finaliza y retorna a la segunda que, a su vez, finaliza y
retorna a la primera invocación que también finaliza y devuelve el control al
programa principal.
1
Elipsis: figura de construcción que consiste en omitir en la oración palabras que no son indispensables para la claridad del sentido.
1
Lección 11 – Estructuras dinámicas lineales (ii)
Búsqueda de un elemento en una lista simplemente enlazada (de forma recursiva)
La operación de búsqueda se basa en la de recorrido; básicamente se trata de recorrer la lista hasta que se
encuentre el elemento o se llegue al final, retornando un puntero al elemento encontrado (o NIL en caso contrario).
puntero a nodo función buscar (cabezaLista ∈ puntero a nodo, elemento ∈ entero)
inicio
si cabezaLista ≠ NIL entonces
si cabezaLista↑.numero = elemento entonces
buscar ß cabezaLista
si no
llamar buscar (cabezaLista↑.siguiente,elemento)
fin si
si no
buscar ß NIL
fin si
fin acción
Inserción de un elemento en una lista simplemente enlazada
Al igual que en la lección anterior, se procederá a implementar las operaciones de inserción en una cola y en
una lista ordenada ascendentemente; la inserción en una pila no se puede implementar de forma recursiva puesto que
es un problema que no consiste en una repetición de pasos.
Inserción en una cola (de forma recursiva)
Como ya se ha dicho, la inserción en una cola precisa buscar el último elemento de la lista e insertar el nuevo
elemento detrás del mismo.
acción insertarCola (cabezaLista ∈ puntero a nodo, dato_nuevo ∈ entero)
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑.siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
A continuación se muestra de forma gráfica la inserción del número 3 en una lista que ya contiene los enteros 1
y 2:
insertarCola (lista, 3)
lista
1
2
lista
2
NIL
NIL
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑
↑ .siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
En la primera invocación se comprueba que la cola no está vacía por lo que se
procede a insertar, recursivamente, el elemento en el resto de la cola (2).
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑
↑ .siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
En la segunda invocación se comprueba que la cola no está vacía por lo que se
invoca recursivamente la acción para insertar el elemetno.
2
Lección 11 – Estructuras dinámicas lineales (ii)
lista
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑.siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
NIL
En la tercera invocación la cola está vacía, por tanto, es posible crear un nuevo
elemento.
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑.siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
lista
lista
lista
lista
NIL
3
2
3
lista
NIL
NIL
El nuevo elemento es el primero y el último de su cola.
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑.siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑
↑ .numero ß dato
fin si
fin acción
Se asigna un valor al nuevo elemento y se retorna desde la tercera invocación a la
segunda.
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑.siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
La segunda invocación ya ha insertado, recursivamente, el nuevo elemento así que
puede retornar a la primera.
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑.siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
1
2
3
El nuevo elemento es creado.
inicio
si cabezaLista ≠ NIL entonces
llamar insertarCola (cabezaLista↑.siguiente,dato_nuevo)
si no
crear (cabezaLista)
cabezaLista↑
↑ .siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
NIL
La primera invocación ha logrado insertar, recursivamente, el nuevo elemento por lo
que puede devolver el control al programa principal.
3
Lección 11 – Estructuras dinámicas lineales (ii)
Inserción en una lista ordenada ascendentemente (de forma recursiva)
acción insertarOrdenado (cabezaLista ∈ puntero a nodo, dato ∈ entero)
variables
nuevo ∈ puntero a nodo
inicio
si cabezaLista ≠ NIL entonces
si cabezaLista↑.numero ≥ dato entonces
crear (nuevo)
nuevo↑.siguiente ß cabezaLista↑.siguiente
cabezaLista↑.siguiente ß nuevo
nuevo↑.numero ß cabezaLista↑.numero
cabezaLista↑.numero ß dato
si no
llamar insertarOrdenado (cabezaLista↑.siguiente,dato)
fin si
si no
crear (cabezaLista)
cabezaLista↑.siguiente ß NIL
cabezaLista↑.numero ß dato
fin si
fin acción
Eliminar un elemento de una lista simplemente enlazada (de forma recursiva)
El algoritmo básico de eliminación simplemente debe recorrer la lista hasta encontrar el dato a eliminar, enlazar
la lista de forma adecuada y destruir el nodo sobrante.
acción eliminarElemento (cabezaLista ∈ puntero a nodo, dato ∈ entero)
variables
cursor ∈ puntero a nodo
inicio
si cabezaLista ≠ NIL entonces
si cabezaLista↑.numero = dato entonces
si cabezaLista↑.siguiente ≠ NIL entonces
cursor ß cabezaLista↑.siguiente
cabezaLista↑.numero ß cursor↑.numero
cabezaLista↑.siguiente ß cursor↑.siguiente
destruir (cursor)
si no
destruir (cabezaLista)
fin si
si no
si cabezaLista↑.siguiente ≠ NIL entonces
si cabezaLista↑.siguiente↑.numero = dato entonces
cursor ß cabezaLista↑.siguiente
cabezaLista↑.siguiente ß cursor↑.siguiente
destruir (cursor)
si no
llamar eliminarElemento (cabezaLista↑.siguiente,dato)
fin si
fin si
fin si
fin si
fin acción
Vaciado de una lista simplemente enlazada (de forma recursiva)
acción vaciarLista (cabezaLista ∈ puntero a nodo)
Para vaciar una lista simplemente enlazada de
inicio
forma recursiva basta con vaciar el resto de la lista, si es
si cabezaLista↑.siguiente ≠ NIL entonces
que existe, y después eliminar el nodo que se encuentra
llamar vaciarLista (cabezaLista↑.siguiente)
en la cabeza.
fin si
destruir(cabezaLista)
fin acción
A continuación se muestra de forma gráfica el vaciado recursivo de la lista que contiene los números 1, 2 y 3.
llamar vaciarLista (lista)
4
Lección 11 – Estructuras dinámicas lineales (ii)
lista
inicio
si cabezaLista↑
↑ .siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑
↑ .siguiente)
fin si
destruir(cabezaLista)
fin acción
1
2
En la primera invocación se debe vaciar el resto de la lista (2 y 3).
3
lista
3
2
lista
NIL
En la segunda invocación se debe vaciar el resto de la lista (3).
inicio
si cabezaLista↑.siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑.siguiente)
fin si
destruir(cabezaLista)
fin acción
Una vez destruida la cabeza de la lista en la tercera invocación se retorna a la
segunda.
inicio
si cabezaLista↑.siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑.siguiente)
fin si
destruir(cabezaLista)
fin acción
En la segunda invocación ya se ha vaciado el resto de la lista, por lo que se puede
destruir la cabeza (2).
inicio
si cabezaLista↑.siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑.siguiente)
fin si
destruir(cabezaLista)
fin acción
lista
lista
NIL
En la tercera invocación no hay resto de lista con lo cual se destruye la cabeza (3).
inicio
si cabezaLista↑.siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑.siguiente)
fin si
destruir(cabezaLista)
fin acción
lista
lista
inicio
si cabezaLista↑
↑ .siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑
↑ .siguiente)
fin si
destruir(cabezaLista)
fin acción
2
3
lista
NIL
1
Destruida la cabeza de la lista de la segunda invocación se puede retornar a la
primera.
inicio
si cabezaLista↑.siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑.siguiente)
fin si
destruir(cabezaLista)
fin acción
Al haberse vaciado el resto de la lista en la primera invocación se elimina la cabeza.
inicio
si cabezaLista↑.siguiente ≠ NIL entonces
llamar vaciarLista (cabezaLista↑.siguiente)
fin si
destruir(cabezaLista)
fin acción
La primera invocación ha destruido todos los elementos de la cola a partir del segundo
y, después, la cabeza. Una vez hecho esto retorna al programa principal.
5
Descargar