关于递归的理解及递归表达式复杂度分析(以求解最大公约数为例)

一,递归的四大基本法则:

①基准情形

基准情形是指那些不需要递归(不需要经过函数调用)之后就能退出的情况。它保证了递归的结束。

②不断推进

每一次递归之后,都要向着基准情形靠近,并且在靠近的过程中问题的规模越来越小。

③设计法则

书上说是:假设所有的递归调用都能运行-----“不是特别理解”

④合成效益法则

不要在不同的递归调用中做重复的工作。

二,实例

求解最大公约数--采用欧几里德算法

 1 public static int gcd_recursive(int m, int n){
 2         if(m < n)
 3         {
 4             int tmp = m;
 5             m = n;
 6             n = tmp;
 7         }
 8
 9         if(n == 0)
10             return m;//基准条件
11         return gcd_recursive(n, m%n);//不断推进
12     }

分析:

第9-10行,是递归的基准条件。如果n=0,函数执行到10返回,不会执行到11行进行递归调用。

第11行,进行递归调用的地方。它是不断推进的,因为递归调用的参数朝着基准条件的方向变小了,如:

gcd_recursive(16,12)---->gcd_recursive(12,4)--->gcd_recursive(4,0)

每次递归调用,问题的规模越来越小了。

时间复杂度分析:

由公式: m%n<=m/2  可知:每次递归调用,问题的规模减小一半,类似于二分查找,这显然是一个非常好的算法。

由于第2-5行,花费的时间为常量时间,同样,在第9-10行的if语句判断也是花费的常量时间,在第11行进行递归调用,问题规模减少一半。

可得出,T(N) = T(N/2)+O(1)  推出:时间复杂度为O(logN)

-------------------------------------------------------------

递归逻辑的分析:

对于 gcd_recursive(16,12),第9行不成立,进入到第11行递归

对于 gcd_recursive(12,4), 第9行不成立,进入到第11行递归

对于gcd_recursive(4,0),直接执行到第9行返回,返回的值是4

返回之后,程序此时执行到gcd_recursive(12,4)中的第11行(即最后一行,不要被第9行干扰!第9行在gcd_recursive(12,4)中根本没有执行!!)

第11行代码是:gcd_recursive(4,0)

因为,gcd_recursive(4,0) 的结果是4,故 return gcd_recursive(4,0) 返回的结果也是 4。也即gcd_recursive(12,4)执行完成之后返回4。

由上面gcd_recursive(12,4)执行完成之后返回4,那么当gcd_recursive(16,12)的第11行代码 return gcd_recursive(12,4)

执行完毕时,

整个程序结束了,返回的结果最终是4。

这种形式的递归又称为尾递归。可以看出,尾递归形式的程序最终返回的值就是 最里层递归调用得到的值。

在这篇文章中:字符数组转换成数字

递归的时间复杂度分析如下:

return recurse(c, len - 1) * 10 + (c[len - 1] - ‘0‘);

使得每次递归时,问题规模减小1,而后面的 + (c[len - 1] - ‘0‘) 操作可视为常量时间,故复杂度:

T(N) = T(N-1)+O(1)  得到T(N)=O(N)

结论:

对于递归操作而言,如果每次递归使问题的规模减半,而其他操作都是常数时间

T(N)=T(N/2)+O(1), 则T(N)=O(logN)

若每次递归使用问题的规模减1,而其他操作是常数时间

T(N)=T(N-1)+O(1),则T(N)=O(N)

若每次递归使问题的规模减半,而其他操作是线性时间,T(N) = T(N/2)+O(N)

则T(N)=O(NlogN)

时间: 2024-08-06 19:48:08

关于递归的理解及递归表达式复杂度分析(以求解最大公约数为例)的相关文章

浅谈 PHP递归的理解(递归听起来很高端的词汇 其实就是两次循环)

$data = [ ['pid'=>0,'id'=>1], ['pid'=>1,'id'=>2], ['pid'=>3,'id'=>4], ['pid'=>0,'id'=>3], ]; //对上面的数据进行递归排序(原生的写法) function genCate( $data, $pid = 0) { static $result = array(); foreach ($data as $key => $row) { if ($row['pid']

对于递归的理解

