C language function recursion explanation~

1. What is recursion?

Recursion is an inescapable topic when learning C language functions, so what is recursion?

Recursion is actually a method of solving problems. In C language, a recursive function just calls itself.

Write the simplest C language recursive code in history:

#include <stdio.h>
int main()
{
    
    
    printf("hehe\n");
	main();//main函数中又调用了main函数
	return 0;
}

The above is a simple recursive program, but the above recursion is only to demonstrate the basic form of recursion, not to solve the problem. The code will eventually fall into dead recursion, causing stack overflow. The details are shown in the figure below

The idea of ​​recursion:

Convert a large and complex problem into a sub-problem that is similar to the original problem but smaller in scale to solve; until the sub-problem can no longer be split, the recursion ends, so the recursive way of thinking It is the process of making big things small.

The recursion in recursion means recursion, and regression means regression< a i=4> means.

2. Restrictions on recursion

When writing recursion, there are two necessary conditions:

  • There are restrictions on recursion. When these restrictions are met, the recursion will no longer continue.
  • Each recursive call will get closer and closer to this limit.

Okay, next the blogger will give a few examples to let everyone gradually understand these two restrictions.

Insert image description here

3. Recursive example

3.1 Example 1: Find the factorial of n

Calculate the factorial of n (without considering overflow). The factorial of n is the cumulative multiplication of numbers from 1 to n.

3.1.1 Analysis and code implementation

We know the factorial formula of n: n!=n*(n-1)!

举例:
5! = 5*4*3*2*1
4! = 4*3*2*1
所以:5! = 5*4!

This idea is to convert a larger problem into a problem that is similar to the original problem but smaller in scale.

​ n!---->n*(n-1)!

​ (n-1)!------>(n-1)*(n-2)!

Until n is 1 or 0, no more disassembly

Let’s do a little analysis. When n<=1, the factorial of n is 1. The factorial of the remaining n can be calculated by the above formula. The recursive formula for the factorial of n is as follows:

Then we can write the function Fact to find the factorial of n. Suppose Fact(n) is to find the factorial of n. Then Fact(n) is to find the factorial of n. Then Fact(n-1) is the factorial of n-1. The function As shown in the following code:

int Fact(int n)
{
    
    
if(n<=0)
	return 1;
else
	return n*Fact(n-1);
}
	
int main()
{
    
    
	int n = 0;
    scanf("%d", &n);
    int ret = Fact(n);
    printf("%d\n", ret);
    return 0;
}

The result of running the code is as follows:

img

In addition, the recursion and regression of the factorial function of n are as shown below:

From this picture, we can knowWhen n=0, Fact(0)=1, Fact(1)=1*Fact(0)=1, and then return to the upper layer one by one, so we can find Fact(5)=120.

3.2 Example 2: Print each digit of an integer sequentially

Input an integer m and print each digit of the integer in order.

for example:

Input: 1234 Output: 1 2 3 4

Input: 530 Output: 530

3.2.1 Analysis and code implementation

If this topic is placed in front of us, the first thing that comes to mind is, how to get each digit of this number?

If n is a single digit, each digit of n is n itself.

If n is more than 1 digit, you have to split each digit

1234%10 will get 4, and then 1234/10 will get 123, which is equivalent to removing 4
Then continue to 123%10, you will get 3, and then divide Remove 3 from 10, and so on
Continue the %10 and \10 operations until every bit of 1234 is obtained
But there is a problem here. The numerical order is reverse

But we got inspiration, and we found that the lowest digit of a number is the easiest to get, which can be obtained through %10. Then we assume that we want to write a function Print to print each digit of n, as shown in the following figure:

img

If we continue in this way, we will have

Print(1234)
==>Print(123) + printf(4)
==>Print(12) + printf(3)
==>Print(1) + printf(2)
==>printf(1)

When the printed number becomes 1 digit, there is no need to split and the recursion ends.

Then the code completion will be relatively clear:

void Print(int n){
    
    
	if(n>9)
	{
    
    
		Print(n/10)
	}
	printf("%d ",n%10);
}

int main(){
    
    
	int n=0;
	scanf("%d",&n);
	Print(n);
	return 0;
}

Let's take a look at the input and output results of the code:

In the process of solving this problem, we used the idea of ​​​​reducing big things into small things.

Disassemble Print(1234) to print each digit of 1234 as first Print(123) prints each digit of 123, and then print the resulting 4

Disassemble Print(123) to print each digit of 123 as first Print(12) prints each digit of 12, and then print the resulting 3

Print (12) prints each digit of 12, and disassembles it into: first Print (1) prints each digit of 1, and then prints the resulting 2

Until Print prints a single digit, just print directly.

The following is the flow chart of recursion and regression , you can take a look~

From the picture, we know that when the user enters 1234 and enters the Print function, it will be recursed layer by layer until it reaches Print(1). If 1<=9, 1 will be printed out, and then it will be returned layer by layer. , until every bit of 1234 is printed out, the recursion ends, and then it returns to the main function to continue executing the next statement.

