【发布时间】:2026-01-30 18:15:01
【问题描述】:
假设我有这种数据类型来表示一棵树(rose tree):
type tree =
| Function of string * tree list
| Terminal of int
例如:
Function ("+", [Function ("*", [Terminal 5; Terminal 6]);
Function ("sqrt", [Terminal 3])])
表示如下树((5 * 6) + sqrt(3)):
我想将这棵树转换为另一个树数据结构,称为“索引树”,其中包含每个节点的深度优先(或呼吸优先)索引。在上图中,我用深度优先索引标记了所有节点。
这是索引树的数据类型:
type index = int
type indexed_tree =
| IFunction of index * string * indexed_tree list
| ITerminal of index * int
这表示上图的索引树(深度优先):
IFunction (0, "+", [IFunction (1, "*", [ITerminal (2, 5); ITerminal (3, 6)]);
IFunction (4, "sqrt", [ITerminal (5, 3)])])
这表示上图的索引树(广度优先):
IFunction (0, "+", [IFunction (1, "*", [ITerminal (3, 5); ITerminal 4, 6)]);
IFunction (2, "sqrt", [ITerminal (5, 3)])])
现在的问题是:如何定义函数tree -> indexed_tree?
我尝试采用 DFS 和 BFS 技术来保持堆栈,但我很快意识到这个问题是完全不同的。 DFS 和 BFS 只搜索一项,它们可以忽略树的其余部分。在这里,我试图用它们的索引号标记树的节点。我该怎么做?
编辑
下面是我在指定索引处获取子树的实现(深度优先索引和广度优先索引都已实现)。我无法看到如何调整此实现以将给定树转换为索引树。我尝试使用counter(参见下面的实现),但复杂的是深度优先遍历必须回溯,并且我不知道在回溯时如何传递计数器。
(* Helper function for subtree_index_dfs and subtree_index_bfs.
* join_func should either be prepend (for depth-first), or postpend
* (for breadth-first). *)
let subtree_index tree index join_func =
let node_children = function
| Terminal _ -> []
| Function (_, children) -> children in
let rec loop counter stack =
match stack with
| [] -> failwith "Index out of bounds"
| (hd::_) when counter = index -> hd
| (hd::tl) -> loop (counter + 1) (join_func (node_children hd) tl)
in
loop 0 [tree]
(* Get the subtree rooted at the specified index.
* Index starts at 0 at the root of the tree and is ordered depth-first. *)
let subtree_index_dfs tree index =
let prepend a b =
a@b
in
subtree_index tree index prepend
(* Get the subtree rooted at the specified index.
* Index starts at 0 at the root of the tree and is ordered breadth-first. *)
let subtree_index_bfs tree index =
let append a b =
b@a
in
subtree_index tree index append
(* Misc. *)
let rec string_of_tree t =
match t with
| Terminal i -> string_of_int i
| Function (sym, children) ->
let children_str = List.map (fun child -> string_of_tree child) children
in
"(" ^ sym ^ " " ^ String.concat " " children_str ^ ")"
let print_tree t =
print_endline (string_of_tree t)
示例用法:
let () =
let t1 = Function ("+", [Function ("*", [Terminal 5; Terminal 6]);
Function ("sqrt", [Terminal 3])])
in
print_tree (subtree_index_dfs t1 0); (* (+ ( * 5 6) (sqrt 3)) *)
print_tree (subtree_index_dfs t1 1); (* ( * 5 6) *)
print_tree (subtree_index_dfs t1 2); (* 5 *)
print_tree (subtree_index_dfs t1 3); (* 6 *)
print_tree (subtree_index_dfs t1 4); (* (sqrt 3) *)
print_tree (subtree_index_dfs t1 5); (* 3 *)
print_tree (subtree_index_dfs t1 6); (* Exception: Failure "Index out of bounds". *)
【问题讨论】:
-
这并没有什么不同。 DFS 和 BFS 可以重新表述为遍历算法,用于转换树而不是搜索任何内容。
-
对于家庭作业问题,您应该包含一些您自己的代码,以证明您实际上已经自己进行了尝试,并指出您到底在努力解决什么问题。否则很难给出不只是提供解决方案的答案。正如代词先生所指出的,调整 DFS 和 BFS 算法来进行转换而不是搜索应该非常简单。获得正确的索引比较棘手,但有一个有效的转换函数是必要的第一步,所以我建议你从那开始。
-
@glennsl 这不是作业问题;我提出了问题并自己绘制了图表。问题是我完全被卡住了。我什至不知道如何绘制函数的骨架。我之前已经解决了一些相关的问题。例如,我使用 continuation-passing 样式解决了How do I get a subtree by index?。
-
即使不是家庭作业,有一些代码作为答案的基础仍然非常有帮助,可以稍微缩小问题范围,也许可以说明是什么让你卡住了。即使只是可以调整的工作 DFS 和/或 BFS 功能。我当然可以只写一个解决方案,但我认为这不是一个非常令人满意的答案,写这样的答案也不是特别令人满意。
-
CPS 似乎是正确的选择。我曾经向very similar problem 发布了一个方案(和伪代码)answer,其中还有两个答案,一个在 Scala 中,另一个在 JS 中。
标签: algorithm tree ocaml graph-algorithm