121-Analysis of Recursion in C Language

Recursion

In the process of calling a function, the function itself is called directly or indirectly, which is called the recursive call of the function.
The Drost effect is a visual form of recursion.
Recursion usually transforms a large and complex problem into a smaller problem similar to the original problem to solve. When the problem is small to a certain extent, the answer is obvious.
Recursion requires boundary conditions, recursive forward section and recursive return section.

Recursive function execution into "Recursion" and "return" two processes, these two processes are controlled by the termination condition, namely recursive layer by layer, until the recursive termination condition is met, the termination recursion, layer by layer and then return to
the recursive call with the ordinary The function is the same, every time a call occurs, a new stack frame (new parameter data, field protection, local variables) must be allocated. Unlike ordinary functions, because the recursive process is a layer-by-layer call process, there is A process of successively allocating stack frames layer by layer does not begin to return until the recursive termination condition is encountered, and then the stack frame space is released layer by layer, returning to the previous layer, and finally returning to the main calling function.

For example: There are 5 students sitting together, and ask how old is the 5th student? He said that he was 2 years older than the fourth student, and asked the age of the fourth student. He said that he was 2 years older than the third student. He asked the third student. He said that he was 2 years older than the second student. Ask the second student. The student said that he was 2 years older than the first student, and finally asked the first student. He said he was 10 years old. How old is the fifth student.

The non-recursive problem-solving method is as follows:

//非递归求年龄
int Age(int n)
{
    
    
	int tmp = 10; //第一个人年龄
	for(int i=1;i<n;i++)
	{
    
    
		tmp += 2;//后面的人比前一个多 2 岁
	}
	return tmp;
}

So how to deal with recursion?
If the Age function is used to find age, then
Age(1) represents the age of the first person;
Age(2) represents the age of the second person;

Age(n-1) represents the first person’s age. n-1 the age of the person;
Age(n) represents the age of the nth person

//递归求年龄
int Age(int n)
{
    
    
	int tmp;//保存年龄

	if(n == 1)
		tmp = 10;
	else
		tmp = Age(n-1) + 2;//当前第n个比第n-1个年龄多 2

	return tmp;
}

Insert picture description here
The red in the figure above indicates the call process of the function. In this process, each function has not been executed yet, so the memory space occupied by each function cannot be released, and the call of the function needs to occupy a certain stack space (a stack frame ), and the stack space is very small (in the dynamic memory chapter, we talked about the stack 1M). When the number of recursions is very large, there may be insufficient stack space

Insert picture description here

//递归求年龄
int Age(int n)
{
    
    
	int tmp;//保存年龄

	if(n == 1)
		tmp = 10;
	else
		tmp = Age(n-1) + 2;//当前第 n 个比第 n-1 个多 2

	return tmp;
}
//递归调用次数太多,程序崩溃
int main()
{
    
    
	printf("%d\n",Age(5000));//windows 系统,程序崩溃

	return 0;
}

Example: Use recursion to find the factorial n!
Insert picture description here

//递归求 n 的阶乘
//Fac(0)表示 0 的阶乘
//Fac(1)表示 1 的阶乘
//Fac(2)表示 2 的阶乘
//......
//Fac(n-1)表示 n-1 的阶乘
//Fac(n)表示 n 的阶乘
#include<stdio.h>
int Fac(int n)
{
    
    
	if(n==0 || n==1)
		return 1;
	else
		return Fac(n-1)*n;
}
int main()
{
    
    
	for(int i=0;i<10;i++)
	{
    
    
		printf("%d!=%d\n",i,Fac(i));
	}

	return 0;
}

Insert picture description here

Use recursion to find 1+2+3+...+n.
Insert picture description here

//递归求和
//Sum(1)表示从 1 加到 1
//Sum(2)表示从 1 加到 2
//Sum(3)表示从 1 加到 3
//......
//Sum(n-1)表示从 1 加到 n-1
//Sum(n)表示从 1 加到 n
int Sum(int n)
{
    
    
	if(n < 0) return -1;
	if(n==0 || n==1)
		return n;
	else
		return Sum(n-1) + n;
}

Example: Using recursion to find the Fibonacci sequence is
Insert picture description here
actually the most unsuitable example for recursion.

//非递归求斐波那契数列
#include<stdio.h>
int Fibon_for(int n)
{
    
    
	int f1 = 1;
	int f2 = 1;
	int f3 = 1;
	for(int i=2;i<n;i++)
	{
    
    
		f3 = f1 + f2;
		f1 = f2;
		f2 = f3;
	}
	return f3;
}

//递归求斐波那契数列
int Fibon(int n)
{
    
    
	if(n==1 || n==2)
		return 1;
	else
		return Fibon(n-1) + Fibon(n-2);
}

int main()
{
    
    
	printf("非递归结果:");
	for(int i=1;i<10;i++)
	{
    
    
		printf("%d ",Fibon_for(i));
	}
	printf("\n");
	printf("递归结果: ");
	for(int i=1;i<10;i++)
	{
    
    
		printf("%d ",Fibon(i));
	}

	return 0;
}

