递归

合集下载

递归的概念递归过程与递归工作栈递归与回溯广义表

递归的概念递归过程与递归工作栈递归与回溯广义表
else { Hanoi ( n-1, A, C, B ); cout << " move " << A << " to " << C << endl; Hanoi ( n-1, B, A, C );
} }
递归过程与递归工作栈
递归过程在实现时,需要自己调用自己。 层层向下递归,退出时的次序正好相反:
}
递归找含x值的结点
f
x

fff
问题的解法是递归的
例如,汉诺塔(Tower of Hanoi)问题的解法: 如果 n = 1,则将这一个盘子直接从 A 柱移到
C 柱上。否则,执行以下三步: ① 用 C 柱做过渡,将 A 柱上的 (n-1) 个盘子移 到 B 柱上: ② 将 A 柱上最后一个盘子直接移到 C 柱上; ③ 用 A 柱做过渡,将 B 柱上的 (n-1) 个盘子移 到 C 柱上。
递归调用
n! (n-1)! (n-2)!
1! 0!=1
返回次序
主程序第一次调用递归过程为外部调用;
递归过程每次递归调用自己为内部调用。
它们返回调用它的过程的地址不同。
递归工作栈
每一次递归调用时,需要为过程中使用的 参数、局部变量等另外分配存储空间。
每层递归调用需分配的空间形成递归工作 记录,按后进先出的栈组织。
while ( n >= 0 ) { cout << "value " << A[n] << endl;
n--;
} }
递归与回溯 常用于搜索过程
n皇后问题 在 n 行 n 列的国际象棋棋盘上,

递归的定义及应用

递归的定义及应用

• (2)式给出了fun(n)的值与fun(n-1)的值之间的关系, 称为递归体。
9
递归求n!
int fun(int n) {
if (n==1)
/*语句1*/
return 1; /*语句2*/
分解过程
else
/*语句3*/
return fun(n-1)*n; /*语句4*/
n=3
n=2
} n=1
fuc(3) ﹕
s
s为空串
f(s)=

