Tema 19: El tipo abstracto de los árboles binarios de búsqueda
Índice
1. Especificación del TAD de los árboles binarios de búsqueda
1.1. Signatura del TAD de los árboles binarios de búsqueda
1.1.1. Descripción de los árboles binarios de búsqueda
- Un árbol binario de búsqueda (ABB) es un árbol binario tal que el valor de cada nodo es mayor que los valores de su subárbol izquierdo y es menor que los valores de su subárbol derecho y, además, ambos subárboles son árboles binarios de búsqueda.
Por ejemplo, al almacenar los valores de [2,3,4,5,6,8,9] en un ABB se puede obtener los siguientes ABB:
5 5 / \ / \ 2 6 3 8 \ \ / \ / \ 4 8 2 4 6 9 / \ 3 9
- El objetivo principal de los ABB es reducir el tiempo de acceso a los valores.
1.1.2. Signatura del TAD de los árboles binarios de búsqueda
Signatura
vacio :: ABB inserta :: (Ord a, Show a) => a -> ABB a -> ABB a elimina :: (Ord a, Show a) => a -> ABB a -> ABB a crea :: (Ord a, Show a) => [a] -> ABB a menor :: Ord a => ABB a -> a elementos :: (Ord a, Show a) => ABB a -> [a] pertenece :: (Ord a, Show a) => a -> ABB a -> Bool valido :: (Ord a, Show a) => ABB a -> Bool
- Descripción de las operaciones:
vacio
es el árbol binario de búsqueda vacío.(pertenece v a)
se verifica siv
es el valor de algún nodo del árbol binario de búsquedaa
.(inserta v a)
es el árbol obtenido añadiendo el valorv
al árbol binario de búsquedaa
, si no es uno de sus valores.(crea vs)
es el árbol binario de búsqueda cuyos valores sonvs
.(elementos a)
es la lista de los valores de los nodos del árbol binario de búsquedaa
en el recorrido inorden.(elimina v a)
es el árbol binario de búsqueda obtenido eliminando el valorv
del árbol binario de búsquedaa
.(menor a)
es el mínimo valor del árbol binario de búsquedaa
.(valido a)
se verifica sia
es un árbol binario de búsqueda correcto.
1.2. Propiedades del TAD de los árboles binarios de búsqueda
valido vacio
valido (inserta v a)
inserta x a /= vacio
pertenece x (inserta x a)
not (pertenece x vacio)
pertenece y (inserta x a) == (x == y) || pertenece y a
valido (elimina v a)
elimina x (inserta x a) == elimina x a
valido (crea xs)
elementos (crea xs) == sort (nub xs)
pertenece v a == elem v (elementos a)
∀x ∈ elementos a (menor a ≤ x)
2. Implementación del TAD de los árboles binarios de búsqueda
2.1. Los ABB como tipo de dato algebraico
Cabecera del módulo:
module Tema_19.ArbolBin (ABB, vacio, -- ABB inserta, -- (Ord a, Show a) => a -> ABB a -> ABB a elimina, -- (Ord a, Show a) => a -> ABB a -> ABB a crea, -- (Ord a, Show a) => [a] -> ABB a crea', -- (Ord a, Show a) => [a] -> ABB a menor, -- Ord a => ABB a -> a elementos, -- (Ord a, Show a) => ABB a -> [a] pertenece, -- (Ord a, Show a) => a -> ABB a -> Bool valido, -- (Ord a, Show a) => ABB a -> Bool escribeABB, -- Show a => ABB a -> String ejemploABB -- Int -> ABB Int ) where
Los ABB como tipo de dato algebraico.
data Ord a => ABB a = Vacio | Nodo a (ABB a) (ABB a) deriving (Show, Eq)
(escribeABB a)
es la cadena correspondiente al ABBa
. Por ejemplo,λ> escribeABB (crea (reverse [5,2,6,4,8,3,9])) " (5 (2 - (4 (3 - -) -)) (6 - (8 - (9 - -))))" λ> escribeABB (foldr inserta vacio (reverse [5,2,4,3,8,6,7,10,9,11])) " (5 (2 - (4 (3 - -) -)) (8 (6 - (7 - -)) (10 (9 - -) (11 - -))))"
Su definición es
escribeABB :: Show a => ABB a -> String escribeABB Vacio = " -" escribeABB (Nodo x i d) = " (" ++ show x ++ escribeABB i ++ escribeABB d ++ ")"
Procedimiento de escritura de ABB.
instance Show a => Show (ABB a) where show = escribeABB
Ejemplos de ABB
λ> ejemploABB 1 (5 (2 - (4 (3 - -) -)) (6 - (8 - (9 - -)))) λ> ejemploABB 2 (5 (2 - (4 (3 - -) -)) (8 (6 - (7 - -)) (10 (9 - -) (11 - -))))
Su definición es
ejemploABB :: Int -> ABB Int ejemploABB 1 = crea (reverse [5,2,6,4,8,3,9]) ejemploABB 2 = foldr inserta vacio (reverse [5,2,4,3,8,6,7,10,9,11]) ejemploABB _ = error "No definido"
vacio
es el ABB vacío. Por ejemplo,λ> vacio -
Su definición es
vacio :: ABB a vacio = Vacio
(pertenece v a)
se verifica siv
es el valor de algún nodo del ABBa
. Por ejemplo,pertenece 3 (ejemploABB 1) == True pertenece 7 (ejemploABB 1) == False
Su definición es
pertenece :: (Ord a, Show a) => a -> ABB a -> Bool pertenece v' Vacio = False pertenece v' (Nodo v i d) | v' == v = True | v' < v = pertenece v' i | v' > v = pertenece v' d
(inserta v a)
es el árbol obtenido añadiendo el valorv
al ABBa
, si no es uno de sus valores. Por ejemplo,λ> inserta 7 (ejemploABB 1) (5 (2 - (4 (3 - -) -)) (6 - (8 (7 - -) (9 - -))))
Su definición es
inserta :: (Ord a, Show a) => a -> ABB a -> ABB a inserta v' Vacio = Nodo v' Vacio Vacio inserta v' (Nodo v i d) | v' == v = Nodo v i d | v' < v = Nodo v (inserta v' i) d | otherwise = Nodo v i (inserta v' d)
(crea vs)
es el ABB cuyos valores sonvs
. Por ejemploλ> crea [3,7,2] (2 - (7 (3 - -) -))
Su definición es
crea :: (Ord a, Show a) => [a] -> ABB a crea = foldr inserta Vacio
(crea' vs)
es el ABB de menor profundidad cuyos valores son los de la lista ordenadavs
. Por ejemplo,λ> crea' [2,3,7] (~3 (2 ~-~ -) (7 -~ ~-)) ~~~
Su definición es
crea' :: (Ord a, Show a) => [a] -> ABB a crea' [] = Vacio crea' vs = Nodo x (crea' l1) (crea' l2) where n = length vs `div` 2 l1 = take n vs (x:l2) = drop n vs
(elementos a)
es la lista de los valores de los nodos del ABBa
en el recorrido inorden. Por ejemplo,elementos (ejemploABB 1) == [2,3,4,5,6,8,9] elementos (ejemploABB 2) == [2,3,4,5,6,7,8,9,10,11]
Su definición es
elementos :: (Ord a, Show a) => ABB a -> [a] elementos Vacio = [] elementos (Nodo v i d) = elementos i ++ [v] ++ elementos d
(elimina v a)
el ABB obtenido eliminando el valorv
del ABBa
. Por ejemplo,λ> elimina 3 (ejemploABB 1) λ> (ejemploABB 1) (5 (2 - (4 (3 - -) -)) (6 - (8 - (9 - -)))) λ> elimina 3 (ejemploABB 1) (5 (2 - (4 - -)) (6 - (8 - (9 - -)))) λ> elimina 2 (ejemploABB 1) (5 (4 (3 - -) -) (6 - (8 - (9 - -)))) λ> elimina 5 (ejemploABB 1) (6 (2 - (4 (3 - -) -)) (8 - (9 - -))) λ> elimina 7 (ejemploABB 1) (5 (2 - (4 (3 - -) -)) (6 - (8 - (9 - -))))
Su definición es
elimina :: (Ord a, Show a) => a -> ABB a -> ABB a elimina _ Vacio = Vacio elimina v' (Nodo v i Vacio) | v'==v = i elimina v' (Nodo v Vacio d) | v'==v = d elimina v' (Nodo v i d) | v' < v = Nodo v (elimina v' i) d | v' > v = Nodo v i (elimina v' d) | otherwise = Nodo k i (elimina k d) where k = menor d
(menor a)
es el mínimo valor del ABBa
. Por ejemplo,menor (ejemploABB 1) == 2
Su definición es
menor :: Ord a => ABB a -> a menor (Nodo v Vacio _) = v menor (Nodo _ i _) = menor i
(menorTodos v a)
se verifica siv
es menor que todos los elementos del ABBa
.menorTodos :: (Ord a, Show a) => a -> ABB a -> Bool menorTodos v Vacio = True menorTodos v a = v < minimum (elementos a)
(mayorTodos v a)
se verifica siv
es mayor que todos los elementos del ABBa
.mayorTodos :: (Ord a, Show a) => a -> ABB a -> Bool mayorTodos v Vacio = True mayorTodos v a = v > maximum (elementos a)
(valido a)
se verifica sia
es un ABB correcto. Por ejemplo,valido (ejemploABB 1) == True
Su definición es
valido :: (Ord a, Show a) => ABB a -> Bool valido Vacio = True valido (Nodo v i d) = mayorTodos v i && menorTodos v d && valido i && valido d
3. Comprobación de la implementación con QuickCheck
3.1. Pragma y librerías auxiliares
Al principio del fichero se añade
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
Importación de la implementación de los ABB que se desea verificar.
import Tema_19.ArbolBin -- import I1M.ArbolBin
Importación de librerías auxiliares.
import Data.List (nub, sort) import Test.QuickCheck
3.2. Generador de árboles binarios de búsqueda
genABB
es un generador de árboles binarios de búsqueda. Por ejemplo,λ> sample genABB - (1 (-1 - -) -) (1 - -) (-1 (-3 - -) (1 - (4 - -)))
Su definición es
genABB :: Gen (ABB Int) genABB = do xs <- listOf arbitrary return (foldr inserta vacio xs) instance Arbitrary (ABB Int) where arbitrary = genABB
Propiedad. Todo los elementos generados por genABB son árboles binarios de búsqueda.
prop_genABB_correcto :: ABB Int -> Bool prop_genABB_correcto = valido
listaOrdenada
es un generador de listas ordenadas de números enteros. Por ejemplo,λ> sample listaOrdenada [1] [-2,-1,0]
Su definición es
listaOrdenada :: Gen [Int] listaOrdenada = frequency [(1,return []), (4,do xs <- orderedList n <- arbitrary return (nub ((case xs of [] -> n x:_ -> n `min` x) :xs)))]
(ordenada xs)
se verifica sixs
es una lista ordenada creciente. Por ejemplo,ordenada [3,5,9] == True ordenada [3,9,5] == False
Su definición es
ordenada :: [Int] -> Bool ordenada xs = and [x < y | (x,y) <- zip xs (tail xs)]
Propiedad. El generador
listaOrdenada
produce listas ordenadas.prop_listaOrdenada_correcta :: [Int] -> Property prop_listaOrdenada_correcta xs = forAll listaOrdenada ordenada
3.3. Especificación de las propiedades de los árboles de búsqueda
vacio
es un ABB.prop_vacio_es_ABB :: Bool prop_vacio_es_ABB = valido (vacio :: ABB Int)
Si
a
es un ABB, entonces(inserta v a)
también lo es.prop_inserta_es_valida :: Int -> ABB Int -> Bool prop_inserta_es_valida v a = valido (inserta v a)
El árbol que resulta de añadir un elemento a un ABB es no vacío.
prop_inserta_es_no_vacio :: Int -> ABB Int -> Bool prop_inserta_es_no_vacio x a = inserta x a /= vacio
Para todo
x
ya
,x
es un elemento de(inserta x a)
.prop_elemento_de_inserta :: Int -> ABB Int -> Bool prop_elemento_de_inserta x a = pertenece x (inserta x a)
En un árbol vacio no hay ningún elemento.
prop_vacio_sin_elementos :: Int -> Bool prop_vacio_sin_elementos x = not (pertenece x vacio)
Los elementos de
(inserta x a)
sonx
y los elementos dea
.prop_elementos_de_inserta :: Int -> Int -> ABB Int -> Bool prop_elementos_de_inserta x y a = pertenece y (inserta x a) == (x == y) || pertenece y a
Si
a
es un ABB, entonces(elimina v a)
también lo es.prop_elimina_es_valida :: Int -> ABB Int -> Bool prop_elimina_es_valida v a = valido (elimina v a)
El resultado de eliminar el elemento
x
en(inserta x a)
es(elimina x a)
.prop_elimina_agrega :: Int -> ABB Int -> Bool prop_elimina_agrega x a = elimina (inserta x a) == elimina x a
(crea xs)
es un ABB.prop_crea_es_valida :: [Int] -> Bool prop_crea_es_valida xs = valido (crea xs)
Para todas las listas ordenadas
xs
, se tiene que(crea' xs)
es un ABB.prop_crea'_es_valida :: [Int] -> Property prop_crea'_es_valida xs = forAll listaOrdenada (valido . crea')
(elementos (crea xs))
es igual a la listaxs
ordenada y sin repeticiones.prop_elementos_crea :: [Int] -> Bool prop_elementos_crea xs = elementos (crea xs) == sort (nub xs)
Si
ys
es una lista ordenada sin repeticiones, entonces(elementos (crea' ys))
es igualys
.prop_elementos_crea' :: [Int] -> Bool prop_elementos_crea' xs = elementos (crea' ys) == ys where ys = sort (nub xs)
Un elemento pertenece a
(elementos a)
syss es un valor dea
.prop_en_elementos :: Int -> ABB Int -> Bool prop_en_elementos v a = pertenece v a == elem v (elementos a)
(menor a)
es menor o igual que todos los elementos de ABBa
.prop_menoresMinimo ::Int -> ABB Int -> Bool prop_menoresMinimo v a = and [menor a <= v | v <- elementos a]
3.4. Comprobación de las propiedades
Para verificar todas las propiedades se escribe
return [] verificaABB :: IO Bool verificaABB = $quickCheckAll
Comprobación de las propiedades de los ABB
λ> verificaABB === prop_genABB_correcto from ArbolBinPropiedades.hs:44 === +++ OK, passed 100 tests. === prop_listaOrdenada_correcta from ArbolBinPropiedades.hs:83 === +++ OK, passed 100 tests. === prop_orderedList_correcta from ArbolBinPropiedades.hs:93 === +++ OK, passed 100 tests. === prop_vacio_es_ABB from ArbolBinPropiedades.hs:109 === +++ OK, passed 1 test. === prop_inserta_es_valida from ArbolBinPropiedades.hs:121 === +++ OK, passed 100 tests. === prop_inserta_es_no_vacio from ArbolBinPropiedades.hs:131 === +++ OK, passed 100 tests. === prop_elemento_de_inserta from ArbolBinPropiedades.hs:140 === +++ OK, passed 100 tests. === prop_vacio_sin_elementos from ArbolBinPropiedades.hs:152 === +++ OK, passed 100 tests. === prop_elementos_de_inserta from ArbolBinPropiedades.hs:162 === +++ OK, passed 100 tests. === prop_elimina_es_valida from ArbolBinPropiedades.hs:174 === +++ OK, passed 100 tests. === prop_elimina_agrega from ArbolBinPropiedades.hs:184 === +++ OK, passed 100 tests. === prop_crea_es_valida from ArbolBinPropiedades.hs:196 === +++ OK, passed 100 tests. === prop_crea'_es_valida from ArbolBinPropiedades.hs:209 === +++ OK, passed 100 tests. === prop_elementos_crea from ArbolBinPropiedades.hs:222 === +++ OK, passed 100 tests. === prop_elementos_crea' from ArbolBinPropiedades.hs:232 === +++ OK, passed 100 tests. === prop_en_elementos from ArbolBinPropiedades.hs:242 === +++ OK, passed 100 tests. === prop_menoresMinimo from ArbolBinPropiedades.hs:255 === +++ OK, passed 100 tests. True
4. Material complementario
El código del tema se encuentra en los siguientes enlaces
- Implementación de los árboles binarios de búsqueda como tipo de dato algebraico.
- ArbolBinPropiedades: Propiedades del TAD de los árboles binarios de búsqueda.
Este tema también se encuentra en los siguientes formatos:
- Como transparencias en PDF.
- Como libro interactivo en IHaskell sobre Jupyter.
- Como vídeo de clase.
5. Bibliografía
- F. Rabhi y G. Lapalme. Algorithms: A functional programming approach.
- Cap. 5.7 Binary search trees.