Ir al contenido principal

Renombramiento de un árbol


Los árboles binarios se pueden representar mediante el tipo Arbol definido por

data Arbol a = H a
             | N a (Arbol a) (Arbol a)
  deriving (Show, Eq, Foldable, Functor)

Por ejemplo, el árbol

      "C"
      / \
     /   \
    /     \
  "B"     "A"
  / \     / \
"A" "B" "B" "C"

se puede definir por

ej1 :: Arbol String
ej1 = N "C" (N "B" (H "A") (H "B")) (N "A" (H "B") (H "C"))

Definir la función

renombraArbol :: Arbol t -> Arbol Int

tal que (renombraArbol a) es el árbol obtenido sustituyendo el valor de los nodos y hojas de a por números tales que tengan el mismo valor si y sólo si coincide su contenido. Por ejemplo,

λ> renombraArbol ej1
N 2 (N 1 (H 0) (H 1)) (N 0 (H 1) (H 2))

Gráficamente,

      2
     / \
    /   \
   /     \
  1       0
 / \     / \
0   1   1   2

Soluciones

{-# LANGUAGE DeriveFoldable, DeriveFunctor #-}
{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}

module Renombra_arbol where

import Data.Map.Strict (Map, (!), fromList)
import Data.List (nub, sort, elemIndex)
import Data.Maybe (fromJust)
import Data.Foldable (toList)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

data Arbol a = H a
             | N a (Arbol a) (Arbol a)
  deriving (Show, Eq, Foldable, Functor)

ej1 :: Arbol String
ej1 = N "C" (N "B" (H "A") (H "B")) (N "A" (H "B") (H "C"))

-- 1ª solución
-- ===========

renombraArbol1 :: Ord t => Arbol t -> Arbol Int
renombraArbol1 a = aux a
  where ys            = valores a
        aux (H x)     = H (posicion x ys)
        aux (N x i d) = N (posicion x ys) (aux i) (aux d)

-- (valores a) es la lista de los valores en los nodos y las hojas del
-- árbol a. Por ejemplo,
--    valores ej1  ==  ["A","B","C"]
valores :: Ord a => Arbol a -> [a]
valores a = sort (nub (aux a))
    where aux (H x)     = [x]
          aux (N x i d) = x : (aux i ++ aux d)

-- (posicion x ys) es la posición de x en ys. Por ejemplo.
--    posicion 7 [5,3,7,8]  ==  2
posicion :: Ord a => a -> [a] -> Int
posicion x ys =
  head [n | (y,n) <- zip ys [0..], y == x]

-- 2ª solución
-- ===========

renombraArbol2 :: Ord t => Arbol t -> Arbol Int
renombraArbol2 a = aux a
  where ys = valores a
        aux (H x)     = H (posicion2 x ys)
        aux (N x i d) = N (posicion2 x ys) (aux i) (aux d)

posicion2 :: Ord a => a -> [a] -> Int
posicion2 x ys =
  fromJust (elemIndex x ys)

-- 3ª solución
-- ===========

renombraArbol3 :: Ord t => Arbol t -> Arbol Int
renombraArbol3 a = aux a
  where
    ys = sort (nub (toList a))
    aux (H x)     = H (fromJust (elemIndex x ys))
    aux (N x i d) = N (fromJust (elemIndex x ys)) (aux i) (aux d)

-- 4ª solución
-- ===========

renombraArbol4 :: Ord t => Arbol t -> Arbol Int
renombraArbol4 a = fmap convertir a
  where
    indice = zip (sort (nub (toList a))) [0..]
    convertir x = fromJust (lookup x indice)

-- 5ª solución
-- ===========

-- (dicValores a) es el diccionario de los valores en los nodos y las
-- hojas del árbol a. Por ejemplo,
--    λ> dicValores ej1
--    fromList [("A",0),("B",1),("C",2)]
dicValores :: Ord a => Arbol a -> Map a Int
dicValores a =
  fromList $ zip (valores a) [0..]

renombraArbol5 :: Ord t => Arbol t -> Arbol Int
renombraArbol5 a =
  repl a
  where
    dic = dicValores a
    repl (H x)     = H (dic ! x)
    repl (N x i d) = N (dic ! x) (repl i) (repl d)

-- Verificación
-- ============

verifica :: IO ()
verifica = hspec spec

specG :: (Arbol String -> Arbol Int) -> Spec
specG renombraArbol = do
  it "e1" $
    show (renombraArbol ej1) `shouldBe`
    "N 2 (N 1 (H 0) (H 1)) (N 0 (H 1) (H 2))"

spec :: Spec
spec = do
  describe "def. 1" $ specG renombraArbol1
  describe "def. 2" $ specG renombraArbol2
  describe "def. 3" $ specG renombraArbol3
  describe "def. 4" $ specG renombraArbol4
  describe "def. 5" $ specG renombraArbol5

-- La verificación es
--    λ> verifica
--    5 examples, 0 failures

-- Equivalencia de las definiciones
-- ================================

-- (genArbol n) genera un árbol aleatorio de orden n. Por ejemplo,
--    λ> generate (genArbol 3 :: Gen (Arbol Int))
--    N (-13) (H 1) (N 30 (H (-10)) (H (-1)))
--    λ> generate (genArbol 3 :: Gen (Arbol Int))
--    N (-3) (H (-29)) (N (-17) (H 8) (H 28))
genArbol :: Arbitrary a => Int -> Gen (Arbol a)
genArbol 0 = H <$> arbitrary
genArbol n = frequency
  [ (1, H <$> arbitrary)
  , (3, N <$> arbitrary <*> sub <*> sub)]
  where
    sub = genArbol (n `div` 2)

-- Arbol es subclase de Arbitraria
instance Arbitrary a => Arbitrary (Arbol a) where
  arbitrary = sized genArbol

-- La propiedad es
prop_renombraArbol :: Arbol Int -> Bool
prop_renombraArbol a =
  all (== renombraArbol1 a)
      [renombraArbol2 a,
       renombraArbol3 a,
       renombraArbol4 a,
       renombraArbol5 a]

-- La comprobación es
--    λ> quickCheck prop_renombraArbol
--    +++ OK, passed 100 tests.

-- Comparación de eficiencia
-- =========================

-- (arbol n) es el árbol completo de profundidad n. Por ejemplo,
--    λ> arbol 2
--    N 12 (N 11 (H 0) (H 0)) (N 11 (H 0) (H 0))
--    λ> renombraArbol1 (arbol 2)
--    N 2 (N 1 (H 0) (H 0)) (N 1 (H 0) (H 0))
arbol :: Int -> Arbol Int
arbol 0 = H 0
arbol n = N (n+10) (arbol (n-1)) (arbol (n-1))

-- La comparación es
--    λ> length (renombraArbol1 (arbol 20))
--    2097151
--    (2.13 secs, 1,191,782,032 bytes)
--    λ> length (renombraArbol2 (arbol 20))
--    2097151
--    (2.26 secs, 1,191,782,056 bytes)
--    λ> length (renombraArbol3 (arbol 20))
--    2097151
--    (2.16 secs, 1,225,336,576 bytes)
--    λ> length (renombraArbol4 (arbol 20))
--    2097151
--    (1.96 secs, 1,032,398,632 bytes)
--    λ> length (renombraArbol5 (arbol 20))
--    2097151
--    (2.00 secs, 1,191,782,056 bytes)