[C Language] Take you to play with recursion and iterative algorithms

insert image description here

Junxi_'s personal homepage

Be diligent and encourage the years to wait for no one

C/C++ game development


Hello, this is Junxi_, today I will bring you an article on function recursion, which is also very important in our future data structures.

foreword

Unlike addition, subtraction, multiplication, and division, we have seen it many times during our studies, and most beginners may have never been exposed to recursive thinking before, which makes it difficult to get started with recursive algorithms. Today I hope to do my best to explain recursive algorithms in an easy-to-understand way combined with drawing and examples, and help everyone get started

  • Not much nonsense, let's start today's content

1. What is recursion?

  • The programming technique in which a program calls itself is called recursion.
  • Recursion is widely used as an algorithm in programming languages. A recursive algorithm usually refers to a method in which a process or function directly or indirectly calls itself in its definition or description, and it usually converts a large and complex problem layer by layer into a smaller-scale problem similar to the original problem for solution.
  • The recursive strategy only needs a small number of programs to describe the multiple repeated calculations required in the problem-solving process, which greatly reduces the amount of code in the program. (That is to say, most of the scenarios we are learning now that need to use recursion can also be solved through loops, and we will also implement loops in the examples introduced below)
  • The main way of thinking about recursion is: to make big things small
  • When you encounter a thinking bottleneck when using recursion, please keep in mind the idea of ​​reducing big things to small ones! !

2. Two necessary conditions for recursion

  • 1. There are restrictions. When this restriction is met, the recursion will not continue.
  • 2. After each recursive call, it gets closer and closer to this constraint .
  • 当你不满足这两个条件的任意一条时,程序会陷入死循环

When you recursively write bugs, you often don’t consider the above two conditions or the conditions are set incorrectly. When debugging, think about the recursive conditions. It’s best to draw the picture below to help you understand.


3. Explain with examples

Print each bit of integer data

Example 1:
Accept an integer value (unsigned), and print each bit of it in order.
For example:
input: 1234, output 1 2 3 4

code show as below:

#include <stdio.h>
void print(int n)
{
    
    
if(n>9)//如果不大于9直接打印当前n的值即可
{
    
    
print(n/10);
}
printf("%d ", n%10);
}
int main()
{
    
    
int num = 1234;
print(num);
return 0;
}
  • We want to strip out each digit of a four-digit or more digit number. The first thing we need to think about is how to get each digit
  • We know that by taking the remainder % of 10, we can get the unit digit of the number, and dividing by 10 can eliminate this digit. At this time, we can realize it like this (the following uses n as a four-digit example)
  • Let n be 10% to get the number of this digit at this time, then move n/10 to the next digit, then let n be 10% to get this digit and continue down until n is 10% equal to 0, which means that n is a number less than 10 at this time and only it needs to be taken, just remove it to end the recursion

drawing explanation

  • 递归的递就是传递的意思,而归的意思是回归
    insert image description here
    This is recursion!
  • After explaining the application of recursion in this code, let's talk about the problems in this code
  • What happens when a user among us enters a negative number on a whim one day?
    insert image description here
  • The conditions set by our program are only for positive numbers. If you want to input a negative number and print it, you have to modify the code like this
void print(int n)
{
    
    
	if (n > 9)//如果不大于9直接打印当前n的值即可
	{
    
    
		print(n / 10);
	}
	else if (n < -9)
	{
    
    
		print(n / 10);
	}
	printf("%d ", n % 10);
}
int main()
{
    
    
	int num = -1234;
	print(num);
	return 0;
}

Or change the int we passed in to an unsigned integer

void print(unsigned int n)
{
    
    
	if (n > 9)//如果不大于9直接打印当前n的值即可
	{
    
    
		print(n / 10);
	}
	/*else if (n < -9)
	{
		print(n / 10);
	}*/
	printf("%d ", n % 10);
}
  • 这里虽然都是非常简单的地方,但我想提醒大家的是,无论在递归或者任何其他程序的编写中我们都得尽可能考虑各方面的情况,在以后的程序猿工作中,我们要知道用户是不总是照着你的指示来使用程序的,我们得尽可能保证程序的避免上面这种bug。

Loop implementation

//判断输入数字位数
#include<math.h>
int Strlen(unsigned int n)
{
    
    
	int count = 0;
	while (n / 10)
	{
    
    
		count++;
		n /= 10;
	}
	return count;
}

void print(unsigned int n)
{
    
    
	int i = 0;
	int ret = Strlen(n);
	if (n > 9)
	{
    
    
		for (i = ret; i > 0; i--)
		{
    
    
			int m = pow(10, i);
			printf("%d ", n / m);
			n %= m;
		}
	}
	printf("%d", n);
	
	
}
int main()
{
    
    
	int num = 123456789;
	print(num);
	return 0;
}
  • Let's see the effect first
    insert image description here

  • 说实话,同样实现一个功能递归比循环简单多了,从代码的行数就能看出来。这就是我们使用递归算法的意义


Find the length of the string

In Example 2,
it is not allowed to create a temporary variable to write a function to find the length of the string.

#include <stdio.h>
int Strlen(const char*str)
{
    
    
if(*str == '\0')//是个空字符串
return 0;
else
    return 1+Strlen(str+1);//每次递归让Strlen朝后移动一位直至遇到"\0"不满足递归条件
}
int main()
{
    
    
char *p = "abcde";
int len = Strlen(p);
printf("%d\n", len);
return 0;
}
  • Let's draw a picture to understand

