力扣刷题班 第2节:递归

(例题1)斐波那契数列

写⼀个函数,输⼊ n ,求斐波那契(Fibonacci)数列的第 n 项。
斐波那契数列的定义如下:
F(0) = 0, F(1) = 1 
F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数
相加⽽得出。
0 1 1 2 3 5 8 13 ··

注意:这里输出的是Fibnacci数列的第n项,而不是前n项之和

def f(n): 
    if n==0: 
        return 0 
    elif n==1: 
        return 1 
    else: 
        return f(n-1)+f(n-2) 
k=f(6) 
print(k)

上面这段代码的意思是

(1)递推关系(recurrentce relation):所有的f(n)都可以被拆成 前一项  f(n-1)  和前两项  f(n-2)  的和

(2)基本情况(bottom cases):递归到最后最基本的组成要素,那就是F(0) = 0, F(1) = 1, 由着两个元素不断的相加就可以衍生出后面所有的可能的值‘

衍生题目’

        求Fibonacci数列中前n个元素之和(暂时不会做)

(例题2)爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的⽅法可以爬到楼顶 呢? 注意:给定 n 是⼀个正整数。下面给出了n=1到5所有的组合的可能

台阶数      总共可能的路径数     所有可能的路径

n=1 jump(n)=1 ——1

n=2 jump(n)=2 ——1 1 / 2

n=3 jump(n)=3 ——1 1 1 / 1 2 / 2 1

n=4 jump(n)=5 ——1 1 1 1/ 1 1 2/ 2 1 1/ 2 2/ 1 2 1

n=5 jump(n)=8 ——1 1 1 1 1/ 1 1 1 2/ 1 1 2 1/ 1 2 1 1 / 2 1 1 1 /  1 2 2 / 2 1 2 / 2 2 1 /

注意:这里是让你你去  求  的  是  有多少种组合

首先咱们不瞎逼逼,直接上答案,从答案中总结做题的思路和解题的规律。

逼逼再多,扣上答案,自己顺着思路写不出来,也是白费。

对于零基础小白,能用自己的思路理解题目,顺着自己的思路能把答案写出来才是王道。

如果你不是科班出身,没学过“算法数据结构”,如果你完全不看答案、自己在那里天马行空的空想,你的思路往往就很奇怪且低效。因为,没经过专业训练的你,想不到正地方上去。

想要培养“科班的”“算法数据结构”思维,只有一条路,那就是多看答案。首先通过举例子,穷举法来理解答案这样设计的合理性。其次,多对答案思路进行提炼和总结,最后用你的话能整个题目的做题思路串起来,然后顺着你写的思路注释,最终把答案代码写出来

def f(n): 
    
    # 这段if elif想表达是bottom case
    # 分别是上一个台阶有几种可能性以及上两个台阶有几种可能性
    if n==1: 
        return 1 
    # 上一个台阶,只有“走一步”这一种上去的方式
    elif n==2: 
        return 2 
    # 上两个台阶,有“走一步,再走一步”和“一次走两步”这两种走法



    else: 
        return f(n-1)+f(n-2) 
        

# f(5) =           f(4)+            f(3)
# f(5) =    f(3)+       f(2)  +   f(2)+ f(1)
# f(5) = f(2) +f(1) +   f(2)  +   f(2)+ f(1)
# f(5) = 2    +  1  +    2    +     2 +  1
# f(5) = 8

print(f(n)) 



所有的递归都是一个套路,递归是由两个要素组成的。

要素(1):找到每次自己连续地、“不确定次数”调用自己(将目前问题拆成更小的问题)  的那个 调用的通用公式,

这个对应的是下面调用代码的这一句“return f(n-1)+f(n-2) ”

        f(n) = return f(n-1)+f(n-2) 这句代码,想表达的思路是:上到目前这个台阶有两种可能性,一种可能是从下面一个台阶上来的,另一种从下面两个台阶上来。那么我们只需要将这两种可能性 各自的 可能的走的方式的数量加在一起,就可以得到所有的可能的走的方式
        也就是说整个可能的步数组合的方式,这个求f(n)的大问题,就被拆解成了求f(n-1)加上f(n-2) 这个两个子问题
