递归

KO递归之汉诺塔---C语言

馋奶兔 提交于 2020-02-19 04:13:31
汉诺塔: 汉诺塔(Tower of Hanoi)源于印度传说中,大梵天创造世界时造了三根金钢石柱子,其中一根柱子自底向上叠着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。 单看这个问题描述有点让人抓瞎,这是当然,无论多么简单的问题描述,在没有建立具体地模型之前都是让人不知所云的,仅仅用生活中的语言去描述一些数学或算法问题往往会让听者产生理解偏差, 这也和每个的理解能力和思维方式有很大关系,这就显示出数学的强大了,数学让问题不再模糊,参数和公式组成的模型让问题不再有理解偏差和误区,只可惜数学没学好,看来以后还得回来把高数、概率论这些给补回来。 说了一些题外话,下面来对汉诺塔问题进行解释和建立模型 这是示意图,a是起始柱,c是目标柱,b起到中转作用 在进行转移操作时,都必须确保大盘在小盘下面,且每次只能移动一个圆盘,最终c柱上有所有的盘子且也是从上到下按从小到大的顺序。 很多时候看到这些蛋疼和矫情操作就觉得数学家真是思维清奇、智慧如海,嗯还有吃饱了撑的,某兄台邪魅地一笑,道:直接把c和a交换位置不就行了,我想这位兄台以后或成大器或被人打死。 问题看起来并不复杂,当a柱子上只有一个盘子时只要把那个盘子直接移到c就行了, 有两个盘子的话把1号盘先移到b柱,在把2号盘移到c柱

一文道破快速排序从理解到优化

本小妞迷上赌 提交于 2020-02-18 20:12:33
快速排序 快速排序(QuickSort)是对冒泡排序(BubbleSort)的一种改进。 快速排序由C. A. R. Hoare在1960年提出,这是这位图灵奖得主在很年轻的时候想出来的,XMSL。 它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 其基本的编程实现,我已经在这篇文章中展示过了,各种排序算法都有—— 《基础排序算法大全(Java语言描述)》 。 快排的效率很高,对随机序列是很好使的,但也可能遇到O(N 2 )的糟糕情况。不过只要加以优化这种情况基本不可能出现。 快速排序是数据结构与算法的重要知识,是程序员和计算机专业学生必知必会的重要算法,是面试的常考重点,其性能优化也是需要思考的问题…… 总之,一定要会!很会很会! 快速排序的流程 快速排序算法通过多次比较和交换来实现排序,其排序流程如下: 首先设定一个分界值,通过该分界值将数组分成左右两部分。 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分

算法图解——递归

大憨熊 提交于 2020-02-18 05:28:09
递归 函数自己调用自己 在用递归的同时,也可以用while循环实现 递归只是让解决方案更加清晰,并没有性能上的优势,有时候甚至循环的性能更好 “如果使用循环,程序性能可能更高;如果使用递归,程序可能更容易理解” 基线条件和递归条件 编写递归函数时,必须告诉他停止的条件 基线条件:函数不再调用自己即停止 递归条件:调用自己 def countdown ( i ) : print ( i , end = ' ' ) if i <= 1 : # 基线条件 return else : # 递归条件 countdown ( i - 1 ) countdown ( 10 ) 10 9 8 7 6 5 4 3 2 1 来源: CSDN 作者: 我是小杨我就这样 链接: https://blog.csdn.net/weixin_44478378/article/details/104361158

快速幂算法 ——递归与迭代

