Ocaml 带未标记节点的树搜索

Ocaml 带未标记节点的树搜索,ocaml,Ocaml,如果我有一个带整数叶子的未标记树。比如说节点(节点(1,2),节点(2,3))。我想有一个函数,它将这个未标记的树转换成一个标记的树,其中节点数据值是两个叶的总和。所以对于这个示例节点(节点(1,3,2),8,节点(2,5,3))。如果未标记的树可能不平衡,我将如何进行此操作?我尝试过使用每个节点/叶的深度来考虑解决方案,但我不确定该怎么办。 首先,让我们弄清楚什么是标记树和未标记树,以及如何将它们表示为数据结构 如果我有一个带整数叶子的未标记树 没有带整数叶子的未标记树。A可以被标记,即当节点

如果我有一个带整数叶子的未标记树。比如说节点(节点(1,2),节点(2,3))。我想有一个函数,它将这个未标记的树转换成一个标记的树,其中节点数据值是两个叶的总和。所以对于这个示例节点(节点(1,3,2),8,节点(2,5,3))。如果未标记的树可能不平衡,我将如何进行此操作?我尝试过使用每个节点/叶的深度来考虑解决方案,但我不确定该怎么办。

首先,让我们弄清楚什么是标记树和未标记树,以及如何将它们表示为数据结构

如果我有一个带整数叶子的未标记树

没有带整数叶子的未标记树。A可以被标记,即当节点被分配标签时,或者未标记,即当节点未被分配任何标签时

通常,当我们在计算机科学的背景下谈论树时,我们想到的是有标签的树。当我们谈到带标签的树时,我们通常会想到每个节点都有标签的树

仅在叶节点上存储数据通常效率低下,因为我们必须使用具有更多层的树来存储相同的元素集,因为k元树的每一层最多可以存储k^(h-1)个元素,其中
h
是层的深度。如果我们不使用内部节点来存储数据,我们最终会丢弃k^(h-2)节点。尽管如此,这些树仍然存在于生产中,并在一些特殊应用中找到了它们的用途,例如或树

现在,让我们看看这个问题。尽管它们是树的精髓,但在计算机科学的背景下,它们并不是经常使用的,它们只是数学上的兴趣,而不是实用的。未标记的树只对结构进行编码,因为没有与节点关联的内容。未标记树在中被研究,可以用来表示分子结构,通常也可以表示不同种类的自动机和研究结构之间的同构

树数据结构 归纳定义 有两种方法可以对树进行编码。规范的方法是使用归纳数据类型,例如,在OCaml中,未标记的二叉树编码为

type tree = Empty | Tree of tree * tree
乍一看,它又一次显得毫无用处和虚假。但我们可以用另一个简单的归纳定义来类比

type nat = Zero | Succ of nat 
这是众所周知的自然数编码。我们可以很容易地看到,未标记树是Peano数的扩展(例如,我们使用的是二进制
构造函数,而不是一元
Succ
构造函数)。同时,我们可以发现PeaNo编码只不过是一个未标记的列表,例如,考虑列表数据结构的定义:

type 'a list = Empty | List of 'a * 'a list
因此,一个未标记的列表,即一个不能携带任何东西的列表仍然在寻找它的用途,尽管事实上,它可以编码的唯一概念是它的长度——这是自然数的本质——计数的概念

未标记树也是如此,因为它们表示决策的简单思想,并且每个未标记树在决策空间中编码一个不同的元素

虽然将数字存储为列表,将决策存储为树可能看起来像是浪费空间,但它们仍然有自己的用途,每个可构造的数据结构都可以作为证明项。因此,这种结构成为整个自动定理证明领域的研究对象

将树表示为图 同样,树是一种特殊的图,因此树的另一种自然表示形式是图,例如,使用表示图,我们可以得到树的以下定义

type tree = (node * node) list
这个定义提出了一个问题,
节点的类型是什么?事实上,我们现在需要一个单独的类型来承载我们的图形。通常,我们只需要能够区分两个节点,因此任何定义了相等操作的数据类型就足够了。例如,我们可以使用
int
string
。因此,具有三个节点的简单树将表示为一组两个分支
[1,2;1,3]
,其中每个分支是两个节点的有序元组

这种表示法的主要问题是不够紧凑,因为可以构造非树的树,例如,
[1,2;2,1]
是我们的
类型的格式良好的值,但它不是树,因为它有一个循环。因此,为了保持图的树结构,我们应该始终在每次插入时检查是否创建了循环或树的任何其他不变量是否被破坏(例如,插入了交叉边)。另一个问题是,必须为每个新节点提供不同整数的源

因此,树的归纳定义有一个很好的特性——它总是正确的。归纳树的每一个实例都是一棵树,通过构造证明了这一点。此外,在归纳定义中,每个新节点实际上都是在堆中创建的新对象,因此它们会自动区分。基本上,是语言运行库负责为我们创建新节点

然而,图形表示法的优点是,它融合了节点和节点标签之间的差异,因此很容易混淆。事实上,如果我们已经需要提供一些数据类型来承载节点(只是为了使一个节点不同于另一个节点),那么为什么不使用相同的数据类型来存储一些标签,即我们可以使用标签本身作为承载集,例如,
[“Bil”,“Adam”;“Bil”,“Eve”]
。这确实是一种在计算中经常使用的表示。归纳定义并没有真正提供这样的自由

你的代码有问题吗 首先,您的数据定义不一致,例如表达式

 Node( Node(1,2) , Node(2,3) )
不太好
type tree = Empty | Node of tree * tree
type ltree = LEmpty | LNode of ltree * int * ltree
let unlabeled = Node (Node (Empty,Empty), Node (Empty, Empty))
let labeled = LNode (LNode (LEmpty, 2, LEmpty), LNode (LEmpty, 2, LEmpty))
type tree =
  | Leaf of int (* Leaves store numbers *)
  | Node of tree * tree

let tree = Node(Node(Leaf 1, Leaf 2), Node(Leaf 2, Leaf 3))
type tree' =
  | Leaf' of int
  | Node' of tree' * int * tree'

let tree' = Node'(Node'(Leaf' 1, 3, Leaf' 2), 8, Node'(Leaf' 2, 5, Leaf' 3))
let weight : tree' -> int = function
  | Leaf' n -> n (* The weight of a leaf is its data *)
  | Node'(_, n, _) -> n (* The weight of a node is its middle value *)

let rec annot_tree : tree -> tree' = function
  | Leaf n -> Leaf' n (* Base case *)
  | Node(l, r) ->
      let labeled_l = annot_tree l in (* Annotate left child *)
      let labeled_r = annot_tree r in (* Annotate right child *)
      Node'(labeled_l, weight labeled_l + weight labeled_r, labeled_r)