——这两个子问题又可以进一步被拆分为 f(n-2)+f(n-3)加上 f(n-3)+f(n-4)这四个子问题
——那么,问题来了,是否会这样子子代代无穷矣地就这么拆下去呢?
        ——肯定不会啊!如果是无穷次的递归下去,电脑不死机了吗?我们的问题也没解决啊
        —— 当问题下钻到f(1)=1, f(2)=2的时候,递归循环终止了。
        ——最后根据递归循环的次数来组合f(1)=1和f(2)=2,得出你要的答案f(5) = 8

有的同学会觉得不应该是f(n-1)加f(n-2),而应该是f(n-1)乘f(n-2)。为了验证是“加”,不是“乘”,下面演示给你看。

——最后一行是f(n)这里是f(5)的爬楼梯各种组合的个数

——通过观察你可以发现f(n), 这里是f(5), 是由蓝色的f(n-1)和黄色的f(n-2)组成的。

——至此,成功验证了f(n) = f(n-1)+f(n-2)这个递归循环的通用公式这一公式的合理之处

要素(2):不停地自己调用自己,一直往下挖,挖到头了,再往下挖不动了的那些情况。不能再继续往下调用的bottom case。实际上也就是这些bottom case作为基本要素的拼装组合,才形成了最后的答案。

(2.1)首先我们要求出bottom case的函数值是多少?

首先,bottom case求的还是f(n),也就是bottom case求的还是上n个台阶有多少种走法

在这里,bottom case是n==1或n==2的时候,也就是f(1)和f(2)是bottom case。

n=1 jump(n)=1 ——1             一步走上去这一种可能性

n=2 jump(n)=2 ——1 1 / 2    “走一步,再走一步” 和“一次走两步”,2种可能性

也就是f(1)=1; f(2)=2

在上面那个图中 粉色的表示f(1)=1, 绿色的表示f(2)=2

前面我们讲了f(n)可以被分解为多个f(1)=1和多个f(2)之和的方式,具体多少个f(1)和多少个f(2)取决于递归的次数。下面展示一下,以防你不信

        首先是演算


# f(5) =           f(4)+                  f(3)
# f(5) =    f(3)+       f(2)  +     f(2)+ f(1)
# f(5) = f(2) +f(1) +   f(2)  +   f(2)+ f(1)
# f(5) =  2    + 1  +      2    +     2 +  1
# f(5) = 8

从中可以发现,f(5)是由3个f(2)和2个f(1)求和得到的。

图形化表示是下面这个图 

        也就是说f(n)最终求得数值,就是通过 多个f(2)=2和多个f(1)=1求和和组合得到的。也就是说f(2)和f(1)是f(n)组成的必要成分elements 。至于多少个f(2)和多少个f(1)这取决于你的递归次数,也就是n的取值

(例题3)正整数拆成多个1和3的相加求和

给正整数N,分解成1和3的组合。这里的组合指的是相加求和,而不是因式分解。⽐如N=4,分解为1111,13,31(递归)


s="" 
def gene(n,s): 
    """
    n -> 既是(1)剩余待填满的字符串的长度(字符串由几个数字组成),也是(2)被求和拆分的那个数字,比如被拆成3和1的那个4
    s -> 初始化字符串的内容,一般是一个空的字符串
    """

    # 如果n(剩余待填满的字符串的长度),在减三和减一的操作后  等于0或小于0了,那就说明“该填满的字符串长度”都已经填满了,任务完成,可以退出函数了
        # 等于0,说明目前这个字符串组合可以存进去
    if n==0: 
        result.append(s) 
        return 
        # 小于0,说明没啥可存了,可以直接退出了
    elif n<0: 
        return 
    

    # ————————这个位置——————————
    # 如果前面两个条件都不满足也就是说 n>=1 函数不会终止,会继续执行后面的代码
    s1=s+str(1) 
    gene(n-1,s1) 
    s2=s+str(3) 
    gene(n-3,s2) 
