C语言细讲——递归问题

递归

1、什么是递归
程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小。递归也可以拆开解读:递和归,递代表传递,将问题化小后不断传递下去,归代表传到某点为某确定答案开始回归

递归的两个必要条件:
(1)存在限制条件,当满足这个限制条件的时候,递归便不再继续。
(2)每次递归调用之后越来越接近这个限制条件。

递归必须满足这两个条件,否则程序比如出现bug

读者须知:递归思想的目的,即将复杂事件简单化,大事化小事。许多递归的文章或书籍都有阶乘或斐波那契数递归的举例,其本意是方便读者深入理解递归(笔者为方便理解后续也会用到此例子),但是注意这里其实不能使用递归思想,会让程序运行非常复杂而且效率极低,往往循环一瞬间计算出的结果递归方法要好久,什么时候使用递归需要我们自己判断,但确定的是:我们使用一个东西的目的必须是提高我们效率!

2、递归思想
许多人觉得递归抽象,想不明白,其实笔者告诉你:递归并不难,是你自己把自己绕死在了“递归圈套”里,在使用递归的时候一定要学会:人脑横向思考,计算机纵向思考。什么意思呢,即你的脑子只用思考这个问题的第一步和最后一步,而中间的不断展开让计算机进行,万万不可把递归的每一步在自己脑子展开,不如犹如进入迷宫一样。例如:求n!,假如此题用递归思想,我们脑中展开的应该是:要求n!我求n*(n-1)!的阶乘就好了,到此戛然而止,再思考,当n=1的时候递归就该停止了,那么就以n=1作为限制条件。

3、递归程序的编写
重点:思考寻找归的那个条件,然后思考第一步要进行的操作,后续的递用代码如何编写
4、举例讲述
(1)递归实现将某数的每个位之和,例如1234为1+2+3+4=10
分析:打印每位的数,从个位开始打印,可以让这个数对10取余。那么可以设计递归函数,让该数对10取余数并打印,然后在调用该函数本身,参数为该数除以10(除以10后该数再对10取余数就是下一位)。再思考归的条件:若该数小于10,直接打印即可,那么我们就以这个作为归的条件

#include 
int DigitSum(int n)     //实现递归函数
{
	if (n > 9)      //判断结束标志
	{
		return n % 10 + DigitSum(n / 10);    //再次调用
	}
	else
		return n;
}
int main()
{
	printf("请输入一个数\n");
	int i,result=0 ;
	scanf("%d", &i);
	result = DigitSum(i);
	printf("%d", result);
	return 0;
}
}

(2)递归实现数组的逆置

void Fun(int arr[], int i, int j)   //递归逆置数组
{
	int x;
	if (i < j)          //当i=j时作为归的条件
	{
		x = arr[i];
		arr[i] = arr[j];
		arr[j] = x;
		Fun(arr, ++i, --j);
	}
}
int main()
{
	int arr[] = { 6, 5, 4, 3, 2, 1 };
	Fun(arr, 0,5);
	for (int i = 0; i < 6; i++)
	{
		printf("%d", arr[i]);
	}
	return 0;
}

(3)递归和非递归实现strlen此题还是非常重要的

#include 
int My_Strlen1(char *str)    //非递归实现strlen
{
	int ret = 0;
	if (*str == '\0')      //以'\0'作为判断依据
		return 0;
	while (*str != '\0')
	{
		ret++;         //每一次加1,ret作为记录
		str++;
	}
	return ret;
}
int My_Strlen(char *str)   //递归实现
{
	if (*str == '\0')
		return 0;
	return My_Strlen(str + 1) + 1;
}
int main()
{
	char *p="hello";
	int len=My_Strlen(p);
	printf("%d\n", len);
	return 0;
}

(4)递归将参数字符串中的字符反向排列

void reverse_string(char* string) //递归将参数字符串中的字符反向排列
{
	if ('\0' != *(++string))
	{
		reverse_string(string);
	}
	printf("%c", *(string - 1));
}
int main()
{
	char string[10];	
	gets(string);
	reverse_string(string);
	printf("\n");
	return 0;
}

关于递归有两个非常有趣味的问题:汉诺塔问题和青蛙跳台问题,这里附上笔者自己写的代码,这两个问题大家选择浏览,笔者就不解释了

汉诺塔:有三个大底座,第一个上面放了n个盘子,盘子从小到大排列,要求将这些盘移动到第三个上面,其中小盘必须放到大盘上面不然会被损坏

#include 
void Move(char pos1,char pos2)        //汉诺塔模拟移动函数
{
	printf("%c->%c\n",pos1,pos2);
}
void Hanota(int n,char pos1,char pos2,char pos3) //汉诺塔功能实现函数
{
	if(n == 1)
	{
		Move(pos1,pos3);
	}
	else
	{
		Hanota(n-1,pos1,pos3,pos2);  //把n-1个盘子从pos1借助pos3移动到pos2
		Move(pos1,pos3);             //此时pos1只剩最大的那一个,移动到pos3
		Hanota(n-1,pos2,pos1,pos3);  //把刚刚移动到pos2的n-1个盘子借助pos1移动到pos3
	}
}
int main()
{
	int input = 0;
	scanf("%d", &input);
	Hanota(input, 'A', 'B', 'C');
	return 0;
}

青蛙跳台:青蛙一次可以跳1个台阶或者2个,现在有n个台阶,要求计算青蛙有多少种跳法

#include 
int Jump(int x)  //递归   青蛙跳台问题
{
	int m=1,n=2;
	int sum = 0;
	if (x == 1)
		return 1;
	else if (x == 2)
		return 2;
	return	sum = sum + Jump(x - 1) + Jump(x - 2);		
}
int Jump1(int x)   //非递归
{
	int m = 1, n = 2;
	int sum = 0;
	if (x == 1)
		return 1;
	else if (x == 2)
		return 2;
	else
	{
		for (int a = 3; a < x; a++)
		{
			m = n;
			n = m + n;
		}
		return	sum = m+n;
	}
}
int main()
{
	int i,result=0;
	scanf("%d", &i);
	result=Jump1(i);
	printf("%d", result);
	return 0;
}

你可能感兴趣的:(C语言细讲)