C语言递归算法练习
递归是一种算法设计中常用的一种方法,其实就是一个过程或者函数直接或间接调用自己的一种方法。
函数调用在栈上开辟空间(形成栈帧),函数 return 时栈帧释放。
递归有两个必要条件:
(1):存在限制条件,即递归出口;当越来越满足这个条件时,递归便不再继续。
(2):每次递归调用之后越来越接近限制条件。 即子问题规模缩小。
C 语言的函数可以调用函数也可以被调用,所以C语言支持递归,即一个函数可以调用其自身。但在使用递归时,也要注意递归出口和递归规模,否则会进入死循环。 (与C语言内存有关详见链接: link-C语言内存.)
练习代码
N的阶乘
源代码
#include<stdio.h>
#include<Windows.h>
#pragma warning(disable:4996)
//递归方法
int Factorial(int n)
{
if (n<=1){
return 1;
}
else {
return n*Factorial(n - 1);
}
}
//迭代方法
//int Factorial(int n)
//{
// int i,s=1;
// for (i = n; i >1; i--)
// {
// s *= i;
// }
// return s;
//}
int main()
{
int num;
scanf("%d", &num);
printf("%d! = %d\n", num, Factorial(num));
system("pause");
return 0;
}
运行结果:
斐波那契数
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……(我们这里不考虑0的情况直接计算从1开始的斐波那契数)
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>
// //迭代方法
//int Fib(int n)
//{
// int num1 = 1, num2 = 1;
// int fib;
// while (n>2){
// n--;
// fib = num1 + num2;
// num1 = num2;
// num2 = fib;
// }
//}
// //递归方法
int Fib(int n)
{
if (n <= 2){
return 1;
}
else{
return Fib(n - 1) + Fib(n - 2);
}
}
int main()
{
int number;
printf("你想知道第几个斐波那契数。\n");
scanf("%d", &number);
printf("%d\n", Fib(number));
system("pause");
return 0;
}
这里推荐使用优化的递归(后期补上代码)或者使用迭代时间按复杂度为O(n)。
有问题!!!
●用斐波那契数函数求第50个时,Fib(50)会耗费非常多的时间。
●用阶乘函数求10000的阶乘时,程序会出现崩溃。(stack overflow栈溢出)
原因:在进行递归调用时许多数据会重复计算,导致大量冗余;在使用递归时如果需要多次调用函数,那样就会引起效率低下,空间复杂度时间复杂度大大提高,甚至出现栈溢出。
例如:调试 factorial 函数时,如果你的参数过大,那就会报错:`stack overflow(栈溢 出)这样的信息。 系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。(栈相关知识详见浅谈C语言内存(栈))
所以在进行coding的时候一定要考虑清楚
解决方法:
- 将递归改成迭代,使用循环降低时间和空间复杂度。
- 使用 static对象代替 nonstatic(非静态)局部变量。这可以减少每次递归调用和返回时产生和释放nonstatic变量的成本, 而且static对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。
打印每位数字
接受一个无符号整数并用递归的方式打印每一位。
//接受一个无符号整数并打印每一位。
#include<stdio.h>
#include<windows.h>
#pragma warning(disable:4996) //vs(vs2013)使用scanf时需要加 _s 否则会报错,用这种是解决方法之一
int Print(int num)
{
if (num > 9){
Print(num / 10);
}
printf(" %d ", num % 10);
}
int main()
{
int n;
printf("请输入一个整数\n");
scanf("%d", &n);
printf("%d的每一位是:", n);
Print(n);
printf("\n");
system("pause"); //vs运行会直接结束运行窗口自动关闭,可以用<window.h>中的syetem函数使运行窗口保留。
return 0;
}
strlen模拟(测量字符串长度)
strlen是一种操作函数,用来获取字符串有效长度(不包括字符串结尾的’\0’)。包含在<string.h>头文件内,具体如下(可以到 C语言.查询)
示例:
#include<stdio.h>
#include<Windows.h>
#pragma warning(disable:4996)
// //非递归实现
//int Strlen(char *str)
//{
// int len = 0;
// while (*str){
// str ++;
// len++;
// }
// return len;
//}
//递归实现
int Strlen(char *str)
{
if (*str == '\0'){
return 0;
}
else {
return 1+Strlen(str+1);
}
}
int main()
{
char s[100];
scanf("%s",&s);
int len = Strlen(s);
printf("len = %d\n",len);
system("pause");
return 0;
}
N的K次方
#include<stdio.h>
#include<windows.h>
#pragma warning(disable :4996)
int Square(int n,int k)
{
if (k<=1){
return n;
}
else{
return n*Square(n,k - 1);
}
}
int main()
{
int num,k;
scanf("%d %d", &num,&k);
printf("%d的%d次方是%d\n",num,k, Square(num,k));
system("pause");
return 0;
}
字符串逆序
利用递归方法对输入字符串进行逆序并输出
//递归
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<windows.h>
void swap(char *p, char *q)
{
*p ^= *q;
*q ^= *p;
*p ^= *q;
}
void ReverseStringHelper(char string[], int start, int end)
{
if (start >= end){
return;
}
else{
swap(&string[start++], &string[end--]);
ReverseStringHelper(string, start, end);
}
}
void ReverseString(char str[]) //NULL ""
{
if (NULL == str || strcmp(str, "") == 0){
return;
}
ReverseStringHelper(str, 0, strlen(str) - 1);
}
int main()
{
char str[10] = "0";
scanf("%s", str);
//printf("%s\n", str);
ReverseString(str);
printf("%s\n", str);
system("pause");
return 0;
}
//非递归方法:
// 非递归
#include <stdio.h>
#include <string.h>
void Swap(char *p, char *q)
{
*p = *p ^ *q;
*q = *p ^ *q;
*p = *p ^ *q;
}
int main()
{
char str[100], temp;
gets(str);
int sz = strlen(str);
for (int i = 0; i < sz / 2; i++)
{
Swap(&str[i], &str[sz - i - 1]);
}
puts(str);
return 0;
}
运行结果:
相关文章:算法之算法的时间复杂度
浅谈C语言内存(栈)