以下是我对递归的理解如果有不对希望大家指正,谢谢. 附上代码: public class Test { public static void main(String[] args) { // TODO Auto-generated method stub int sum = DiGui(5); System.out.println("mainSum="+sum); } private static int DiGui(int n) { int sum = 0; if(0 == n) {

&lt;11&gt;【了解】递归函数概述及构成条件+【理解】递归应用举例

[了解]递归函数概述及构成条件 递归函数: 在函数的内部存在调用当前函数本身的语句,这个函数就是递归函数 递归调用: 递归函数中,调用自己的操作,递归调用 递归调用注意: 1)主调函数就是被调函数 2)在递归函数中应该存在能够让递归结束的条件 构成递归的条件: 1)存在自己调用自己 2)存在一个条件 ,能够让递归结束(否则,是死循环) n==1 age = 10 3)能够找到一个规律,让要解决的问题的规模缩小 递归的阶段: 1)递推阶段 内存栈的特点:先进后出, 2)回归迭代阶段 迭代计算,一步

递归的理解

//十进制的数转化为二进制    public static void toBin(int num){        if(num > 0){            toBin(num/2);            System.out.println(num%2);        }    } 以上面的十进制转化为二进制为例:传参数传入6,把6转化为二进制:使用递归函数不断的调用自身,由于方法在java中是存放到栈中的:在栈中分配空间如下图: 递归总有结束的时候,当if(0>0)的时候结束,

递归--练习10--noi1696逆波兰表达式

递归--练习10--noi1696逆波兰表达式 一.心得 递归大法好 二.题目 1696:逆波兰表达式 总时间限制:  1000ms 内存限制:  65536kB 描述 逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3.逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的逆波兰表示法为* + 2 3 4.本题求解逆波兰表达式的值,其中运算符包括+ - * /四个. 输入 输入为一行,其中运算符和运算

对递推和递归的理解

对递推和递归的理解 1.此问题能否用递推解决 我们以斐波拉契数列问题为例进行阐述,斐波拉契数列为0.1.1.2.3.5.8.13.21.-表现斐波拉契数列为瘦柱状图的形式,如下 我们现在所要解决的问题是求取第n个位置的数值,下面我们正式开始考虑能否用递推解决这个问题(关于递推和递归的区别,将会在下文阐述): 我们考察3号位置,我们发现3号位置的值依赖于1号位置和2号位置,即在1号位置的值和2号位置的值的基础上,我们再进行一些(一个)操作就可以得到3号位置的值.在本例中,这个操作为加法运算. 我们

C#基础:用简单的文件管理程序来理解应用递归

最近在紧张的学习C#,说实话对C#之前没有太多的接触过,只知道C#的特性与java很相似,接触了之后才发现C#跟java相比区别不是很多,但它是一门实现程序能力比Java还要好的语言(仅代表个人观点). 有许多新手在学习编程语言的时候,都会在递归上面卡住,理解和应用起来会十分的吃力,所以我就自己尝试用递归写了一个很简单很简单很简单的文件管理程序,说它简单是因为他真的没有什么难度,都是很底层的循环和递归,也就只有130多行代码,只是希望能够帮助大家理解应用递归.如果你一点编程基础木有,那请不要直接

斐波那契数与二分法的递归与非递归算法及其复杂度分析

1. 什么是斐波那契数? 这里我借用百度百科上的解释:斐波那契数,亦称之为斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.--在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字来说,就是斐波那契数列列由 0 和 1 开始,之后的斐波那契数列系数就由之前的两数相加.特别指出:0不是第一

递归如何转换为非递归

递归算法实际上是一种分而治之的方法,它把复杂问题分解为简单问题来求解.递归的特点包括:递归过程简洁.易编.易懂:递归过程效率低.重复计算多. 考虑递归的执行效率低,可以尝试将递归过程转换为非递归过程.本文就是来探讨怎么转换的. 将递归算法转换为非递归算法有两种方法,一种是直接求值(迭代/循环),不需要回溯:另一种是不能直接求值,需要回溯.前者使用一些变量保存中间结果,称为直接转换法:后者使用栈保存中间结果,称为间接转换法,下面分别讨论这两种方法. 一.直接转换法 直接转换法通常用来消除尾递归和单