递归的三大要素
1.明确这个函数想要干什么
例如,我定义了一个函数,想要算n的阶乘:
// 算 n 的阶乘(假设n不为0)
def f(n):
return None
第二要素:寻找递归结束条件
所谓递归,就是会在函数内部代码中,调用这个函数本身,所以,我们必须要找出递归的结束条件,不然的话,会一直调用自己,进入无底洞。也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。
上面那个例子,当 n = 1,2时,那你应该能够直接知道 f(n) 是啥吧?此时,f(1) = 1,f(2) = 2。完善我们函数内部的代码,把第二要素加进代码里面,如下:
#当n为1或者2时,返回的是自己,也意味着函数终止
def f(n):
if(n<=2):return n
第三要素:找出函数的等价关系式
第三要素就是,我们要不断缩小参数的范围,缩小之后,我们可以通过一些辅助的变量或者操作,使原函数的结果不变。
说白了,就是要找到原函数的一个等价关系式,f(n) 的等价关系式为 n * f(n-1),即f(n) = n * f(n-1)。
找出了这个等价,继续完善我们的代码,我们把这个等价式写进函数里。如下:
def f(n):
if(n<=2):return n
else:
return n*f(n-1)
常见递归(使用以上思路):
案例1:斐波那契数列
斐波那契数列的是这样一个数列:1、1、2、3、5、8、13、21、34….,即第一项 f(1) = 1,第二项 f(2) = 1……,第 n 项目为 f(n) = f(n-1) + f(n-2)。求第 n 项的值是多少。
1、第一递归函数功能
假设 f(n) 的功能是求第 n 项的值,代码如下:
#定义函数为实现斐波那契数列
def f(n):
return None
2、找出递归结束的条件
显然,当 n = 1 或者 n = 2 ,我们可以轻易着知道结果 f(1) = f(2) = 1。所以递归结束条件可以为 n <= 2。代码如下:
#终止条件
def f(n):
if (n<=2):return 1
3、找出函数的等价关系式
def f(n):
#先写递归结束条件
if (n<=2):return n
#在写递归的关系条件
return f(n-1)+f(n-2)
案例2:小青蛙跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路:
如在三阶台阶的情况:当青蛙第一步跳了一级台阶,那么就只剩下了两级台阶,将问题转化成为两级台阶的跳法,当青蛙第一步跳了两级台阶,那么就只剩下了一级台阶,就将问题转化为了一级台阶的跳法。
1阶台阶时,有1种跳法 (1) f(1) = 1
2阶台阶时,有2种跳法 ([2],[1,1]) f(2) = 2
3阶台阶时,有3种跳法([2,1],[1,2],[1,1,1])f(3) = f(2) + f(1)
4阶台阶时,有5种跳法 …f(4) = f(3) + f(2)
…
n阶台阶时,有 f(n) = f(n-2) + f(n-1) 种跳法
代码实现:
def f(n):
#1.先确定终止条件:f(0) = 0,f(1) = 1,等价于 n<=2时,f(n) = n。
if(n<=2):return n
#n情况的关系
return f(n-1)+f(n-2)
案例3:反转单链表。
反转单链表。例如链表为:1->2->3->4。反转后为 4->3->2->1
链表的节点定义如下:
#定义链表
class ListNode:
def __init__(self,x):
self.val = x
self.next = None
#创建[0->1->2->3->4]的链表
def create(arr):
pre = ListNode(0)
# pre是固定的开头,tmp为指针
tmp = pre
for i in arr:
print(i)
tmp.next = ListNode(i)
tmp = tmp.next
return pre.next
#输出链表
def print_ll(head):
tmp = head
while tmp:
print(tmp.val)
tmp=tmp.next
def reverse_linkedlist(head):
#寻找结束条件
if head is None or head.next is None:
return head
#寻找等价关系
else:
newhead=reverse_linkedlist4(head.next)
head.next.next=head
head.next=None
return newhead
a = create_ll(range(5))
a = reverse_linkedlist(a)
print_ll(a)
#---> 4 3 2 1 0
思路:
把 2->3->4 递归成 4->3->2。不过,1 这个节点我们并没有去碰它,所以 1 的 next 节点仍然是连接这 2。接下来只需要把节点 2 的 next 指向 1,然后把 1 的 next 指向 null。