一、递归函数

江湖上流传这这样一句话叫做:人理解循环,神理解递归。所以你可别小看了递归函数,很多人被拦在大神的门槛外这么多年,就是因为没能领悟递归的真谛。

递归函数:在一个函数里执行再调用这个函数本身。
递归的默认最大深度:998

举例,先来一个死循环

def func1():
print(666) while True:
func1()

执行输出:

666

...

递归函数

def func1():
print(666)
func1() func1()

执行输出:

666

...

RecursionError: maximum recursion depth exceeded while calling a Python object

那么它是执行到多少次时,报错呢?

加一个计数器

count = 0
def func1():
global count
count += 1
print(count)
func1() func1()

执行输出:

1

...

998

...

RecursionError: maximum recursion depth exceeded while calling a Python object

说明默认递归深度为998

这个递归深度是可以改的

import sys
#更改默认递归深度
sys.setrecursionlimit(100000) count = 0
def func1():
global count
count += 1
print(count)
func1() func1()

执行输出:

...

3807

我的wind10系统,深度只能到这么多

至于实际可以达到的深度就取决于计算机的性能了

linux系统,深度能够达到更深。比如说1000万,我开虚拟机测试的。

不过我们还是不推荐修改这个默认的递归深度,因为如果用997层递归都没有解决的问题要么是不适合使用递归来解决要么是你代码写的太烂了

举个例子来说明递归能做的事情

现在你们问我,alex老师多大了?我说我不告诉你,但alex比 egon 大两岁。

你想知道alex多大,你是不是还得去问egon?egon说,我也不告诉你,但我比武sir大两岁。

你又问武sir,武sir也不告诉你,他说他比太白大两岁。

那你问太白,太白告诉你,他18了。

这个时候你是不是就知道了?alex多大?

1 金鑫   18
2 武sir   20
3 egon   22
4 alex    24
你为什么能知道的?

首先,你是不是问alex的年龄,结果又找到egon、武sir、太白,你挨个儿问过去,一直到拿到一个确切的答案,然后顺着这条线再找回来,才得到最终alex的年龄。这个过程已经非常接近递归的思想。我们就来具体的我分析一下,这几个人之间的规律。

age(4) = age(3) + 2
age(3) = age(2) + 2
age(2) = age(1) + 2
age(1) = 40

那这样的情况,我们的函数怎么写呢?

def age(n):
if n == 1:
return 40
else:
return age(n-1)+2 print(age(4))

执行输出: 24

二、二分查找法

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列

算法有很多种,比如:二分查找,树运算,堆,栈....

使用二分查找的前提

数据是有序且唯一的数字数列

举例:

有一个列表,需要查找出索引为66的值

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

第一种方法: 直接使用index查询

print(l.index(66))

执行输出:17

第二种方法: 使用for循环

count = 0
for i in l:
if i == 66:
print(count)
break
count += 1

执行输出,效果同上

假如我们这个列表特别长,里面好好几十万个数,效率太低了

需要使用二分查找算法

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

你观察这个列表,这是不是一个从小到大排序的有序列表呀?
如果这样,假如我要找的数比列表中间的数还大,是不是我直接在列表的后半边找就行了?

这就是二分查找算法!
那么落实到代码上我们应该怎么实现呢?
简单版二分法

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

def func(l,aim):
mid = (len(l)-1)//2
if l:
if aim > l[mid]:
func(l[mid+1:],aim)
elif aim < l[mid]:
func(l[:mid],aim)
elif aim == l[mid]:
print("bingo",mid)
else:
print('找不到') func(l,66)
func(l,6)

执行输出:

bingo 0
找不到

升级版二分法

把列表改短一点

li = [2, 3, 5, 10, 15, 33, 55]
def two_search(li, aim, start=0, end=None):
end = len(li) if end is None else end
mid_index = (end - start) // 2 + start # 3
if start <= end:
if li[mid_index] < aim:
return two_search(li, aim, start=mid_index+1, end=end)
elif li[mid_index] > aim:
return two_search(li, aim, start=start, end=mid_index-1) #([2,3,5],3)
elif li[mid_index] == aim:
return mid_index
else:
return '没有此值'
else:
return '没有此值'
print(two_search(li,15))

执行输出:

4

加日志执行:

li = [2, 3, 5, 10, 15, 33, 55]
count = 0
def two_search(li, aim, start=0, end=None):
global count
count += 1
end = len(li)-1 if end is None else end #结束索引值,len(li)要减1,否则如果传100,函数无法结束。
mid_index = (end - start) // 2 + start #中间索引 #加日志
print('第{}执行,start的值为{},end的值为{},mid_index的值为{},li[mid_index]的值为{}'.format(count, start,end,mid_index,li[mid_index])) if start <= end:
if li[mid_index] < aim:
return two_search(li, aim, start=mid_index+1, end=end)
elif li[mid_index] > aim:
return two_search(li, aim, start=start, end=mid_index-1) #([2,3,5],3)
elif li[mid_index] == aim:
return mid_index
else:
return '没有此值'
else:
return '没有此值' print(two_search(li,15))

执行输出:

