数据结构 递归笔记
/* 递归: 简单的说就是函数自己调用自己常用来解决一些复杂的问题。 采用递归的算法的特征: 为求解规模为N的问题,可以将其分解为较小规模的问题,并且这些问题 还可以划分为规模更小的问题。从而从这些规模小的问题求解出原问题。 一般,问题规模为 1 时的解是已知的,即递归的出口。 递归的优点: 使用递归使程序简洁,结构清晰,可读性强。 递归的缺点: 效率较低,运行时开销较大。 说明: 由于递归形式的定义容易使人们使用递归来解决问题,但递归并不一定 适合这个问题,例如:使用递归计算斐波拉契数列时,由于递归的实现 是通过函数调用,而每一个递归调用都会触发另外的递归调用,以此类 推,需要的计算量增加的非常快,会造成大量不必要的浪费。而使用循 环来代替递归计算斐波拉契数列,虽然循环形式不如递归形式符合斐波 拉契数列的抽象定义,但循环的效率却比递归提高了很多。 */
// 利用递归的思想求解斐波拉契序列 # include <stdio.h> int Fic(int); int main(void) { int n, aim; printf("请输入您想要求解的数所在的位置:"); scanf("%d", &n); aim = Fic(n); printf("\naim = %d\n", aim); return 0; } int Fic(int n) { if (n < 3) { return 1; } else { return Fic(n-1)+Fic(n-2); } }
/* 编写一个递归函数,要求给定一个正整数 n ,输出所有和为 n 的 所有不增的正整数和式。 例如: 当 n = 3 时 3 = 3 3 = 2 + 1 3 = 1 + 1 + 1 */ # include <stdio.h> # include <stdlib.h> # include <malloc.h> void Add_Type(int * arry, int i, int k); // 输出所有不增和式 int main(void) { int n; printf("Data: "); scanf("%d", &n); printf("Result: \n"); // 构造一个保存和式中所有项的数组,数组长度最长为 n+1 int * arry = (int *)malloc(sizeof(int) * (n+1)); if (NULL == arry) { printf("动态内存分配失败!\n"); exit(-1); } arry[0] = n; // 数组第一个保存和式的和 Add_Type(arry, n, 1); return 0; } void Add_Type(int * arry, int i, int k) // k 表示和式新的一项开始出现的位置 { int j, p; for (j = i; j >= 1; --j) // 使和式呈从n开始依次递减的形式出现 { if (j <= arry[k-1]) // 保证和式不出现项的数目与值不变,只是出现次序发生改变的情况 { arry[k] = j; // 此时使数组中下标为 k 的元素储存了和式此时的最后一项 if (j == i) { printf("%d = %d", arry[0], arry[1]); for (p = 2; p <= k; ++p) // 打印和式的剩余项 { printf(" + %d", arry[p]); } printf("\n"); } Add_Type(arry, i-j, k+1); // 即将 i-j 继续拆分为不增和式的剩余项 } } return; }
/*
经典算法——1 汉诺塔: 有三个柱子和 n 个大小各不相同的
盘子。开始时,所有盘子以塔状叠放在柱子 A 上,要求一定规
则,将柱子 A 上的所有盘子移到柱子 B 上,柱子 C 为移动缓
冲柱。
移动规则如下:
(1)一次只能移动一个盘子。
(2)任何时候不能把盘子放在比他小的盘子下面。
*/
/*
采用递归的方法解决此问题。
(1)描述解决问题的思想:
1. 若只有一个盘子,则可以直接从A移动到B
2. 若盘子数大于 1 ,则需要先将 n-1 个盘子从 A 移动
到缓冲区 C ,再将 A 中剩下的最大的一个盘子移动到终
点盘 B ,最后再将缓冲区 C 上的 n-1 个盘子移动到终
点盘 B 。
(2)递归算法(见下面代码)
*/
# include <stdio.h> void hanoi(int, char, char, char); int main(void) { int disks; // 定义盘子总数。 printf("请输入盘子总数目:"); scanf("%d", &disks); printf("\n"); hanoi(disks, 'A', 'B', 'C'); // A 为最开始盘子所在的地方 B 为最后盘子要放置的地方 C 为缓冲地带 return 0; } void hanoi(int n, char A, char B, char C) // A 为最开始盘子所在的地方 B 为最后盘子要放置的地方 C 为缓冲地带 { if (1 == n) { printf("%c 移到 %c", A, B); } else // 整个移动过程中的一个基本单元 { // hanoi 函数中的不同的盘子作用要与形参一致, 见下一个改变了盘子功能的程序 hanoi(n-1, A, C, B); // 将 n-1 个盘子从起始区移到缓冲区 printf("\n"); printf("%c 移到 %c", A, B); // 将最大的盘子移到终点区 printf("\n"); hanoi(n-1, C, B, A); // 将缓冲区的 n-1 个盘子移到终点区 printf("\n"); } return; } /* 输出结果: 请输入盘子总数目:4 A 移到 C A 移到 B C 移到 B A 移到 C B 移到 A B 移到 C A 移到 C A 移到 B C 移到 B C 移到 A B 移到 A C 移到 B A 移到 C A 移到 B C 移到 B -------------------------------- Process exited after 1.463 seconds with return value 0 请按任意键继续. . . */
注意:
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但
循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实
现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于
栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
转载请注明出处