用函数式编程语言实现数据结构,是非常返璞归真的一件事情。
树的定义
用参数化类型定义二叉树。
data Tree a = Empty | Node (Tree a) a (Tree a) deriving (Show)
多叉树可以用左孩子右兄弟来表示。在此基础上,森林可以用“所有的树有共同的根节点”表示成一棵多叉树,从而用左孩子右兄弟表示成二叉树。(或者二叉树森林用“左第一右其余”表示成二叉树。)
树的性质(类型类)
顺手给出树遵从的类型类。下文除了toList
之外,与本节无关,阅读时可以跳过。
instance Foldable Tree where
-- foldMap :: Monoid m => (a -> m) -> Tree a -> m
foldMap f Empty = mempty
foldMap f (Node l k r) = foldMap f l `mappend` f k `mappend` foldMap f r
instance Functor Tree where
-- fmap :: (a -> b) -> Tree a -> Tree b
fmap f Empty = Empty
fmap f (Node l k r) = Node (fmap f l) (f k) (fmap f r)
instance Traversable Tree where
-- traverse :: (Traversable t, Applicative f) => (a -> f b) -> Tree a -> f (Tree b)
traverse f Empty = pure Empty
traverse f (Node l k r) = Node <$> traverse f l <*> f k <*> traverse f r
前中后序遍历
简单递归即可。
import Data.Foldable -- toList
preOrderTraversal :: Tree a -> [a]
preOrderTraversal Empty = []
preOrderTraversal (Node l k r) =
k:(preOrderTraversal l) ++ preOrderTraversal r
inOrderTraversal :: Tree a -> [a]
inOrderTraversal = toList
postOrderTraversal :: Tree a -> [a]
postOrderTraversal Empty = []
postOrderTraversal (Node l k r) =
postOrderTraversal l ++ postOrderTraversal r ++ [k]
注意中序遍历对库函数Data.Foldable.toList
的使用。因为data Tree
的结构限制,三种遍历顺序中总是只有一种能使用库函数,剩下两种结构需要重写一遍。
层序遍历
思路是bfs:使用一个队列(这里用列表模拟),一开始队列中只有原树,每次取出队首的树,将其根节点加入输出列表,将其左右子树加入队列末尾。队列为空时停止。
我们使用一个尾递归来描述每一步的操作。尾递归可以保存状态,最适合这种一步一步改变状态的场景。(进一步地,如果是一个会失败的操作,可以用state monad)
levelOrderTraversal :: Tree a -> [a]
levelOrderTraversal tree = reverse $ step ([], [tree])
where
step (result, trees) = case trees of
[] -> result
Empty:ts -> step (result, ts)
(Node l k r):ts -> step (k:result, ts++[l, r])
注意到,输出列表在尾递归过程中是倒序存储的,以便使用默认构造函数(:)
提升效率。在最终返回时reverse
成正序。
测试
testTree = Node (Node (Node Empty 4 Empty) 2 Empty) 1 (Node Empty 10 (Node Empty 15 Empty))
testList = map ($ testTree) [preOrderTraversal, inOrderTraversal, postOrderTraversal, levelOrderTraversal]
-- testList == [[1,2,4,10,15],[4,2,1,10,15],[4,2,15,10,1],[1,2,10,4,15]]
目前CSDN不支持Haskell的高亮。在目前支持的高亮样式中,似乎使用swift的高亮效果比较好🤔还是知乎和Gitee比较香orz