若如初见. 提交于 2020-02-17 23:54:27
快速幂算法讲解 递归算法: int cpow(int m,int n) { if(n==0) return 1; else if(n%2==1){ return cpow(m,n-1)*m; }else if(n%2==0) { int temp = cpow(m,n/2); return temp*temp; } } 例如:当我们已知了 2^3,那么在计算2^6不就是相当于2^3*2^3,而快速幂就是运用了这样的原理,但是,如果我们碰到的幂为奇数时怎么办?直接提出来一个幂不就好了,例如在计算2^7,将此转换成2*2^6,之后2^6继续运用上面对待偶数的办法不就好了; 总结以上啰里啰嗦的话,转换成公式就是一下: 1 )当b是奇数时,那么有 a^b = a * a^*(b-1) 2)当b是偶数时,那么有 a^b = a^(b/2) * a^(b/2) 迭代算法: int qpow(int a,int n) { int nas = 1; while(n){ if(n&1) ans *= a; a *= a; n>>=1; } return ans; } 迭代算法运用到了位运算&,将递归算法的(n%2==0)改为(n&1); 对于 a ^ b来说,若果把 b 写成2 进制,那么b 就可以写成若干二次幂之和,如13 的二进制 1101,于是3 号位 、2号位、0号位就都是1

非标准树状结构数据的处理

那年仲夏 提交于 2020-02-17 17:44:34
之前笔者写过两篇博客来讲如何处理标准的树状结构数据,分别是 js遍历树形结构方法 和 过滤树形结构数据并获取新的树形结构 。这次就来聊一下如何处理非标准树状结构数据。标准的树状结构数据的下级节点存放的字段是统一的比如说我们常见的children,有一些特别的数据为了语义上更加明确,所以每一级的子级所在的字段都不一样,比如存放省市区标识的数据用cityList存放下级市,areaList存放下级的区。下面来讲一下,如何遍历这种非标准树形结构,并且如何将他们转换成标准的树状结构数据。 先来看一下我们要处理的数据结构,因为内容较多这里就只放出部分截图: 处理树状结构的方法用递归是最适合不过了。这里也顺带讲一下为什么我们不用循环嵌套的方式去遍历树形结构比如每一级的循环都用for,或者是forEach等等。主要的原因是后期难以维护和扩展,比如树状结构再新增一级,要是用for, map, forEach 等遍历,那你就得再写上一层。层级结构会越来越深,而且每一层相关的判断逻辑可能都需要重新复制一遍。如果用递归,那么他的逻辑结构是很稳定的,如果仅仅是新增了一级,递归的逻辑是仍然有效的,代码无需变动。如果需要修改判断逻辑,基本上只要在递归的主逻辑上稍微修改一下就行,数据的封装和向下传递是基本不需要动的。 处理树状结构数据考验的就是递归的功力。递归主要搞清楚两点,第一点是进入下一次递归的条件

如何k个一组反转链表

本小妞迷上赌 提交于 2020-02-17 10:58:32
之前的文章「递归反转链表的一部分」讲了如何递归地反转一部分链表,有读者就问如何迭代地反转链表,这篇文章解决的问题也需要反转链表的函数,我们不妨就用迭代方式来解决。 本文要解决「K 个一组反转链表」,不难理解: 这个问题经常在面经中看到,而且 LeetCode 上难度是 Hard,它真的有那么难吗? 对于基本数据结构的算法问题其实都不难,只要结合特点一点点拆解分析,一般都没啥难点。下面我们就来拆解一下这个问题。 一、分析问题 首先,前文 学习数据结构的框架思维 提到过,链表是一种兼具递归和迭代性质的数据结构,认真思考一下可以发现 这个问题具有递归性质 。 什么叫递归性质?直接上图理解,比如说我们对这个链表调用 reverseKGroup(head, 2) ,即以 2 个节点为一组反转链表: 如果我设法把前 2 个节点反转,那么后面的那些节点怎么处理?后面的这些节点也是一条链表,而且规模(长度)比原来这条链表小,这就叫 子问题 。 我们可以直接递归调用 reverseKGroup(cur, 2) ,因为子问题和原问题的结构完全相同,这就是所谓的递归性质。 发现了递归性质,就可以得到大致的算法流程: 1、先反转以 head 开头的 k 个元素 。 2、将第 k + 1 个元素作为 head 递归调用 reverseKGroup 函数 。 3、将上述两个过程的结果连接起来 。

动态规划和递归算法求解斐波那契数列的效率对比

