根据人们长时间接触以来,发现计算机在计算某些一些简单的数据的时候会表现的比较笨拙,而这些数据的计算会消耗大量计算机资源,并且耗时,这个时候就有人对这类计算编写了一些策略,这些策略就是算法。这些策略会加快数据计算时间,大大减小计算机的资源消耗。算法
在长时间人们编写代码的工做中,一些优秀的算法就被流传下来,可是不是全部的算法都能实现目的一步到位的工做,它只能减小你的代码,提升工做效率,随着知识的不断积累,你会发现有更好的办法来完成算法所完成的事情。ide
当有一个文件,文件里面都是数字,要写一段代码查找用户须要的数字在什么位置。以前的知识咱们可能会使用循环去读取这个文件的每一行并最终找到这个数字所在的位置,此类方法能够达到目的,可是消耗了不少没必要要的时间和资源。函数
这个其实也都想到了就是算法,上面的问题思路是这样的,咱们首先把这个文件中数据的中间值取出来,让用户须要查找的数据这个这个中间值作比对,若是用户输入的数字大于这个中间值,那么我就日后找,再取一次这个中间值,再作比较,这样以此类推直到找到这个数字,并取出这个数字所在的位置便可。优化
#二分查找算法: #版本1: l = [2,4,5,6,8,13,22,24,44,56,66,67,68,88,89,90,99] def find(l,tag): #tag是用户输入要查找大数字 half_tag = len(l)//2 #取一半的下标,此处要使用整除,防止除不尽的状况 #假如中间值是44 if l[half_tag] < tag: #用取一半列表再去哥用户要找的数字比对 num_l = l[half_tag+1 :] #若是用户输入的数字大于一半的下标,那么就从44后面找,这个位置要加1,是由于44在大于的时候已经比对过了,要从56开始比对 #num_l = l[half_tag+1 :] #把上面切掉的一半数据赋予新的变量名。 find(num_l,tag) #从上面新变量里面再去找,用户输入的是否是大于或者小于列表,一直重复下去,直到找到数字取到下标 elif l[half_tag] > tag: num_l = l[:half_tag] #若是小于,就从24往前找,这个时候就不用加1,这个是切片的顾头不顾尾的原则 #half_tag = len(num_l)//2 find(num_l,tag) else: print('找到了!',half_tag,l[half_tag]) #最后一种状况就是找到了,那就直接打印便可。 find(l,66)
从上面的版本1,咱们初步的把整个咱们想实现目的的大体过程给写了出来,可是会发现有问题,咱们要取的数字下标在l列表里面,结果出来的下标是1,这个和实际的状况不相符,咱们要找的数字66是在l列表里面,不是在num_l新列表里面,接下里进行优化。spa
l = [2,4,5,6,8,13,22,24,44,56,66,67,68,88,89,90,99] def find(l,tag,start=0,end=len(l)): #既然上面咱们发现错误点是在新列表里面找的,那么咱们须要让代码不断的从原始的l列表里面找。 #那么就须要定义一个开始start和结束end,让下面的代码在l列表里面调整start和end的位置便可。 #half_tag=len(l)//2 若是仍是使用len(l)//2的话,half_tag就会是一个新的列表,那么仍是没有解决从l列表找的问题。 half_tag = (end - start)//2 + start #例如end表99的位置是20,start的值10,那么就是20-10/2=5,下标是5就不对了。 #下标是5就从l表前面去找了,因此要加一个start,这样取的就是l的中间值了。 if l[half_tag] < tag: find(l,tag,start=half_tag+1,end=end) #当取出一半的列表以后,find读取到用户在l列表里面找,开始的位置是上面比较以后的结果 #结束位置就是l列表结束的位置。 elif l[half_tag] > tag: find(l,tag,start=start,end=half_tag-1) else: print('找到了!',half_tag,tag) find(l,66)
版本2里面咱们已经解决了每次重新列表取的bug了,可是仍是存在问题,1:end这个参数有问题,2:返回值的问题,咱们虽然取到告终果,可是这个结果不能用于其它代码去调用,功能单一了,3:要是用户找的数字再也不l列表里面怎么办。code
l = [2,4,5,6,8,13,22,24,44,56,66,67,68,88,89,90,99] #问题1:若是l列表在函数代码下面出现,find函数就会出现问题。 def find(l,tag,start=0,end=None): #要解决上面的问题,须要把end变成默认参数 end = len(l) if end is None else end #在经过三元运算符来返回end,当end是空的时候就返回len(l),不然返回end(用户传过来的指) half_tag = (end - start)//2 + start if start <= end: #在find函数内部不断在执行的时候,会不断给start,end传输新的指,那么若是函数里面出现开始的指大于或者等于结束指了。 #那么理论上就出现错误了,说明该数字就不存在表里面。 if l[half_tag] < tag: find(l,tag,start=half_tag+1,end=end) elif l[half_tag] > tag: find(l,tag,start=start,end=half_tag-1) else: print('找到了!',half_tag,tag) else: print('找不到该数字') #当开始大于等于结束指了,就直接报错找不到 find(l,23) #问题2:要是要找的数字不在l列表里面,find函数确定会报错 # l = [2,4,5,6,8,13,22,24,44,56,66,67,68,88,89,90,99] # def find(l,tag,start=0,end=None): # end = len(l) if end is None else end # half_tag = (end - start)//2 + start # if start <= end: # if l[half_tag] < tag: # find(l,tag,start=half_tag+1,end=end) # elif l[half_tag] > tag: # find(l,tag,start=start,end=half_tag-1) # else: # return half_tag #若是我在此处使用return,他是直接把返回值给到了上一级的find,也就是判断l>tag地方的find,或者l<tag地方的find. # #由于函数内部在调用find,而这个find是不能接受这个返回值的,也就等于函数在上面的if地方就结束了。 # #函数在if的位置结束了,也就不会走到else的地方了,那么加在此处的return就return的是一个空,天然获得的结果就是none了 # else: # print('找不到该数字') # find(l,23) # #问题3:被查找到的数字不能被二次调用。 l = [2,4,5,6,8,13,22,24,44,56,66,67,68,88,89,90,99] def find(l,tag,start=0,end=None): end = len(l) if end is None else end half_tag = (end - start)//2 + start if start <= end: if l[half_tag] < tag: return find(l,tag,start=half_tag+1,end=end) elif l[half_tag] > tag: return find(l,tag,start=start,end=half_tag-1) else: return half_tag else: return '找不到该数字' #上面既然会出现大于,小于,等于,找不到四种状况,每种状况都有可能返回了指接收不了,那么咱们 #每次调用的指都返回回去,这样最外层的find就不会中断继续执行了,整个find函数就能够继续执行了 ret=find(l,23) print(ret) #问题3:被查找到的数字不能被二次调用。
上面的二分查找算法是使用递归函数来完成的,通过上面的验证,咱们队递归函数有又新的理解。blog
1:只要写递归函数,必定就须要结束的条件,而这个结束的条件就是你知道结果就应该要结束掉了;递归
2:返回值这个地方,须要看返回操做是在递归到第几层的时候发生的,返回了给了谁,若是这个返回值不是返回到最外层函数,调用的层面是接收不到的,因此说这个返回值必定要返回到最外层函数;资源
3:在实际的场景中,只要能用算法解决的事情,一定多多少少会用到递归函数,并且因此语言都有递归的概念;event
4:递归函数,最好是从结果往前推。
def fib(x): if x==1 or x==2: return 1 return fib(x-1)+fib(x-2) fib1=fib(6) print(fib1)
上面的斐波拉契函数正常咱们要查询数字比较小的,很快就能查出来,可是要是查询80或者100等,就会发现很慢,这个是由于return fib(x-1) + fib(x-2),这里面调用了两次fib函数,这会致使函数执行效率大打折扣,从表面咱们看到fib(x-1)就是fib(5),fib(x-2)就是fib(4),可是程序在执行的时候,是要先算出fib(5)=fib(x-1)也就是fib(4),fib(4)=fib(x-1)也就是fib(3),就这样一层层的算到初始值1+1,若是是50,等于fib两边的数字先要分别以一种金字塔的形式分别算下去,这个就是致使程序执行慢的根本缘由,因此说在递归函数,千万不要在内部调用屡次。
def fib(x): if x==2: return 1,1 else: a,b=fib(x-1) #这个else里面的代码就是解决上面fib调用屡次致使效率低的关键,由于你在计算3的时候你确定知道是1+2,那么在fib计算一次以后 #我就计算的数据赋值给两个变量。 return b,a+b#上面获得的两个值为了避免要重复计算我下一个指,我就把上一次计算的指返回去一个,例如:用户找4,那么就是fib(1)+fib(2), #用户在查询5的时候,我已经把fib(4)和fib(2)+fib(3)准备好了,且返回给了上一层的a,b两个变量,等于a,b从新被赋值了。 print(fib(3)) 解决查询的值会显示两位的问题 def fib(x,l=[0]): l[0]+=1 if x==1 or x==2: l[0]-=1 return 1,1 else: a,b=fib(x-1) l[0]-=1 if l[0]==0: return a+b return b,a+b print(fib(10))
def fac(x): if x==1: return 1 return x*fac(x-1) print(fac(5))
5:递归函数必定要考虑到最大递归的998的问题,在以前的文章中有必定的介绍。