第5章 递归 5.1 什么是递归 5.2 递归算法的设计 本章小结.

Slides:



Advertisements
Similar presentations

Advertisements

2 和 5 的倍数的特征 运动热身 怎样找一个数的倍数? 从小到大写出 2 的倍数( 10 个): 写出 5 的倍数( 6 个) 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 5 , 10 , 15 , 20 , 25 , 30.
一、 一阶线性微分方程及其解法 二、 一阶线性微分方程的简单应用 三、 小结及作业 §6.2 一阶线性微分方程.
第八章 第四节 机动 目录 上页 下页 返回 结束 一个方程所确定的隐函数 及其导数 隐函数的微分法.
2.5 函数的微分 一、问题的提出 二、微分的定义 三、可微的条件 四、微分的几何意义 五、微分的求法 六、小结.
练一练: 在数轴上画出表示下列各数的点, 并指出这些点相互间的关系: -6 , 6 , -3 , 3 , -1.5, 1.5.
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
第五章 二次型. 第五章 二次型 知识点1---二次型及其矩阵表示 二次型的基本概念 1. 线性变换与合同矩阵 2.
6.9二元一次方程组的解法(2) 加减消元法 上虹中学 陶家骏.
计算学科的基本问题 本章首先介绍一个对问题进行抽象的典型实例——哥尼斯堡七桥问题。然后,通过“梵天塔”问题和“停机问题”分别介绍学科中的可计算问题和不可计算问题。从“梵天塔”问题再引出算法复杂性中的难解性问题、P类问题和NP类问题,证比求易算法,P=NP是否成立的问题。
计算机三级考试C语言上机试题专题.
《高等数学》(理学) 常数项级数的概念 袁安锋
计算机硕士专业基础—C语言 赵海英
小学生游戏.
第五节 微积分基本公式 、变速直线运动中位置函数与速度 函数的联系 二、积分上限函数及其导数 三、牛顿—莱布尼茨公式.
第二节 微积分基本公式 1、问题的提出 2、积分上限函数及其导数 3、牛顿—莱布尼茨公式 4、小结.
第5章 定积分及其应用 基本要求 5.1 定积分的概念与性质 5.2 微积分基本公式 5.3 定积分的换元积分法与分部积分法
第三节 格林公式及其应用(2) 一、曲线积分与路径无关的定义 二、曲线积分与路径无关的条件 三、二元函数的全微分的求积 四、小结.
专题研讨课二: 数组在解决复杂问题中的作用
第5章 §5.3 定积分的积分法 换元积分法 不定积分 分部积分法 换元积分法 定积分 分部积分法.
Linked List Operations
强连通分量 无向图 1、任意两顶点连通称该图为连通图 2、否则将其中的极大连通子图称为连通分量 A D C B E 有向图
cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学
第2章 线性表 2.1 线性表的基本概念 2.2 线性表的顺序存储 2.3 线性表的链式存储 2.4 线性表的应用 2.5 有序表 本章小结.
走进编程 程序的顺序结构(二).
第2章 线性表 线性表抽象数据类型 顺序表 主要知识点 单链表 循环单链表 循环双向链表 静态链表 设计举例.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
Cyclic Hanoi问题 李凯旭.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
内容回顾 线性表的定义和特点 线性表的顺序存储及查找、插入和删除.
§2 求导法则 2.1 求导数的四则运算法则 下面分三部分加以证明, 并同时给出相应的推论和例题 .
第一章 函数 函数 — 研究对象—第一章 分析基础 极限 — 研究方法—第二章 连续 — 研究桥梁—第二章.
What have we learned?.
动态规划(Dynamic Programming)
第三章 栈和队列.
专题作业.
顺序表的删除.
概 率 统 计 主讲教师 叶宏 山东大学数学院.
第四章 一次函数 4. 一次函数的应用(第1课时).
11.1 递归的定义 11.2 常见递归问题 11.3 递归的实现 11.4 递归转化为非递归的一般过程 11.5 递归的时间和空间复杂度
离散数学─归纳与递归 南京大学计算机科学与技术系
第1章 绪论(二) 教学目标 理解算法的特性及评价标准 掌握算法时间复杂度和空间复杂度的分析方法 1/
第 四 讲 线性表(二).
1、递归的概念 2、递归函数的设计 3、递归过程与递归工作栈 4、例 5、递归过程的非递归化
成绩是怎么算出来的? 16级第一学期半期考试成绩 班级 姓名 语文 数学 英语 政治 历史 地理 物理 化学 生物 总分 1 张三1 115
第二章 线性表.
第4章 Excel电子表格制作软件 4.4 函数(一).
定理21.9(可满足性定理)设A是P(Y)的协调子集,则存在P(Y)的解释域U和项解释,使得赋值函数v(A){1}。
用穷举法设计程序 南京师范大学 教育技术系 倪佳慧
§6.7 子空间的直和 一、直和的定义 二、直和的判定 三、多个子空间的直和.
1.2 子集、补集、全集习题课.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
一元二次不等式解法(1).
第15讲 特征值与特征向量的性质 主要内容:特征值与特征向量的性质.
2019/5/20 第三节 高阶导数 1.
正弦、余弦函数的性质 华容一中 伍立华 2017年2月24日.
实验目的:掌握数据的顺序存储结构及它们在计算机中的操作。 实验内容:
第三章 线性表 3.1 线性表的类型定义 3.2 顺序存储的线性表 3.3 链式存储的线性表 3.4 有序表 3.5 顺序表和链表的综合比较.
定义5 把矩阵 A 的行换成同序数的列得到的矩阵,
算法3.3 void InitList_sq(SqList &L,int msize=LIST_INIT_SIZE)
Chapter 2 Entity-Relationship Model
第十七讲 密码执行(1).
本节内容 指针类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
一元一次方程的解法(-).
最小生成树 最优二叉树.
§3.1.2 两条直线平行与垂直的判定 l1 // l2 l1 ⊥ l2 k1与k2 满足什么关系?
Presentation transcript:

