验证二叉树的大小?

问题描述

我以这种方式具有数据类型

config.py

因此,正确的树的示例为:

datatype 'a bin_tree = 
    Leaf of 'a
  | Node of 'a bin_tree    (* left tree *)
           * int           (* size of left tree *)
           * int           (* size of right tree *)
           * 'a bin_tree   (* right tree *)

一个违反树的例子是

val tree1 =
  Node(Node(Node(Leaf 47,1,Leaf 38),2,Leaf 55),3,Node(Leaf 27,Leaf 96))

如何编写这样的谓词测试

val tree1false =
  Node(Node(Node(Leaf 47,4,Leaf 96))

解决方法

这是一个递归问题。在解决树上的递归问题之前,牢牢掌握列表上的递归是一个好主意。您可以说树是列表的概括,或者列表是树的特殊情况:列表只有一条尾巴,树可以有任意数量的尾巴,具体取决于树的类型。因此,您可以使用列表来重建和解决问题:

如果您有一个列表也可以记住其自身的长度,而不是典型的列表定义:

(* datatype 'a list = [] | :: of 'a * 'a list *)

datatype 'a lenlist = Nil | Cons of int * 'a * 'a lenlist

然后您可以测试存储的长度是否符合实际值的数量。

我将从创建一个重要的函数开始,以说明该函数执行递归的部分:

(* For regular built-in lists *)
fun count0 [] = 0
  | count0 (x::xs) = 1 + count0 xs

(* Counting the memoized list type disregarding the n *)
fun count1 Nil = 0
  | count1 (Cons (n,x,xs)) = 1 + count1 xs

下一部分是,我想在每个递归步骤中测试存储的数字n是否也与实际计数一致。此函数的返回类型是什么?好吧,您想要的test函数应该是'a lenlist -> bool,而我创建的count函数应该是'a lenlist -> int

我建议您制作一个testcount可以做到这两者。但是您可以通过多种方式进行操作,例如通过给它“额外的参数”,或给它“额外的返回值”。我将同时演示这两个示例,只是为了表明有时一个优于另一个,经验会告诉您哪个。

这是一个val testcount1 : 'a lenlist -> bool * int函数:

fun testcount1 Nil = (true,0)
  | testcount1 (Cons (n,xs)) =
    let val (good_so_far,m) = testcount1 xs
        val still_good = good_so_far andalso n = m + 1
    in (still_good,m + 1)
    end

val goodList = Cons (4,#"c",Cons (3,#"o",Cons (2,Cons (1,#"l",Nil))))
val badList = Cons (3,#"d",#"e",#"r",Cons (0,#"p",Nil))))

对此进行测试

- testcount1 goodList;
> val it = (true,4) : bool * int
- testcount1 badList;
> val it = (false,4) : bool * int

这表明testcount1返回数字是否相加列表的实际长度,这在递归过程中测试数字是否相加是必需的每个步骤,但最后不再需要。您可以将此testcount函数包装在一个更简单的test函数中,该函数仅关心bool

fun test xs = #1 (testcount1 xs)

这是另一种解决方法:testcount1的递归方式有些令人不满意。即使能够确定列表(例如,在m + 1处的列表已损坏,它也会继续计算Cons (0,Nil)

这里是备用val testcount2 : 'a lenlist * int -> bool,它在一个额外的参数中存储数字:

fun testcount2 (Nil,0) = true
  | testcount2 (Nil,_) = false
  | testcount2 (Cons (n,xs),m) =
      n = m andalso testcount2 (xs,m - 1)

对我来说,这似乎整洁了很多:该函数是尾递归的,当它感觉到有鱼时会立即停止。因此,如果头部中断,则无需遍历整个列表。缺点是它需要知道长度,我们还不知道。但是,我们可以通过假设所宣传的任何东西都是正确的,直到事实明确与否,以此作为补偿。

测试此功能,您不仅需要给它一个goodList或一个badList,还需要给一个m

- testcount2 (goodList,4);
> val it = true : bool
- testcount2 (badList,4);
> val it = false : bool
- testcount2 (badList,3);
> val it = false : bool

此函数不只是比较n = m是很重要的,因为在badList中,这将导致同意badList的长度为3个元素,因为n = m在所有Cons情况下,每次迭代均适用。这在需要我们达到Nil而不是例如0的两个~1案例中有帮助。 badList,就像test一样。

此函数也可以包装在'a lenlist中,以掩盖我们从fun size Nil = 0 | size (Cons (n,_,_)) = n fun test xs = testcount2 (xs,size xs) 本身派生的额外参数的事实:

foo -> bar

到目前为止有些道德:

  • 有时有必要创建帮助函数来解决您的最初问题。
  • 这些辅助函数不限于与您提供的函数具有相同的类型签名(无论是在学校中进行练习还是用于外部API /库)。
  • 有时有助于扩展函数返回的类型。
  • 有时有助于扩展函数的参数。
  • 仅仅因为您的任务是“编写函数foo”,这并不意味着您不能通过编写返回大于或等于bar或{{1} }。

现在,对于在二叉树上解决此问题的一些提示:

重复数据类型,

datatype 'a bin_tree = 
    Leaf of 'a
  | Node of 'a bin_tree    (* left tree *)
           * int           (* size of left tree *)
           * int           (* size of right tree *)
           * 'a bin_tree   (* right tree *)

您可以根据上述想法为功能构建骨架:

fun testcount3 (Leaf x,...) = ...
  | testcount3 (Leaf x,...) = ...
  | testcount3 (Node (left,leftC,rightC,right),...) = ...

我在此处嵌入了som提示:

  • 您的解决方案可能应该包含针对Leaf xNode (left,right)的模式匹配。考虑到“额外参数”类型的解决方案(至少证明对列表有用,但我们会看到)需要两个Leaf x案例。为什么会这样?
  • 如果在列表的情况下,“额外参数” m表示列表的预期长度,那么在树的情况下,“额外参数”表示什么?您不能说“这是列表的长度”,因为它是一棵树。您如何捕获树枝的一部分?
  • 如果这仍然太困难,请考虑解决列表中没有复制粘贴的问题。也就是说,您可以查看此答案中的解决方案(但请不要这样做),但不允许您复制粘贴代码。如果要复制,则必须输入。
  • 首先,尝试定义用于从size生成test的辅助函数testcount2,但对于树。因此,可以将其命名为sizeTree以避免名称重叠,但除此之外,请尝试使其相似。这是骨架:
fun sizeTree (Leaf x) = ...
  | sizeTree (Node (left,right)) = ...

testcount3sizeTree一起粘贴后,就很容易像tau一样。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...