首页 文章 精选 留言 我的

精选列表

搜索[递归],共3790篇文章
优秀的个人博客,低调大师

通过阶乘的例子,练习在JavaScript, Scala和ABAP里实现尾递归(Tail Recursion)

Before we start to research tail recursion, let’s first have a look at the normal recursion. A simple factorial implementation by recursion: function factorial(n){ if(n ===1) { return 1; } return n *factorial(n -1); } Let N = 5, see how new stack frame is created for each time of recursive call: We have two stack frames now, one stores the context when n = 5, and the topmost one for current calculation: n = 4 Now since n equals to 1, we stop recursion. The current stack frame ( n = 1 ) will be poped up, the frame under it will be activated and become the topmost frame, with calculated result 1 passed into. key note for normal recursion: during recursion, every generated stack frame is needed and could not e destroyed until the final result is calculated. The calculation is actually not started until the recursion reaches the end ( the condition n === 1 fulfills ). If N is a big integer, it will lead to huge number of stack frames and finally the “stack overflow” or “out of memory” is inevitable. tail recursion Source code below: function tailFactorial(n, total) { if(n ===1) return total; return tailFactorial(n -1, n * total); } function factorial2(n) { return tailFactorial(n,1); } There are two biggest differences compared with normal recursion: (1) A new internal function tailFactorial is introduced here. (2) The calculation is actually now spread within every recursive stack frame. Each frame finishes one part of calculation and pass the current result to the next frame. Once the current stack frame finishes its task, it is actually not needed any more. And thus for example the model browser can then do some optimization on those useless stack frames. Observe the stack frame for tail recursion step by step: stack popped up: When N = 20, the tail recursion has a far better performance than the normal recursion: Update 2016-01-11 Tail recursion implementation via Scala: The interesting thing is, after the Scala code is compiled into Java Byte code, compiler will eliminate the recursion automatically: Tail Recursion in ABAP First this is the normal recursion: REPORT zrecursion. START-OF-SELECTION. DATA: lv_result TYPE int4. PERFORM fac USING 6 CHANGING lv_result. WRITE: / lv_result. FORM fac USING iv_n TYPE int4 CHANGING cv_result TYPE int4. DATA: lv_n TYPE i. cv_result = lv_n = iv_n. lv_n = lv_n - 1. IF lv_n > 1. PERFORM fac USING lv_n CHANGING cv_result. ENDIF. IF lv_n = 1. cv_result = cv_result * lv_n. ELSE. cv_result = cv_result * iv_n. ENDIF. ENDFORM. And here comes tail recursion version: REPORT ztail. START-OF-SELECTION. DATA: lv_result TYPE int4. PERFORM fac USING 5 1 CHANGING lv_result. WRITE: / lv_result. FORM fac USING iv_n TYPE int4 iv_acc TYPE int4 CHANGING cv_result TYPE int4. DATA: lv_n TYPE i, lv_accumulate TYPE i. IF iv_n < 1. cv_result = 1. ELSEIF iv_n = 1. cv_result = iv_acc * iv_n. ELSEIF iv_n > 1. lv_n = iv_n - 1. lv_accumulate = iv_acc * iv_n. PERFORM fac USING lv_n lv_accumulate CHANGING cv_result. ENDIF. ENDFORM. 本文来自云栖社区合作伙伴“汪子熙”,了解相关信息可以关注微信公众号"汪子熙"。

优秀的个人博客,低调大师

python链表冒泡排序、二叉树顺序递归遍历、顺序表的快排

