递归算法

对递归算法的理解

北战南征 提交于 2019-12-16 20:19:26
递归算法 ① 程序调用自身的编程技巧称为递归。 ② 一个方法在其定义或说明中又直接或间接的调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需的多次重复计算,大大地减少了程序的代码量。 递归算法注意 ① 递归就是在方法里调用自身。 ② 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。 示例:使用递归算法求5!(阶乘) 分析:5!=5*4!,4!=4*3!,3!=3*2!,2!=2*1!,1!=1 再将最后的值从后往前返回到它的上一个,返回1!,2!,3!,4! 最后是5!。 1 class FacUtil{ 2 public static int getFac(int number){ 3 if(number==1){ 4 return 1; 5 }else{ 6 return number*getFac(number-1); 7 } 8 } 9 } 主方法调用 1 System.out.println(FacUtil.getFac(5)); 对递归算法的理解   1.递归的定义:程序调用自身的编程技巧称为递归。递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法

递归

不问归期 提交于 2019-12-16 17:35:49
一、什么是递归? 1.递归是一种非常高效、简洁的编码技巧,一种应用非常广泛的算法,比如DFS深度优先搜索、前中后序二叉树遍历等都是使用递归。 2.方法或函数调用自身的方式称为递归调用,调用称为递,返回称为归。 3.基本上,所有的递归问题都可以用递推公式来表示,比如 f(n) = f(n-1) + 1; f(n) = f(n-1) + f(n-2); f(n)=n*f(n-1); 二、为什么使用递归?递归的优缺点? 1.优点:代码的表达力很强,写起来简洁。 2.缺点:空间复杂度高、有堆栈溢出风险、存在重复计算、过多的函数调用会耗时较多等问题。 三、什么样的问题可以用递归解决呢? 一个问题只要同时满足以下3个条件,就可以用递归来解决: 1.问题的解可以分解为几个子问题的解。何为子问题?就是数据规模更小的问题。 2.问题与子问题,除了数据规模不同,求解思路完全一样 3.存在递归终止条件 四、如何实现递归? 1.递归代码编写 写递归代码的关键就是找到如何将大问题分解为小问题的规律,并且基于此写出递推公式,然后再推敲终止条件,最后将递推公式和终止条件翻译成代码。 2.递归代码理解 对于递归代码,若试图想清楚整个递和归的过程,实际上是进入了一个思维误区。 那该如何理解递归代码呢?如果一个问题A可以分解为若干个子问题B、C、D,你可以假设子问题B、C、D已经解决。而且

【讲●解】超全面的线段树:从入门到入坟

