[CS61A课堂记录]Lecture #7: Tree Recursion

树形递归

线性递归每次调用最多进行一次递归调用。尾部递归每次调用最多有一个递归调用,并且它是最后计算的东西。

在这里插入图片描述
上图中,左边是线性递归,右边是树形递归。注意区分。

A Problem

def find_zero(lowest, highest, func):
    """Return a value v such that LOWEST <= v <= HIGHEST and
    FUNC(v) == 0, or None if there is no such value.
    Assumes that FUNC is a non-decreasing function from integers
    to integers (that is, if a < b, then FUNC(a) <= FUNC(b)."""
    
    if lowest > highest: 	# Base Case
    	return None
    elif func(lowest) == 0: # Base Case
    	return lowest
    else: 					# Inductive (Recursive) Case
    	return find_zero(lowest + 1, highest, func)

上述程序是尾部递归,可转换为循环结构。

# Equivalent iterative solution
while lowest <= highest:
    if func(lowest) == 0:
    	return lowest
    lowest += 1
# If we get here, returns None

Problem, Take 2

可以通过利用函数有非递减的属性使其更快。

def find_zero(lowest, highest, func):
    ...
    if lowest > highest: 	# Base Case
    	return None
    middle = (lowest + highest) // 2
    if func(middle) == 0: 	# Base Case
    	return middle
    elif func(middle) < 0: 	# Inductive Case
    	return find_zero(middle + 1, highest, func)
    else: 					# Inductive Case
    	return find_zero(lowest, middle - 1, func)

二分法。上述程序是尾部递归(有两个调用,但是只有一个执行),可转换为循环结构。

# Equivalent iterative solution
while lowest <= highest:
    middle = (lowest + highest) // 2
    if func(middle) == 0:
    	return middle
    elif func(middle) < 0:
    	lowest = middle + 1
    else:
    	highest = middle - 1

Side Trip: Base Cases Without If

实现程序不使用 if 语句,使用 and、or。

def is_a_zero(lowest, highest, func):
    """Return true iff there is a value v such that LOWEST <= v <= HIGHEST
    and FUNC(v) == 0. Assumes that FUNC is a non-decreasing function
    from integers to integers."""
    
    middle = (lowest + highest) // 2
    return lowest <= highest \
    		and (func(middle) == 0 \
    			or (func(middle) < 0 and is_a_zero(middle + 1, highest, func))
   				or (func(middle) > 0 and is_a_zero(lowest, middle - 1, func)))

上述程序是线性调用,而不是尾部调用,因为执行is_a_zero(middle + 1, highest, func))后仍有可能执行(func(middle) > 0,递归调用就不是最后执行的了。

iff表示当且仅当。优先级:and > or

在Python中,一行的结束意味着语句的结束。想要在下一行继续上一行的语句有两种办法:反斜杠、括号。

def is_a_zero(lowest, highest, func):
    """Return true iff there is a value v such that LOWEST <= v <= HIGHEST
    and FUNC(v) == 0. Assumes that FUNC is a non-decreasing function
    from integers to integers."""
    
    middle = (lowest + highest) // 2
    return lowest <= highest \
    		and (func(middle) == 0 \
    			or (func(middle) < 0 and is_a_zero(middle + 1, highest, func))
   				or is_a_zero(lowest, middle - 1, func)))

经修改,上述程序是树形递归。

寻路问题

考虑在迷宫中找到出路(通往底层的路径)的问题:
在这里插入图片描述

从给定的起始方块(最上面一行的某一个)开始,只要移动到的方块未被占用,就可以向下移动一行,并可以选择向左或向右移动一列。

在这里插入图片描述

def is_path(blocked, x0, y0):
    """True iff there is a path of squares from (X0, Y0) to some
    square (x1, 0) such that all squares on the path (including first and
    last) are unoccupied. BLOCKED is a predicate such that BLOCKED(x, y)
    is true iff the grid square at (x, y) is occupied or off the edge.
    Each step of a path goes down one row and 1 or 0 columns left or 		right."""
    
    if blocked(x0, y0):
    	return False
    elif y0 == 0:
    	return True
    else:
    	return is_path(blocked, x0-1, y0-1) \
   				or is_path(blocked, x0, y0-1) \
    			or is_path(blocked, x0+1, y0-1)

数出有几条路径

在这里插入图片描述
例如,num paths(M2, 5, 6) 的结果是5。

def num paths(blocked, x0, y0):
    """Return the number of unoccupied paths that run from (X0, Y0)
    to some square (x1, 0). BLOCKED is a predicate such that BLOCKED(x, y)
    is true iff the grid square at (x, y) is occupied or off the edge. """
    
    if blocked(x0, y0):
    	return 0
    elif y0 == 0:
    	return 1
    else:
    	return num paths(blocked, x0-1, y0-1) \
    		 + num paths(blocked, x0, y0-1) \
    		 + num paths(blocked, x0+1, y0-1)

增加方向

现在不仅可以向左下方、正下方、右下方移动,还可以向左侧、右侧。原先对三个方块进行递归调用(x0 − 1, y0 − 1),(x0, y0 − 1),(x0 − 1, y0 + 1),现在添加了对其他两个方块的调用(x0 − 1, y0) and (x0 + 1, y0)

会发生什么?无限递归,像(6,2)->(7,2)->(6,2)

时间分析

在某种意义上,所有的线性递归花费的时间都与问题的规模成正比。每次调用都可以产生另外三个调用,最多 y0 个子递归。这意味着可能的调用次数可能多达 3 ** y0——呈指数级增长。显然指数级增长是不好的。

分区计数问题

def num partitions(n, k):
    """Returns number of distinct ways to express N as a sum of positive
    integers each of which is <= K, where K > 0. (Empty sum is 0.)"""

考虑num partitions(6, 3):

在这里插入图片描述

在这里插入图片描述

用 k 以内的整数对 n 进行分割的方法的数量等于:

  • 使用不超过 m 的整数划分 n-m 的方法数
  • 使用不超过 m-1 的整数划分 n 的方法数
def num partitions(n, k):
    """Number of distinct ways to express N as a sum of positive
    integers each of which is <= K, where K > 0. (The empty sum is 0.)"""
    
    if n == 0:
        return 1
    elif n < 0:
        return 0
    elif k == 0:
        return 0
    else:
        return count_partitions(n-k, k) + count_partitions(n, k-1)

斐波那契数列

划分问题是数学递归关系的典型例子,另一个相似的例子是斐波那契数列:

在这里插入图片描述

def fib(n):
	if n == 0 or n == 1:
		return 1
	else:
		return fib(n-2) + fib(n-1)

同样,这是一个树状递归,需要指数级的计算量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值