一、python实现链表冒泡排序 - 冒泡排序的概念:冒泡排序是一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直至没有反序的记录为止。因为按照该算法,每次比较会将当前未排序的记录序列中最小的关键字移至未排序的记录序列最前(或者将当前未排序的记录序列中最大的关键字移至未排序的记录序列最后),就像冒泡一样,故以此为名。 - 冒泡排序算法的算法描述如下: -- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 -- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 -- 针对所有的元素重复以上的步骤,除了最后一个。 -- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 1、基本的冒泡排序实现: li = [23, 43, 1, 2, 4, 5, 253] def bubble(li): if not len(li): return count = 0 for i in range(len(li)): for j in range(len(li) - 1): count+=1 if li[i] < li[j]: li[i], li[j] = li[j], li[i] print(count) return li ret_li = bubble(li) print(ret_li) 42 //循环的次数 [1, 2, 4, 5, 23, 43, 253] //排序结果 2、基本优化 这种方法利用了双重循环,会造成不必要的比较,所以优化一下,可以考虑从尾部开始,这样可以将以排好序的部分不再检查 def bubble(li): if not len(li): return count = 0 for i in range(len(li)): j = len(li) - 1 while j > i: count +=1 if li[j] < li[j - 1]: li[j], li[j - 1] = li[j - 1], li[j] j -= 1 print(count) return li ret_li = bubble(li) print(ret_li) 21//循环的次数 [1, 2, 4, 5, 23, 43, 253] //排序结果 3、进一步优化 通过设置flag来判断某次循环是否没有出现位置交换,没有交换就说明排序已完成 def bubble(li): if not len(li): return count = 0 for i in range(len(li)): flag = False j = len(li) - 1 while j > i: count += 1 if li[j] < li[j - 1]: li[j], li[j - 1] = li[j - 1], li[j] flag = True j -= 1 if not flag: print(count) return li return li ret_li = bubble(li) print(ret_li) 20 //循环次数 [1, 2, 4, 5, 23, 43, 253] //排序结果 二、二叉树的顺序遍历 二叉树是有限个元素的集合,该集合或者为空、或者有一个称为根节点(root)的元素及两个互不相交的、分别被称为左子树和右子树的二叉树组成。 -- 二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。 -- 二叉树的第i层至多有2^{i-1}个结点 -- 深度为k的二叉树至多有2^k-1个结点; -- 对任何一棵二叉树T,如果其终端结点数为N0,度为2的结点数为N2,则N0=N2+1 二叉树有三种遍历方式:先序遍历,中序遍历,后续遍历 即:先中后指的是访问根节点的顺序 eg:先序 根左右 中序 左根右 后序 左右根 遍历总体思路:将树分成最小的子树,然后按照顺序输出 #实现树结构的类,树的节点有三个私有属性 左指针 右指针 自己的值 class Node(): def __init__(self,data =None,left=None,right = None): self._data = data self._left = left self._right = right #先序遍历 遍历过程 根左右 def pro_order(tree): if tree == None: return False print(tree._data) pro_order(tree._left) pro_order(tree._right) #后序遍历 遍历过程 左右根 def pos_order(tree): if tree == None: return False # print(tree.get_data()) pos_order(tree._left) pos_order(tree._right) print(tree._data) #中序遍历 遍历过程 左根右 def mid_order(tree): if tree == None: return False # print(tree.get_data()) mid_order(tree._left) print(tree._data) mid_order(tree._right) #层次遍历 def row_order(tree): # print(tree._data) queue = [] queue.append(tree) while True: if queue==[]: break print(queue[0]._data) first_tree = queue[0] if first_tree._left != None: queue.append(first_tree._left) if first_tree._right != None: queue.append(first_tree._right) queue.remove(first_tree) if __name__ == '__main__': tree = Node('A',Node('B',Node('D'),Node('E')),Node('C',Node('F'),Node('G'))) pro_order(tree) mid_order(tree) pos_order(tree) row_order(tree) 三、python实现顺序表的快排 1、快排的介绍: 快速排序采用的思想是分治思想,先简单的介绍一下分治的思想。分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可以得到原问题的解。下面这张图会说明分治算法是如何进行的:将cn分成了两个cn/2,转而分成了cn/4,cn/8......我们通过这样一层一层的求解规模小的子问题,将其合并之后就能求出原问题的解。 图解.jpg 2、快排的基本思路是: 在待排序的序列中选取一个值作为一个基准值,按照这个基准值得大小将这个序列划分成两个子序列,基准值会在这两个子序列的中间,一边是比基准小的,另一边就是比基准大的。这样快速排序第一次排完,我们选取的这个基准值就会出现在它该出现的位置上。这就是快速排序的单趟算法,也就是完成了一次快速排序。然后再对这两个子序列按照同样的方法进行排序,直到只剩下一个元素或者没有元素的时候就停止,这时候所有的元素都出现在了该出现的位置上。 附图:快排的图解 快排.jpg 3、快排的特点 快速排序之所比较快,因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样每次只能在相邻的数之间进行交换,交换的距离就大的多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。其实快速排序是基于一种叫做“二分”的思想。 4、快排的代码实现 def quick_sort(li): if len(li) <= 1: return li base_value = li[len(li) // 2] left_part = [item for item in li if item < base_value] right_part = [item for item in li if item > base_value] eq_part = [item for item in li if item == base_value] return quick_sort(left_part) + eq_part + quick_sort(right_part) print(quick_sort(li = [23, 43, 1, 2, 4, 5, 253])) 简洁版的快排,两种代码其实是一样的 def quick_sort(li): if len(li) <= 1: return li return quick_sort([item for item in li[1:] if item < li[0]])+ li[0:1] + quick_sort([item for item in li[1:] if item > li[0]]) print(quick_sort(li = [23, 43, 1, 2, 4, 5, 253]))

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册