第1执行,start的值为0,end的值为6,mid_index的值为3,li[mid_index]的值为10
第2执行,start的值为4,end的值为6,mid_index的值为5,li[mid_index]的值为33
第3执行,start的值为4,end的值为4,mid_index的值为4,li[mid_index]的值为15
4

解释:

先来解释一下中间索引  // 表示取整除 - 返回商的整数部分 ,比如9//2 输出结果 4

列表有7个元素,第一次,执行时,start为0,(end - start) // 2 + start 等于(6-0)//2 + 0    mid_index的值为3,li[mid_index]的值为10

那么为什么mid_index的等式,后面要加start呢?

第一次执行时,中间索引值,取3

第二次执行时,中间索引值,取5

如果不加start,那么第二只还是取的是3

第一次执行时,li[mid_index]的值为10,aim的值为15,进入if判断,走第一个if条件

因为找到的中间值比目标值要小,所以start要在mid_index的基础上加1

通俗的来讲,劈一半,左边找的值太小,就找右边,所以要加1

第二次执行时,li[mid_index]的值为33,aim的值为15,进入if判断,走第二个if条件

因为找到的中间值比目标值要大,start依然不变,end要在mid_index的基础上减1

通俗的来讲,再劈一半,左边找的值太大,就继续找左边的,所以要减1

第三次执行时,li[mid_index]的值为15,aim的值为15,进入if判断,走第三个if条件

已经找到目标值了,直接return mid_index 结束整个函数

python 全栈开发,Day15(递归函数,二分查找法)的更多相关文章

  1. python全栈开发之匿名函数和递归函数

    python 匿名函数和递归函数 python全栈开发,匿名函数,递归函数 匿名函数 lambda函数也叫匿名函数,即函数没有具体的名称.是为了解决一些功能很简单需求而设计的一句话函数.如下: #这段 ...

  2. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

  3. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  4. python全栈开发中级班全程笔记(第二模块、第四章(三、re 正则表达式))

    python全栈开发笔记第二模块   第四章 :常用模块(第三部分) 一.正则表达式的作用与方法 正则表达式是什么呢?一个问题带来正则表达式的重要性和作用      有一个需求 : 从文件中读取所有联 ...

  5. python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)

    python全栈开发笔记第二模块 第四章 :常用模块(第二部分)     一.os 模块的 详解 1.os.getcwd()    :得到当前工作目录,即当前python解释器所在目录路径 impor ...

  6. python全栈开发中级班全程笔记(第二模块、第三章)(员工信息增删改查作业讲解)

    python全栈开发中级班全程笔记 第三章:员工信息增删改查作业代码 作业要求: 员工增删改查表用代码实现一个简单的员工信息增删改查表需求: 1.支持模糊查询,(1.find name ,age fo ...

  7. python全栈开发之正则表达式和python的re模块

    正则表达式和python的re模块 python全栈开发,正则表达式,re模块 一 正则表达式 正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的 ...

  8. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  9. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

随机推荐

  1. Java中Dom4j解析XML

    与利用DOM.SAX.JAXP机制来解析xml相比DOM4J表现更优秀,具有性能优异.功能强大和极端易用使用的特点,只要懂得DOM基本概念,就可以通过dom4j的api文档来解析xml.dom4j是一 ...

  2. xml总结(一 )

    一.了解 XML(eXtensive Markup Language)可扩展标记语言. XML是被用来传输和存储数据,焦点是内容,是对html的补充. HTML是将数据进行格式化显示.xml需要自定义 ...

  3. VS设置以管理员方式运行

    一直以为VS不能直接以管理员方式运行,原来它是在高级里的.

  4. AQS框架

    java并发包基石 AQS是JUC中很多同步组件的构建基础,简单来讲,它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败 ...

  5. 【BARTS计划】【Review_Week1】Google Docs 成为青少年们喜爱的聊天 app

    BARTS计划 · Review :每周阅读并点评至少一篇英文技术文章. 附原文链接 Google Docs 本是作为协作办公工具存在的,却成了学生们现代版“传纸条”的工具.认真的说,“你永远不知道用 ...

  6. 2017/05/08 java 基础 随笔

    1.null pointer exception 指针变量obj 没有指向任何空间 你调用它的方法和属性就会出错 2.基本数据类型值传递,不改变原值,调用后就会弹栈,局部变量随时消失 引用数据类型值传 ...

  7. ProcessHacker可编译版本

    说明 做一个批量进程内搜索字符串的工具. 试了processhacker-2.39-src.zip. https://sourceforge.net/projects/processhacker/fi ...

  8. 【逆向知识】动态调试技巧-C++代码逆向

    1.C++类代码的特点 寄存器ECX传参时一般用作this指针(对象地址)或是计数器. 有ecx传参的call,是成员函数,构造函数,析构函数 能访问成员变量的函数都会有ecx传参 静态函数.全局函数 ...

  9. python日志和异常

    “日志”转载:http://www.cnblogs.com/dkblog/archive/2011/08/26/2155018.html "异常"转载:http://www.cnb ...

  10. 【shell】查找后拷贝find . -name *.csv -exec cp {} /home/ \;

    Find命令的一般形式为: find pathname -options [-print -exec -ok] 让我们来看看该命令的参数: pathname: find命令所查找的目录路径.例如用.来 ...