第5章 递归 5.1 什么是递归 5.2 递归算法的设计 本章小结

5.1 什么是递归 5.1.1 递归的定义 在定义一个过程或函数时出现调用本过程或本函数的成分,称之为递归。若调用自身,称之为直接递归。若过程或函数p调用过程或函数q,而q又调用p,称之为间接递归。 如果一个递归过程或递归函数中递归调用语句是最后一条执行语句,则称这种递归调用为尾递归。

例如,以下是求n!(n为正整数)的递归函数。 int fun(int n) { if (n==1) //语句1 return 1; //语句2 else //语句3 return fun(n-1)*n; //语句4 } 在该函数fun(n)求解过程中,直接调用fun(n-1)(语句4)自身,所以它是一个直接递归函数。又由于递归调用是最后一条语句,所以它又属于尾递归。

5.1.2 何时使用递归 在以下三种情况下,常常要用到递归的方法。 1. 定义是递归的 有许多数学公式、数列等的定义是递归的。例如,求n!和Fibonacci数列等。这些问题的求解过程可以将其递归定义直接转化为对应的递归算法。 思考题:指出正整数的定义。

有些数据结构是递归的。例如,第2章中介绍过的单链表就是一种递归数据结构,其结点类型定义如下: 2. 数据结构是递归的 有些数据结构是递归的。例如,第2章中介绍过的单链表就是一种递归数据结构,其结点类型定义如下: typedef struct LNode { ElemType data; struct LNode *next; } LinkList; 该定义中,结构体LNode的定义中用到了它自身,即指针域next是一种指向自身类型的指针,所以它是一种递归数据结构。 为什么可以这样递归定义类型

对于递归数据结构,采用递归的方法编写算法既方便又有效。例如,求一个不带头结点的单链表L的所有data域(假设为int型)之和的递归算法如下: int Sum(LinkList *L) { if (L==NULL) return 0; else return(L->data+Sum(L->next)); }

3. 问题的求解方法是递归的 有些问题的解法是递归的,典型的有Hanoi问题求解,该问题描述是:设有3个分别命名为X,Y和Z的塔座,在塔座X上有n个直径各不相同,从小到大依次编号为1,2,…,n的盘片,现要求将X塔座上的n个盘片移到塔座Z上并仍按同样顺序叠放。   盘片移动时必须遵守以下规则:每次只能移动一个盘片;盘片可以插在X、Y和Z中任一塔座;任何时候都不能将一个较大的盘片放在较小的盘片上。设计递归求解算法,并将其转换为非递归算法。 设Hanoi(n,x,y,z)表示将n个盘片从x通过y移动到z上,递归分解的过程是:

Hanoi(n-1,x,z,y); move(n,x,z):将第n个圆盘从x移到z; Hanoi(n-1,y,x,z) Hanoi(n,x,y,z)

递归模型是递归算法的抽象,它反映一个递归问题的递归结构。例如前面的递归算法对应的递归模型如下: 5.1.3 递归模型 递归模型是递归算法的抽象,它反映一个递归问题的递归结构。例如前面的递归算法对应的递归模型如下: fun(1)=1 (1) fun(n)=n*fun(n-1) n>1 (2) 其中,第一个式子给出了递归的终止条件,第二个式子给出了fun(n)的值与fun(n-1)的值之间的关系,我们把第一个式子称为递归出口,把第二个式子称为递归体。

一般地,一个递归模型是由递归出口和递归体两部分组成,前者确定递归到何时结束,后者确定递归求解时的递推关系。递归出口的一般格式如下: f(s1)=m1 (5.1) 这里的s1与m1均为常量,有些递归问题可能有几个递归出口。递归体的一般格式如下: f(sn+1)=g(f(si),f(si+1),…,f(sn),cj,cj+1,…,cm) (5.2) 其中,n、i、j和m均为正整数。这里的sn+1是一个递归“大问题”,si、si+1、…、sn为递归“小问题”,cj、cj+1、…、cm是若干个可以直接(用非递归方法)解决的问题,g是一个非递归函数,可以直接求值。

递归思路是:   把一个不能或不好直接求解的“大问题”转化成一个或几个“小问题”来解决;   再把这些“小问题”进一步分解成更小的“小问题”来解决;   如此分解,直至每个“小问题”都可以直接解决(此时分解到递归出口)。但递归分解不是随意的分解,递归分解要保证“大问题”与“小问题”相似,即求解过程与环境都相似。

为了讨论方便,简化上述递归模型为: f(s1)=m1 (5.3) f(sn)=g(f(sn-1),cn-1) (5.4) 求f(sn)的分解过程如下: f(sn) ↓ f(sn-1) … f(s2) f(s1)

 一旦遇到递归出口,分解过程结束,开始求值过程,所以分解过程是“量变”过程,即原来的“大问题”在慢慢变小,但尚未解决,遇到递归出口后,便发生了“质变”,即原递归问题便转化成直接问题。上面的求值过程如下: f(s1)=m1 ↓ f(s2)=g(f(s1),c1) f(s3)=g(f(s2),c2) … f(sn)=g(f(sn-1),cn-1)

这样f(sn)便计算出来了,因此递归的执行过程由分解和求值两部分构成。

求解fun(5)的过程如下:

F(n)=F(n-1)+F(n-2) n>=2 递归树

思考题:   递归的本质是什么?

5.2 递归算法的设计 递归的求解的过程均有这样的特征: 先将整个问题划分为若干个子问题,通过分别求解子问题,最后获得整个问题的解。   先将整个问题划分为若干个子问题,通过分别求解子问题,最后获得整个问题的解。 而这些子问题具有与原问题相同的求解方法,于是可以再将它们划分成若干个子问题,分别求解,如此反复进行,直到不能再划分成子问题,或已经可以求解为止。这种自上而下将问题分解、求解,再自上而下引用、合并,求出最后解答的过程称为递归求解过程。这是一种分而治之的算法设计方法。 递归算法设计先要给出递归模型,再转换成对应的C/C++语言函数。

 求递归模型的步骤如下: (1)对原问题f(s)进行分析,假设出合理的“较小问题”f(s‘)(与数学归纳法中假设n=k-1时等式成立相似); (2)假设f(s‘)是可解的,在此基础上确定f(s)的解,即给出f(s)与f(s’)之间的关系(与数学归纳法中求证n=k时等式成立的过程相似); (3)确定一个特定情况(如f(1)或f(0))的解,由此作为递归出口(与数学归纳法中求证n=1时等式成立相似)。

例如,采用递归算法求实数数组A[0..n-1]中的最小值。 假设f(A,i)函数求数组元素A[0]~A[i]中的最小值。 当i=0时,有f(A,i)=A[0]; 假设f(A,i-1)已求出,则f(A,i)=MIN(f(A,i-1),A[i]),其中MIN()为求两个值较小值函数。因此得到如下递归模型: f(A,i)=A[0] 当i=0时 f(A,i)= MIN(f(A,i-1),A[i]) 其他情况

float f(float A[],int i) 由此得到如下递归求解算法: float f(float A[],int i) { float m; if (i==0) return A[0]; else { m=f(A,i-1); if (m>A[i]) return A[i]; return m; }

例如,设计不带头结点的单链表的相关递归算法 f(L):大问题 L ... a1 a2 an f(L->next):小问题 为什么在设计单链表的递归算法时不带头节点?

f(L)=f(L->next)+1 其他情况 (1)求单链表中数据结点个数 递归模型如下: f(L)=0 当L=NULL f(L)=f(L->next)+1 其他情况 递归算法如下: int count(Node *L) { if (L==NULL) return 0; else return count(L->next)+1; }

(2)正向显示以L为首节点指针的单链表的所有节点值 递归模型如下: f(L)不做任何事件 当L=NULL f(L)输出L->data;f(L->next) 其他情况 递归算法如下: void traverse(Node *L) { if (L==NULL) return; printf("%d ",L->data); traverse(L->next); }

(3)反向显示以L为首节点指针的单链表的所有节点值 递归模型如下: f(L)不做任何事件 当L=NULL f(L)f(L->next);输出L->data; 其他情况 递归算法如下: void traverseR(Node *L) { if (L==NULL) return; traverseR(L->next); printf("%d ",L->data); }

(4)删除以L为首节点指针的单链表中值为x的第一个节点 递归模型如下: f(L,x)不做任何事件 当L=NULL f(L,x)  删除L所指结点 当L->data=x f(L,x)f(L->next,x) 其他情况 递归算法如下: void del(Node *&L,ElemType x) { Node *t; if (L==NULL) return; if (L->data==x) { t=L; L=L->next; free(t); return; } else del(L->next,x);

(5)删除以L为首节点指针的单链表中值为x的所有节点 递归模型如下: f(L,x)不做任何事件 当L=NULL f(L,x)  删除L所指结点;f(L->next,x) 当L->data=x f(L,x) f(L->next,x) 其他情况 递归算法如下: void delall(Node *&L,ElemType x) { Node *t; if (L==NULL) return; if (L->data==x) { t=L; L=L->next; free(t); delall(L,x); } else delall(L->next,x);

(6)输出以L为首节点指针的单链表中最大节点值 递归模型如下: f(L)=L->data 当L中只有一个结点 f(L)=MAX(f(L->next),L->data) 其他情况 递归算法如下: ElemType maxv(Node *L) { ElemType m; if (L->next==NULL) return L->data; m=maxv(L->next); if (m>L->data) return m; else return L->data; }

(7)输出以L为首节点指针的单链表中最小节点值。 递归模型如下: f(L)=L->data 当L中只有一个结点 f(L)=MIN(f(L->next),L->data) 其他情况 递归算法如下: ElemType minv(Node *L) { ElemType m; if (L->next==NULL) return L->data; m=minv(L->next); if (m>L->data) return L->data; else return m; }

(8)释放L为首节点指针的单链表的所有节点 递归模型如下: f(L)不做任何事件 当L=NULL f(L)f(L->next);释放L所指结点; 其他情况 递归算法如下: void Destroy(Node *L) { if (L==NULL) return; Destroy(L->next); free(L); }

   例如,采用递归算法求解皇后问题:在n×n的方格棋盘上,放置n个皇后,要求每个皇后不同行、不同列、不同左右对角线。 6皇后问题的4个解:

  采用整数数组q[N]求解结果,因为每行只能放一个皇后,q[i](1≤i≤n)的值表示第i个皇后所在的列号,即该皇后放在(i,q[i])的位置上。   设queen(k,n)是在1~k-1行上已经放行了k-1个皇后,用于在k~n行放置n-k+1个皇后,则queen(k+1,n)表示在1~k行上已经放好了k个皇后,用于在k+1~n行放置n-k个皇后。显然queen(k+1,n)比queen(k,n)少放置一个皇后。 设queen(k+1,n)是“小问题”,queen(k,n)是“大问题”,则求解皇后问题的递归模型如下: queen(k,n)  n个皇后放置完毕,输出解   若k>n queen(k,n)  对于第k行的每个合适的位置i,       在其上放置一个皇后; 其他情况 queen(k+1,n);

得到递归过程如下: void queen(int k,int n) { if (k>n) 输出一个解; else for (j=1;j<=n;j++) //在第k行找所有的列位置 if (第k行的第j列合适) { 在(k,j)位置处放一个皇后即q[k]=j queen(k+1,n); }

int q[N]; //存放各皇后所在的列号 对于(k,j)位置上的皇后,是否与已放好的皇后(i,q[i])(1≤i≤k-1)有冲突呢? 只要满足以下条件,则存在冲突,否则不冲突: (q[i]==j) || (abs(q[i]-j)==abs(k-i))

int place(int k,int j) //测试(k,j)位置能否摆放皇后 { int i=1; while (i<k) //i=1~k-1是已放置了皇后的行 { if ((q[i]==j) || (abs(q[i]-j)==abs(k-i))) return 0; i++; } return 1;

void queen(int k,int n) //放置1~k的皇后 { int j; if (k>n) print(n); //所有皇后放置结束 else for (j=1;j<=n;j++) //在第k行上穷举每一个位置 if (place(k,j)) //在第k行上找到一个合适位置(k,j) { q[k]=j; queen(k+1,n); }

用栈求解皇后问题: void queen(int n) //求解n皇后问题 { int i,j,k; bool find; StType st; //定义栈st st.top=0; //初始化栈顶指针 st.top++; //将(1,1)进栈 st.col[st.top]=1; while (st.top>0) //栈不空时循环 { i=st.top; //当前皇后为第i个皇后 if (st.top==n) //所有皇后均放好,输出一个解 { printf(" 第%d个解:",++count); for (k=1;k<=st.top;k++) printf("(%d,%d) ",k,st.col[k]); printf("\n"); }

find=false; for (j=1;j<=n;j++) if (place(st,i+1,j)) //在i+1行找到一个放皇后的位置(i+1,j) { st.top++; st.col[st.top]=j; find=true; break; }

if (find==false) //在i+1行找不到放皇后的位置,回溯 { while (st.top>0) { if (st.col[st.top]==n) //本行没有可放位置,退栈 st.top--; for (j=st.col[st.top]+1;j<=n;j++) //在本行找下一个位置 if (place(st,st.top,j)) { st.col[st.top]=j; break; } if (j>n) //当前皇后在本行没有可放的位置 st.top--; //退栈 else //本行找到一个位置后退出回溯

本章小结 本章基本学习要点如下: (1)理解递归的定义和递归模型。 (2)重点掌握递归的执行过程。 (3)掌握递归设计的一般方法。 (4)灵活运用递归算法解决一些较复杂应用问题。

练习题5    5.1和5.2题。