递归算法

递归、尾递归与迭代

橙三吉。 提交于 2019-11-27 02:04:39
很久以前写过一篇《递归与迭代》,写得不是很好。可能是我自己也没把思路理清楚,现在就有了个重新整理思路炒冷饭的机会,也算是一个新的开始吧。 首先解释一个术语叫“尾调用”。直接从wiki的“ 尾调用 ”条目抄:尾调用是指一个函数里的最后一个动作是一个函数调用的情形:即这个调用的返回值直接被当前函数返回的情形。这种情形下称该调用位置为尾位置。若这个函数在尾位置调用本身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归,是递归的一种特殊情形。 比如,下面这个是尾调用: return f(); 而这个不是: return 1 + f(); 关于尾调用,有个重要的优化叫做尾调用消除。经过尾调用消除优化后的程序在执行尾调用时不会导致调用栈的增长。其实,这个优化可能不算是优化。因为在某些计算模型中,尾调用天然不会导致调用栈的增长。在EOPL提到过一个原理: 导致调用栈增长的,是对参数的计算,而不是函数调用。 由于尾调用不会导致调用栈的增长,所以尾调用事实上不像函数调用,而更接近GOTO。尾递归则等同于迭代,只是写法上的不同罢了。 下面以计算幂为例,分别用递归、尾递归和迭代三种方法实现程序。其中尾递归方法是递归方法的改进,而迭代方法纯粹是从尾递归方法翻译而来。 问题 已知数(整数,或浮点数,这里假设是整数)\(b\) 和非负整数\(n\) ,求\(b\)的\(n\)次幂\(b^n\)。

oracle递归函数

邮差的信 提交于 2019-11-27 01:37:06
oracle start with connect by 用法 oracle中 connect by prior 递归算法 Oracle中start with...connect by prior子句用法 connect by 是结构化查询中用到的,其基本语法是: select ... from tablename start with 条件1 connect by 条件2 where 条件3; 例: select * from table start with org_id = 'HBHqfWGWPy' connect by prior org_id = parent_id; 简单说来是将一个树状结构存储在一张表里,比如一个表中存在两个字段: org_id,parent_id那么通过表示每一条记录的parent是谁,就可以形成一个树状结构。 用上述语法的查询可以取得这棵树的所有记录。 其中: 条件1 是根结点的限定语句,当然可以放宽限定条件,以取得多个根结点,实际就是多棵树。 条件2 是连接条件,其中用PRIOR表示上一条记录,比如 CONNECT BY PRIOR org_id = parent_id就是说上一条记录的org_id 是本条记录的parent_id,即本记录的父亲是上一条记录。 条件3 是过滤条件,用于对返回的所有记录进行过滤。 简单介绍如下: 早扫描树结构表时

【转】二叉树的非递归遍历

和自甴很熟 提交于 2019-11-27 01:23:49
原文: http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有前序、中序以及后序三种遍历方法。因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁。而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点。 一.前序遍历 前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问。 1.递归实现 void preOrder1(BinTree * root) // 递归前序遍历 { if (root != NULL) { cout << root -> data << " " ; preOrder1(root -> lchild); preOrder1(root -> rchild); } } 2.非递归实现 根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下: 对于任一结点P: 1)访问结点P,并将结点P入栈

递归再一次让哥震惊了

怎甘沉沦 提交于 2019-11-26 23:57:18
递归再一次让哥震惊了 先说那两个让哥震惊的递归问题: 1:用递归实现单链表的倒序输出 2:从二叉查找树中删除节点,并保证还是二叉查找树 同学们可以开始思考这两个问题了,当然你可能N年前就遇到过这两个问题,那么不妨看看,看你是否真的理解了递归。实现这两个问题的代码当然很简单,就在下面。 百度百科中递归的名片:递归做为一种算法在程序设计语言中广泛应用.是指函数/过程/子程序在运行过程中直接或间接调用自身而产生的重入现象.递归是计算机科学的一个重要概念,递归的方法是程序设计中有效的方法,采用递归编写程序能使程序变得简洁和清晰。 刚开始学习的递归的时候,觉得他好强大,实现某些功能不用递归可能要几十行代码,用递归可能几行就搞定了,而且代码清晰简洁。一直以为递归也就是自己调用自己,有一个出口条件,让他停止递归,退出函数,其实的特点并非就这些。 递归还有一个非常重要的特点: 先进后出 ,跟栈类似,先递进去的后递出来。由于递归一直在自己调用自己,有时候我们很难清楚的看出,他的返回值到底是哪个,只要你理解了先进后出这个特点,你就会明白,第一次调用时,作为返回值的那个变量的值就是递归函数的返回值。先进后出吗,他是第一个进来,也就是最后出去的那个,当然就是递归的返回值啦。 第一个让哥震惊的问题:用递归实现单链表的倒序输出。 我前段时间写过一篇博客《 四种方式实现--从尾到头输出链表 》