五迷三道 提交于 2020-02-16 12:05:52
动态规划有效的解决了递归算法的效率低下的问题,它剔除了递归中的重叠的子问题,对每个子问题只求解一次。 斐波那契数列格式为:1、1、2、3、5、8、13、21、34、......, 递归(状态转移)函数为 f[n]=f[n-1]+f[n-2] 采用递归求解: #采用递归求解 def f_recu(n): assert isinstance(n,int),'必须输入一个整数' assert n>=1,'不能小于1' if n==1 or n==2: return 1 return f_recu(n-1) + f_recu(n-2) #测试,n=40 计算时间 %time f_recu(40) 输出: Wall time: 48.4 s 102334155 采用动态规划求解: #采用动态规划方法求解 def f_dyna(n): assert isinstance(n,int),'必须输入一个整数' assert n>=1,'不能小于1' if n == 1 or n==2: return 1 else: pre_n_2 = 1 pre_n_1 = 1 for i in range(3,n+1): target_n = pre_n_2 + pre_n_1 pre_n_1 = pre_n_2 pre_n_2 = target_n return target_n #测试,n=40 计算时间

递归函数的使用及注意事项

两盒软妹~` 提交于 2020-02-16 03:15:11
#递归函数:函数内调用自身的函数(俄罗斯套娃) 举例:求4的阶乘 分析: 4! = 4 * 3! 3! = 3 * 2! 2! = 2 * 1! 1! = 1 先定义函数,只有当num 为1时,是他本身,所以只有这一个特例而已,用if写出来,其余的情况用else写。 >> def rec ( num ) : >> if num == 1 : >> return 1 >> else : >> return num * rec ( num - 1 ) >> result = rec ( 4 ) >> print ( result ) 24 不知道else后怎么写,怎么办? : 函写到数第五行的时候,可以用2!来举特例。2! = 2 * 1! 先写成: return 2 * rec(1) 然后用变量num替换掉数值,变成 return num * ruc (num - 1) 注意: 递归函数容易造成死循环,原因是没有结束递归的条件,而且会造成栈溢出。 在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。 拓展: import sys #设置递归次数 sys.setrecursionlimit = 1000 #最多递归1000次 #获取递归次数

栈与递归

谁说我不能喝 提交于 2020-02-16 00:06:38
文章目录 递归 函数调用 递归工作栈 递归算法的效率分析 递归转换为非递归 递归算法优点 递归 若在一个函数、过程或数据结构定义的内部又直接(或间接)出现定义本身的应用,则称它们是递归。 三种常使用递归的情况: 定义是递归的 例1:阶乘函数 long Fact ( long n ) { if ( n == 0 ) return 1 ; //递归终止的条件 else return n * Fact ( n - 1 ) ; //递归步骤 } 例2:Fibonacci数列 long Fib ( long n ) { if ( n == 1 || n == 2 ) return 1 ; //递归终止的条件 else return Fib ( n - 1 ) + Fib ( n - 2 ) ; //递归步骤 } 分解-求解的策略称为 分治法 采用分治法进行递归求解问题需要满足的条件: (1)能将一个问题转变成一个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,并且这些处理对象更小且变化有规律 (2)可以通过上述转化而使问题简化 (3)必须有一个明确的递归出口,或称递归的边界 分治法: void p ( 参数表 ) { if ( 递归结束条件成立 ) 可直接求解; //递归终止条件 else p ( 较小的参数 ) ; //递归步骤 } 数据结构是递归的

LeetCode 112. 路径总和 (递归遍历二叉树)

浪子不回头ぞ 提交于 2020-02-15 03:03:01
题目链接: https://leetcode-cn.com/problems/path-sum/ 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树,以及目标和 sum = 22, 5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1 返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。 /** * Definition for a binary tree node. * struct TreeNode { * int val; * struct TreeNode *left; * struct TreeNode *right; * }; */ bool hasPathSum(struct TreeNode* root, int sum){ if(root==NULL) return false; sum-=root->val; if(root->left==NULL&&root->right==NULL){ return sum==0; }else{ return hasPathSum(root->left,sum)||hasPathSum(root->right,sum); } } 来源: https: