SICP 习题 (2.29) 解题总结:二叉活动体

SICP 习题 2.29题目很长,实现起来有点繁琐,不过题目不难,花点时间还是容易完成的。

题目引入了一个概念叫二叉活动体,这种东西有一个左分支,有一个右分支,每个分支包括一个长度和一个悬挂物,悬挂物可以是一个数,也可以是另一个二叉活动体。

题中给出了二叉活动体的实现代码:

(define (make-mobile left right)
  (list left right))

也给出了分支的实现代码:

(define (make-branch length structure)
  (list length structure))

题目要求我们写出left-branch和right-branch的过程,用于返回左分支或者是右分支。

这个比较简单,因为二叉活动体的实现代码中左分支和右分支是通过list过程组合起来的,将他们取出来的过程就分别是car和cadr,代码如下:

(define (left-branch mobile)
  (car mobile))

(define (right-branch mobile)
  (cadr mobile))

题目还要求我们实现branch-length和branch-structure过程,用于返回分支的长度和悬挂物。

同样,实现的时候还是通过car和cadr分别取得,代码如下:

(define (branch-length branch)
  (car branch))

(define (branch-structure branch)
  (cadr branch))

此外,题目还要求我们实现过程total-weight,用于获得一个二叉活动体的总重量。

这个实现起来麻烦一点点,需要遍历整棵树,然后统计所有总量,代码如下:

(define (total-weight mobile)
 (if (not (list? mobile))
     mobile

  (+
   (branch-weight (left-branch mobile))
   (branch-weight (right-branch mobile)))))

(define (branch-weight branch)
  (newline)
  (display "caculating branch-weight")
  (display branch)
  (if (list? branch)
      (if (list? (branch-structure branch))
	  (total-weight (branch-structure branch))
	  (branch-structure branch))
      branch))

接着,题目还要求我们实现一个balance?过程用于检查一个二叉活动体是否是平衡的,平衡的定义书中有说到,就是左分支重量乘以长度等于右分支重量乘以长度,而且所有子树都满足这样的条件。

借助以上实现的方法,检查一个二叉活动体是否平衡可以通过不断递归,不断判断每一个二叉分叉点来实现,代码如下:

