递归算法

漫谈递归和迭代

亡梦爱人 提交于 2019-12-01 04:59:06
先讲个故事吧。 从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……’”。 这个故事永远也讲不完,因为没有递归结束条件。老师讲递归时总是说,递归很简单,一个递归结束条件,一个自己调用自己。如果递归没有结束条件,那么就会无限递归下去。在编程的时候,没有递归结束条件或者递归过深,一般会造成栈溢出。 下面这个函数,可以利用栈溢出来估测栈的大小: 1 void stack_size() 2 { 3 static int call_time = 0; 4 char dummy[1024*1024]; 5 call_time++; 6 printf("call time: %d\n",call_time); 7 stack_size(); 8 } 递归算法一般用于解决三类问题:这个函数定义了1M的局部变量,然后调用自己。栈溢出时会崩溃,根据最后打印出的数字可以算一下栈的大小。 (1)数据的定义是按递归定义的。(Fibonacci函数) (2)问题解法按递归算法实现。(回溯) (3)数据的结构形式是按递归定义的。(树的遍历,图的搜索) 对于求1+2+3+…+n这种问题,大部分人不会用递归方式求解: 1 2 3 4

第2章作业

僤鯓⒐⒋嵵緔 提交于 2019-12-01 02:12:28
1.对分治法思想的体会 分治法思想是将一个规模较大的问题分成若干个与原问题相同、互相独立且规模大致相同的问题。运用分治策略可以提高一些算法的效率,如二分搜索算法与普通的搜索算法相比,在时间复杂度上有很大的改进。使用分治法,一个难点就是如何分割子问题,在解决这一问题上,需要用到平衡子问题的思想。但是,采用分治法设计的程序一般是递归算法,也就是说,我们在使用分治策略的同时,必须对递归有一定的掌握,需要注意递归结束条件的设置,以及建立合理的递归方程,从而尽量避免一些潜在的且难以发现的问题。使用递归尤其需要注意的是,当递归层次过深时,可能会造成递归调用工作栈溢出,即递归的使用对于问题的规模有一定的限制。因此,在运用分治策略时需要考虑到这些因素。 2.结对编程情况汇报 结队编程,可以帮助我们打破自己存在的潜在的思维禁锢,也有助于改进我们的算法,比如潜在错误的发现、可优化的点等。同时,我们也可以积累更多的解决问题的方法,从而设计出更优化的算法。 本次结对编程过程中,存在的问题有遗漏方法调用的声明、while循环与递归嵌套使用、全局变量与局部变量的选择(使用全局变量简化代码,增强程序的可读性)、if条件语句分情况的讨论存在赘余,以及控制条件语句执行的结束。最后,通过和队友一起debug以及在老师的指导下,解决以上问题的同时也优化了代码。不断地发现问题并解决问题,是提高程序代码质量的途径之一

汉诺塔的图解递归算法

断了今生、忘了曾经 提交于 2019-11-30 23:27:30
转自: https://www.cnblogs.com/dmego/p/5965835.html https://dmego.me/2016/10/16/hanoi 一.起源:   汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。 二.抽象为数学问题:   如下图所示,从左到右有A、B、C三根柱子,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有一个原则:一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数 解:(1)n == 1       第1次 1号盘 A---->C sum = 1 次 (2) n == 2       第1次 1号盘 A---->B        第2次 2号盘 A---->C       第3次 1号盘 B---->C sum = 3 次   (3)n == 3         第1次 1号盘 A---->C         第2次 2号盘 A---->B         第3次 1号盘 C---->B         第4次 3号盘 A---->C        

递归

