码迷,mamicode.com
首页 > 其他好文 > 详细

10.3.1 树处理的难点

时间:2015-01-03 17:26:39      阅读:155      评论:0      收藏:0      [点我收藏+]

标签:f#   函数编程   实用函数编程   递归      

10.3.1 树处理的难点

 

我们来看一个简单的处理树的例子。清单 10.15 声明了一个表示整数树的类型,并用递归函数,统计树中所有值的和。

 

清单 10.15 树型数据结构并计算元素的和 (F# Interactive)

> type IntTree =    [1]

    | Leaf of int

    | Node of IntTree * IntTree;;

type IntTree = (...)

 

> let rec sumTree(tree) =    [2]

    match tree with

    | Leaf(n) -> n

    | Node(l, r) -> sumTree(l) + sumTree(r);;  ? 递归统计子树中值的和

val sumTree : IntTree -> int

 

表示树的 IntTree 类型,是有两个选项的差别联合。注意,它其实与列表类型非常相似的!树值既可以表示为包含整数的叶子,也可以节点;节点不包含数值,但它有两个 IntTree 类型的子树。求和的递归函数[2]使用模式匹配,区分这两种情况:对于叶子,返回数值;对于节点,需要递归地对左、右子树的元素求和,并把这两个值加到一起。

如果我们看一下 sumTree 函数,可以发现,它不是尾递归。它执行递归调用 sumTree,计算左子树中元素的和,然后需要执行一些另外的操作;更确切地说,还要计算右子树中元素的和,最后把这两个数字相加。我们不知道如何用尾递归的方式写这个函数,因为它要执行两个递归调用。最后,通过一些努力(通过使用某种类型的累加器参数),这两个调用可以改成尾递归,但是,我们还必须实现普通的递归调用!这很烦人,因为,对于一些大型的树,这种方法将导致栈溢出。

我们需要考虑不同的方法,首先,需要知道树到底是什么样子,图 10.6 显示了两种树。

图 10.6 平衡树和不平衡树的例子。暗圈对应节点,亮圈包含值,对应叶子

 

在图 10.6 中的平衡树(balancedtree),是一种相当典型的情况,树中的元素合理分布在左、右子树。这不是太坏的情况,因为我们从来不会有特别深的递归。(用我们当前的算法,最大递归深度,就是树的根和叶之间存在的较长路径。)不平衡(Imbalanced tree)的例子要危险得多,在右侧有许多节点元素,所以,当我们递归处理时,必须进行大量的递归调用。处理这两种树之间的差异如清单 10.16 所示。

 

清单 10.16 使用自然的递归函数统计树的和 (F# Interactive)

> let tree = Node(Node(Node(Leaf(5),Leaf(8)), Leaf(2)),

                      Node(Leaf(2), Leaf(9)))

  sumTree(tree);;

val it : int = 26

 

> let numbers = List.init 100000 (fun _-> rnd.Next(– 50, 51);;

val numbers : int list = [29; -44; -1; 25;-33; 36; ...]

 

> let imbalancedTree =

     numbers |> List.fold (fun currentTree num –>

       Node(Leaf(num), currentTree)) (Leaf(0));;  [1] ? 在当前树的右边创建节点

val imbalancedTree : IntTree

 

> sumTree(imbalancedTree);;

Process is terminated due toStackOverflowException.

 

第一个命令创建了简单的树,并计算了叶子值的和。第二个[好像应该是第三个]命令使用 fold 函数创建树,类似于在图 10.6 中的不平衡树,只是要更大一些;它首先有一个包含零的叶子,然后,每一步在当前树的右侧,追加一个有左侧叶子的新节点[1],从我们在清单 10.2 中创建的列表中取数,包含10 万个 –50 到 50 之间的随机数。结果,我们就会得到有 10 万个节点高度的树。当我们试图计算树中叶子的和时,会遭遇栈溢出。这不是特别典型的情况,但在处理树的代码中,我们仍可能会遇到;幸运的是,连续给我们提供了一种方法,即使像这样的树,函数也能正常运行。

10.3.1 树处理的难点

标签:f#   函数编程   实用函数编程   递归      

原文地址:http://blog.csdn.net/hadstj/article/details/42363975

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!