递归算法

一文学会递归解题!

懵懂的女人 提交于 2019-12-06 07:14:09
前言 递归是算法中一种非常重要的思想,应用也很广,小到阶乘,再在工作中用到的比如统计文件夹大小,大到 Google 的 PageRank 算法都能看到,也是面试官很喜欢的考点 最近看了不少递归的文章,收获不小,不过我发现大部分网上的讲递归的文章都不太全面,主要的问题在于解题后大部分都没有给出相应的时间/空间复杂度,而时间/空间复杂度是算法的重要考量!递归算法的时间复杂度普遍比较难(需要用到归纳法等),换句话说,如果能解决递归的算法复杂度,其他算法题题的时间复杂度也基本不在话下。另外,递归算法的时间复杂度不少是不能接受的,如果发现算出的时间复杂度过大,则需要转换思路,看下是否有更好的解法 ,这才是根本目的,不要为了递归而递归! 本文试图从以下几个方面来讲解递归 什么是递归? 递归算法通用解决思路 实战演练(从初级到高阶) 力争让大家对递归的认知能上一个新台阶,特别会对递归的精华:时间复杂度作详细剖析,会给大家总结一套很通用的求解递归时间复杂度的套路,相信你看完肯定会有收获 什么是递归 简单地说,就是如果在函数中存在着调用函数本身的情况,这种现象就叫递归。 以阶层函数为例,如下, 在 factorial 函数中存在着 factorial(n - 1) 的调用,所以此函数是递归函数 public int factorial(int n) { if (n < =1) { return 1;

二叉树的非递归遍历的思想

北慕城南 提交于 2019-12-06 04:37:16
最近在学习数据结构,从书上的代码示例中学习到了一种抽象的思考方式,记录一些学习二叉树的感悟 先序遍历 先序遍历相对简单,我一开实现的时候考虑了四种情况 左孩子为空 && 右孩子为空 访问根节点,然后出栈 左孩子不为空 && 右孩子为空 访问根节点,然后继续访问左孩子 左孩子为空 && 右孩子不为空 访问根节点,入栈右孩子,出栈 左孩子不为空 && 右孩子不为空 访问根节点,入栈右孩子,继续访问左孩子 template<typename Elemtype> template<typename Fun> bool BiTree<Elemtype>::PreOrderTraverse(Fun &visit){ Stack<BiTNode<Elemtype>*> S; InitStack<bool,BiTNode<Elemtype>*>(S); BiTNode<Elemtype> *p = root; while (p != nullptr) { visit(p); if(p->rChrilde != nullptr) Push<bool,BiTNode<Elemtype>*>(S,p->rChrilde); if(p->lChrilde != nullptr) p = p->lChrilde; else //如果栈空Pop()返回false,用于判断栈空结束循环 if(!Pop<bool

几种常见排序方法的基本介绍,性能分析,和c语言实现

不羁的心 提交于 2019-12-06 00:50:17
本文介绍8种常见的排序算法,以及他们的原理,性能分析和c语言实现: 为了能够条理清楚, 本文所有的算法和解释全部按照升序排序进行 首先准备一个元素无序的数组arr[],数组的长度为length,一个交换函数swap, 在main函数中实现排序函数的调用,并输出排序结果: void swap(int*x , int*y) { int temp = *x; *x = *y; *y = temp; } int main() { int arr[] = { 1,8,5,7,4,6,2,3}; int length = sizeof(arr) / sizeof(int); sort(arr, length); for (int i = 0;i < length;i++) { printf("%d\n", arr[i]); } return 0; } 插入排序 第一次循环: 第二次循环: 第三次循环: 外层循环每执行一次就从无序区向有序区中插入一个数据arr[i]   里层循环控制插入的数据arr[i]与其前一个数据比较,如果比前一个数据小,就让前一个数据后移1位   ...不断重复上述步骤,直到找到不比arr[i]小的数据arr[j],因为arr[j]后面的数据都后移了1位,所以直接将arr[i]放在空闲的arr[j+1]位置 c程序实现: void CRsort(int arr[],

数据结构与算法简记--递归

做~自己de王妃 提交于 2019-12-05 23:32:59
递归 是什么 搞懂递归非常重要:递归应用非常广泛,很多数据结构和算法的编码实现都要用到递归,比如 DFS 深度优先搜索、前中后序二叉树遍历等等。 简单讲就是自己调自己 两个步骤:递--层层调用的过程;归--层层返回的过程 可以写出递推公式,如: f(n) = f(n-1) + 1; f(n) = f(n-1) + f(n-2); f(n)=n*f(n-1); 为什么 优点:代码的表达力很强,写起来简洁。 缺点:空间复杂度高、有堆栈溢出风险、存在重复计算、过多的函数调用会耗时较多等问题。 怎么做 成立需三个条件: 一个问题的解可以分解为几个子问题的解 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样 存在递归终止条件 如何编写递归代码? 写出递推公式,找到终止条件 编写递归代码的关键是,只要遇到递归,我们就把它抽象成一个递推公式,不用想一层层的调用关系,不要试图用人脑去分解递归的每个步骤。 f(n)=f(n-1)+1 其中,f(1)=1 防止堆栈溢出:限制递归次数,超过次数报错退出 防止重复计算:利用缓存数据结构(如散列表)缓存计算过的项 来源: https://www.cnblogs.com/wod-Y/p/11950445.html

递归

我只是一个虾纸丫 提交于 2019-12-05 19:44:40
递归 To Iterate is Human, to Recurse, Divine 递归的定义 递归,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法 递归的三三要素 1.明确的函数要干什么 2.明确终止条件 3.提取重复逻辑,去找等价关系 递归思想的精髓 递归:递是传递传递的意思,参数需要传递到下一层去,如果把递归形容成用要是打开一扇一扇的门的话,传递的就是钥匙; 归有归还的意思,打开所有的门需要把最终的目的归还到问题开始的地方。递归的精髓是对问题的解读,需要抽象出重复的 问题和现象,通过反复的调用将结果返回。 递归的经典例子 1.阶乘 f(int n){ if(n==1)return 1; return n*f(n-1); } 2.斐波那契数列 f(int n){ if(n==1||n==2){ return 1; } return f(n-1)+f(n-2); } 3.杨辉三角 f(int x,iny){ if(y<=x&&y>=0){ if(y==0||y==x){ return 1; }else{ return f(x-1,y-1)+f(x-1,y); } } return -1; } 4.汉诺塔问题 f(int level,String begin,String end){ if(level==1){ //结束 }else{ //倒数第二层移到b f

Algorithm lesson final exam

纵饮孤独 提交于 2019-12-05 18:10:25
1、algorithm analysis O B/W/AV/AMOR,混入其他问题,设计+分析 2、传统算法(肯定要考) 1)divide and conquer master therem、 recursive tree、分析复杂度、递归树加起来得到最终结果 2)greedy algorithm example sort->select 拟阵 独立系统的贪心模板,直接得到近似比 3)dynamic programming sub-optimal structrue、编辑距离 3、graph algorithm 1)exproration bfs and dfs、最小生成树 2)最短路,single path all pair 负环、负边 3)max-flow 优化方式 4、turing machine Np/NPC NP-hard np-reduction 5、approximation greedy/sequential/local search/lp rouding 三种算法的设计、图算法、图灵机、近似算法 来源: https://www.cnblogs.com/elpsycongroo/p/11939165.html

经典排序算法

随声附和 提交于 2019-12-05 08:34:10
  排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。用一张图概括: 关于时间复杂度: 平方阶 (O(n2)) 排序 各类简单排序:直接插入、直接选择和冒泡排序。 线性对数阶 (O(nlog2n)) 排序 快速排序、堆排序和归并排序。 O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数。 希尔排序。 线性阶 (O(n)) 排序 基数排序,此外还有桶、箱排序。 关于稳定性: 稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。 不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。 名词解释: n:数据规模 k:“桶”的个数 In-place:占用常数内存,不占用额外内存 Out-place:占用额外内存 稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同 冒泡排序   冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

DOM深度优先遍历算法

妖精的绣舞 提交于 2019-12-05 07:02:38
通过深度优先遍历算法,可以依次获取每个后代节点的对象。 顺序:有子元素先获取子元素,再获取兄弟元素 主要有2步骤: //1.创建节点迭代器对象(parent是要遍历的节点) var iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT, null, false); //2.反复调用iterator的nextNode方法跳到下一个 do{   var node = iterator.nextNode();   if(node != null) console.log(node.nodeName);   else break; }while(true); 以上也可以使用递归实现,但递归效率较低,不建议使用。 来源: https://www.cnblogs.com/1016391912pm/p/11912503.html

如何教会女友递归算法?

与世无争的帅哥 提交于 2019-12-05 06:05:00
一到周末就开始放荡自我,这不带着女朋友去万达电影院看电影(其实是由于整天呆在家敲代码硬是 被女朋友强行拖拽去看电影,作为一个有理想的程序员,我想各位应该都能体谅我),一到电影院, 女朋友说要买爆米花和可乐,我当时二话没说,臣本布衣躬耕于南阳,壤中羞涩,所以单买了爆米 花,买完都不带回头看老板的那种,饮料喝多了不好,出门的时候我带了白开水,还得亏我长得销 魂,乍一看就能看出是个社会精神小伙,女朋友也没多说什么,只是对我微了微笑(我估计是被我的 颜值以及独到的见解所折服),刚坐下没多久,女朋友突然问我,咱们现在坐在第几排啊?电影院里 面太黑了,看不清,没法数,这个时候,如果是你现在你怎么办?别忘了你我是程序员,这个可难不 倒我,递归就开始排上用场了。于是我就问前面一排的人他是第几排,你想只要在他的数字上加一, 就知道自己在哪一排了。但是,前面的人也看不清啊,所以他也问他前面的人。就这样一排一排往前 问,直到问到第一排的人,说我在第一排,然后再这样一排一排再把数字传回来。直到你前面的人告 诉你他在哪一排,于是你就知道答案了。这就是一个非常标准的递归求解问题的分解过程,去的过程 叫“递”,回来的过程叫“归”。基本上,所有的递归问题都可以用递推公式来表示。我们用递推公式将 它表示出来就是这样的 f ( n ) = f (n - 1) + 1 其中,f ( 1 ) = 1 f(n

迭代与递归

佐手、 提交于 2019-12-05 04:35:28
迭代是逐渐逼近,用新值覆盖旧值,直到满足条件后结束,不保存中间值,空间利用率高。 递归是将一个问题分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈保存,问题规模较大时会占用大量内存。 迭代是更新变量的旧值。递归是在函数内部调用自身。 https://www.zhihu.com/question/20278387 表示“重复”这个含义的词有很多, 比如循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate). 循环 算是最基础的概念, 凡是重复执行一段代码, 都可以称之为循环. 大部分的递归, 遍历, 迭代, 都是循环. 递归 的定义是, 根据一种(几种)基本情况定义的算法, 其他复杂情况都可以被逐步还原为基本情况. 在编程中的特征就是, 在函数定义内重复调用该函数. 例如斐波那契数列, 定义F(0)=1, F(1)=1, 所有其他情况: F(x)=F(x-1)+F(x-2). 所有大于1的整数经过有限次的反推之后都可以转换到两种基本情况. 而在编程中, 算法则是这样的: int F (x) { if (x==0 || x==1) return 1; //这里是退出递归的条件, 以保证在有限次递归后能够得到结果 return F(x-1)+F(x-2); //转化为更为基本的情况,