1 递归
1.1 递归3要素:
- 定义函数的功能
- 找出递归终止条件
- 找出递推关系式
1.2 实例解析
1.2.1 阶乘
第一步定义函数的功能, 定义factorial(n)
为求n的阶乘。
def factorial(n):
pass
第二步找出递归终止条件, 终止条件就是足够简单的问题, 能直接得出结果的。比如本例中n=1 的时候能直接知道结果是1。
第三步是找出递推关系式, 递推关系式就是从更小的问题能推出当前问题的关系式,比如本例中factorial(n) = n* factorial(n-1)
。找递推关系式通常是最复杂的一步。
找到之后我们就可以直接写出完整递归的代码:
def factorial(n):
if ==1:
return 1 # 递归终止条件
return n*factorial(n-1) # 递推关系式
递归的目的是把一个大的问题拆成更小的子问题,我们只需要知道递归的功能即可,不要把递归一层层拆开来想, 这样很容易形成循环调用而进入死循环。
1.2.2 斐波那契数列
第一步定义函数的功能,,定义fibonacci(n)
为斐波那契的第n项。
def fibonacci(n):
pass
第二步找出递归终止条件,fibonacci(1)=1, fibonacci(2)=1
第三步找出递推关系式 fibonacci(n)=fibonaccio(n-1)+fibonacci(n-2)
def fibonnai(n):
if n<=2:
return n #递归终止条件
return fibonacci(n-1) + fibonacci(n-2)
注意: 上面的递归终止条件有2个, 在写递归时, 一个常见的错误就是递归终止条件遗漏导致无限循环。比如,上面的示例中, 如果遗漏了fibonacci(2)=1
, 就会计算fibonacci(2)=fibonacci(1)+fibonacci(0)
,继而会计算fibonacci(0)=fabonacci(-1)+fibonacci(-2)
, 无限循环下去。丛上面的示例也可以看出, 所谓的递归终止条件, 其实就是最小子问题的解, 是不能通过其他子问题推导出来的, 必须能直接给出。
1.2.3 二叉树的遍历
二叉树的遍历是非常典型的问题, 借助递归实现非常简单。
二叉树的遍历有3种方式, 分别是前序遍历, 中序遍历, 后序遍历, 其中前中后指的都是根节点的访问次序, 因此前序遍历的访问顺序是:根节点->左子树->右子树, 中序遍历的访问顺序是左子树->根节点->右子树, 后序遍历的访问顺序是左子树->右子树->根节点。
前序遍历:
1) 定义函数功能:定义PreOrder(binary_tree)
为二叉树的前序遍历;
2)找出递归终止条件:如果根节点为空, 那么遍历结束
3) 找出递推关系式:按照前序遍历的访问次序, 那么接下来应该是PreOrder(left)
和PreOrder(right)
。
假设二叉树的结构为:
class BinaryTree():
def __init__(self, root, left, right):
self.root = root
self.left = left
self.right = right
def PreOrder(binary_tree):
if binary_tree.root is None:
return
print(binary_tree.root)
if binary_tree.left is not None:
PreOrder(binary_tree.left)
if binary_tree.right is not None:
PreOrder(binary_tree.right)
if __name__ == "__main__":
tree1 = BinaryTree(3, None, None)
tree2 = BinaryTree(4, None, None)
tree3 = BinaryTree(2, tree1, tree2)
tree4 = BinaryTree(6, None, None)
tree5 = BinaryTree(7, None, None)
tree6 = BinaryTree(5, tree4, tree5)
tree7 = BinaryTree(1, tree3, tree6)
PreOrder(tree7)
遍历结果为:1 2 3 4 5 6 7
同样,可以写出中序遍历如下:
def MidOrder(binary_tree):
if binary_tree.root is None:
return
if binary_tree.left is not None:
MidOrder(binary_tree.left)
print(binary_tree.root)
if binary_tree.right is not None:
MidOrder(binary_tree.right)
遍历结果为: 3 2 4 1 6 5 7
后序遍历:
def PostOrder(binary_tree):
if binary_tree.root is None:
return
if binary_tree.left is not None:
PostOrder(binary_tree.left)
if binary_tree.right is not None:
PostOrder(binary_tree.right)
print(binary_tree.root)
遍历结果为: 3 4 2 6 7 5 1
1.2.4 汉诺塔游戏
A、B、C 3根柱子,A柱子上从底部到顶部依次叠着n个从大到小的圆盘,将圆盘从A柱子移动到C柱子,遵循以下规则:
1. 一次只能移动一个圆盘;
2. 小盘必须放在大盘之上;
编写程序输出把圆盘从A移动到C的过程。
还是遵循递归解题的3要素:
1) 定义函数的功能:定义hanoi(A, B, C) 为借助B把A的圆盘移动到C;
2)找出递归终止条件:如果A为空,则递归结束
3) 找出递推关系式:
2.1 动态规划3要素:
- 定义数组的含义
- 找出数组元素的关系式
- 找出初始值
2.2 实例解析
2.2.1 阶乘
第一步定义数组的含义:定义dp[i]为i的阶乘, 这样最终所求的答案就是dp[n]
第二步找出数组元素的关系式 dp[i]=i*dp[i-1]
第三步找出初始条件, dp[1]=1
def facrotial(n):
dp = [0]*n
dp[0] =1
for i in range(1,n):
dp[i] = i*dp[i-1]
return dp[n-1]
2.2.2 斐波那契数列
第一步定义数组的含义:定义dp[i]为斐波那契数列的第i项, 那么dp[n]就是最终的答案
第二步找出数组元素的关系式: dp[i]=dp[i-1]+dp[i-2]
第三步找出初始条件: dp[0]=1, dp[1]=1
def fibonacci(n):
if n<=2:
return 1
dp = [0]*n
dp[0]=1
dp[1]=1
for i in range(2,n):
dp[i] = dp[i-1]+dp[i-2]
return dp[n-1]