并发数据结构 : .NET Framework 中提供的读写锁

て烟熏妆下的殇ゞ 提交于 2019-11-26 21:43:18
在多线程编程时,开发人员经常会遭遇多个线程读写某个资源的情况。这就需要进行线程同步来保证线程安全。一般情况下,我们的同步措施是使用锁机制。但是,假如线程只对资源进行读取操作,那么根本不需要使用锁;反之,假如线程只对资源进行写入操作,则应当使用互斥锁(比如使用 Monitor 类等)。还有一种情况,就是存在多个线程对资源进行读取操作,同时每次只有一个线程对资源进行独占写入操作。这正是本文主题--读写锁的用武之地。 ReaderWriterLock 类 .NET Framework BCL 在 1.1 版本时,给我们提供了一个 ReaderWriterLock 类来面对此种情景。但是很遗憾,Microsoft 官方不推荐使用该类。Jeffrey Richter 也在他的《CLR via C#》一书中对它进行了严厉的批判。下面是该类不受欢迎的主要原因: 性能。这个类实在是太慢了。比如它的 AcquireReaderLock 方法比 Monitor 类的 Enter 方法要慢 5 倍左右,而等待争夺写锁甚至比 Monitor 类慢 6 倍。 策略。假如某个线程完成写入操作后,同时面临读线程和写线程等待处理。ReaderWriterLock 会优先释放读线程,而让写线程继续等待。但我们使用读写锁是因为存在大量的读线程和非常少的写线程,这样写线程很可能必须长时间地等待,造成写线程饥饿

浅谈并发与并行(一)

拥有回忆 提交于 2019-11-26 18:41:04
一、引言 前天在 GitHub 上看到一幅图,问如何向五岁的小孩讲解并发和并行。然后有人以这幅图做答: 这幅图有点儿意思,用咖啡机的比喻来形容并发和并行,从中最直接的体会是,并发是有状态的,某一线程同时执行一个任务,完了才能进行到下一个,而并行是无状态的。 近些年,计算机的处理能力成指数能力增长。处理能力也越来越快,以前的一些工作站现在都可以移植到笔记本电脑或者手持设备上。但是近几年,由于处理器的处理速度已经达到了极限,所以处理器开始向多核方向发展,而提高程序性能的一个最简单的方式之一就是充分利用多核处理器的计算资源。但要编写利用多核处理器处理的程序并不那么简单。所以一些函数是编程语言,如F#,Scala,Erlang等又开始流行起来,因为他们带来的不可变性,递归思想等在一定程度上简化了并行和并发编程。 本文和下文从任务并行和数据并行两个方面,简要讨论一下.NET中的并行编程。这篇文章不可能讲完所有的API,框架,工具或者设计模式。对着方面感兴趣的同学可以看看专门的书籍如Concurrent Programming on Windows 、Concurrency and Parallelism in .NET, 这些书专门讲解了.NET中的并发和并行编程,本文主要参考.NET Performance 一书的部分章节。 二、问题及挑战 对于并行

十三、递归及反射

徘徊边缘 提交于 2019-11-26 17:39:10
递归原理:递归要慎用 斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368 我们发现其规律为:后一个数等于它前面两个数之和 需求: 打印出如上所述序列: def func(arg1,arg2): if arg1 == 0: print (arg1, arg2) arg3 = arg1 + arg2 print (arg3) func(arg2, arg3) func(0,1) 递归的特点就是函数自己调用自己 典型的应用就是通过递归实现简单计算功能: 如下,需要计算这个的值 a = '1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))' def handle(a): #获取只包含数字和+-*/ #1-2*((60-30+( #我把它截断成三段 #-40/5 = -8 #先把这个-40/5的值算出来 #)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2)) #第三段 #b = 新表达式 handle(b) handle(a) ================

TopK问题算法详解

