P70 - 多路树的节点串表示

2026-09-07 00:00    #Haskell   #99题   #多路树  

P70 - 多路树的节点串表示

Construct a multiway tree from a node string

官方模块:Problems.P70 核心函数:stringToMultitreemultitreeToString


← P69 点串表示 | P71 内部路径长度 →


数据结构与编码

多路树不能为空,每个节点保存任意数量的子树。深度优先输出节点字符,每次回退到父节点时输出 ^,包括从根退出。

1data MultiwayTree a = MultiwayTree a [MultiwayTree a]
2  deriving (Eq, Show)
3
4multitreeToString :: MultiwayTree Char -> String
5multitreeToString (MultiwayTree x children) =
6  x : concatMap multitreeToString children ++ "^"

解析

方法一:递归下降

 1stringToMultitree :: String -> Maybe (MultiwayTree Char)
 2stringToMultitree input = do
 3  (tree, rest) <- parseNode input
 4  if null rest then Just tree else Nothing
 5
 6parseNode :: String -> Maybe (MultiwayTree Char, String)
 7parseNode []       = Nothing
 8parseNode ('^':_)  = Nothing
 9parseNode (x:rest) = do
10  (children, remaining) <- parseChildren rest
11  pure (MultiwayTree x children, remaining)
12
13parseChildren :: String -> Maybe ([MultiwayTree Char], String)
14parseChildren [] = Nothing
15parseChildren ('^':rest) = Just ([], rest)
16parseChildren input = do
17  (child, afterChild) <- parseNode input
18  (siblings, rest)    <- parseChildren afterChild
19  pure (child : siblings, rest)

方法二:显式节点栈

 1import Control.Monad (foldM)
 2
 3stringToMultitreeStack :: String -> Maybe (MultiwayTree Char)
 4stringToMultitreeStack input = do
 5  (frames, result) <- foldM step ([], Nothing) input
 6  case (frames, result) of
 7    ([], Just tree) -> Just tree
 8    _               -> Nothing
 9  where
10    step (_, Just _) _ = Nothing
11    step (frames, Nothing) '^' = close frames
12    step (frames, Nothing) value =
13      Just ((value, []) : frames, Nothing)
14
15    close [] = Nothing
16    close [(value, reversedChildren)] =
17      Just ([], Just (MultiwayTree value (reverse reversedChildren)))
18    close ((value, reversedChildren):(parent, siblings):rest) =
19      let child = MultiwayTree value (reverse reversedChildren)
20      in Just ((parent, child : siblings) : rest, Nothing)
21
22multitreeToStringDL :: MultiwayTree Char -> String
23multitreeToStringDL tree = build tree ""
24  where
25    build (MultiwayTree value children) =
26      (value :) . foldr ((.) . build) id children . ('^' :)

普通字符把新节点框架压栈,^ 关闭栈顶节点并把它接到父节点。根节点关闭后不得再出现字符。编码的差异列表版本同样只用函数组合追加内容。

方法对比

方法解析结构特点
递归下降调用栈对应树层级与文法直接对应
显式节点栈框架保存尚未关闭的节点单次 fold,状态清晰

测试

 1>>> multitreeToString multitree5
 2"afg^^c^bd^e^^^"
 3>>> stringToMultitree "afg^^c^bd^e^^^" == Just multitree5
 4True
 5>>> stringToMultitree "a^junk"
 6Nothing
 7>>> stringToMultitreeStack (multitreeToString multitree5) == Just multitree5
 8True
 9>>> multitreeToStringDL multitree5 == multitreeToString multitree5
10True

编码和解析都线性访问字符串,时间复杂度 O(n)O(n)

参考


← P69 点串表示 | P71 内部路径长度 →