什么是递归?

递归,就是函数在运行的过程中调用自己。

代码示例

def recursion(n):

    print(n)
recursion(n+) recursion()  

出现的效果就是,这个函数在不断的调用自己,每次调用就n+1,相当于循环了。

可是为何执行了900多次就出错了呢?还说超过了最大递归深度限制,为什么要限制呢?

通俗来讲,是因为每个函数在调用自己的时候 还没有退出,占内存,多了肯定会导致内存崩溃。

本质上讲呢,在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

递归的特点

让我们通过现象来看本质, 下面是是用递归写的,让10不断除以2,直到0为止。

打印的是1, 然后最里层的函数就结束了,结束后会返回到之前调用它的位置。即上一层,上一层打印的是2,再就是5,再就是10,即最外层函数,然后结束,总结,这个递归就是一层层进去,还要一层层出来。

通过上面的例子,我们可以总结递归几个特点:

  1. 必须有一个明确的结束条件,要不就会变成死循环了,最终撑爆系统
  2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  3. 递归执行效率不高,递归层次过多会导致栈溢出

递归有什么用呢?

可以用于解决很多算法问题,把复杂的问题分成一个个小问题,一一解决。

比如求斐波那契数列、汉诺塔、多级评论树、二分查找、求阶乘等。用递归求斐波那契数列、汉诺塔 对初学者来讲可能理解起来不太容易,所以我们用阶乘和二分查找来给大家演示一下。

求阶乘

任何大于1的自然数n阶乘表示方法: 
n!=1×2×3×……×n 
或 
n!=n×(n-1)!

即举例:4! = 4x3x2x1 = 24

用递归代码来实现

def factorial(n):

    if n == : #是0的时候,就运算完了
return
return n * factorial(n-) # 每次递归相乘,n值都较之前小1 d = factorial()
print(d)

2分查找

在一个已排序的数组data_set中,使用二分查找n,假如这个数组的范围是[low...high],我们要的n就在这个范围里。查找的方法是拿low到high的正中间的值,我们假设是mid,来跟n相比,如果mid>n,说明我们要查找的n在前数组data_set的前半部,否则就在后半部。无论是在前半部还是后半部,将那部分再次折半查找,重复这个过程,知道查找到n值所在的地方。

data_set = list(range())

def b_search(n,low,high,d):

    mid = int((low+high)/) # 找到列表中间的值
if low == high:
print("not find")
return
if d[mid] > n: # 列表中间值>n, 代数要找的数据在左边
print("go left:",low,high,d[mid])
b_search(n,low,mid,d) # 去左边找
elif d[mid] < n: # 代数要找的数据在左边
print("go right:",low,high,d[mid])
b_search(n,mid+,high,d) # 去右边找
else:
print("find it ", d[mid]) b_search(, ,len(data_set),data_set)
go right:
go right:
go right:
go right:
go right:
go right:
not find

最多将会操作7次,其实因为每一次我们都抛掉当前确定的区间的一半的区间作为不可能解部分,那么相当于求最多操作次数,就是在区间内,最多将有多少个一半可以抛去、那么就是将100一直除以2,直到不能除为止。

那么这个运算过程,其实就是相当于求了一个log2(100)≈7。

补充:

在讲特性时,我们说递归效率不高,因为每递归一次,就多了一层栈,递归次数太多还会导致栈溢出,这也是为什么python会默认限制递归次数的原因。但有一种方式是可以实现递归过程中不产生多层栈的,即尾递归,

尾递归

在函数最尾部有return,return值是递归形式调用,且返回值与上一层函数无任何依赖。

尾递归例子

def calc(n):
print(n - )
if n > -:
return calc(n-)

我们之前求的阶乘是尾递归么?

def factorial(n):

    if n == : #是0的时候,就运算完了
return
return n * factorial(n-) # 每次递归相乘,n值都较之前小1 d = factorial()
print(d)

上面的这种递归计算最终的return操作是乘法操作。所以不是尾递归。因为每个活跃期的返回值都依赖于用n乘以下一个活跃期的返回值,因此每次调用产生的栈帧将不得不保存在栈上直到下一个子调用的返回值确定。