隐身守侯 提交于 2019-12-16 14:42:58
\(Pre\) :其实线段树已经学了很久了,突然想到线段树这个数据结构比较重要吧,想写篇全面的总结,帮助自己复习,同时造福广大 \(Oier\) ( 虽然线段树的思维难度并不高 )。本篇立志做一篇最浅显易懂,最全面的线段树讲解,采用 \(lyd\) 写的《算法竞赛进阶指南》上的顺序,从最基础的线段树到较深入的主席树,本篇均会涉及,并且附有一定量的习题,以后可能会持续更新,那么现在开始吧! 目录一览 更新日志 线段树想 \(AC\) 之基本原理(雾*1 线段树想偷懒之懒标记(雾*2 线段树想应用之扫描线(雾*3 线段树想瘦身之开点与合并(雾*4 线段树想持久之主席树(雾*5 线段树想带修之树套树(雾*6 线段树想...不,你不想 更新日志 5.19 update:懒标记20%完成。 5.12 update:添加题目链接, 然后颓去了 。 5.11 update:修改部分字词,基本原理基本完成,大纲完成。 5.4 update:基本原理20%完成。 线段树想 \(AC\) 之基本原理 什么是线段树啊? 首先,你得有 树 的基本知识。 然后。 以下内容摘自百度百科 线段树是一种 二叉搜索树 ,与 区间树 相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树的一个结点。 很懵?没关系,我们继续。 其实,线段树( \(Segment\) \(Tree\)

Java实现八大排序算法

痞子三分冷 提交于 2019-12-15 14:34:07
我对java的八大排序算法进行了总结,以此文展示Java八大算法 常见排序算法如下: 1.直接插入排序 2.希尔排序 3.简单选择排序 4.堆排序 5.冒泡排序 6.快速排序 7.归并排序 8.基数排序 排序方法示例简介 直接插入排序 基本思想 通常人们整理桥牌的方法是一张一张的来,将每一张牌插入到其他已经有序的牌中的适当位置。在计算机的实现中,为了要给插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。 算法描述 一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下: 从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果该元素(已排序)大于新元素,将该元素移到下一位置 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 将新元素插入到该位置后 重复步骤2~5 注意: 如果 比较操作 的代价比 交换操作 大的话,可以采用二分查找法来减少 比较操作 的数目。该算法可以认为是 插入排序 的一个变种,称为二分查找插入排序。 代码实现 /** * 通过交换进行插入排序,借鉴冒泡排序 * * @param a */ public static void sort ( int [ ] a ) { for ( int i = 0 ; i < a . length - 1 ; i ++ ) { for (

正则表达式匹配(详细多解法)

流过昼夜 提交于 2019-12-15 05:10:43
目录 •写在前面 •题目 •解法一 •解法二 •解法三 •写在前面 所谓真的勇士,敢于直面不用API的手撕,我最开始看到这道题的时候,觉得简直就是到秒杀题,不过我还是按捺住了内心用java正则表达式API的冲动,手撕匹配代码,算法使用的思想就是递归,我最开始是使用直接递归解决了这道题,后面看别人的解法的时候,发现在递归上进行了改进,使用了动态规划的思路,降低了子串创建的时间成本,觉得挺有意思的,就记录下来,毕竟正则表达式我从来没有想过自己去实现过,哈哈哈。 •题目 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。 '.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。 说明: s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。 示例 1: 输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。 示例 2: 输入: s = "aa" p = "a*" 输出: true 解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。 示例 3: 输入: s =

动态规划套路详解

故事扮演 提交于 2019-12-14 21:54:21
动态规划套路详解 转自 作者:labuladong 链接:https : / / leetcode - cn . com / problems / coin - change / solution / dong - tai - gui - hua - tao - lu - xiang - jie - by - wei - lai - bu - ke / 来源:力扣(LeetCode) 下面通过对斐波那契数列和这道凑零钱问题详解动态规划。如果只想看本题的答案,请直接翻到最后查看。 动态规划算法似乎是一种很高深莫测的算法,你会在一些面试或算法书籍的高级技巧部分看到相关内容,什么状态转移方程,重叠子问题,最优子结构等高大上的词汇也可能让你望而却步。 而且,当你去看用动态规划解决某个问题的代码时,你会觉得这样解决问题竟然如此巧妙,但却难以理解,你可能惊讶于人家是怎么想到这种解法的。 实际上,动态规划是一种常见的「算法设计技巧」,并没有什么高深莫测,至于各种高大上的术语,那是吓唬别人用的,只要你亲自体验几把,这些名词的含义其实显而易见,再简单不过了。 至于为什么最终的解法看起来如此精妙,是因为动态规划遵循一套固定的流程: 递归的暴力解法 -> 带备忘录的递归解法 -> 非递归的动态规划解法 。这个过程是层层递进的解决问题的过程,你如果没有前面的铺垫,直接看最终的非递归动态规划解法

二叉树结构的建立与遍历

坚强是说给别人听的谎言 提交于 2019-12-14 07:50:24
#include <stdio.h> #include <stdlib.h> #define MAX_LEN 50 /*二叉树的二叉链表结构*/ typedef struct node { struct node *lchild; struct node *rchild; char data; }BTREE; /*栈的数组实现*/ typedef struct { int top; BTREE *a[MAX_LEN]; //结构体数组,每个元素均为一个结点 }Stack; /*队列的数组实现*/ typedef struct { BTREE *a[MAX_LEN]; //结构体数组,每个元素均为一个结点 int front; int rear; }Queue; /*为栈分配空间*/ Stack *Createstack() { Stack *p; p = (Stack *)malloc(sizeof(Stack)); p->top = -1; return p; } /*为队列分配空间*/ Queue *Createqueue() { Queue *p; p = (Queue *)malloc(sizeof(Queue)); p->front = 0; p->rear = 0; return p; } /*菜单*/ int Menu() { int x; printf("\n\n"

实战算法——多叉树全路径遍历(完整版)

陌路散爱 提交于 2019-12-14 02:26:23
目录 前言 递归和非递归比较 递归 非递归 递归的劣势和优势 问题构建 问题解决 递归方法 非递归方法 测试 结论 前言 本文研究的是如何对一个 多叉树进行全路径的遍历 ,并输出全路径结果。该问题的研究可以用在:Trie树中 查看所有字典值 这个问题上。本文将对该问题进行 详细的模拟 及进行 代码实现 ,讨论了 递归 和 非递归 两种方法优劣并分别进行实现,如果读者对这两种方法的优劣不感兴趣可直接跳到 问题构建 章节进行阅读。文章较长,推荐大家 先收藏 再进行阅读。 递归和非递归比较 这个问题知乎上已经有了很多答案,在其基础上我进行了一波总结: 递归 将一个问题分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈保存,问题规模较大时会占用大量内存。 非递归 执行效率高,运行时间只因循环次数增加而增加,没什么额外开销。空间上没有什么增加 递归的劣势和优势 递归的劣势 递归容易产生" 栈溢出 "错误(stack overflow)。因为需要同时保存成千上百个调用记录,所以递归非常耗费内存。 效率方面,递归可能存在 冗余计算 。使用递归的方式会有冗余计算(比如最典型的是斐波那契数列,计算第6个需要计算第4个和第5个,而计算第5个还需要计算第4个,所处会重复)。迭代在这方面有绝对优势。 递归的优势 递归拥有较好的代码 可读性

零钱兑换-动态规划

*爱你&永不变心* 提交于 2019-12-13 01:26:30
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 示例 1: 输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2], amount = 3 输出: -1 说明: 你可以认为每种硬币的数量是无限的。 题解 动态规划算法似乎是一种很高深莫测的算法,你会在一些面试或算法书籍的高级技巧部分看到相关内容,什么状态转移方程,重叠子问题,最优子结构等高大上的词汇也可能让你望而却步。 而且,当你去看用动态规划解决某个问题的代码时,你会觉得这样解决问题竟然如此巧妙,但却难以理解,你可能惊讶于人家是怎么想到这种解法的。 实际上,动态规划是一种常见的「算法设计技巧」,并没有什么高深莫测,至于各种高大上的术语,那是吓唬别人用的,只要你亲自体验几把,这些名词的含义其实显而易见,再简单不过了。 至于为什么最终的解法看起来如此精妙,是因为动态规划遵循一套固定的流程:递归的暴力解法 -> 带备忘录的递归解法 -> 非递归的动态规划解法。这个过程是层层递进的解决问题的过程,你如果没有前面的铺垫,直接看最终的非递归动态规划解法,当然会觉得牛逼而不可及了。 当然,见的多了,思考多了

[LeetCode] 全排列

你说的曾经没有我的故事 提交于 2019-12-12 14:31:42
#转载于 https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/ Given a collection of distinct integers, return all possible permutations. Example: Input: [1,2,3] Output: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 以示例输入: [1, 2, 3] 为例,如果让我们手写,要做到不重不漏,我们书写的策略可能是这样:“一位一位确定”,这样说比较笼统,具体是这样的: 1、先写以 1 开始的两个排列:[1, 2, 3]、[1, 3, 2]; 2、再写以 2 开始的两个排列:[2, 1, 3]、[2, 3, 1]; 3、最后写以 3 开始的两个排列:[3, 1, 2]、[3, 2, 1]。 如果数组元素多一点的话,也不怕,我们写的时候遵循下面的原则即可: 1、按数组的顺序来(不要求排序,但我们选取元素的顺序是从左到右的),每次排定 1 个元素; 说明:只有按照顺序才能做到不重不漏。 2、新排定的元素一定不能在之前排定的元素中出现。 说明:如果违反了这一条