递归算法
递归算法及经典例题详解

递归算法及经典例题详解
1.什么是递归
递归简单来说就是在运行过程中不断调用自己,直到碰到终止条件,返回结果的过程。
递归可以看作两个过程,分别是递和归。
递就是原问题把要计算的结果传给子问题;归则是子问题求出结果后,把结果层层返回原问题的过程。
下面设一个需要经过三次递归的问题,为大家详细看一下递归的过程:当然,现实中我们遇到递归问题是不会按照图中一样一步一步想下来,主要还是要掌握递归的思想,找到每个问题中的规律。
2.什么时候使用递归
递归算法无外乎就是以下三点:1.大问题可以拆分为若干小问题2.原问题与子问题除数据规模不同,求解思路完全相同3.存在递归终止条件
而在实际面对递归问题时,我们还需要考虑第四点:
当不满足终止条件时,要如何缩小函数值并让其进入
下一层循环中
3.递归的实际运用(阶层计算)
了解了大概的思路,现在就要开始实战了。
下面我们来看一道经典例题:
求N的阶层。
首先按照思路分析是否可以使用递归算法:
1.N!可以拆分为(N-1)!*N
2.(N-1)!与N!只有数字规模不同,求解思路相同
3.当N=1时,结果为1,递归终止
满足条件,可以递归:
publicstaticintFactorial(int num){if(num==1){return num;}return num*Factorial(num-1);}
而最后的return,便是第四步,缩小参数num的值,让递归进入下一层。
一般来说,第四步往往是最难的,需要弄清该如何缩
小范围,如何操作返回的数值,这一步只能通过不断
地练习提高了(当然如果你知道问题的数学规律也是
可以试出来的)。
简述递归算法的执行过程

简述递归算法的执行过程摘要:1.递归算法的定义和基本原理2.递归算法的执行过程3.递归算法的应用实例4.递归算法的时间复杂度和优化方法5.总结正文:递归算法是一种自调用算法,通过将问题分解为更小的子问题来解决问题。
它在计算机科学和数学领域中广泛应用,具有可读性和实用性。
下面详细介绍递归算法的执行过程、应用实例、时间复杂度和优化方法。
一、递归算法的定义和基本原理递归算法是一种算法,它通过将问题分解为更小的子问题来解决问题。
这些子问题与原始问题具有相似的特征,从而使得算法可以通过重复调用自身来解决这些子问题。
在递归算法中,有一个基本情况(base case)和递归情况(recursive case)。
基本情况是问题规模足够小,可以直接给出答案的情况;递归情况则是将问题分解为更小的子问题,并重复调用算法本身来解决这些子问题。
二、递归算法的执行过程1.初始化:定义问题的初始条件,通常是基本情况。
2.判断基本情况:如果问题规模足够小,直接给出答案。
3.划分问题:将问题分解为更小的子问题,并确保这些子问题与原始问题具有相似的特征。
4.递归调用:将子问题传递给算法本身,重复执行步骤1-3,直到基本情况出现。
5.合并结果:将递归调用返回的结果合并,得到最终答案。
三、递归算法的应用实例1.计算阶乘:递归算法可以用于计算一个正整数的阶乘。
例如,计算5的阶乘:```def factorial(n):if n == 0:return 1else:return n * factorial(n-1)```2.计算Fibonacci 数列:递归算法可以用于计算Fibonacci 数列。
例如,计算第n个Fibonacci 数:```def fibonacci(n):if n == 0:return 0elif n == 1:return 1else:return fibonacci(n-1) + fibonacci(n-2)```四、递归算法的时间复杂度和优化方法1.时间复杂度:递归算法的时间复杂度通常为O(2^n),其中n为问题的规模。
递归算法

一.递归算法概述程序调用自身的编程技巧称为递归( recursion)。
一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的能力在于用有限的语句来定义对象的无限集合。
用递归思想写出的程序往往十分简洁易懂。
二.递归算法的特点递归算法是一种直接或者间接地调用自身算法的过程。
在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归算法解决问题的特点:(1) 递归就是在过程或函数里调用自身。
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。
所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。
递归次数过多容易造成栈溢出等。
所以一般不提倡用递归算法设计程序。
三.递归算法要求递归算法所体现的“重复”一般有三个要求:一是每次调用在规模上都有所缩小(通常是减半);二是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);三是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。
四.例子(用从C++描述):行数程序#include <iostream>using namespace std;0 void p (int w){1 if(w>0){2 cout<<w<<" ";3 p(w-1);4 p(w-1);5 }6 }void main(){int a;cin>>a;p(a);}当输入a=4后的打印结果:当p(0)执行完了,就会执行p(1)中的语句5(所以在方格a中,填“5”)。
递归算法原理

