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 万个节点高度的树。当我们试图计算树中叶子的和时,会遭遇栈溢出。这不是特别典型的情况,但在处理树的代码中,我们仍可能会遇到;幸运的是,连续给我们提供了一种方法,即使像这样的树,函数也能正常运行。

时间: 2024-10-13 09:16:30

10.3.1 树处理的难点的相关文章

第10章 2-3-4树和外部存储

2-3-4树 定义 234表示一个节点可能还有子节点的个数,有以下三种情况: 有1个数据项的节点含有2个子节点 有2个数据项的节点含有3个子节点 有3个数据项的节点含有4个子节点 如果使用L表示子节点的个数,D表示数据项的个数,那么L=D+1,非叶子节点个数总比它数据项含有的数据项多1. 树的组织 节点中的数据项按照关键字升序排列. 搜索2-3-4树 从根开始查找,除非查找的关键字就是根,否则会选择关键字所在的范围,直到找到位置. 插入 查找时没有满节点时很简单,将数据项插入即可,当节点已满时,

有用函数编程

<序> 感谢 关于本书 关于封面 第一部分 学习函数式思维 第一章 不同的思维 1.1 什么是函数式编程? 1.2 通往有用函数编程之路 1.3 用函数式编程提高生产力 1.3.1 函数范式 1.3.2 声明式编程风格 1.3.3 了解程序的执行 1.3.4 设计并发友好的应用程序 1.3.5 函数风格怎样形成代码 1.4 函数式编程演示样例 1.4.1 用声明式风格表达意图 1.4.1.1 用 LINQ 处理数据 1.4.1.2 用 XAML 描写叙述用户界面 1.4.1.3 声明式函数动画

poj 2528 线段树+特殊离散化

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 51098   Accepted: 14788 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

hdu 3397 Sequence operation (线段树 区间合并 多重标记)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3397 题意: 给你一串01串,有5种操作 0. 区间全部变为0 1.区间全部变为1 2.区间异或 3.询问区间1的个数 4.询问区间被最长连续1的长度 思路: 这5个操作都是比较基础的线段树操作,难点在于有两种修改操作,这类题之前也写过,之前是乘法和加法,这个是区间亦或和区间更新值,但是思路是可以借鉴的,我们要推出这两个操作的关系,这样才能维护好这两个标记,我们用两个标记:same , rev ,分别表

南阳理工 题目9:posters(离散化+线段树)

posters 时间限制:1000 ms  |  内存限制:65535 KB 难度:6 描述 The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally deci

浅谈线段树

 数据结构——线段树 O.引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出. 一种回答:这也太简单了,O(n)枚举搜索就行了. 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定. 那好,我再修改一下题目. B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1.在某个位置加上一个数:2.询问区间[l,r]的和,并输出. 回答:o(n)枚举. 动态修改最起码不能用静态的前缀和做了. 好,我再修改题目: C.给出n个数,n<=1000000,

[转载]伸展树(一)之 图文解析 和 C语言的实现

概要 本章介绍伸展树.它和"二叉查找树"和"AVL树"一样,都是特殊的二叉树.在了解了"二叉查找树"和"AVL树"之后,学习伸展树是一件相当容易的事情.和以往一样,本文会先对伸展树的理论知识进行简单介绍,然后给出C语言的实现.后序再分别给出C++和Java版本的实现:这3种实现方式的原理都一样,选择其中之一进行了解即可.若文章有错误或不足的地方,希望您能不吝指出! 目录 1. 伸展树的介绍 2. 伸展树的C实现 3. 伸展树的

Java实现二叉搜索树的添加,前序、后序、中序及层序遍历,求树的节点数,求树的最大值、最小值,查找等操作

什么也不说了,直接上代码. 首先是节点类,大家都懂得 /** * 二叉树的节点类 * * @author HeYufan * * @param <T> */ class Node<T extends Comparable<? super T>> { /** * 节点储存的值 */ private T data; /** * 左子节点 */ private Node<T> leftNode; /** * 右子节点 */ private Node<T>

线段树入门(poj 3274 3468 2528)

概念: 在一类问题中,我们需要经常处理可以映射在一个坐标轴上的一些固定线段,例如说映射在OX轴上的线段.由于线段是可以互相覆盖的,有时需要动态地取线段的并,例如取得并区间的总长度,或者并区间的个数等等.一个线段是对应于一个区间的,因此线段树也可以叫做区间树. 线段树常用于区间多次插入查询,经常改变数据. 而线段树的核心在于如何设计一个节点的信息 这里针对线段树的应用有三个方面: 1. 每次查询一个区间的最大差值(结点存放这个区间的最大最小值 3274) 2.不断修改区间内的数值,并查询区间的和