递归 | 二分查找
主要内容:
- 1.递归
- 2.列表查找 - 二分查找
1. 递归
1.1 递归的特点
- 调用自身
- 结束条件
判断下列函数是否是递归
def func(x): print(x) func(x-1) #没有结束条件 --> 递归出口 def func(x): if x > 0: print (x) func(x +1)
# 递归调用之前对x进行 打印 def func(x): if x > 0: print(x) func(x-1) func(3) # 输出结果 3,2,1
#递归调用之后进行调用 def func(x): if x > 0: func(x-1) print(x) func(3) # 输出结果 1,2,3
1.2 递归实例 -- 斐波那契问题
斐波那契数列:
斐波那契数列数列从第3项开始,每一项都等于前两项之和。 例子:数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ..........
求解斐波那契第 n 项
- 使用递归 时间复杂度 O(2^n)
#使用递归,但是注意此时最后的return 语句不是尾递归 def func(n): if n==1 or n==2: return 1 else: return func(n-1) + func(n-2)
使用递归慢的原因: 由于需要多次的重复计算,导致很慢
- 使用递归的改进方法 : 通过记录值解决重复计算的问题,但是时间复杂度没有改变
def func(n): #创建一个列表,此时列表中的元素 未被赋值前是-1 ,当赋值后变为正数 lst = [-1 for i in range(n+1)] def fib(n): if n == 0 or n == 1: return 1 elif lst[n] >= 0: return lst[n] else: v = fib(n-1) + fib(n-2) #只是记录一下,下次再用的时候直接扔出去 lst[n] = v return lst[n] return fib(n)
- 使用列表,将后一项添加到列表中,return 列表最后一项 时间复杂度O(n)
def func(n): res = [1,1] for i in range(2,n+1): res.append(res[-1] + res[-2]) return res[-1]
上述方式的缺点: 虽然时间复杂度为O(n) ,但是创造了列表 ,空间复杂度增加
- 使用3变量的方式 ,时间复杂度为 O(n) ,但是省空间
def func(n): if n == 0 or n == 1: return 1 a = 1 b = 1 c = 0 #c可以是任何值 for i in range(2,n+1): c = a + b a = b b = c return c
更优化版:
def func(n): a = 1 b = 1 c = 1 for i in range(2,n+1): c = a + b a = b b = c return c
1.3 递归实例2 - 汉诺塔问题
问题描述:
- 假设有三个命名为 A B C 的塔座 ,在塔座A上插有n个直径大小不相同,由小到大编号为1 ,2 ,3 ,··· ,n的圆盘,要求将A座上的圆盘移至塔座C
- 并按同样的顺序叠排
圆盘移动规则:
- 每次只能移动一个圆盘
- 圆盘可以插在任意一个塔座上
- 任何时刻都不能将一个较大的圆盘放在一个较小的圆盘上
问题简化 :
- 将 n - 1 个圆盘 看成一个整体,将其从A经过C移动到B
- 将第 n个盘子从A移动到C
- 将开始的n - 1 个盘子从B 经过A移动到C
代码书写
- 汉诺塔移动次数的递推式 h(x) = 2h(x-1) +1
def hanoi(n,A,B,C): ''' :param n: 需要从A移到C 的盘子总数 :param A: 移动盘子的起始柱子 :param B: 移动过程中经过的柱子 :param C: 移动结束的柱子 ''' if n > 0 : hanoi(n-1,A,C,B) #将起始A柱子上的n-1个盘子通过C移动到B print("%s -> %s" %(A,C)) #将底层的盘子从A放到C hanoi(n-1,B,A,C) #将B上的n-1 个盘子经由A移动到C
1.4 题目练习
问题描述:
- 一段有n个台阶组成的楼梯,小明从楼梯的最底层向最高处前进,
- 他可以选择一次迈一级台阶或者一次迈两级台阶。问:他有多少种不同的走法?
2. 列表查找 - 二分查找
列表查找 :从列表中查找指定元素
- 输入: 列表,带查找元素
- 输出: 元素下标或未查找到的元素
顺序查找:从列表的第一个元素开始,顺序进行搜索,直到找到为止
# 顺序查找代码,时间复杂度为O(n) def linear_search(data,value): ''' :param data: 查找的目标列表 :param value:目标查找元素 :return: ''' for i in range(data): if data[i] == value: return i return
二分查找: 从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半
def binary_search(lst,val): left = 0 right = len(lst) -1 while left <= right: mid = (left + right) // 2 if val == lst[mid]: return mid elif val < lst[mid]: right = mid -1 else: left = mid +1 return
递归版本的二分查找
def bin_search(lst,val,left,right): if left <= right: mid = (left + right) // 2 if lst[mid] == val: return mid elif lst[mid] > val: return bin_search(lst,val,left,mid-1) else: return bin_search(lst,val,mid+1,right) else: return # 上述return属于尾递归 : 编译器会优化成迭代 但是Python没有针对尾递归进行优化