Concat(f(SubStr(s,2,StrLength(s)-1)),SubStr(s,1,1)) 其他
15
SqString invert(SqString &s) {
SqString s1,s2; if(StrLength(s)>0) { s1=invert(SubStr(s,2,StrLength(s)-1));
• 体现了分而治之的算法思想
3
6.1.2 何时使用递归
• 在以下3种情况下,常常要用到递归的方法。
• 1. 定义是递归的 • 2. 数据结构是递归的
求n!和Fibonacci数列
例如,单链表就是一种递归的数据结构,其结
点类型定义如下:
typedef struct LNode
{
ElemType data;
else return(head->data+Sum(head->next));
}
5
3. 问题的求解方法是递归的
• 汉诺塔问题是印度的一个古老的传说。 • 在世界中心贝拿勒斯(在印度北部)的圣庙里, 一块黄铜板上插着三根宝石针。印度教的主神梵天在 创造世界的时候,在其中一根针上从下到上地穿好了 由大到小的64片金片,这就是所谓的汉诺塔。 • 不论白天黑夜,总有一个僧侣在按照下面的法则 移动这些金片:一次只移动一片,不管在哪根针上, 小片必须在大片上面。 • 僧侣们预言,当所有的金片都从梵天穿好的那根 针上移到另外一根针上时,世界就将在一声霹雳中消 灭,而梵塔、庙宇和众生也都将同归于尽。

递归的作用与意义

递归的作用与意义

递归的作用与意义递归是一种常见的编程技术,它在解决问题时具有重要的作用与意义。

递归是指一个函数或过程在执行过程中不断调用自身的过程。

通过递归,可以将复杂的问题分解成更小的子问题,并通过解决子问题来解决原始问题。

递归的作用之一是简化问题。

对于一些复杂的问题,我们可以使用递归的方式将其划分为更小的子问题。

这样一来,我们只需要解决每个子问题,然后将它们的解合并起来,就能得到原始问题的解。

递归的这种特性使得问题的解决变得更加清晰和简明。

递归还可以提高代码的可读性和可维护性。

使用递归的方式可以将代码分解成多个独立的函数,每个函数只负责解决一个子问题。

这样一来,我们可以更加专注地思考每个子问题的解决方案,而不需要同时考虑多个问题。

这种模块化的设计使得代码更易于理解和修改,从而提高了代码的可读性和可维护性。

除了简化问题和提高代码的可读性和可维护性之外,递归还可以解决一些特定的问题。

例如,在树的遍历中,递归可以帮助我们实现前序遍历、中序遍历和后序遍历等操作。

在图的搜索中,递归可以帮助我们实现深度优先搜索和广度优先搜索等算法。

递归的这种特性使得它成为解决这些问题的有效工具。

然而,递归也存在一些潜在的问题和限制。

首先,递归的实现需要消耗额外的内存空间,因为每次递归调用都需要保存函数的状态和局部变量。

如果递归的层数很深,可能会导致栈溢出的问题。

其次,递归的效率通常较低,因为每次递归调用都需要进行函数的调用和返回操作,而这些操作会带来额外的开销。

因此,在一些性能要求较高的场景中,可能需要考虑使用其他的解决方案。

递归在编程中具有重要的作用与意义。

它可以简化问题的解决过程,提高代码的可读性和可维护性,并解决一些特定的问题。

然而,递归也存在一些潜在的问题和限制。

因此,在使用递归时需要仔细考虑问题的性质和场景的要求,以确保递归的有效性和有效性。

只有在合适的场景下,递归才能发挥其最大的作用和意义。

希望通过本文的介绍,读者能够更好地理解递归的作用与意义,并在实际的编程中灵活运用。

c语言的递归

c语言的递归

c语言的递归递归,作为一种编程技巧和思维方式,是计算机科学中一个重要的概念。

在C语言中,递归可以被广泛应用于各种算法和问题的解决过程中。

本文将介绍C语言中递归的基本原理、优缺点以及一些常见的递归应用场景。

一、递归的基本原理递归是指在一个函数的定义中调用函数本身的过程。

在C语言中,递归函数的定义通常包括两个部分:基准情况(base case)和递归情况(recursive case)。

基准情况是指满足特定条件时递归停止,递归情况则是通过不断调用函数本身来实现问题的拆解和解决。

下面是一个简单的例子,演示了如何使用递归来实现计算n的阶乘:```cint factorial(int n) {// 基准情况if (n == 0 || n == 1) {return 1;}// 递归情况return n * factorial(n - 1);}int main() {int n = 5;int result = factorial(n);printf("%d的阶乘是:%d\n", n, result);return 0;}```在上面的代码中,当n等于0或者1时,递归停止,返回结果1。

否则,函数将n与factorial(n-1)相乘,并返回结果。

通过逐步拆解n的值,直到满足基准情况,递归函数可以完成阶乘的计算。

二、递归的优缺点递归在解决某些问题时可以提供简洁、优雅的解决方案,但同时也存在一些缺点。

1. 优点:简洁清晰:递归能够将问题分解为更小的子问题,代码结构更加清晰易懂。

解决复杂问题:某些问题如果采用迭代的方式来解决会非常复杂,而递归提供了一种更加直观的解决思路。

2. 缺点:性能开销:递归往往需要调用函数本身多次,会产生额外的函数调用开销,导致性能下降。

内存占用:递归的过程中需要保存每一次函数调用的上下文,可能会导致内存占用较大。

三、递归的应用场景递归可以用于解决一些常见的问题和算法,比如树的遍历、图的搜索、排列组合等。

递归

递归
R的全排列可归纳定义如下: 当n=1时,perm(R)=(r),其中r是集合R中唯一的元素; 当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),…, (rn)perm(Rn)构成。
17
n
n-1 n-1
n-1
……
n-2 n-2
n-2
n-2 n-2
n-2
…… 例4 排列问题
1
n0
F
(n)
1
n 1
F (n 1) F (n 2) n 1
边界条件 递归方程
非递归定义:
F(n)
1 5
1
2
5 n1
1
2
5
n1
7
第n个Fibonacci数可递归地计算如下: int fibonacci(int n)
{ if (n <= 1) return 1; return fibonacci(n-1)+fibonacci(n-2);
q(6,1) q(6,2) q(6,3) q(6,4) q(6,5) q(6,6)
28
用栈模拟q(6,6)的递归计算过程。
q(4,1) q(4,2) q(6,2) q(6,3) q(6,4) q(6,5) q(6,6)
29
用栈模拟q(6,6)的递归计算过程。
q( q(6,5) q(6,6)
第2章 递归与分治策略
学习要点: • 理解递归的概念。 • 掌握设计有效算法的分治策略。 • 通过下面的范例学习分治策略设计技巧。 (1)二分搜索技术; (2)大整数乘法; (3)Strassen矩阵乘法; (4)棋盘覆盖; (5)合并排序和快速排序; (6)线性时间选择; (7)最接近点对问题; (8)循环赛日程表。

递归的三个条件

递归的三个条件

递归的三个条件
1. 基线条件
基线条件指的是递归函数的退出条件,也就是递归最底层的情况,通常是能够被直接求解或解决的情况。

2. 递归条件
递归条件指的是递归函数中,解决问题所需要的步骤,这些步骤通常需要调用函数本身。

递归条件应该包含递归操作,这样递归才能一步步向基线条件靠近。

3. 向基线条件靠近
向基线条件靠近指的是递归过程中每次调用函数时,问题规模会不断减小,最终会到达基线条件。

如果递归函数不能向基线条件靠近,则递归会陷入无穷循环,导致程序崩溃。

什么是递归

什么是递归

什么是递归?递归(Recursion)是一种编程技术,其中一个函数调用自身来解决问题。

递归是通过将问题分解成更小的子问题来解决复杂问题的一种方法。

这种自我调用的过程在每次调用中都会处理一个更小的问题,直到达到基本情况(终止条件),然后逐步返回结果,最终解决整个问题。

递归的核心思想是将复杂问题分解成更小规模的相同问题。

每次递归调用都会将问题的规模减小,直到问题达到一定的规模,可以直接解决。

这个基本情况通常是一个简单且不再需要递归的情况。

递归的实现通常包括以下几个要素:1. 基本情况:基本情况是递归调用的终止条件。

当问题的规模减小到一定程度,可以直接解决时,递归将停止。

在基本情况下,函数通常直接返回结果而不再进行递归调用。

2. 递归调用:递归函数在解决问题的过程中会调用自身来处理更小规模的子问题。

递归函数通过传递问题的子集或更小的输入来减小问题的规模。

递归调用将重复进行,直到达到基本情况。

3. 问题的规模减小:递归函数通过每次调用将问题的规模减小来逐步解决问题。

这通常涉及到在每次递归调用中使用不同的输入或参数。

问题的规模必须在每次递归调用中减小,否则递归将无法终止,导致无限循环(无穷递归)。

递归的一个典型示例是计算阶乘。

阶乘是指从1到某个正整数之间所有整数的乘积。

使用递归,我们可以将阶乘问题分解为更小的子问题,直到达到基本情况。

例如,计算5的阶乘(5!)可以使用递归的方式来实现:```factorial(n):if n == 0: // 基本情况return 1else:return n * factorial(n-1) // 递归调用```在上面的示例中,当n等于0时,递归调用将停止,返回1作为结果。

否则,函数将n与n-1的阶乘相乘,并继续递归调用,直到达到基本情况。

递归函数需要小心处理,确保递归调用能够终止,并且问题的规模在每次递归调用中减小。

否则,递归可能会导致堆栈溢出或无限循环的问题。

递归在许多算法和数据结构问题中都有应用,例如树的遍历、图的搜索、排序算法等。

递归名词解释

递归名词解释

递归名词解释
递归:
1、概念:递归是一种反复出现的算法,它将一个大问题拆分成若干小
问题,并且使用结果解决大问题,在解决大问题时候可以多次重复调
用自身以达到解决问题的目的。

2、定义:递归是一种编程技术,它提供了一种可以让程序创建动态的、多层的数据结构的一种方法,也就是用较少的语句可以实现数据结构
的表示。

3、原理:递归调用的原理是多次执行同一个程序段,在程序段内部,
对已知的特殊情况保存结果,减少运算量,并根据条件调用自身以解
决整个问题。

4、特点:
* (1)可以根据已知条件简化问题
* (2)可以多次调用自身、产生迭代结果,在一次调用中展开多次迭代,以实现复杂数据结构的表示
* (3)可以在程序段中使用条件语句,在特定条件真伪时返回特定的结

* (4)可以实现交替操作,多次调用自身,每次执行的逻辑可能不一样。

5、应用场景:
* (1)求解较复杂的数学问题,比如斐波那契数列问题、汉诺塔问题等* (2)求解复杂算法,比如快速排序、归并排序等
* (3)实现迭代计算,让程序可以定义无限长度的数据结构。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
递归算法
1
递归算法
什么是递归 递归调用的实现原理 递归经典例题
递归定义


在定义一个过程或函数时出现调用本过 程或本函数的成分,称之为递归。 若调用自身,称之为直接递归。 若过程或函数p调用过程或函数q,而q又调 用p,称之为间接递归。
何时用到递归 以下三种情况常常用到递归方法。
1 定义是递归的 2 数据结构是递归的 3 问题的解法是递归的
int main() { int a[1000]; f(6,a,0); return 0; }
经典问题:整数划分
要求:如果对上一个题改一下要求,求解一共多少种划分方 法,如果不用上一种思路,请再尝试另一种递归解决方案
经典问题:整数划分






整数划分问题是算法中的一个经典命题之一,有关这个问题的讲述在 讲解到递归时基本都将涉及。所谓整数划分,是指把一个正整数n写成 如下形式: n=m1+m2+...+mi; (其中mi为正整数,并且1 <= mi <= n) ,则{m1,m2,...,mi}为n的一个划分。 如果{m1,m2,...,mi}中的最大值不超过m,即 max(m1,m2,...,mi)<=m,则称它属于n的一个m划分。这里我们记 n的m划分的个数为f(n,m); 例如但n=4时,他有5个划分, {4},{3,1},{2,2},{2,1,1},{1,1,1,1}; 注意4=1+3 和 4=3+1被认为是同一个划分。 该问题是求出n的所有划分个数,即f(n, n)。下面我们考虑求f(n,m) 的方法; 1.递归法:
15
f (int a[], int begin, int n) if (begin==n) return 0; else return a[begin]+f(a,begin+1,n);
{
经典例题:组合问题
从n个球(不同色)中取m个(不放回),有 多少种取法
(这种题跟上次做过的乒乓球问题有点相似,假如取3个球的话, 也许有人想到三层循环嵌套,但有局限性而且效率差,如果 取球个数较大就不适合用循环了,那么怎么化成有相似性规 模较小的问题呢?) 思路:假设有一个球为红球,则在m个球里可能取到也可能没 取到,总共只有这两种情况。 int f ( int n, int m) { if (n==0) return 0; if (n<m) return 0; if (n==m) return 1; return f (n-1,m-1) + f (n-1 , m); }


递归过程在实现时,需要自己调用自己。 每一次递归调用时,需要为过程中使用的参 数、局部变量等另外分配存储空间。 层层向下递归,退出时的次序正好相反: 递归次序 n! (n-1)! (n-2)! 1! 0!=1 返回次序 因此,每层递归调用需分配的空间形成递归 工作记录,按后进先出的栈组织。
函数调用与递归实现
2 数据结构是递归的
对于递归数据结构,采用递归的方法编写算法既 方便又有效。例如,拿一个最简单的例子,求一 个不带头结点的单链表L的所有域(假设为int型) 之和递归算法如下:
int Sum (LinkList * L) {
if (L==NULL )return 0;
else return L->data+Sum(L->next); }
经典问题:整数划分
根据n和m的关系,考虑以下几种情况: (1)当n=1时,不论m的值为多少(m>0),只有一种划分即{1}; (2)当m=1时,不论n的值为多少,只有一种划分即n个1,{1,1,1,...,1}; (3)当n=m时,根据划分中是否包含n,可以分为两种情况: (a)划分中包含n的情况,只有一个即{n}; (b)划分中不包含n的情况,这时划分中最大的数字也一定比n小,即n的所有(n-1)划分。 因此 f(n,n) =1 + f(n,n-1); (4)当n<m时,由于划分中不可能出现负数,因此就相当于f(n,n); (5)但n>m时,根据划分中是否包含最大值m,可以分为两种情况: (a)划分中包含m的情况,即{m, {x1,x2,...xi}}, 其中{x1,x2,... xi} 的和为n-m,因此这情况下 为f(n-m,m) (b)划分中不包含m的情况,则划分中所有值都比m小,即n的(m-1)划分,个数为f(n,m-1); 因此 f(n, m) = f(n-m, m)+f(n,m-1); 综上所述: f(n, m)= 1; (n=1 or m=1) f(n,m) = f(n, n); (n<m) 1+ f(n, m-1); (n=m) f(n-m,m)+f(n,m-1); (n>m)
经典问题:整数分化问题
分析 :
把一个正整数n分成n=x+y,若使 x=n,可求出一种划分,再使x递减, 令x是划分好的数,按此方法继续对y继续进行分解,以此类推,直到 x=0(不包含 x=0)为止,这样就求出了所有划分。 在分解时应保证分解数x有序,即上一次的x要大于等于这一次的x,以避 免求出元素相同但位置不同的结果。
经典问题:求n个元素全排列
void f(int a[],int k,int n) { #include <iostream> if(k==n) using namespace std; { for(int i=0;i<n;i++) void f(int a[],int k,int n); cout<<a[i]<<' '; int main() cout<<endl; { } int a[5]={1,2,3,4,5}; for(int j=k;j<n;j++){ f(a,0,5); {int temp1=a[k];a[k]=a[j];a[j]=temp1;} f(a,k+1,n); return 0; {int temp2=a[k];a[k]=a[j];a[j]=temp2;} }
经典问题:整数分化问题
void f(int n) { //6 //5。。。f(1) //4。。。f(2) //… }
一开始最先想到的就是这一种大体递归结 构,先把第一位确定了,剩下的部分进一 步划分。 但是可以发现参数传达信息太少,只是对 某一个数字进行划分,打印出它的所有划 分,但是这个划分可能会打印多项,并且 没有返回值。 那么一般解决思路就是增加一个缓存、变 量来记录当前特征。 比如3+3, 3+2+1, 3+1+1+1 当第二个3 确定后才开始打印,前面的分解过程 (3+3)需要记录下来
递归算法解题通常有三个步骤:



1)分析问题、寻找递归:找出大规模问题 与小规模问题的关系,这样通过递归使问 题的规模逐渐变小。 2)设置边界、控制递归:找出停止条件, 即算法可解的最小规模问题。 3)设计函数、确定参数:和其它算法模块 一样设计函数体中的操作及相关参数。
递归过程与递归工作栈

} 为了不破坏初始局面,每次交换后还需换回来,用到 回溯
经典问题:整数分化问题

问题描述:
例如,对于正整数n=6,可以分划为: 6 5+1 4+2, 4+1+1 3+3, 3+2+1, 3+1+1+1 2+2+2, 2+2+1+1, 2+1+1+1+1 1+1+1+1+1+1 现在的问题是,对于给定的正整数n,编写算法打印所有划分。 用户从键盘输入 n (范围1~10) 程序输出该整数的所有划分。
1 定义是递归的
有许多数学公式、数列的定义是递归的。例如,阶乘 函数或者Fibonacci数列等,求解这类问题可以将其递归 定义直接转化为对应的递归算法。
1, 当n 0时 n! n (n 1)!, 当 n 1时
求解阶乘函数的递归算法 long Factorial ( long n ) { if ( n == 0 ) return 1; else return n*Factorial (n-1); }
3.问题的求解方法是 递归的
有些问题的解法是递归的,典型的就是汉诺塔问题。
设Hanoi (n,x,y,z)表示将n个盘片从X移动到Z上,递归的分解过程就是先 按照移动规则将(n-1)个从X移到Y,再将第n个从X移到Z,再按规则将 (n-1)个从Y移动到Z void hanoi(int n,char x,char y,char z) { if(n==1) move(1,x,z); else{ hanoi(n-1,x,z,y); move(n,x,z); hanoi(n-1,y,x,z); } }
2 数据结构是递归的
有些数据结构是递归的,例如单链表就是一种递 归数据结构,其节点类型定义如下:
typedef struct LNode
{
ElemType data; struct LNode *next; }LinkList; 该定义中,结构体LNode 的定义用到了自己本身, 即指针域next是一种指向自身类型的指针,所以 它是一种递归数据结构。
3+3, 3+2+1,3+1+1+1
经典问题:整数划分
void f (int n, int a[], int k) { if(n<=0) { for(int i=0;i<k;i++) cout<<a[k]<<' ';cout<<endl; return; //a[]装着历史上已经分解好的每个元素 } //k表示当前从数组的哪一个位置开始处理 for(int j=n;j>=0;j--) { if(k>0 && j>a[k-1])continue;//元素中后一个数字不能大于前一项 a[k]=j; f(n-j,a,k+1); } }
相关文档
最新文档