insert image description here

  • It is relatively simple to understand when combined with our diagram. I hope that when you write by yourself in the future, you can draw a similar diagram to understand when you are confused about the logic.

Loop implementation

int Strlen(char* s)
{
    
    
	int count = 0;
	while (*s != '\0')
	{
    
    
		count++;
		s++;
	}
	return count;
}

int main()
{
    
    
	char* p = "abcde";
	int len = Strlen(p);
	printf("%d\n", len);
	return 0;
}

  • It is relatively simple to understand when combined with our diagram. I hope that when you write by yourself in the future, you can draw a similar diagram to understand when you are confused about the logic.

4. Recursion and iteration

  • Recursion is to call itself during the running process.
  • The iterative method, also known as the rolling method, is a process of continuously using the old value of the variable to recurse the new value. The direct method (or one-time solution method) corresponding to the iterative method is to solve the problem at one time.
  • Iterative algorithm is a basic method for solving problems with computers, and is generally used for numerical calculations. Accumulation and accumulation are the basic applications of iterative algorithms

Fibonacci sequence

1. Recursive solution

  • Let's take a look at the example of finding the Fibonacci sequence.
  • Before we start let's talk about what is the Fibonacci sequence

Find the nth Fibonacci number (regardless of stack overflow)
1 1 2 3 5 8 13 21 34 55 ... The
sum of the first two numbers is the third number. We call the sequence with this property the Fibonacci sequence.

//求第n个斐波那契数
//1 1 2 3 5 8 13 21 34 55 ...

int fib(int n)
{
    
    
	if (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);
}
  • example result
    insert image description here
  • I hope that everyone can draw the illustrations here by themselves. If you understand the two examples I mentioned above, it should not be difficult for you.

Insufficiency of recursive algorithm

  • Let's focus on the problems that arise in the above code
  • Inputting a small number Our program can run perfectly, but what about inputting a large number?
    Let's test it out:
    insert image description here
  • No results, why?
  • We now want to test how many times the function is executed at this point
int count = 0;//全局变量
int fib(int n)
{
    
    
	if (n == 3)
		count++;
	if (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\n ", ret);
	printf("n等于3执行了%d次 ", count);
}

insert image description here

  • When n=30, only the case of n=3 has been executed more than 300,000 times, which shows the time complexity of our function.
  • Why does the above situation occur?
  • Draw a picture to explain
    insert image description here
  • The picture is too ugly and spicy, as long as everyone can understand what I mean
  • We found that a lot of calculations in the process of calling the fib function are actually repeated, which leads to a large number of repetitions in our program and unnecessary calculations to waste time.
  • at the same time:
  • When debugging the factorial function, if your parameters are relatively large, an error will be reported: information such as stack overflow (stack overflow).
    The stack space allocated by the system to the program is limited, but if there is an infinite loop, or (dead recursion), this may lead to the creation of stack space all the time, and eventually the stack space will be exhausted. This phenomenon is called stack overflow

2. Improve our code using an iterative algorithm

    1. Rewrite recursively to non-recursively .
    1. Use static objects instead of nonstatic local objects.
      In the design of recursive functions, static objects can be used instead of local objects (that is, stack objects), which not only reduces the overhead of generating and releasing objects nonstatic for each recursive call and return , but also saves the intermediate state of recursive calls and can be accessed by each calling layernonstatic static
  • If you don’t know much about the keyword static here, you can take a look at the blog in the link below. There are specific usages of static in the operator part: [ C Language Elementary] Wanzi analysis, taking you to a quick introduction to C language with 0 basics (Part 2)
  • The improved code is as follows:
//求第n个斐波那契数
int fib(int n)
{
    
    
	int result;
	int j;
	int i;
	result = j = 1;
	while (n > 2)
	{
    
    
		n -= 1;
		//实现每一次n减小时新的n-1与n-2并赋值给n
		i = j;//把n-1的值给n-2
		j = result;//把此时n的值给n-1
		result = i + j;//n的值等于此时的(n-1)+(n-2)
	}
	return result;
}


int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n ", ret);
	
}
  • look at the results now
    insert image description here

  • Here, because the number that can be stored in the int type is not enough, the stack will definitely overflow, but don't worry about whether the result is right or not, just say whether it is fast or not, can I give you a result? This means that we have solved the problem of repetitive and complicated code!
    insert image description here
    Tips:
    1. Many problems are explained in a recursive form, simply because it is clearer than the non-recursive form.
    2. However, the iterative implementation of these problems is often more efficient than the recursive implementation, although the readability of the code is slightly worse .
    3. When a problem is quite complex and difficult to implement iteratively, the simplicity of recursive implementation can compensate for the runtime overhead it brings

  • There are also several classic questions here, such as the Tower of Hanoi and the frog jumping steps, and there will be specific blogs to talk about them later.


5. Recursion in minesweeper games


Summarize

  • The above is all the content of today. Today we analyze the recursive algorithm and iterative algorithm in detail, and compare the differences, advantages and disadvantages between them.
  • If you have any questions, please point them out in the comment area or private message me, I will reply as soon as I see it!

It is not easy for a new blogger to create. If you feel that the content of the article is helpful to you, you may wish to click on this new blogger before leaving. Your support is my motivation to update! ! !

(Ke Li asks you to support the blogger three times in a row!!! Click the comment below to like and collect to help Ke Li)
insert image description here

Guess you like

Origin blog.csdn.net/syf666250/article/details/131345550