递归算法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实际案例
function mk_Dir($path){ // 目录存在返回 ture if(is_dir($path)){ return true; } // 父目录存在 或 递归找到父目录,创建目录 return is_dir(dirname($path)) || mk_Dir(dirname($path)) ? mkdir($path) : "false"; }
代码样例
• 样例问题
一个猴子看一堆桃子,每天,吃了一半,又 多吃一个!当到第十天时,只有一个桃子了。 问题,有几个桃子?
分析
第十天,只有一个桃子,第九天就是(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; }
• 通过这种方式$a=test();得到的其实不是函数的引用返回,这跟 普通的函数调用没有区别 至于原因:这是PHP的规定。P HP规定通过$a=&test(); 方式得到的才是函数的引用返回 至于 什么是引用返回呢(PHP手册上说:引用返回用在当想用函 数找到引用应该被绑定在哪一个变量上面时。) • 用上面的例子来解释就是 $a=test()方式调用函数,只是将函数 的值赋给$a而已, 而$a做任何改变,都不会影响到函数中的 $b,而通过$a=&test()方式调用函数呢, 他的作用是 将return $b中的 $b变量的内存地址与$a变量的内存地址 指向了同一 个地方 即产生了相当于这样的效果($a=&b;) 所以改变$a的值 也同时改变了$b的值 所以在执行了 $a=&test(); $a=5; 以后, $b的值变为了5
对wenku.baidu.com归的理解
• 递归很多时候是一个问题最直觉、最方便 的解法。想想快速排序。
• 递归是算法里很能体现把「未知不熟悉的 问题转化为已知熟悉问题」这一数学思想 的。 • 递归的意义在于,在寻求问题的解决思路 时,让人像人一样去思考,让计算机像计 算机一样去计算。
对递归的理解
• 递归的用处:递归最大的益处是,可以简化问题。事实上,有 些问题,不用递归的思想,几乎是无法代数解决的举个例子, 玩汉诺塔_百度百科ABC三个柱子,在A上按顺序放置n个大小递 增的圆盘,如何借助B柱,将原盘放到C柱,要求:一次仅能移 动一个原盘 移动中依然要求保持上小下大当n=3,你可能能 很容易算出来,当n=4,应该你二年级可以做,当让n=64,这超 出了一般人的智力,按照百度的解释,你移动到身心俱焚都弄 不完,更不要说你写出算法。然而,用递归的思想,做这件事, 仅仅是计算量的问题。没有递归,冥思苦想,往往也得不出一 个有效地解决办法。 总之,递归就是给傻人开的一扇窗,给聪 明人留的一个巧。
• 其实函数的引用返回多用在对象中。 • 对象的引用 : <?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 ?>
例子
5. 这里有一个规律就是最先执行的最后返回, 比如张三去借钱,一队人转下来最后才会 把钱给到他。这种先进后出的特点,与程 序的执行顺序和堆栈数据结构的特点相一 致。 6. 理解递归,就是忘记递归;即假设问题的 子问题已经解决,从上面的例子说就是假 设李四已经有钱。由此逆推的过程就是问 题的解决过程和算法步骤。
• 引用的作用 如果程序比较大,引用同一个对象的变量 比较多,并且希望用完该对象后手工清除它,个 人建议用 "&" 方式,然后用$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。
• 通俗的讲 1:如果有下面的代码 <?php
$a="ABC"; $b=$a; ?>
其实此时,$a与$b都是指向同一内存地址,而并不是$a与$b占用不同的 内存
对递归的理解
• 说道递归慢,更多的可能是指类似C语言下的情况;因为递归是 函数调用函数自身,而在类C语言的编程语言下,函数调用是需 要切换CPU和寄存器的上下文环境的,其实就是先把CPU和寄存 器状态压入栈,然后加载函数地址,再初始化CPU和寄存器状 态,最后开始执行函数代码。显然相比较于C语言下,同一问题 的迭代循环方式大多只操作堆上的变量而言,确实是多了很多 次保存上下文环境,压栈出栈,切换寄存器内容和状态。但这 是在更接近与硬件语言的情况下。而在像js和php等解释型语言 下,其本身就是在一个虚拟的执行环境中,其虚拟机的上下文 切换更多是由变量模拟的,所以其执行和响应速度和迭代循环 的差别没有那么大,甚至一些语言(如 Scheme)循环只是递归 的语法糖,并且保证可以用循环解决的问题,递归不会比循环 慢。
递归算法
递归的介绍,理解和使用
定义
• 递归算法: 是将问题转化为规模缩小的同类问题的子 问题,每一个子问题都用一个同样的算法 去解决。 一般来说,一个递归算法就是函数调用自 身去解决它的子问题。
特点
• 递归就是在过程或函数里调用自身。 • 在递归过程中,必须有一个明确的条件判 断递归的结束,既递归出口。 • 递归算法解题通常显得很简洁,但递归算 法解题的运行效率较低。 • 在递归调用的过程当中系统为每一层的返 回点、局部量等开辟了栈来存储。递归次 数过多过深可以造成栈溢出等问题(就js和 php而言这种可能性很小很少见,而由递归 边界设置不当造成的死循环则更为常见)。
代码
/** * 用迭代的方法 */ 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
对递归的理解
• 递归的本质是栈。
• 如果一个算法不需要栈,那么你用递归的 时候,这种递归是可以优化的。只是现实 是很少有语言会进行优化。 • 如果一个算法需要栈,即使你不用递归, 也要手工写一个栈,不如递归方便。
php变量引用、函数引用、对象引用
php的引用(就是在变量或者函数、对象等前 面加上&符号) //最重要就是 删除引用的变 量 ,只是引用的变量访问不了,但是内容并 没有销毁 在PHP 中引用的意思是:不同的名 字访问同一个变量内容. 变量的引用 PHP 的引用允许你用两个变量来指向同一 个内容
• 示例: <?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规定传递的引 用不能为常量(可以看错误提示)。
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 quoteTest(){ global $var ; //相当于 $var = &$GLOBALS['var']; $var = 5; //因为他们都指向 同一内存内容 } $var=1; quoteTest(); echo $var; //结果 5
•
global 引用 当用 global $var 声明一个变量时实际上建立了一个到全局变量 的引用。也就是说和这样做是相同的: <?php $var =& $GLOBALS["var"]; ?>
• 函数的引用返回: <?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 ?>
• 以上代码是在PHP5中的运行效果 在PHP5中 对象的复制是通过引用来实现的。上列中 $b=new a; $c=$b; 其实等效于$b=new a; $c=&$b; PHP5中默认就是通过引用来调用对 象, 但有时你可能想建立一个对象的副本, 并希望原来的对象的改变不影响到副本 . 为 了这样的目的,PHP定义了一个特殊的方法, 称为__clone.
例子
1. 张三去和李四借钱,李四说你等一下,我 去找王五借给你, 2. 然后李四去找王五借钱,王五说你等一下, 我去找赵六借给你, 3. 最后王五找赵六借钱,赵六借给了王五。 (这里就是递归出口) 4. 王五从赵六那里返回把钱给了李四,李四 从王五那里返回把钱给到张三,最终张三 得到需要的结果,得到钱。
使用条件和限制
1. 每次调用在规模上都有所缩小(通常是减半); 2. 相邻两次重复之间有紧密的联系,前一次要 为后一次做准备(通常前一次的输出就作为后 一次的输入); 3. 在问题的规模极小时必须用直接给出解答从 而停止递归调用,因而每次递归调用都是有 条件的(以规模未达到直接解答的大小为条件), 无条件递归调用将会成为死循环而不能正常 结束。也可以理解为递归必须要有边界条件, 也就是递归出口(退出递归)
•
•
•
这意味着,例如,unset $var 不会 unset 全局变量。
$this 在一个对象的方法中,$this 永远是调用它的对象的引用。
•
//下面再来个小插曲 php中对于地址的指向(类似指针)功能不是由用户 自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝” 的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会 被拷贝的。