Insert picture description here
When the value is large, the efficiency (time) of these two executions is very different
. Use recursive processing. The time complexity is O(2^n), that is, 2 to the nth power.
Use non-recursive processing. The time complexity is O (n)

Insert picture description here
describes the most unsuitable for recursive lease Fibonacci number that column described below is a suitable example of recursion: Tower of Hanoi
Tower of Hanoi . There was a Vatican Pagoda in ancient times. There were three seats A, B, and C in the tower. At the beginning,
there were several plates on the A seat . The plates were of different sizes, with the larger one on the bottom and the smaller one on the top. An old monk wanted to move these plates from seat A to seat C, but it was stipulated that only one plate was allowed to be moved at a time, and during the movement, the large plate was always on the bottom and the small plate was on the top. Block B can be used during the movement. Request to program to output the steps of moving a plate

Insert picture description here
Insert picture description here
Insert picture description here
The above is the case of 2 plates, which is very simple. What if there are 3 plates?
Insert picture description here
Insert picture description here
Insert picture description here
Carefully analyze Figures 2 and 5, only when this situation is reached, can the bottom plate be moved from A to C, and then the other small plates can be moved to C.

#include<stdio.h>
//模拟从 x 搬运到 y 的过程
void Move(char x,char y)
{
    
    
	printf("%c -> %c\n",x,y);
}

//将 n 个盘子的汉诺塔从 a 通过 b 搬到 c
void Hanoi(int n,char a,char b,char c)
{
    
    
	if(n == 1)//只有一个盘子,直接搬
	{
    
    
		Move(a,c);
	}
	else //先将上面的n-1个搬到b上,然后搬最下边的一个到c,再把n-1个从b搬到c
	{
    
    
		Hanoi(n-1,a,c,b);//上面 n-1 个从 a 通过 c 搬到 b
		Move(a,c);//只剩最后一个,直接搬
		Hanoi(n-1,b,a,c);//把上面搬到 b 上的 n-1 个盘子有从 b 通过 a 搬到 c
	}
}

int main()
{
    
    
	Hanoi(2,'A','B','C');
	return 0;
}

The result of the operation is as follows to
Insert picture description here
illustrate the recursive process:
Insert picture description here
Insert picture description here
Summary: When a function is called, whether it is called by itself or by other functions, stack frames will be allocated to the called function.
There is no infinite recursion.
That is, the recursive function must have an exit that is the end of the recursion (there must be a conditional statement that terminates in the recursion)
. The scale of the problem should not be too large, and the recursion is too deep, causing stack overflow

Look at the following question:
Input an integer (unsigned integer), and use the recursive algorithm to output the integers in reverse order.
Insert picture description here
What are the output results of the above two methods?
Left: 4 3 2 1
printf("%d ",n%10); then recursively executed each time

#include<stdio.h>
void backward(int n)
{
    
    
	if(n>0)
	{
    
    
		printf("%d ",n%10);
		backward(n/10);
	}
}
int main()
{
    
    
	int n=0;
	scanf("%d",&n);
	printf("原整数:%d\n",n);
	printf("反向数:");
	backward(n);
	printf("\n");
	return 0;
}

Insert picture description here

Right: 1 2 3 4
keeps recursively until the termination condition is encountered, and then returns to print one by one

#include<stdio.h>
void backward(int n)
{
    
    
	if(n>0)
	{
    
    
		backward(n/10);
		printf("%d ",n%10);
		
	}
}
int main()
{
    
    
	int n=0;
	scanf("%d",&n);
	printf("原整数:%d\n",n);
	printf("反向数:");
	backward(n);
	printf("\n");
	return 0;
}

Insert picture description here
Insert picture description here
The termination condition of the recursion is very important, otherwise it will recurse endlessly and fall into an infinite loop, which will eventually cause the stack space to be exhausted and a StackOverflow error will be reported.

Note:
1. Restrictions: When designing a recursive process, there must be at least one condition that can terminate the recursion, and it must also deal with situations where such conditions are not met within a reasonable number of recursive calls. If there is no condition that can be met under normal circumstances, the process will fall into the high risk of executing an infinite loop.
2. Memory usage: The space used by the application's local variables is limited. Each time a procedure calls itself, it will take up more memory space to save additional copies of its local variables. If this process continues indefinitely, it will eventually lead to StackOverflowException error
3. Efficiency: It is possible to loop instead of recursion in almost any situation. Loop does not produce the overhead required to pass variables, initialize additional storage space and return values, so using loops is equivalent to using recursive calls to greatly improve performance.
4. Mutual recursion: If two procedures call each other, performance may deteriorate. Even produce infinite recursion. The problems caused by this kind of design are the same as those caused by a single recursive process, but it is more difficult to detect and debug
5. Use parentheses when calling: When a Function procedure calls itself recursively, parentheses must be added after the procedure name ( Even if there is no parameter list). Otherwise, the function name will be regarded as the return value of the function.
6. Test: When writing a recursive process, you should test it very carefully and confidently to ensure that it can always meet certain constraints and terminate the recursive process. Ensure protection from running out of memory due to excessive recursive calls

Insert picture description here

Guess you like

Origin blog.csdn.net/LINZEYU666/article/details/111852228