微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将索引号添加到树的节点

如何解决如何将索引号添加到树的节点

假设我有这个数据类型来表示一棵树(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)):


GraphViz:
digraph mytree {
forcelabels=true;
node [shape=circle];
"+"->"";
"+"->"sqrt";
node [shape=rect];
""->5;
""->6;
"sqrt"->3;
"+" [xlabel="0"];
"" [xlabel="1"];
"5" [xlabel="2"];
"6" [xlabel="3"];
"sqrt" [xlabel="4"];
"3" [xlabel="5"];
}
dot -Tpng tree.dot -O

我想将这棵树转换成另一种称为“索引树”的树数据结构,其中包含每个节点的深度优先(或呼吸优先)索引。在上图中,我用深度优先索引标记了所有节点。

这是索引树的数据类型:

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,[ITerminal (3,5); ITerminal 4,6)]);
                    IFunction (2,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 ("+",[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". *)

解决方法

这不是一个完整的答案,我认为同时要求深度优先和广度优先有点太宽泛了,因为我认为没有什么可以概括的,但我希望它至少能让你更进一步,也许会激发一些想法。

我认为您遇到的问题源于您当前的代码过于笼统。您基本上是将树转换为一个列表,然后索引到该列表中,效率很低。然后你不能将列表转换回树,因为你已经把这些信息扔掉了。

深度优先遍历确实非常简单,并且很自然地适用于递归实现。如果我们暂时忽略索引,quantity 函数就是这样:

tree -> indexed_tree

然后我们只需要填写索引。这是通过在递归时向上传递递增索引来实现的,并在向下的过程中返回节点计数以及构造的子树,以便我们知道索引递增多少:

let indexed tree =
  let rec loop = function
    | Terminal value -> ITerminal (0,value)
    | Function (name,children) -> IFunction (0,name,loop_children children)
  and loop_children = function
    | [] -> []
    | child :: rest -> loop child :: loop_children rest
  in loop tree

每次调用也可以只返回最后一个索引而不是计数,但我认为遵循两个独立的概念而不是重载一个概念更简单。

不幸的是,广度优先转换要复杂得多,因为我们不能轻易地构建一个新的广度优先树。但我们也不应该需要。我认为不是跟踪总计数,而是通过传递迄今为止看到的级别的一堆偏移量来跟踪每个级别的累积计数或偏移量,然后使用它来计算当前正在构建的节点的索引。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。