递归算法 (算法图解笔记3)

什么是递归

再计算机程序中, 描述迭代的一种方法是使用循环,比如for 和while。另一种完全不同的方法就是递归 。
递归是一种技术,这种技术再通过一个函数在执行过程中一次或者多次调用器本身 。举一个具体的例子,如何用递归写一个函数用来求阶乘

n! = n(n-1)(n-2)···2x1

def factorial(n):
    if n ==1 :
        return 1 
    return n* factorial(n-1)
In: factorial(5)
Out: 120

可以看出,在执行factorial的过程中,调用了factorial自身,这就是递归。

基线条件和递归条件

实际上一切循环过程都可以用递归解决,Smalltalk语言就不支持循环结构,必须使用递归。
而通常使用for和while的思路是明确的,在满足循环条件时,执行循环语句 。换句话说,在满足循环停止条件时,不再执行循环语句。 而递归是自己调用自己,用这样的思路解决问题,思路好像不是很明确 。但只要确定基线条件和迭代条件就没问题了 。

递归条件,就是当该条件满足时,函数自己调用自己、
基线条件,就是当该条件满足时,函数不再调用自己(相当于while循环的停止条件)

比如,下面编写一个倒计时的函数

递归算法 (算法图解笔记3)_第1张图片

栈是什么

栈是由一系列对象组成的一个集合,这些对象满足后近先出(LIFO)的原则。 和自动贩卖机的那一列快乐肥宅水一样,也就是删除和插入都是最后一个。

调用栈

执行递归的过程可以是调用栈的过程, 如下图, 调用的函数的执行内存都存放在栈对象中,执行完后被释放。
递归算法 (算法图解笔记3)_第2张图片

使用栈虽然很方便,但是也要付出代价:存储详尽的信息可能占用大量的内存。每个函数调
用都要占用一定的内存,如果栈很高,就意味着计算机存储了大量函数调用的信息。

分而治之

分而治之是一种著名的递归方法 ,优雅而聪明,提供了一种解决问题的思路,是另一个可供你使用的工具。面对新问题时,你不再束手无策,而是自问:“使用分而治之能解决吗?” 分而治之的核心思路:
(1) 找出简单的基线条件;
(2) 确定如何缩小问题的规模,直到其符合基线条件。
举个例子更容易说清楚 , 下面定义一个的列表求和函数 。
首先确定基线条件,什么时候最简单呢,肯定是

list = [ ] ,sum(list) = 0 。

不断的分解列表,直到符合基线条件 。

递归算法 (算法图解笔记3)_第3张图片

def sum_list(list):
    if list == []:
        return 0 
    else : 
        return list[0] + sum_list(list[1:])
 
In:sum_list([2,3,1,4])        
Out: 10

最经典的分而治之的例子就是递归法解决快速排序 。
什么样的情况对于排序算法最简单呢 ?

  1. len(list) <=1 ,不用排序
  2. len(list) = 2 两个元素排序, 小的在前,大的在后

上述两种作为基线条件, 下一步将其他的情况不断缩小问题规模直到达到基线条件。
如果是两个以上的列表,就用第一个元素的值为基准线将列表拆开, [2,0,3] 拆为 [2], [0], [3], 每个部分都排序后再拼接。更大长度的列表,以此类推 。

递归算法 (算法图解笔记3)_第4张图片

def quicksort(List):  # DIVIDE AND CONQUER 
    if len(List)<= 1:
        return List
    elif len(List)==2 :
        if List[0]>List[1]:
            return List[::-1] # 调换次序
        else: return List
    else:        
        left_list = [i for i in List if i<List[0]]
        right_list = [i for i in List if i >List[0]]
        return quicksort(left_list)+ List[:1] + quicksort(right_list)

In: quicksort([3,2,1,5,6,4])
Out: [1, 2, 3, 4, 5, 6]

总结

  1. 分而治之将问题逐步分解。使用D&C处理列表时,基线条件一般是该问题最简单的情况,很可能是空数组或只包含一个元素的数组。

你可能感兴趣的:(python)