用函數式編程語言實現數據結構,是非常返璞歸真的一件事情。
樹的定義
用參數化類型定義二叉樹。
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