(define (balance? mobile)
  (newline)
  (display "evaluating: ")
  (display mobile)
  (if (not (list? mobile))
      (begin
	(display "not list")
	#t
	)
      (let ((left-one (branch-structure (left-branch mobile)))
	    (right-one (branch-structure (right-branch mobile))))
	(newline)
	(display "inner evaluating:")
	(display left-one)
	(display right-one)
	(if (and (balance? left-one)
		 (balance? right-one)
		 (= (* (branch-length (left-branch mobile))
		       (total-weight left-one))
		    (* (branch-length (right-branch mobile))
		       (total-weight right-one))))
	    #t
	    #f))))

最后,题目问我们如果活动体的实现过程中不是使用list过程,而是使用cons过程,我们的程序是否需要很大量的修改。

因为我们使用接口封装了不同的获取函数,所以我们的代码不需要大量的修改,只需要改一下那些获取函数就可以了,比如

right-branch过程中的cadr修改成cdr就好了:

(define (right-branch mobile)
  (cdr mobile))

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-15 11:47:38

SICP 习题 (2.29) 解题总结:二叉活动体的相关文章

SICP 习题 (2.30)解题总结 : Square-Tree

SICP 习题 2.30 要求我们完成一个叫square-tree的过程,其作用和之前的square过程差不多,square过程是针对简单列表的,将列表中的所有元素求平方,然后返回新的平方数列表.不过square不能对嵌套列表进行处理,如果列表中还包含列表的话会报错. 题目要求我们实现一个square-tree的过程,当输入的列表包含另一个列表时可以对嵌套的列表进行处理,生成所有列表元素的平方数. 这个和之前几道题差不多,都是对树状列表的遍历和处理.题目还要求我们用两种方式实现,一种使用map,

树、递归、遍历————二叉搜索树的最近公共祖先

可以根据二叉搜索树的特点来进行解题: 二叉搜索树左边节点的值小于根节点 右边节点的值大于根节点. 所以左右都小于根节点的值说明在左子树上 左右的值大于根节点的值就代表值在右子树上 介于两者中间则输入的本身就是根节点,即为最近公共祖先. 一.遍历 1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * Tr

SICP 习题 (1.46)解题总结

SICP 习题 1.46 要求我们写一个过程iterative-improve,它以两个过程为参数,其中一个参数用来检测猜测是否足够好,另一个参数用来改进猜测.过程iterative-improve应该返回另一个过程,所返回的过程接收一个参数作为初始猜测,然后不断改进猜测直到结果足够好.题目还要求我们使用iterative-improve重写1.1.7的sqrt过程和1.3.3节的fixed-point过程. 因为涉及到高阶函数,所以整个题目理解起来有一点点费劲.不过这道题作为第一章的收官题确实

SICP 习题 (1.45)解题总结

SICP 习题 1.45是对前面很多关于不动点的习题的总结. 题目回顾了我们之前在1.3.3节使用的不动点寻找方法,当寻找y -> x/y 的不动点的时候,这个变换本身不收敛,需要做一次平均阻尼才可以. 对于y -> x/(y^2)这个变换也可以通过一次平均阻尼使它变得收敛. 不过一次平均阻尼对于四次方程是不够的,就是说,对y -> x/(y^3)这样的变换,一次平均阻尼不足以使它收敛,需要做两次平均阻尼才行. 题目遵从一直以来的抽象原则,要求我们去多做几次测试,找出 y -> x

SICP 习题 (2.10)解题总结: 区间除法中除于零的问题

SICP 习题 2.10 要求我们处理区间除法运算中除于零的问题. 题中讲到一个专业程序员Ben Bitdiddle看了Alyssa的工作后提出了除于零的问题,大家留意一下这个叫Ben的人,后面会不断出现这个人,只要是这个人提到的事情一般是对的,他的角色定位是个计算机牛人,不过是办公室经常能看到的那种牛人,后面还有更牛的. 对于区间运算的除于零的问题,处理起来也比较简单,只需要判断除数是不是为零,除数为零就报错.对于一个区间来讲,所谓为零就是这个区间横跨0,再直接一点讲就是起点是负数,终点是正数

SICP 习题 (2.22)解题总结: 迭代过程中的列表处理

SICP 习题 2.22是习题2.21的后续题目,题目中讲到叫Louis Reasoner的人想重写suqare-list过程,希望使用迭代计算过程,而不是递归计算过程,有关迭代计算过程和递归计算过程,如果你没什么印象了,请翻回习题1.9 的解题总结看看. 那个叫Louis Reasoner的人写的迭代版的suqre-list是这样的: (define (square-list-revert items) (define (iter things answer) (if (null? thing

SICP 习题 (2.2) 解题总结

SICP 习题 2.2要求我们使用这一节的数据抽象方法定义几何里"点"的概念,还要定义"线段"的概念,最后还要求我们定义midpoint-segment过程,这个过程根据参数中的线段进行计算,返回该线段的中点. 题目还给出了一个print-point过程,用于输出一个点,代码如下: (define (print-piont p) (newline) (display "(") (display (point-x p)) (display &quo

SICP 习题 (1.36)解题总结

SICP 习题 1.36 要求我们修改fixed-point函数,使它能够打印出计算中产生的近似值序列,使用练习1.22展示的newline和display方法.而后通过找出变换x => log (1000)/log(x)的不动点的方式确定x^x=1000的一个根(书中还提示你使用Scheme的基本过程log,用于计算自然对数值).然后比较一下使用平均阻尼和不用平均阻尼的计算步数.要注意的是不能使用1作为初始猜测去启动fixed-point,因为log(1)=0,会导致0除数错误. 从题目来看,

SICP 习题 (1.38)解题总结

SICP 习题1.38 紧跟着习题1.37的方向,要求我们用习题1.37中定义的cont-frac过程计算数学家欧拉大师在论文De Fractionibus Continuis 中提到的e-2的连分式.说实话,我不知道论文De Franctionibus Continuis讲的是什么,我甚至不知道论文的题目是什么意思.不过,这一切都不能阻止我这个数学盲去解答这道SICP习题. 仔细阅读题目,我们可以发现题目要求我们计算的是下面这样的无穷连分式: 其中N永远等于1, D等于1 ,  2 , 1 ,