n=4 

# n表示被拆分的数字,这里是4
# s表示??暂时被拆分的东西?

result=[] 
gene(n=4,s="") 
print(result)

疑问

是通过怎么样的方式让,这个数字拆成3和1的组合的?

(例题4)括号生成

数字 n 代表⽣成括号的对数,请你设计⼀个函数,⽤于能够⽣成所有可能的并 且 有效的 括号组合。

• 示例 1: 输⼊:n = 3 输出:["((()))","(()())","(())()","()(())","()()()"]。输出的是3个括号对可能的组合

• 示例 2: 输⼊:n = 1 输出:["()"]

result=[] 
s="" 
def gene(n,l,r,s): 
    """
    l记录左边的括号数量
    r记录右边的括号数量
    """

    # 当左右两边的括号数,都同时到达n,将s这个位置的数据存进result里面
    if l==n and r==n: 
        result.append(s) 
        return 

    # 如果左边的括号数量少,就在左边加括号
    if l<n: 
        s1=s+"(" 
        # 左边的括号多一个,表示左边括号个数的l也要加一
        gene(n,l+1,r,s1) 
    
    # 如果右边括号数量小,就往右边加括号
    if l>r: 
        s2=s+")" 
        # 右边的括号多一个,表示右边括号个数的l也要加一
        gene(n,l,r+1,s2) 
n=3 
gene(n,0,0,s) 
print(result)

(例题5)全排列-不含重复数字

给定⼀个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按 任意顺序 返回答案。

Leetcode-46

输⼊:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]。

输⼊:nums = [0,1] 输出:[[0,1],[1,0]

result=[] 
def gene(nums,l): 
    if len(l)==len(nums): 
        result.append(l) 
        return 
    for a in nums: 
        if a not in l: 
            l1=l+[a] 
            gene(nums,l1) 
nums=[1,2,3] 
gene(nums,[]) 
print(result) 

不太懂

(例题6)全排列-含重复数字

给定⼀个含重复数字的数组 nums ,返回其 所有可能的全排列。(输出数组的长度要和输入长度是一样的) 。你可以 按任 意顺序 返回答案。

输⼊:nums = [2,2,3]

输出: [[2, 2, 3], [2, 3, 2], [3, 2, 2]]

result=[] 
def gene(nums,l,m): 
    if len(l)==len(nums) and l not in result: 
        result.append(l) 
        return 
    for i in range(len(nums)): 
        if i not in m: 
            l1=l+[nums[i]] 
            m1=m+[i] 
            gene(nums,l1,m1) 
nums=[2,2,3] 
gene(nums,[],[]) 
print(result)

真是没看懂

(例题7)求组合数

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

输⼊: n = 4, k = 2。 return 1到4中,所有可能的2个数的组合。

输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

res=[] 
def gene(n,k,l,start): # start 开始取数的位置
    """
    n表示数字的可能性是1到n
    k表示一个组合的数字的数量
    l表示初始的数组,一般是空列表
    start表示从哪个数字开始,一般是1
    """
    # 如果l这个列表的长度和要求的组合长度k相等,则说明组合完毕了,把最后一个结果放进result,就结束即可
    if len(l)==k: 
        res.append(l) 
        return 

    # 如果还没到达就继续组合
        # ????这段没看懂???
    for i in range(start,n+1): 
        gene(n, k, l+[i], i+1) 
n=4 
k=2 
gene(n,k,[],1)

res # [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

有一段没看懂 

如何学算法数据结构的题目?

(1)上面给出的例题,你能能过一张白纸扣上答案,通过自己的思考写出来。如果你通过自己的思考写不出来,你就把你这个思维链条上断开的地方拿着去找老师,问他为什么就能想出来、你为什么想不出来,这就是你和老师之间的差距,这也就是你知识上的欠缺

(2)去牛客网或力扣网,找这个 专题(如递归专题)的最简单的习题去做。同样的,不会的,去问老师。

猜你喜欢

转载自blog.csdn.net/Albert233333/article/details/132900266
今日推荐