这一节内容非常重要,是一个经常需要使用的算法,关于递归我会分成及格小章来进行讲述,希望大家能从浅至深的了解递归。
那么递归是什么呢?程序调用自身就是递归 再程序设计中经常被使用,这里用一段代码来帮助大家理解
#define _CRT_SECURE_NO_WARNINGS 1//取消警告
#include<stdio.h>
int main() {
printf("main函数调用\n");
main();
return 0;
}
这段代码看似只有短短几行,但是却简单的解释了递归,在这段代码中main函数不断的调用他本身,从而达到了不断输出"main函数调用" 这样不断打印后就会出现栈溢出的错误,这里先不进行特别的解释,以后会仔细讲述。那么通过这样的性质,我们可以将一个大的复杂的问题层层分解为几个相似且非常小的问题。这样我们就能通过非常少的程序就可以解决一个需要重复计算的问题。这样大大的减少了代码量,代码虽少,但是思想却不简单。这里出一道简单的题目,作为对递归思想的解释。
构造一个函数,接受一个值(整型),输出他的每一位
#define _CRT_SECURE_NO_WARNINGS 1//取消警告
#include<stdio.h>
#include<string.h>
void DividNum(int num);
int main() {
int n;
scanf("%d", &n);
DividNum(n);
return 0;
}
void DividNum(int num) {
int a;
int arr[20];
int i = 0, j;
while (num) {
a = num % 10;
arr[i] = a;
i++;
num /= 10;
}
for (i-=1; i >= 0; i--) {
printf("%d", arr[i]);
}
}
花费了许多行,但是使用递归就会非常简单,根据递归的思想应该这样
也就是当这个函数中最后的数为一位数时,就停止递归,这里我先把代码放在上面
#define _CRT_SECURE_NO_WARNINGS 1//取消警告
#include<stdio.h>
#include<string.h>
void DividNum(int num);
int main() {
int n;
scanf("%d", &n);
DividNum(n);
return 0;
}
void DividNum(int num) {
if (num > 9) {
DividNum(num / 10);
}
printf("%d", num % 10);
}
假设我输入的时123,此时num>9,所以再将num/10 ,也就是12传入回这个函数,这个函数又再次进行判断是否num>9,此时Num=12,满足if条件,所以继续执行,将num/10,也就是1传入回这个函数,这次不再符合if条件了,所以开始执行printf输出数字,此时num=1所以先打印了1.
函数开始逐步回调,执行前一个DividNum函数,前一个num=12,所以此时会在屏幕上输出2,此时再执行前一个DibidNum函数,这时num=123,所以输出结果就为3
这里我们画个图可能让大家理解的更加方便
然后再逐步返回
总而言之,递归的思路就是把大事化小
这个if语句其实时非常重要的,递归不仅有开始条件,自然也就需要结束条件,不然无止尽的调用函数,你的内存迟早有一天回使用干净。这个num每次/10,让他不断接近跳出递归的条件,这样这个函数总有一天,这个函数会停下来。
这里划重点 递归一定有他的限制条件 并且每次递归调用之后应该会不断接近这个限制条件
这里我再写一个递归让大家更方便的了解
#define _CRT_SECURE_NO_WARNINGS 1//取消警告
#include<stdio.h>
void text(int n);
int main() {
text(1);
return 0;
}
void text(int n) {
if (n < 10000) {
text(n + 1);
}
printf("text马上结束");
}
这样一段代码大家可以自己复制粘贴到你的编译器上进行试验
但是你会发现,我所写的这段代码并没有成功,你的代码在走着走着就挂掉了,这就是之前说的栈溢出。
这里稍微解释一下,内存有三个区域:栈区,堆区,静态区
局部变量,函数的形参都会放入栈区
堆区时用来动态内存分配
静态区用来存储静态变量和全局变量
每一次函数调用的时候都需要调用一块空间来实现储存这个函数的复制,每一块被分配到的空间被称为栈帧,这样不断的为函数分配空间,最后栈空间被用干了,就会出现栈溢出的现象,这样大量的递归最终就会栈溢出
这里再次进行总结 递归应该有跳出条件,且每次递归都要接近跳出条件,递归层次不能太深