Python3-递归函数的更多相关文章

  1. python3 递归函数return返回None

    今天写了一个函数,执行之后打印出来的结果是None,不明白,之后百度了一下,这里记一下过程,免得之后再踩坑 #!/usr/bin/python3# -*- coding:utf-8 -*- def b ...

  2. 第十一天python3 递归函数

    递归Recursion 函数直接或者间接调用自身就是递归: 递归需要有边界条件,递归前进段.递归返回段: 递归一定要有边界条件: 当边界条件不满足的时候,递归前进: 当边界条件满足的时候,递归返回: ...

  3. Python3学习(二)-递归函数、高级特性、切片

    ##import sys ##sys.setrecursionlimit(1000) ###关键字参数(**关键字参数名) ###与可变参数不同的是,关键字参数可以在调用函数时,传入带有参数名的参数, ...

  4. Python自学笔记-递归函数(来自廖雪峰的官网Python3)

    感觉廖雪峰的官网http://www.liaoxuefeng.com/里面的教程不错,所以学习一下,把需要复习的摘抄一下. 以下内容主要为了自己复习用,详细内容请登录廖雪峰的官网查看.   递归函数 ...

  5. Python3练习题 022:用递归函数反转字符串

    方法一 str = input('请输入若干字符:')   def f(x):     if x == -1:         return ''     else:         return s ...

  6. 4.6 Python3 进阶 - 递归函数

    >>返回主目录 源码 # 定义及特性 # 简单递归思维练习,求和:1+2+3+-+100 # 思路:sum(100) = sum(99) + 100 # sum(99) = sum(98) ...

  7. Python 第四篇:生成器、迭代器、装饰器、递归函数与正则表达式

    一:生成器:Generator,可以理解为是一种一个函数产生一个迭代器,而迭代器里面的数据是可以通过for循环获取的,那么这个函数就是一个生成器,即生成器是有函数生成的,创建生成器使用()表示,比如g ...

  8. 汉诺塔python3函数编写和过程分析

    !/usr/bin/env python3 -- coding: utf-8 -- 利用递归函数计算阶乘 N! = 1 * 2 * 3 * ... * N def fact(n): if n == 1 ...

  9. 廖雪峰Python3笔记

    主要复习过一遍 简介 略 安装 略 *** 第一个Python程序 第一行的注释: # _*_ coding: utf-8 _*_ #!/usr/bin/env python3 print() 可以接 ...

  10. 14 python初学(高阶函数 递归函数 内置函数)

    高阶函数:1.  函数名是一个变量,函数名可以进行赋值 2. 函数名可以作为函数参数,还可以作为函数返回值(函数名称作为函数返回值时返回的是:函数的地址:print 这个返回值的调用相当于执行这个函数 ...

随机推荐

  1. 用count sql做jmeter的while条件

    ${__groovy(vars.get("pboc_req_record_counter_1") == "Invalid"  || vars.get(" ...

  2. 信用评分卡 (part 4 of 7)

    python信用评分卡(附代码,博主录制) https://study.163.com/course/introduction.htm?courseId=1005214003&utm_camp ...

  3. docker 私有仓库之Harbor搭建与使用

    Harbor搭建 下载Harbor: http://harbor.orientsoft.cn/ #下载offline版本 配置Harbor tar xvf harbor-online-installe ...

  4. Java Web之Web组件之间的跳转方式

    方法有3种: 第一种:请求转发 我们来写两个类,一个是Servlet1一个是Servlet2 package main.com.vae.forward; import javax.servlet.Se ...

  5. java assert的用法简介【转】

    assert的基本用法 assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制,如C,C++和Eiffel等,但是支持的形式不尽相同,有的是通过语言本身.有的是通过库 ...

  6. jenkins笔记

    java -jar jenkins.war -httpPort=9090 以9090端口启动jekins的web应用(内置jetty)

  7. C#中ICollection介绍

    ICollection 接口是 System.Collections 命名空间中类的基接口,ICollection 接口扩展 IEnumerable,IDictionary 和 IList 则是扩展 ...

  8. svn Edge访问规则配置

    权限的配置方法: 首先在用户页面定义用户,然后在版本库页面,手工填写访问规则. 以下面的配置为例(实际配置中应删除“//”及其后的注释): [groups]               //定义用户组 ...

  9. 042、用volume container 共享数据 (2019-03-05 周二)

    参考https://www.cnblogs.com/CloudMan6/p/7188479.html   volume container 是专门为其他容器提供 volume 的容器,他提供的卷也可以 ...

  10. docker 系列 - 基础镜像环境和Docker常用命令整理

    =======================docker 基础镜像环境 alpine=======================可以使用 docker search 命令搜索指定的 image, ...