Presentation is loading. Please wait.

Presentation is loading. Please wait.

递 归 (recursion)  定义 : 若一个对象部分地包含它自己, 或用 它自己给自己定义, 则称这个对象是递归的; 若一个过程直接地或间接地调用自己, 则称这个过程是递归的过程。  三种递归情况   定义是递归的  数据结构是递归的  问题的解法是递归的.

Similar presentations


Presentation on theme: "递 归 (recursion)  定义 : 若一个对象部分地包含它自己, 或用 它自己给自己定义, 则称这个对象是递归的; 若一个过程直接地或间接地调用自己, 则称这个过程是递归的过程。  三种递归情况   定义是递归的  数据结构是递归的  问题的解法是递归的."— Presentation transcript:

1 递 归 (recursion)  定义 : 若一个对象部分地包含它自己, 或用 它自己给自己定义, 则称这个对象是递归的; 若一个过程直接地或间接地调用自己, 则称这个过程是递归的过程。  三种递归情况   定义是递归的  数据结构是递归的  问题的解法是递归的

2 定义是递归的 求解阶乘函数的递归算法 long Factorial ( long n ) { if ( n == 0 ) return 1; else return n * Factorial (n - 1); } 例如,阶乘函数

3 求解阶乘 n! 的过程 主程序 主程序 main : fact(4) 参数 4 计算 4*fact(3) 返回 24 参数 3 计算 3*fact(2) 返回 6 参数 2 计算 2*fact(1) 返回 2 参数 1 计算 1*fact(0) 返回 1 参数 0 直接定值 = 1 返回 1 参数传递参数传递 结果返回结果返回 递归调用递归调用递归调用递归调用 回归求值回归求值回归求值回归求值

4 数据结构是递归的 有若干结点的单链表 例如,单链表结构 f  f  只有一个结点的单链表

5 例 1. 搜索链表最后一个结点并打印其数值 void Search ( ListNode *f ) { if ( f -> next == NULL ) printf (“%d\n”, f -> data ); else Search ( f -> next ); } f ffff  a0a0 a1a1 a2a2 a3a3 a4a4 递归找链尾

6 例 2. 在链表中寻找等于给定值的结点, 并打印其数值 void Search ( ListNode * f, ListData x ) { if ( f ! = NULL ) if ( f -> data == x ) printf ( “ %d\n ”, f -> data ); else Search ( f -> next, x ); } 递归找含 x 值的结点 f fff  x

7 例如,汉诺塔 (Tower of Hanoi) 问题的解法: 如果 n = 1 ,则将这一个盘子直接从 X 柱 移到 Z 柱上。否则,执行以下三步: ① 用 Z 柱做过渡,将 X 柱上的 (n - 1) 个盘子移 到 Y 柱上: ② 将 X 柱上最后一个盘子直接移到 Z 柱上; ③ 用 X 柱做过渡,将 Y 柱上的 (n - 1) 个盘子移 到 Z 柱上。 问题的解法是递归的

8