4. Recursion and iteration

Recursion is a good programming technique, but like many techniques, it can also be misused. Just like the example, when you see the derived formula, it is easy to write it in recursive form:

int Fact(int n)
{
    
    
    if(n<=0)
        return 1;
    else
        return n*Fact(n-1);
}

In fact, although the Fact function can produce correct results, there is some runtime overhead involved in the recursive function call.

Every function call in C language requires a memory space in the stack area to save the values ​​of various local variables during the function call
, this space is called the runtime stack, or function stack frame.
If the function does not return, the stack frame space corresponding to the function will always be occupied, so if there is a recursive call in the function call, each recursive
function call will open up the space belonging to Your own stack frame space will be released layer by layer until the function recursion stops and begins to return.
Therefore, if you use function recursion to complete the code and the recursion level is too deep, too much stack frame space will be wasted, and it may also cause stack overflow
( stack over flow) problem.

So if we don't want to use recursion, we have to think of other methods, usually iteration (iteration refers to the loop method)

For example, we calculate the factorial of n, or the numbers 1~n are accumulated together, so our code can be written like this:

int sum(int n) {
    
    
	int ret = 1;
	for (int i = 1; i <= n; i++)
		ret *= i;
	return ret;
}

int main() {
    
    

	int n = 0;
	scanf("%d", &n);
	printf("%d", sum(n));
	return 0;
}

The above code can complete the task, and the efficiency is better than the recursive method.

In fact, many of the problems we see are explained in recursive form simply because it is clearer than non-recursive, but iterative implementations of these problems are often more efficient than recursive implementations.

When a problem is too complex to be implemented iteratively, the simplicity of the recursive implementation can compensate for the runtime overhead it brings.

Example 3: Find the nth Fibonacci number

We can also give more extreme examples, such as calculating the nth Fibonacci number, which is not suitable for recursive solution, but Fibonacci
number The problem is described using recursion. As shown below:

When we see this formula, it is easy to induce us to write the code in a recursive form. The specific code is as follows:

int Fib(int n)
{
    
    
if(n<=2)
        return 1;
else
        return Fib(n-1)+Fib(n-2);
}

Main functionThe code is as follows:

#include <stdio.h>
int main(){
    
    

	int n=0;
	scanf("%d",&n);
	int ret=Fib(n);
	printf("%d\n",ret);

	return 0;
}

But when our n input is 50, it takes a long time to calculate the result. The time spent in this calculation is difficult for us to accept. This also shows that the recursive writing method is very inefficient. Why is this?

In fact, when the recursive program continues to expand, during the expansion process, we can easily find that there will be a lot of repeated calculations during the recursive process, and the deeper the recursive level, the more redundant calculations will be. We can use code to have a test:

#include <stdio.h>
int count = 0;

int Fib(int n) {
    
    
	if (n == 3)
		count++;
	 if (n ==1 || n==2) {
    
    
		return 1;
	}
	else
		return Fib(n - 1) + Fib(n - 2);
}

int main() {
    
    

	int  n = 0;
	scanf("%d", &n);
	int ret = Fib(n);
	printf("%d", ret);
	printf("\ncount=%d\n", count);

	return 0;
}

The output is as follows:

From here we can see that when calculating the 40th Fibonacci number, using the recursive method, the third Fibonacci number was repeatedly calculated 39088169 times. In fact, these calculations are very redundant. . Therefore, it is very unwise to use recursion to calculate the Fibonacci number, so we have to use iteration to solve it.

We know that the first two numbers of the Fibonacci number are both 1, and then the sum of the first two numbers is the third number. Just calculate from bottom to top.

So, we have the following line of code:

int Fib(int n) {
    
    

	int a = 1;
	int b = 1;
	int c = 1;
	for (int i = 3; i <= n; i++) {
    
    
		c = a + b;
		a = b;
		b = c;
	}

	return c;
}

int main() {
    
    

	int n = 0;
	scanf("%d",&n);
	int t= Fib(n);
	printf("%d", t);

	return 0;
}

In this line of code, we first assign the values ​​of variables a, b, c to 1. When the user enters the value n>=3, it will enter a loop. First, we add the sum of the first two numbers. Assign it to c, then assign the value of b to a, and then assign the value of c to b. In this way, the numbers of a and b will gradually increase with the size of n, so we can calculate the nth What is the Fibonacci number.

Therefore, we can find that implementing this code in an iterative way is much more efficient.

Sometimes, although recursion is good, it can also introduce some problems, so we must not be obsessed with recursion and stop it in moderation.

Okay, that’s it for today’s recursion knowledge points. If you think the blogger’s talk is pretty good, you are welcome to support it with one click and three consecutive clicks. Thank you! ! !
Insert image description here

Guess you like

Origin blog.csdn.net/m0_63564767/article/details/132413362