递归算法原理
递归是一种算法设计技巧,它的原理是通过将一个问题分解成一个或多个规模较小但类似于原问题的子问题来解决。
递归算法通过反复调用自身来解决这些子问题,直到子问题的规模足够小并可以直接解决为止。
递归算法的主要思想是将问题转化为更小的同类问题的子问题,并在每一次递归调用中将问题的规模减小。
递归算法需要定义一个基准情况,即问题的最小规模情况,当问题达到基准情况时,递归的调用将停止,得到最终的解。
当使用递归算法时,需要注意以下几点:
1. 递归的结束条件:为了避免无限递归,递归函数必须定义结束条件,即基准情况。
2. 递归调用:在函数内部调用自身来解决规模较小的子问题。
3. 子问题规模的减小:每次递归调用时,子问题的规模应该比原问题要小。
4. 递归栈:在每次递归调用时,系统会将当前的函数调用信息存储在递归栈中,当递归调用结束后,系统将会按照递归栈的顺序逐个弹出函数调用信息,直到返回最终的解。
递归算法在解决某些问题时非常有效,例如树和图的遍历、排列组合、分治算法等。
然而,递归算法也存在一些缺点,例如
递归调用会消耗较多的内存空间和时间复杂度较高等问题,因此在实际应用中需要根据具体情况来选择是否使用递归算法。
递归算法知识点总结

递归算法知识点总结一、基本概念递归算法的基本概念是基于递归函数的思想。
递归函数是一个调用自身的函数。
递归算法通常可以分为两种类型:简单递归和复杂递归。
简单递归是指在递归函数中直接调用自身,而复杂递归则是指在递归函数中可能会有多个递归调用。
递归算法通常用于解决可以分解为若干子问题的问题,这种方法通常可以更加简洁地解决问题,但同时也可能会带来一些计算复杂度的问题。
递归算法的设计通常包括以下几个步骤:1. 确定基本情况:在设计递归函数时,通常需要确定一个或多个基本情况。
基本情况通常是指在递归函数中不需要再次调用自身的情况。
2. 确定递归情况:在设计递归函数时,需要确定一个或多个递归情况。
递归情况通常是指在递归函数中需要调用自身的情况。
3. 确定递归方式:当确定了递归函数的基本情况和递归情况之后,就需要确定递归函数的调用方式。
通常有两种方式:直接递归和间接递归。
4. 编写递归函数:根据确定的基本情况、递归情况和递归方式,编写递归函数。
5. 测试递归函数:编写递归函数后,需要对递归函数进行测试,确保递归函数能够正确地解决问题。
二、递归算法的原理递归算法的原理是基于递归函数的调用。
当一个递归函数被调用时,它会将自身的执行环境保存到栈中,并且在栈中分配一些空间。
在递归函数中,如果有一些局部变量,这些变量会在栈中分配空间。
随着递归函数的深入调用,栈中的空间也会不断增加。
在递归函数的执行过程中,通常需要考虑递归栈的压栈和出栈操作。
在递归函数被调用时,会执行一些初始化操作,并将递归参数保存到栈中。
在递归函数中,如果遇到递归情况,会再次调用自身,并且将自身的执行环境保存到栈中。
在递归函数的执行过程中,如果遇到基本情况,就会结束当前递归调用,并且从栈中释放空间。
递归算法的原理是基于递归函数的深度调用的。
当递归函数被调用时,会执行一些初始化过程,并将递归参数保存到栈中。
当递归函数执行完毕后,会从栈中释放空间。
在递归函数的执行过程中,栈中的空间会不断增加和释放。
递归算法详解完整版

递归算法详解完整版递归算法是一种重要的算法思想,在问题解决中起到了很大的作用。
它通过将一个大问题划分为相同或类似的小问题,并将小问题的解合并起来从而得到大问题的解。
下面我们将详细介绍递归算法的定义、基本原理以及其应用。
首先,我们来定义递归算法。
递归算法是一种通过调用自身解决问题的算法。
它通常包括两个部分:基础案例和递归步骤。
基础案例是指问题可以被直接解决的边界情况,而递归步骤是指将大问题划分为较小问题并通过递归调用自身解决。
递归算法的基本原理是"自顶向下"的思维方式。
即从大问题出发,不断将问题划分为较小的子问题,并解决子问题,直到达到基础案例。
然后将子问题的解合并起来,得到原始问题的解。
递归算法的最大特点是简洁而优雅。
通过将复杂问题分解为简单问题的解决方式,可以大大减少代码的复杂程度,提高程序的效率和可读性。
但是递归算法也有一些缺点,包括递归深度的限制和复杂度的不确定性。
过深的递归调用可能导致栈溢出,而不合理的递归步骤可能导致复杂度过高。
递归算法有许多应用场景,我们来介绍其中一些典型的应用。
1.阶乘问题:计算一个数的阶乘。
递归算法可以通过将问题划分为更小的子问题来解决。
例如,n的阶乘可以定义为n乘以(n-1)的阶乘。
当n 等于1时,我们可以直接返回1作为基础案例。
代码如下:```int factorial(int n)if (n == 1)return 1;}return n * factorial(n - 1);```2.斐波那契数列问题:求斐波那契数列中第n个数的值。
斐波那契数列的定义是前两个数为1,然后从第三个数开始,每个数都是前两个数的和。
递归算法可以通过将问题划分为两个子问题来解决。
当n等于1或2时,直接返回1作为基础案例。
代码如下:```int fibonacci(int n)if (n == 1 , n == 2)return 1;}return fibonacci(n - 1) + fibonacci(n - 2);```3.二叉树问题:对于给定的二叉树,递归算法可以通过递归调用左子树和右子树的解来解决。
6种基本算法 递归

6种基本算法递归递归是一种重要的算法思想,它在计算机科学中得到广泛应用。
本文将介绍六种基本的递归算法,并对其原理和应用进行讲解。
一、递归的基本概念递归是指一个函数在其定义中调用自身的过程。
递归算法通过将一个大问题划分为一个或多个相同或相似的子问题,然后通过解决子问题来解决原始问题。
递归算法具有简洁、优雅以及可读性强的特点,但同时也需要注意递归的停止条件,以避免无限递归的发生。
二、阶乘算法阶乘算法是递归算法中最经典的例子之一。
它的定义如下:```n! = n * (n-1) * (n-2) * ... * 1```其中,n为一个非负整数。
阶乘算法可以通过递归的方式实现,即:```fact(n) = n * fact(n-1)```其中,停止条件为`n=0`时,返回1。
三、斐波那契数列算法斐波那契数列是一个无限序列,其定义如下:```F(0) = 0F(1) = 1F(n) = F(n-1) + F(n-2) (n>1)```斐波那契数列算法可以通过递归的方式实现,即:```fib(n) = fib(n-1) + fib(n-2)```其中,停止条件为`n=0`或`n=1`时,返回相应的值。
四、二分查找算法二分查找算法是一种高效的查找算法,它的基本原理是将已排序的数组分成两部分,然后判断目标值在哪一部分,并继续在该部分中进行查找,直到找到目标值或者查找范围为空。
二分查找算法可以通过递归的方式实现,即:```binarySearch(arr, target, start, end) = binarySearch(arr, target, start, mid-1) (target < arr[mid])= binarySearch(arr, target, mid+1, end) (target > arr[mid])= mid (target = arr[mid])```其中,`arr`为已排序的数组,`target`为目标值,`start`和`end`为查找范围的起始和结束位置。
常见递归算法

常见递归算法
常见的递归算法包括以下几种:
1. 斐波那契数列:斐波那契数列是一个经典的递归问题,每个数都是前两个数的和。
通过递归可以很容易地计算出斐波那契数列的每一项。
2. 树的遍历:在树的遍历中,递归是一种常见的实现方式。
例如,前序遍历、中序遍历和后序遍历都可以通过递归算法来实现。
3. 汉诺塔问题:汉诺塔问题是一个经典的递归问题,需要将一系列圆盘从一个柱子移动到另一个柱子,且在移动过程中不能将较大的圆盘放在较小的圆盘上面。
递归算法可以有效地解决这个问题。
4. 快速排序算法:快速排序是一种常用的排序算法,它采用了分治法的思想,通过递归将数组分成两部分并对其进行排序。
5. 归并排序算法:归并排序也是一种基于分治思想的排序算法,通过递归将数组分成子数组,然后合并它们以得到排序后的数组。
6. 二分查找算法:二分查找是一种在有序数组中查找特定元素的高效算法。
通过将数组一分为二并递归地在子数组中查找,可以快速缩小查找范围。
7. Tower of Hanoi(汉诺塔)问题:这是一个经典的数学谜题,需要将一系列圆盘从一个柱子移动到另一个柱子,遵循特定的规则。
递归算法可以用来解决这个问题。
这些只是一些常见的递归算法示例,递归在很多其他问题和算法中也有广泛应用。
递归的优点是代码简洁易懂,但需要注意防止递归深度过大导致栈溢出等问题。
在实际应用中,需要根据具体情况选择合适的算法和数据结构来解决问题。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
• 引用的作用 如果程序比较大,引用同一个对象的变量 比较多,并且希望用完该对象后手工清除它,个 人建议用 "&" 方式,然后用$var=null的方式清 除. 其它时候还是用php5的默认方式吧. 另外, php5中对于大数组的传递,建议用 "&" 方式, 毕 竟节省内存空间使用。
• 取消引用 当你 unset 一个引用,只是断开了变量名和变量内容 之间的绑定。这并不意味着变量内容被销毁了。例如: • <?php $a = 1; $b =& $a; unset ($a); ?> • 不会 unset $b,只是 $a。 function quoteTest(){ global $var ; //相 当于 $var = &$GLOBALS['var']; unset($var); //删除只是删除引用, 而引用的内容还存在,同上这并不意味着变量内容被销毁 了}$var=1;quoteTest();echo $var; // 结果 1 • 不会 unset $b,只是 $a。
递归算法
递归的介绍,理解和使用
定义
• 递归算法: 是将问题转化为规模缩小的同类问题的子 问题,每一个子问题都用一个同样的算法 去解决。 一般来说,一个递归算法就是函数调用自 身去解决它的子问题。
特点
• 递归就是在过程或函数里调用自身。 • 在递归过程中,必须有一个明确的条件判 断递归的结束,既递归出口。 • 递归算法解题通常显得很简洁,但递归算 法解题的运行效率较低。 • 在递归调用的过程当中系统为每一层的返 回点、局部量等开辟了栈来存储。递归次 数过多过深可以造成栈溢出等问题(就js和 php而言这种可能性很小很少见,而由递归 边界设置不当造成的死循环则更为常见)。
• 通俗的讲 1:如果有下面的代码 <?php
$a="ABC"; $b=$a; ?>
其实此时,$a与$b都是指向同一内存地址,而并不是$a与$b占用不同的 内存
• 函数的引用返回: <?php function &test(){ static $b=0;//申明一个静态变量 $b=$b+1; echo $b; return $b; } } $a=test();//这条语句会输出 $b的值 为1 $a=5; $a=test();//这条语句会输出 $b的值 为2 $a=&test();//这条语句会输出 $b的值 为3 $a=5; $a=test();//这条语句会输出 $b的值 为6 ?>
• 其实函数的引用返回多用在对象中。 • 对象的引用 : <?php class a{ var $abc="ABC"; } $b=new a; $c=$b; echo $b->abc;//这里输出ABC echo $c->abc;//这里输出ABC $b->abc="DEF"; echo $c->abc;//这里输出DEF ?>
Length=0
abcd bcd cd d null
Echo ’a’
Echo ‘b’
Echo ‘c’
返 回 顺 序
Echo ‘d’
实际案例
使用递归的方式创建目录 /** * 递归创建目录 * @param [string] $path [创建的目录] * @return [type] [description] */ function mk_Dir($path){ // 如果目录存在返回 ture if(is_dir($path)){ return true; } // 如果上级目录存在 创建目录 if(is_dir(dirname($path))){ return mkdir($path); } // 递归 查找父目录 mk_Dir(dirname($path)); return mkdir($path); }
代码
/** * 用迭代的方法 */ function f($n){ $v = 1; // 设定初始值 for($i=9;$i>=$n;--$i){ $v = ($v+1)*2; } return $v; } echo f(1);
递归的执行过程
Length=3
执 行 顺 序
Length=2
Length=1
例子
1. 张三去和李四借钱,李四说你等一下,我 去找王五借给你, 2. 然后李四去找王五借钱,王五说你等一下, 我去找赵六借给你, 3. 最后王五找赵六借钱,赵六借给了王五。 (这里就是递归出口) 4. 王五从赵六那里返回把钱给了李四,李四 从王五那里返回把钱给到张三,最终张三 得到需要的结果,得到钱。
例子
5. 这里有一个规律就是最先执行的最后返回, 比如张三去借钱,一队人转下来最后才会 把钱给到他。这种先进后出的特点,与程 序的执行顺序和堆栈数据结构的特点相一 致。 6. 理解递归,就是忘记递归;即假设问题的 子问题已经解决,从上面的例子说就是假 设李四已经有钱。由此逆推的过程就是问 题的解决过程和算法步骤。
对递归的理解
• 递归很多时候是一个问题最直觉、最方便 的解法。想想快速排序。
• 递归是算法里很能体现把「未知不熟悉的 问题转化为已知熟悉问题」这一数学思想 的。 • 递归的意义在于,在寻求问题的解决思路 时,让人像人一样去思考,让计算机像计 算机一样去计算。
对递归的理解
• 递归的用处:递归最大的益处是,可以简化问题。事实上,有 些问题,不用递归的思想,几大小递 增的圆盘,如何借助B柱,将原盘放到C柱,要求:一次仅能移 动一个原盘 移动中依然要求保持上小下大当n=3,你可能能 很容易算出来,当n=4,应该你二年级可以做,当要说你写出算法。然而,用递归的思想,做这件事, 仅仅是计算量的问题。没有递归,冥思苦想,往往也得不出一 个有效地解决办法。 总之,递归就是给傻人开的一扇窗,给聪 明人留的一个巧。
对递归的理解
• 递归的本质是栈。
• 如果一个算法不需要栈,那么你用递归的 时候,这种递归是可以优化的。只是现实 是很少有语言会进行优化。 • 如果一个算法需要栈,即使你不用递归, 也要手工写一个栈,不如递归方便。
php变量引用、函数引用、对象引用
php的引用(就是在变量或者函数、对象等前 面加上&符号) //最重要就是 删除引用的变 量 ,只是引用的变量访问不了,但是内容并 没有销毁 在PHP 中引用的意思是:不同的名 字访问同一个变量内容. 变量的引用 PHP 的引用允许你用两个变量来指向同一 个内容
• 以上代码是在PHP5中的运行效果 在PHP5中 对象的复制是通过引用来实现的。上列中 $b=new a; $c=$b; 其实等效于$b=new a; $c=&$b; PHP5中默认就是通过引用来调用对 象, 但有时你可能想建立一个对象的副本, 并希望原来的对象的改变不影响到副本 . 为 了这样的目的,PHP定义了一个特殊的方法, 称为__clone.
•
•
•
这意味着,例如,unset $var 不会 unset 全局变量。
$this 在一个对象的方法中,$this 永远是调用它的对象的引用。
•
//下面再来个小插曲 php中对于地址的指向(类似指针)功能不是由用户 自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝” 的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会 被拷贝的。
代码样例
• 样例问题
一个猴子看一堆桃子,每天,吃了一半,又 多吃一个!当到第十天时,只有一个桃子了。 问题,有几个桃子?
分析
第十天,只有一个桃子,第九天就是(1+1) *2个桃子,假设子问题已经解决,那 f(1)=(f(2)+1)*2,第一天的桃子等于第二天桃子 加1乘以2,上代码:
代码
/** * 递归实现方法 */ function f($n=1){ if($n==10){ return 1; // 递归出口 } return (f($n+1)+1)*2; }
• 示例: <?php $a="ABC"; $b =&$a; echo $a;//这里输出:ABC echo $b;//这里输出:ABC $b="EFG"; echo $a;//这里$a的值变为EFG 所以输出EFG echo $b;//这里输出EFG ?>
• 函数的传址调用: <?php function test(&$a){ $a=$a+100; } $b=1; echo $b;//输出1 test($b); //这里$b传递给函数的其实是$b的变量内容所处的内存地址,通过 在函数里改变$a的值就可以改变$b的值了 echo "<br>"; echo $b;//输出101 ?> 要注意的是,在这里test(1);的话就会出错,原因是:PHP规定传递的引 用不能为常量(可以看错误提示)。
对递归的理解
• 说道递归慢,更多的可能是指类似C语言下的情况;因为递归是 函数调用函数自身,而在类C语言的编程语言下,函数调用是需 要切换CPU和寄存器的上下文环境的,其实就是先把CPU和寄存 器状态压入栈,然后加载函数地址,再初始化CPU和寄存器状 态,最后开始执行函数代码。显然相比较于C语言下,同一问题 的迭代循环方式大多只操作堆上的变量而言,确实是多了很多 次保存上下文环境,压栈出栈,切换寄存器内容和状态。但这 是在更接近与硬件语言的情况下。而在像js和php等解释型语言 下,其本身就是在一个虚拟的执行环境中,其虚拟机的上下文 切换更多是由变量模拟的,所以其执行和响应速度和迭代循环 的差别没有那么大,甚至一些语言(如 Scheme)循环只是递归 的语法糖,并且保证可以用循环解决的问题,递归不会比循环 慢。