若如初见. 提交于 2019-11-30 23:01:17
递归 什么是递归 递归就是自己调用自己,每次调用传入不同的变量,递归有助于编程者解决复杂的问题,同时可以让代码变得更加简洁 递归执行过程 1.当程序执行到一个方法的时候,就会开辟一个新的栈 2.每个空间的局部变量是独立的 递归的案例 package recursion; public class RecursionTest { public static void main(String[] args) { // TODO Auto-generated method stub test(5); System.out.printf("factorial(%d)=%d\n",5,factorial(5)); } public static void test(int n) { if(n>2) { test(n-1); } System.out.println("n="+n); } public static int factorial(int n) { if(n==1||n==0) { return 1; }else { return factorial(n-1)*n; } } } 递归解决什么问题 1.8皇后、汉诺塔、阶乘、二分查找、迷宫等问题 2.快速排序、归并排序、二分查找、分治算法 3.用栈解决问题,代码整洁 递归重要的规则 1.执行一个方法会创建一个独立受到保护的栈的空间 2

递归习题

微笑、不失礼 提交于 2019-11-30 22:31:17
有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一个算法,计算小孩有多少种上楼梯的方式。输入n,返回一个整数。 分析: f(1) = '1' f(2) = '11','2' f(3) = '111','12','21','3' f(4) 本身可以看作如下三种情况: 1. 走了1阶后再走3阶也就是f(1)+走3阶:===>'1'+'3' ==>'13' ==1种走法 f(1)=1 2. 走了2阶后再走2阶也就是f(2)+走2阶:===>'11'+'2','2'+'2' ==> '112','22' = 2种走法 f(2)=2 3. 走了3阶后再走1阶也就是f(3)+走1阶===>'111'+'1','12'+'1','21'+'1','3'+'1' ==>'1111','121','211','31'= 4种走法 f(3)=4 f(4) = '13','112','22','1111','121','211','31' =====> 7种走法=1 + 2+ 4=f(1)+f(2)+f(3) ---->递归取值 f(1)+f(2)+f(3) 即:f(n)=f(n-3)+f(n-2)+f(n-1) #代码实现 >>> def func(n): ... if n==1: ... return 1 ... elif n==2: ... return 2 ...

算法分析之递归与分治策略

二次信任 提交于 2019-11-30 20:40:45
递归与分治策略 直接或间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数。 在计算机算法设计与分析中,使用递归技术往往使函数的定义和算法的描述简洁且易于理解。 例1 阶乘函数 可递归地定义为: 其中: n=0 时,n!=1为边界条件 n>0 时,n!=n(n-1)!为递归方程 边界条件与递归方程是递归函数的二个要素,递归函数只有具备了这两个要素,才能在有限次计算后得出结果。 例2 Fibonacci数列 无穷数列1,1,2,3,5,8,13,21,34,55,…,被称为Fibonacci数列。它可以递归地定义为: 第n个Fibonacci数可递归地计算如下: int fibonacci(int n){ if (n <= 1) return 1; return fibonacci(n-1)+fibonacci(n-2); } 递归需要有边界条件、递归前进段和递归返回段。 当边界条件不满足时,递归前进; 当边界条件满足时,递归返回。 注意:在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口,否则将无限进行下去(死锁)。 递归的缺点: 递归算法解题的运行效率较低。 在递归调用过程中,系统为每一层的返回点、局部变量等开辟了堆栈来存储。递归次数过多容易造成堆栈溢出等。 例3逆序输出一个正数中的每一位数 例如,对于数12345,依次输出5 4 3 2 1 分析:

非递归的扩展欧几里得算法

一个人想着一个人 提交于 2019-11-30 20:20:03
$\DeclareMathOperator{\extgcd}{extgcd}$ 作者按:写这篇随笔是为了解释 tourist 的逆元模板 template <typename T> T inverse(T a, T m) { T u = 0, v = 1; while (a != 0) { T t = m / a; m -= t * a; swap(a, m); u -= t * v; swap(u, v); } assert(m == 1); return u; // 注意:u 可能为负数 } 一般的扩展欧几里德算法是求 $x, y$ 使得 $ax + by = \gcd(a,b)$ 。若 $a$ 和 $m$ 互素,扩展欧几里得算法可以用来求 $a$ 在模 $m$ 下的逆元,常见的实现如下 int extgcd(int a, int b, int& x, int& y) { int d = a; if (b != 0) { d = extgcd(b, a % b, y, x); y -= (a / b) * x; } else { x = 1; y = 0; } return d; } int mod_inverse(int a, int m) { int x, int y; extgcd(a, m, x, y); return (m + x % m) % m; } 求 $a$

python--递归函数讲解

核能气质少年 提交于 2019-11-30 16:59:15
递归算法是一种直接或间接调用自身算法的过程。 递归算法解决问题的特点: (1)递归就是在过程或函数里调用自身 (2)在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。 (3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低,所以一般不提倡用递归算法设计程序。 (4)在递归调用的过程中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出等。 2、递归的要求 递归算法所体现的“重复”一般有三个要求: (1)每次调用在规模上都有所缩小(通常是减半) (2)是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出作为后一次的输入) (3)在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模位达到直接解答的大小为条件)无条件递归调用将会成为死循环而不能正常结束。 实例:写函数,利用递归获取斐波那契数列中的第10个数,并将该值返回给调用者。 1 def foo(depth, a1, a2): 2 if depth == 10: 3 return a1 4 a3 = a1 + a2 5 r = foo(depth + 1, a2, a3) 6 return r 7 8 ret = foo(1, 0, 1) 9 print(ret) View Code 来源: https://www.cnblogs

算法图解学习笔记之算法

浪子不回头ぞ 提交于 2019-11-30 16:08:03
一、二分查找 二分查找的输入是一个有序的元素列表。如果元素在列表中,返回其位置,否则返回null 二分查找每次都将列表分成两半,分别与查询元素对比。舍弃其中一半。然后再另一半中查找元素。 每次都可以排除一半的元素。所以其效率为O(logn以2为底) 二分查找查询的速度非常快,但是要求也比较严格,需要有序的列表。如果是无序的列表,就不能每次排查一半了。 二、递归 递归应该不算算法的一种,但是还是稍微提一下 递归其实就是循环调用某个函数,直到满足条件才退出。 递归中很重要的是基线条件和递归条件 基线条件是退出递归的条件,如果没有基线条件,那么递归不会停止。陷入无限的循环中。 递归条件是满足这些条件才循环调用函数。 两者缺一不可 三、快速排序(排序算法的一种,简称快排) 快排经常会和递归一起使用 其基线条件为,数组为空或者只包含一个元素。因为为空和只有一个元素就不需要排序了,直接返回。 递归条件为,从数组中取出一个元素,将其他元素分成两部分,一部分大于该元素,一部分小于该元素(等于可以包含再大于或者小于之中,都可以),然后递归调用大于该元素的数组和小于该元素的数组。返回大于元素的数组(递归调用结束后有序)+元素+小于元素的数组(递归调用结束后有序)。 快排的最好情况效率是O(n*logn),最坏情况是O(n*n) 假设传入的是一个有序的数组 我们选择的元素是数组第一个元素。

递归算法应用

你离开我真会死。 提交于 2019-11-30 14:45:07
一、递归的定义 递归就是程序在运行的过程中调用自己(用自己定义自己) 递归的三要素: 边界条件 递归前进段 递归返回段 递归和栈     递归和栈有这紧密的联系,大多数编译器都是使用栈来实现递归的,当调用方法时,编译器会把这个方法的所有参数和返回地址都压入栈中,然后把控制转移给这个方法。当方法返回时,这些值退栈。参数消失了,并且控制权重新回到返回地址处。     调用一个方法时,所进行的步骤:     1)当一个方法被调用时,它的参数和返回地址压入栈中     2)这个方法可以通过获取栈顶元素访问参数     3)当这个方法返回时,它查看栈已获取返回地址,然后这个地址和所有参数退栈,并且销毁   下图是求阶乘的图示,帮助理解:    二、求整数n的阶乘  1 /** 2 * 求整数n的阶乘 3 * 4 * @param n 整数n 5 * @return 6 */ 7 public static int factorial(int n) { 8 if (n < 0) { 9 return -1;//返回-1证明参数有问题 10 } 11 if (n == 1 || n == 0) { 12 return 1; 13 } else { 14 return n * factorial(n - 1); 15 } 16 } 三、在有序数组中查找目标值(二分法) 1 /** 2 *