我有一个问题。
我必须在Haskell中实现一个函数maxT
,它从二叉树中返回一个节点的最大值。
data Tree a = Leaf a | Node (Tree a) a (Tree a)
这是给出的。接下来我该怎么办?
maxT :: (Tree Integer) -> Integer
maxT (Leaf a) = a
maxT (Node l a r) = max a (max (maxT l) (maxT r))
这是对的吗?
答案 0 :(得分:4)
让我们看看证明正确有多难。为什么?因为这是分析程序错误的好方法。特别是递归的。我们在技术上会使用感应,但它并不复杂。关键是要认识到maxT t
必须始终是树t
中的最大值 - 此声明“maxT t
必须始终是树t
中的最大值”被称为不变量,我们将尝试证明它。
首先,我们假设t
是Leaf
。在这种情况下,您已定义maxT (Leaf a) = a
,并且由于此树中确实存在没有其他值,因此a
必须是最大的。因此,maxT
在通过Leaf
时支持我们的不变量。这是“基本情况”。
现在,我们会考虑当t = Node (Leaf a) b (Leaf c)
Integer
a
b
,c
和maxT
出现maxT t
===
maxT (Node (Leaf a) b (Leaf c))
===
max b (max (maxT (Leaf a)) (maxT (Leaf c)))
时会发生什么。这是一个高度为1的树,形成了你可能称之为“示例案例”的感应。让我们试试maxT
,看看不变量是否成立。
Leaf
此时我们将使用我们的基础案例步骤,并说由于此maxT (Leaf _)
中===
max b (max a c)
的唯一应用程序位于max
s上,因此每个应用程序都必须支持我们的不变量。这有点愚蠢,但那是因为它只是一个例子。我们稍后会看到更一般的模式。
现在,让我们评估我们的max a b
位,知道结果是每个特定左子树或右子树的最大值。
a
现在,我不想深入研究b
的定义,但根据其名称,我很高兴地假设max b (max a c)
返回{{1}之间的最大值}和Node
。我们可以在这里详细介绍细节,但很明显maxT
已经获得了有关我们Leaf
的所有相关信息,用于计算整个高度-1树的最大值。我称之为Node
适用于height-0和height-1树(Leaf
s和n
仅包含maxT t
s)的成功证明。< / p>
下一步是概括这个示例案例。
因此,让我们应用相同的模式来推广树的高度。我们会问,如果我们修正了一些数字t
会发生什么,并假设n
维持我们对所有n = 0
高度n = 1
或更低的不变量的不变量。这有点奇怪 - 我们只展示了Tree
和n
的作品。很明显为什么这会稍晚起作用。
那么这个假设对我们有什么影响?好吧,让我们取两个l
个高度r
或更少(称为x
和t = Node x l r
),任意整数maxT t
,并将它们组合成一个一棵新树maxT t
===
maxT (Node x l r)
===
max x (max (maxT l) (maxT r))
。当我们maxT l
时会发生什么?
maxT r
我们知道,根据我们的假设,max
和t
支持我们的不变量。然后,(n+1)
es的链继续维持我们的不变量,因为树Tree
的高度为(n+1)
。此外(这非常重要)我们组装新maxT
的过程是通用的 - 我们可以在此方法中生成任何高度 - (n+1)
树。这意味着n
适用于任何高度 - maxT
树。
感应时间!我们现在知道,如果我们选择n
并且相信(出于某种原因)(n+1)
适用于任何高度 - n = 0
树,那么它必须立即适用于任何高度 - {{1树。我们选择maxT
。我们知道Leaf
适用于maxT
的“基本情况”,所以突然我们知道1
适用于高度 - maxT
树。这是我们的“案例”。现在,鉴于这些知识,我们可以立即看到2
适用于高度 - 3
树。然后是高度 - 4
树。然后是身高 - maxT
。等等,等等。
这样就完成了max
正确的证明*。
*我必须留下一些警告。虽然有意义,但我们并没有真正做出精确的细节来证明(n+1)
链是有效的。我也没有真正证明诱导步骤有效 - 如果有更多的方法来制作高度 - Node
树而不仅仅是在高度上使用n
- n
或更小的树?更强大的方法是“分开”一个高度 - maxT (Leaf undefined)
树,但我觉得这有点难以辨认。最后,如果我们发送{{1}}或其他类似的病态值,我们真的想要认真思考会发生什么。这些出现在Haskell中,因为它是一种(图灵完备的)计算机语言而不是纯数学。老实说,这些小小的东西对你的情况并没有太大的改变。