9  算法 void hanoi (int n, char X, char Y, char Z) { // 解决汉诺塔问题的算法 if ( n == 1 ) printf (" move %s",X, " to %s”,Z); else { Hanoi ( n-1, X, Z, Y ); printf (" move %s",X, " to %s”,Z); Hanoi ( n-1, Y, X, Z ); }

10 递归方法是设计非数值计算程序的重要方法,它使得程 序的结构清晰,形式简洁,易于阅读,正确性容易证明。 一般地讲,一个问题采用递归算法求解时,须具备 3 个 条件。 (1) 能将一个问题转变成一个新问题,而新问题与原问 题的解法相同或类同,所不同的仅是所处理的对象,且 这些处理对象的变化是有规律的。 (2) 可以通过上述转化使问题逐步简单化。 (3) 必须有一个明确的递归出口 ( 递归的边界 ) 。

11 递归过程及其实现 递归过程在实现时,需要自己调用自己。 层层向下递归,退出时的次序正好相反: 递归调用 n! (n-1)! (n-2)! 1! 0!=1 返回次序 递归函数运行的 “ 层次 ”

12 递归工作栈 函数嵌套调用时的 “ 后调用先返回 ” 原则 递归函数调用也是一种嵌套调用;每一次递归调用时, 也需要分配存储空间(工作区)来存储参数、局部变 量、返回地址等信息。 每层递归调用需分配的空间形成递归工作记录,按后 进先出 (LIFO) 的栈组织。 局部变量 返回地址 参 数 活动 记录 框架 递归 工作记录

13 函数递归时的活动记录 ………………. Function( ) ………………. 调用块 函数块 返回地址 ( 下一条指令 ) 局部变量 参数

14 递归过程改为非递归过程 递归过程简洁、易编、易懂 递归过程效率低,重复计算多 改为非递归过程的目的是提高效率 单向递归和尾递归可直接用迭代实现其 非递归过程 其他情形必须借助栈实现非递归过程

15 计算斐波那契数列的函数 Fib(n) 的定义 求解斐波那契数列的递归算法 long Fib ( long n ) { if ( n <= 1 ) return n; else return Fib (n - 1) + Fib (n - 2); } 如 F 0 = 0, F 1 = 1, F 2 = 1, F 3 = 2, F 4 = 3, F 5 = 5

16 斐波那契数列的递归调用树 Fib(1)Fib(0) Fib(1)Fib(2) Fib(3) Fib(4) Fib(1)Fib(0) Fib(2) Fib(1)Fib(0) Fib(1)Fib(2) Fib(3) Fib(5)

17 Fib(1)Fib(0) Fib(2)Fib(1)Fib(0) Fib(2) Fib(1) Fib(3) Fib(4)          栈结点 n tag tag = 1, 向左递归; tag = 2, 向右递归

18 计算斐波那契数列的递推算法 Long FibIter(long n){ if (n <=1) return n; long twoback=0,oneback=1,current; for (int I=2;I<=n;I++){ current =twoback+oneback; twoback=oneback;oneback=current; } return current; } 事件复杂度: O(n)

19 队列 (Queue)  定义 : 只允许在表的一端进行插入,而在另 一端删除元素的线性表。  在队列中,允许插入的一端叫队尾 ( rear ), 允许删除的一端称为队头 (front) 。  特点: 先进先出 (FIFO) a 1 , a 2 , a 3 , … , a n 出队列入队列 队头队头 队尾队尾

20 链队列:队列的链式表示  链队列中,有两个分别指示队头和队尾的指针。  链式队列在进队时无队满问题,但有队空问题。 data next front rear

21 front rear ^ 空队列 front rear x^ 元素 x 入队 front rear x^y^ 元素 y 入队 front rear x^^y 元素 x 出队 ^

22 typedef int QElemType; typedef struct Qnode { QElemType data; // 队列结点数据 struct Qnode *next; // 结点链指针 } Qnode,*QueuePtr; typedef struct { QueuePtr rear, front; } LinkQueue; 链式队列的定义

23 链队列的主要操作 Status InitQueue ( LinkQueue &Q ) Status DestroyQueue(LinkQueue &Q) Status ClearQueue(LinkQueue &Q) Status QueueEmpty ( LinkQueue Q ) int QueueLength(LinkQueue Q) Status GetHead ( LinkQueue Q, QElemType &e) Status EnQueue(LinkQueue &Q,QElemType e) Status DeQueue(LinkQueue &Q,QElemType &e) Status QueueTraverse(LinkQueue Q,visit())

24  入队 Status EnQueue ( LinkQueue &Q, QElemType e ) { p = ( QueuePtr ) malloc( sizeof ( QNode ) ); ….. p->data = e; p->next = NULL; Q.rear->next = p; // 入队 Q.rear =p; return OK; }

25  出队 int DeQueue ( LinkQueue &Q, QElemType &e) { // 删去队头结点,并返回队头元素的值 if ( Q.front==Q.rear ) return ERROR;// 判队空 p = Q.front ->next; e = p->data;// 保存队头的值 Q.front->next = p->next; // 新队头 if (Q.rear == p) Q.rear = Q.front ; free (p); return OK; }

26 循环队列 (Circular Queue)  顺序队列 — 队列的顺序存储表示 插入新的队尾元素,尾指针增 1 , rear = rear + 1 , 删除队头元素,头指针增 1 , front = front + 1 ,  因此,在非空队列中,头指针始终指向队列头 元素,而尾指针始终指向队列尾元素的下一个 位置。  队满时再进队将溢出 假溢出(图 3.12)  解决办法:将顺序队列臆造为一个环状的空间, 形成循环 ( 环形 ) 队列

27 队列的进队和出队 front rear 空队列 frontrear A,B,C, D 进队 A B C D frontrear A,B 出队 C D front rear E,F,G 进队 C D E F G front rear H 进队, 溢出

28 循环队列 (Circular Queue)  队头、队尾指针加 1 ,可用取模 ( 余数 ) 运算实现。  队头指针进 1: front = (front+1) %maxsize;  队尾指针进 1: rear = (rear+1) % maxsize;  队列初始化: front = rear = 0;  队空条件: front == rear;  队满条件: (rear+1) % maxsize == front; 0 1 23 4 5 67 循环队列 front rear Maxsize-1

29 0 1 2 3 4 567 front B CD rear 一般情况 A 0 1 23 4 567rear 空队列 front 队满条件: (rear+1) % maxsize == front C 0 1 23 4 5 67 front rear D E F G A B C 队满 C 0 1 23 4 5 67 front rear D E F G A B C H

30 #define MAXSIZE 100 Typedef struct{ QElemType *base; int front; int rear; } SqQueue; 循环队列的类型定义

31 循环队列操作的实现  初始化队列 Status InitQueue ( SqQueue &Q ) { // 构造空队列 Q.base=(QElemType *) malloc (MAXSIZE*sizeof(QElemType)); if (! Q.base) exit(OVERFLOW); Q.rear = Q.front = 0; return OK; }

32  入队 Status EnQueue ( SqQueue &Q, QElemType e ) { if ( (Q.rear+1) % MAXSIZE ==Q.front) return ERROR; // 队满 Q.base[Q.rear] = e; Q.rear = ( Q.rear+1) % MAXSIZE; return OK; }

33  出队 Status DeQueue ( SqQueue &Q, QElemType &e ) { if ( Q.front == Q.rear ) return ERROR; // 队空 e = Q.base[Q.front]; Q.front = ( Q.front+1) % MAXSIZE; return OK; }

34 队列的应用 --- 离散事件模拟 银行业务模拟程序 模拟银行业务活动并计算一天中客户在银行平均逗留时间 事件驱动模拟 — 按事件(客户到达或客户离开)发生的先后 顺序进行处理 。 主要的数据结构: 一个事件表(有序链表) -------- 用来记录待处理的事件 n 个队列 ------ 用来对应 N 个窗口的客户队列

35 typedef struct{ int OccurTime; int Ntype; } Event,ElemType; // 事件类型 typedef LinkList EventList // 事件链表类型 Type struct{ int ArrivalTime; int Duration; } QElemType; // 队列元素类型


Download ppt "递 归 (recursion)  定义 : 若一个对象部分地包含它自己, 或用 它自己给自己定义, 则称这个对象是递归的; 若一个过程直接地或间接地调用自己, 则称这个过程是递归的过程。  三种递归情况   定义是递归的  数据结构是递归的  问题的解法是递归的."

Similar presentations


Ads by Google