递归调用

巧用记忆化搜索代替暴力递归(洛谷P1464题题解,Java语言描述)

强颜欢笑 提交于 2020-01-30 15:44:59
题目要求 P1464题目链接 分析 如果……你信了这题干,真的写了递归……TLE警告!!! 所以,就需要优化嘛…… [−9223372036854775808,9223372036854775807]这个范围,就是C的 longlong / Java的 long 诶,算是一种数很大但还有良心的提示吧。 这题比较适合记忆化搜索,这也是我第一次写 记忆化搜索 的题解诶,就扯一扯…… 一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。更重要的是搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,以后再次遇到这个状态的时候,就不必重新求解了。这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的。 对于本题的话,只要一个记忆化储存就可以避免大量运算量(大佬们都说这玩意和递推/动态规划差不多)。 主要思路就是 开一个三维数组 ,把每一个“w”函数的值储存起来,下一次就可以 直接调用 ,节省大量时间。 使用的时候还要先想,记忆化的数组要开多大。对于这个题来说,输入数据在long(Java)范围内,对于每一组a,b,c都使用一个变量来进行记忆化是不现实的。 但是,根据题意,当a<0 or b<0 or c<0时,返回值都是1,当a>20 or b>20

27.递归

 ̄綄美尐妖づ 提交于 2020-01-30 09:30:01