南楼画角 提交于 2019-11-26 16:47:00
面试中,TopK,是问得比较多的几个问题之一,到底有几种方法,这些方案里蕴含的优化思路究竟是怎么样的,今天和大家聊一聊。 画外音:除非校招,我在面试过程中从不问TopK这个问题,默认大家都知道。 问题描述: 从arr[1, n]这n个数中,找出最大的k个数,这就是经典的TopK问题。 栗子: 从arr[1, 12]={5,3,7,1,8,2,9,4,7,2,6,6} 这n=12个数中,找出最大的k=5个。 一、排序 排序是最容易想到的方法,将n个数排序之后,取出最大的k个,即为所得。 伪代码: sort(arr, 1, n); return arr[1, k]; 时间复杂度:O(n*lg(n)) 分析:明明只需要TopK,却将全局都排序了,这也是这个方法复杂度非常高的原因。那能不能不全局排序,而只局部排序呢?这就引出了第二个优化方法。 二、局部排序 不再全局排序,只对最大的k个排序。 冒泡是一个很常见的排序方法,每冒一个泡,找出最大值,冒k个泡,就得到TopK。 伪代码: for(i=1 to k){ bubble_find_max(arr,i); } return arr[1, k]; 时间复杂度:O(n*k) 分析:冒泡,将全局排序优化为了局部排序,非TopK的元素是不需要排序的,节省了计算资源。不少朋友会想到,需求是TopK,是不是这最大的k个元素也不需要排序呢

通过扩欧得出线性同余方程的通解以及x的最小正整数解

ぃ、小莉子 提交于 2019-11-26 15:55:19
同余定理 若 ax与b模m的余数相同(其中x为未知数,即所需要求的数),即 ax%m=b%m ,则这个式子可以记作成 a≡b (mod m) 。 设ax对m取模后的余数为r1,则有: ax=y1 m+r1。 ① 同理,设b对m取模后的余数为r2,则有: b=y2 m+r2。 ② 其中y1与y2均为任意整数,此时两则互不相干。 那么我们知道,由于ax%m=b%m,则 r1=r2 ,联立①②得:ax-y1 m=b-y2 m,移项最后得出方程: ax+my=b 这个方程叫 线性同余方程 ,由于未知数x为一阶的,所以也称为 一次同余方程。 这个方程的形式也使它叫作 不定方程。 这个方程的一个性质是:若至少有一组解(x0,y0)能使得这个方程成立,则当且仅当gcd(a,m)|b, 即a与m的最大公约数能被b整除。 (裴蜀定理) 如果我们直接求解的话,当然是不行的了~那么接下来会由扩展欧几里得算法来求出这个方程的通解。 在接下来之前,我们有牢记一个东西,方程ax+my=b,它是关于(x,y)的一个二元一次方程,切记它的 右半边式子是已知的数b。 (一般题目推出来,a、b、m都是已知的。求x,y) 设g=gcd(a,m), 而扩展欧几里得算法只是求方程:ax+my=g的一组特解。 扩展欧几里得算法 对于方程: ax+my=g ,用ex_gcd(扩欧)求出一组解(x0,y0)满足这个方程。

算法导论基础(第一~五章)

心不动则不痛 提交于 2019-11-26 14:57:54
插入排序 最好情况输入数组开始时候就是满足要求的排好序的,时间代价为 θ(n); 最坏情况输入数组是按逆序排序的,时间代价为 θ(n^2)。 归并排序 归并排序采用了算法设计中的分治法,分治法的思想是将原问题分解成n个规模较小而结构与原问题相似的小问题,递归的解决这些子问题,然后再去合并其结果,得到原问题的解。 分治模式在每一层递归上有三个步骤: 分解(divide):将原问题分解成一系列子问题。 解决(conquer):递归地解答各子问题,若子问题足够小,则直接求解。 合并(combine):将子问题的结果合并成原问题的解。 归并排序(merge sort)算法按照分治模式,操作如下: 分解:将n个元素分解成各含n/2个元素的子序列 解决:用合并排序法对两个序列递归地排序 合并:合并两个已排序的子序列以得到排序结果 算法中含有对其自身的递归调用,其运行时间可以用一个递归方程(或递归式)来表示。归并排序算法分析采用递归树进行,递归树的层数为lgn+1,每一层的时间代价是cn,整棵树的代价是cn(lgn+1)=cnlgn+cn,忽略低阶和常量c,得到结果为 θ(nlg n)。 转载于:https://www.cnblogs.com/lucas-hsueh/p/3731403.html 来源: https://blog.csdn.net/weixin_30586257/article