目录
1.基本概念
"""
递归:递“去”,回“归”!
递推:像上边递归实现所拆解,递归每一次都是基于上一次进行下一次的执行,这叫递推
回溯:则是在遇到终止条件,则从最后往回返一级一级的把值返回来,这叫回溯
注意:
1.首先,递归会执行“去”的过程,只需要满足终止条件,就会一直在函数内,带着更新的参数,调用函数自身
2.此时 随着函数每调用一次自身,还没有触发 返回值和到达终止条件,等同于在原来的基础上不断“向下/向内”开辟新的内存空间,所以, 每次调用一次函数,就不是处在同一空间
3.一个普通函数从执行到结束,就是一个开辟空间和释放空间的过程;而递归函数是在调用最外层函数时,先开辟一个最外层空间,每调用一次自身,就会在最外层空间内,再自己开辟本次的空间(所以递归耗内存)
4.return 是返回到函数调用者,递归函数和普通函数也是一样的,所以递归最后一层空间走到尽头(一是:指向完毕,二是:遇到return)遇到return,就要开始返回了,返回到它的调用者(即是阻塞的位置),直到回到最外层空间
"""
2.案例说明
# 排序-快速排序(递归)
def quick_sort_arithmetic(list_tmp):
"""
注意: 分区虽然没有让整个数组变得有序,但是让基准值找到了自己应该在的位置,多左右两侧重复分
区动作,每一次分区动作都至少让一个元素找到自己应该在的位置
1. 在数列之中,选择一个元素作为”基准”(pivot),或者叫比较值。
2. 数列中所有元素都和这个基准值进行比较,如果比基准值小就移到基准值的左边,如果比基
准值大就移到基准值的右边
3. 以基准值左右两边的子列作为新数列,不断重复第一步和第二步,直到所有子集只剩下一个
元素为止。
例如,假设我现在有一个数列需要使用快排来排序:[39, 33 , 69, 77, 88, 55, 11, 33, 36,39,
66, 44, 22],
快排步骤:
1. 选取中间的66作为基准值(基准值可以随便选)
2. 数列从第一个元素11开始和基准值66进行比较,小于基准值,那么将它放入左边的分区中,第二个
元素99比基准值66 大,把它放入右边的分区中。
3. 然后依次对左右两个分区进行再分区,直到最后只有一个元素
分解完成再一层一层返回,返回规则是:左边分区+基准值+右边分区
因为每次将序列切分为2部分,如果切割了n次把元素切割完,序列的长度为m,则2n=m,所以切割的时
间复杂度为log(n),在每次切割的时候都会比较所有的元素,需要n次,所以整体的时间复杂度为
O(nlog(n))。在比较的过程中元素的位置可能挪动,所以快速排序是不稳定的排序。
"""
if len(list_tmp) < 2:
return list_tmp
# 选取基准
mid_pivot = list_tmp[len(list_tmp) // 2] # ’//‘ 取整
# 定义基准值左右两个数列
left_list, right_list = [], []
# 从原始数组中移除基准值
list_tmp.remove(mid_pivot)
for item in list_tmp:
# 大于基准值放在右边
if item > mid_pivot:
right_list.append(item)
else:
# 小于基准值放在左边
left_list.append(item)
# 使用迭代进行比较, 递归调用,排列左边和右边的序列
return quick_sort_arithmetic(left_list) + [mid_pivot] + quick_sort_arithmetic(right_list)
二分查找
"""
1. 二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。
2. 二分查找的主要思想是:充分利用数组nums已经排好序的特点,我们取数组nums的中间位置(mid)的元素n_1,用n_1与目标数字x进行比较,如果n_1 等于x,那么直接返回n_1的位置mid;如果n_1大于x,则说明x如果在数组中的话,一定是在n_1的左侧;如果n_1小于x,则说明x如果在数组中的话,一定是在n_1的右侧。
3. 二分查找: (1)预处理 —— 如果集合未排序,则进行排序。
(2)二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半。
(3)后处理 —— 在剩余空间中确定可行的候选者。
"""
# 二分查找-一般方法
def binary_search(list_tmp, target):
"""
一般方法实现二分查找
二分查找的最基础和最基本的形式。
初始条件:left = 0, right = length-1
终止:left > right
向左查找:right = mid-1
向右查找:left = mid+1
:param list_tmp:待查找列表
:param target:待查找目标元素
:return:找到返回索引,否则,返回-1
"""
# 1. 列表排序
list_tmp_sort = quick_sort_arithmetic(list_tmp)
# 2. 二分查找
if len(list_tmp) == 0:
return -1
left_num = 0
right_num = len(list_tmp_sort)
while left_num <= right_num:
mid_num = (left_num + right_num) // 2
if list_tmp_sort[mid_num] == target:
return mid_num
elif list_tmp_sort[mid_num] > target: # 向左查找
right_num = mid_num - 1
else:
left_num = mid_num + 1
# 终止:left > right
return -1
# 二分查找-递归
def binary_search_recursive(list_tmp, target_num, left, right):
"""
通过递归实现二分查找,如果找到了要求的数,返回它的索引
:param list_tmp:待查找列表
:param target_num:待查找元素
:param left:查找下限,一般为0
:param right:查找上限,一般为len(list_tmp) - 1
:return:存在返回查询到的数的索引,不存在返回-1
"""
# 1. 列表排序
list_tmp_sort = quick_sort_arithmetic(list_tmp)
# 2. 二分查找
if left <= right: # 边界
mid = (left + right) // 2 # 折中取整
if target_num > list_tmp_sort[mid]: # 向右查找
left = mid + 1
return binary_search_recursive(list_tmp_sort, target_num, left, right) # 每一层都要返回给上一层的调用者
elif target_num < list_tmp_sort[mid]:
right = mid - 1
return binary_search_recursive(list_tmp_sort, target_num, left, right)
elif target_num == list_tmp_sort[mid]:
return mid # 多层函数只会将返回值返回给上一层的调用者
else:
return -1