递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。 利用递归可以用简单的程序来解决一些复杂的问题。比如:斐波那契数列的计算、汉诺塔、快排等问题。 递归结构包括两个部分: 1.定义递归头。解答:什么时候不调用自身方法。如果没有头,将陷入死循环,也就是递归的结束条件。 2.递归体。解答:什么时候需要调用自身方法。 递归都可以改为非递归(循环和迭代) public class TestRecursion { public static void main ( String [ ] args ) { long d1 = System . currentTimeMillis ( ) ; //返回当前毫秒总数 System . out . printf ( "%d阶乘的结果:%s%n" , 10 , factorial ( 10 ) ) ; //%n = \n long d2 = System . currentTimeMillis ( ) ; System . out . printf ( "递归费时:%s%n" , d2 - d1 ) ; //耗时:32ms factorialIterator ( 10 ) ; } /** 求阶乘的递归方法*/ static long factorial ( int n

zkw线段树

一世执手 提交于 2020-01-29 21:09:16
zkw线段树是zkw大神搞的自底向上线段树,以常数小,代码短著称。然而zkw大神的原ppt中描述简单,想了好长时间才想粗来。 以下内容针对 区间最小值 ,使用更好理解的递归方式描述。 定义 zkw线段树定义如下: 1. 它是一棵 满二叉树 2. 他的叶节点是一个数 3. 每一个非叶节点是一个数,且这个数是它的两个孩子中的较小值 显然,zkw线段树和普通线段树类似。他的叶节点从左到右是一个数列A1..n,非叶节点存一些信息以便查询区间最小值。 由于它是一个满二叉树,可以用 堆式储存法 储存。特别的,由于叶节点的个数为2的正整数幂,对于数据规模n,叶子节点的实际个数为 2 lg n + 1 , 没有数据的节点用 ∞ 填充 。 性质 不难发现,一棵有tn个节点的满二叉树有tn-1个非叶子节点。所以在堆式结构中,第i个叶子节点的位置是 tree[tn-1+i] 。这是一个很重要的性质,zkw线段树的许多操作是建立在他的基础上的。 由1容易得出,对于树上(堆中)任意一个位置i,如果i是偶数,那么它是一个左孩子;否则是一个右孩子。 1 2 3 4 5 6 7 //显然,所有奇数都是右孩子,偶数都是左孩子 建立数据结构 直接用静态数组建立即可,i的左右孩子分别为 i*2,i*2+1 const int maxn = 100000; // 最多节点数 int tree[maxn*4]; //

二叉树的遍历.队列与栈实现

天大地大妈咪最大 提交于 2020-01-29 20:45:32
树的遍历方法有广度优先(层序遍历),以及深度优先两种方法,分成先序遍历,中序遍历,后序遍历三种。 一.深度优先: 1.递归实现: 先序遍历 输出顺序:根节点,左子树,右子树。 void PreOrderTraversal ( BinTree BT ) { if ( BT ) { //如果树非空 printf ( "%d" , BT - > Data ) ; //输出根节点 PreOrderTraversal ( BT - > Left ) ; //继续递归执行其左子树 PreOrderTraversal ( BT - > Right ) ; //继续递归其右子树 } } 中序输出:左子树,根节点,右子树。 只需要将输出语句放到两个递归语句之间即可。 void PreOrderTraversal ( BinTree BT ) { if ( BT ) { PreOrderTraversal ( BT - > Left ) ; printf ( "%d" , BT - > Data ) ; PreOrderTraversal ( BT - > Right ) ; } } 后序输出:左子树,右子树,根节点。 void PreOrderTraversal ( BinTree BT ) { if ( BT ) { PreOrderTraversal ( BT - > Left ) ;

递归与尾递归

送分小仙女□ 提交于 2020-01-29 16:34:12
1.递归 递归是一个函数直接或间接地调用自身,是为直接或间接递归。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。用递归需要注意以下两点: (1) 递归就是在过程或函数里调用自身。(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。 递归一般用于解决三类问题:   (1)数据的定义是按递归定义的。(Fibonacci函数,n的阶乘)   (2)问题解法按递归实现。(回溯)   (3)数据的结构形式是按递归定义的。(二叉树的遍历,图的搜索) 递归的缺点:   递归解题 相对常用的算法如普通循环等,运行效率较低 。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成 栈溢出 。 代码: int FibonacciRecursive(int n) { if( n < 2) return n; return (FibonacciRecursive(n-1)+FibonacciRecursive(n-2)); } 递归写的代码非常容易懂,完全是根据函数的条件进行选择计算机步骤。例如现在要计算n=5时的值,递归调用过程如下图所示: 2.尾递归 尾递归就是从最后开始计算,

递归

孤街醉人 提交于 2020-01-29 15:16:36
一、符合运用递归的条件: 能够将大问题进行分解为各个类似的小问题 各个小问题的解法类同与大问题 有问题终止的条件 二、递归代码的写法: 先推导出数学公式 找到终止条件 基于此写代码 三、递归代码注意事项: 警惕堆栈溢出: 因为不停的函数调用,建立方法栈,若无终止条件,或者超出栈内存,则会溢出 解决方案: 1)确立的正确终止条件 2)限制递归调用的深度,即建立调用深度计数器。 递归中函数值的重复计算: 如:f(4) 可能会被重复计算好几次 解决方案: 用散列表存储函数的值,在每次计算之前查询。 递归代码的调试方法: 因为对于规模较大,层次较深的递归代码,如果仅仅单步调试会非常麻烦困难。 解决方案: 1)日志输出每个阶段的值 2)结合条件断点进行调试 来源: CSDN 作者: alone away 链接: https://blog.csdn.net/quanzywy/article/details/104106355

函数——递归

∥☆過路亽.° 提交于 2020-01-29 12:01:23
  递归就是函数直接或者是间接的调用自己,相当于循环,使用时必需要有一个跳出条件,否则会进入死循环。它是栈的存取方式,先进后出,后进先出。   主要用于循环,如阶乘函数、幂函数和斐波那契数列。 <script> function fn(n) { if (n == 1) { return n; } return fn(n - 1) + n; } console.log(fn(5)); //15 </script>   以下为分析过程: <script> //分析: n = 5 function fn(5) { if (5 === 1) { //不满足 return n; } return fn(4) + 5; //10+5=15 } n = 4 function fn(4) { if (4 === 1) { //不满足 return n; } return fn(3) + 4; //6+4=10 } n = 3 function fn(3) { if (3 === 1) { //不满足 return n; } return fn(2) + 3; //3+3=6 } n = 2 function fn(2) { if (2 === 1) { //不满足 return n; } return fn(1) + 2; //1+2=3 } n = 1 function fn(1) { if

LeetCode 94/144/145: Binary Tree Inorder/Preorder/Postorder Traversal LeetCode

怎甘沉沦 提交于 2020-01-29 01:16:49
LeetCode 94 Inorder 问题描述 思路 中序遍历: 按照 左结点 根节点 右结点的顺序 递归实现 : preorder方法实现按照 左结点 根节点 右结点的顺序加入。以左结点为根的子树和 以右结点为根的子树 遍历也是由inorder方法实现。 非递归实现 :通过一个栈,从根节点开始入栈,如果当前结点不为空或者栈不为空,进入循环,只要一直存在左节点就一直入栈,不存在左节点就出栈访问当前结点,当前结点设置为出栈节点的右节点, 然后继续循环遍历 java实现 /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ // //way1递归 // class Solution { // List<Integer> result=new ArrayList<Integer>(); // public List<Integer> inorderTraversal(TreeNode root) { // inorder(root); // return result; // } // private void inorder

10 - 递归

我们两清 提交于 2020-01-28 12:54:07
1. 一个应用场景 2. 概述 递归就是方法自己调用自己 递归需要遵守的重要规则 执行一个方法时,就创建一个新的受保护的独立空间(栈空间) 方法的局部变量是独立的,不会相互影响;但如果方法中使用的是 引用类型变量 (比如数组),就会 共享 该引用类型的数据 递归必须向退出递归的条件逼近,即该函数所处理的数据规模必须在递减,否则就是无限递归,出现 StackOverflowError 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕 理解 - 方法调用 逻辑 内存 https://www.jianshu.com/p/e7a22923867f 3. 过程 当在一个函数运行期间,调用另一个函数时,在运行被调用函数之前,系统需要先完成3件事 将所有实参,返回地址等信息传递给被调用函数保存 为被调用函数的局部变量分配存储区 将控制转移到被调函数的入口 从被调用函数返回调用函数之前,系统也应完成3件工作 保存被调函数的计算结果 释放被调函数的数据区 依照被调函数保存的返回地址将控制转移到调用函数 当有多个函数构成嵌套调用时,按照"后调用先返回"的原则,上述函数之间的信息传递和控制转移必须通过"栈"来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数,就为它在栈顶分配一个存储区

几道简单的c语言编程题

二次信任 提交于 2020-01-28 07:30:05
几道基础C语言题 1.递归求阶乘 分析 : n!=1*2*...*n 那么我们有两种方法 递归 思路 :根据阶乘的规律 n!=n*(n-1)! 定义函数f(x),使函数返回 x*f(x-1) 的值,直到n=1。经过函数调用本身求出结果。 代码 : #include<stdio.h> int cnt(int n);//调用声明函数 int main() { int n; scanf("%d",&n); printf("%d",cnt(n)); return 0; } int cnt(int n)//定义声明函数 { if(n>0) n*=cnt(n-1); else n=1; return n; } 循环 思路 :由于 n!=n*(n-1)*...*1 ,则从a=1开始,+1之后与a相乘。 代码 : #include<stdio.h> int main() { int n; scanf("%d",&n); int a=1; //a要初始化为1 for(int i=1;i<=n;i++) //注意i应从1取到n,因此判断条件为<= a=a*i; printf("%d",a); return 0; } 2.分制转换 题目 :给出一个百分制成绩,要求输出成绩等级‘A’(90分及90分以上),‘B’(80到89分),’C’(70到79分),’D’(60到69分),’E’(60分以下)。