递归与尾递归(完)
内容概要
一、递归注意事项
二、递归的两个环节
三、尾递归
四、递归练习——汉诺塔、快速排序
1、递归注意事项
递归指的是函数的递归,函数的递归简单来说就是自身调用自身的过程
#include <stdio.h> void func(void); void func(void){ printf("...\n"); func(); } int main(void){ func(); return 0; }
添加了一个if条件判断,同时将参数作为条件一起传递给下层函数
#include <stdio.h> void func(int); void func(int i){ if (i > 0){ printf("...\n"); func(i-1); } else{ printf("end"); } } int main(void){ int a; printf("please input a number:"); scanf("%d",&a); func(a); return 0; }
这里递归执行到func(0)时,会转为执行else语句,不再调用自身,递归结束
总结:递归一定要有结束语句,通常通过if条件判断实现
2、递归的两个环节
这两个环节分别为
-回溯
-递推
回溯和递推验证代码
#include <stdio.h> void func(int); void func(int i){ if (i > 0){ printf("go back i:%d\n",i); //回溯过程 func(i-1); printf("go to i:%d\n",i); //递推过程 } else{ printf("end\n"); } } int main(void){ int a; printf("please input a number:"); scanf("%d",&a); func(a); return 0; }
图解

如果想要使用递归,那么递归代码的实现思路结合 回溯结束,递推开始处和回溯开始两步 思路会比较清晰
3、尾递归
尾递归就是将函数调用自身的代码写在return语句后面,return下行没有后续代码
一个计算阶层的函数
#include <stdio.h> long func(int); long func(int i){ if (i < 1){ return 1; } else{ return i * func(i-1); //func后续没有代码要执行了 } } int main(void){ int a; long result; printf("please input a number:"); scanf("%d",&a); result = func(a); printf("result is %ld\n",result); return 0; }
尾递归与普通递归不同的是,尾递归可以视为没有递推过程,因为每次调用自身后,后续没有其它代码需要执行。
下层函数要将结果返回给上层函数,必须使用return
在python中是没有尾递归的,因为无论如何都会有递推过程 **有空详细了解原因**
4、递归练习——汉诺塔、快速排列
-汉诺塔实现
一般情况下,如果可以使用迭代就用迭代;万不得已才使用递推

汉诺塔游戏规则
每次只能移动一个圆环,而且小圆环只能放在大圆环上面。要将最左边64个圆环移动到最右边。(图上只画了3个)
思路:
将三个柱子分别命名为X、Y、Z,要想将64个圆盘移动到最右边;
必须经过将前63个圆盘移动到中间的柱子(Y),再将第64个圆盘从X移动到Z;
然后将Y上63个圆环重新移动到Z上。
要移动63个圆环;
必须先将前62个圆环移走,放到一个临时柱子上;
再将第63个圆环移动到目标柱子上,;
将前62个圆环移动到第63个圆环之上;
要移动62个圆环;
必须先将前61个圆环移走,放到一个临时柱子上;
再将第62个圆环移动到目标柱子上;
将前61个圆环移动到第62个圆环之上;
无限套娃下去...
记录汉诺塔解法函数
#include <stdio.h> void hanoi(int); void hanoi(int i, char start, char end, char temp){ //i是圆盘数量 if (i > 1){ hanoi(i-1, start, temp, end); //将i-1个圆盘移动到临时柱子上 printf("%c -> %c\n",start,end); //将第i个(也就是最下面一个)从开始柱子移动到目标柱子 hanoi(i-1, temp, end, start); //重新将放在临时柱子上的i-1个圆盘移动到i圆盘上面 } else{ //printf("%c -> %c\n",start,temp); //这里是思路错误时候写的,还是留下来好 printf("%c -> %c\n",start,end); //printf("%c -> %c\n",temp,end); } } int main(void){ int a; long result; printf("please input a number:"); scanf("%d",&a); hanoi(a, 'X', 'Z', 'Y'); return 0; }
记录汉诺塔移动次数函数
#include <stdio.h> long long hanoi(int n); long long hanoi(int n){ static long long result; if (n > 1){ result = (2*hanoi(n-1))+1; } else{ result = 1; } return result; } int main(void){ int input; long long result; scanf("%d",&input); result = hanoi(input); printf("%lld\n",result); return 0; }
-快速排列
这个写到头都大了还是有错 **有空再弄**
图解

#include <stdio.h> void array_sort(int array[], int, int); void array_sort(int array[], int start, int end){ if(start == end){ return; } int i,j,temp,center; i = start; j = end; center = array[(start+end) / 2]; do{ while(array[i] < center){ i++; } while(array[j] > center){ j--; } temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; if (i > end){ i = end; } if (j < start){ j = end; } }while(i <= j); array_sort(array, start, j); array_sort(array, i, end); } int main(void){ int array[] = {1, 22, 35, 76, 23, 647, 1341, 135, 423, 52}; int length = sizeof(array) / sizeof(array[0]); int end = length - 1; array_sort(array, 0, end); for(int i = 0; i < length; i++){ printf("%d ",array[i]); } return 0; }
再战快速排列
#include <stdio.h> void quick_sort(int [], int, int); void quick_sort(int array[], int start, int end){ if (start == end){ return; } int center, i, j, temp; i = start; j = end; center = array[(start+end) / 2]; do{ while (array[i] < center){ i++; } while (array[j] > center){ j--; } if (i <= j){ temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } if (j < start){ j = start; } if (i > end){ i = end; } } while (i <= j); quick_sort(array, start, j); quick_sort(array, i, end); } int main(void){ int array[] = {1,2,3,4,5,8,5,1,6,5}; int end_index = sizeof(array) / sizeof(int); quick_sort(array, 0, end); for (int i = 0; i < 10; i++){ printf("%d ", array[i]); } return 0; }
对于递归的一些思考
递归就是——大事化小,小事化无;
要考虑好递归函数的功能;
递归函数的功能虽然单一,也能通过参数变得灵活;
递归函数必须要有结束条件,避免死循环和无限递归;
思考递归函数实现过程,先从一般再到特殊
#include <stdio.h> void quick_sort(int [], int, int); void quick_sort(int array[], int start, int end){ //要进行排序的数组, 开始索引, 结束索引 if (start == end){ return; //当传入的实参相同时,说明只有一个数,结束本次排序 } int center, i, j, temp; //中间值, 用于遍历的下标i, j // 初始化下标 i = start; j = end; center = array[(start+end) / 2]; do{ // goto tip1: while (array[i] < center){ //从左到右遍历,找到大于中间值的值 i++; } while (array[j] > center){ //从右往左遍历,找到小于中间值的值 j--; } if (i <= j){ //goto tip2: temp = array[i]; //替换两个值 array[i] = array[j]; array[j] = temp; i++; j--; } // goto tip3: if (j < start){ j = start; } if (i > end){ i = end; } } while (i <= j); //当i>j时,说明数组所有值都被遍历 //将数组分为两个小数组 quick_sort(array, start, j); //小于中间值的数组 quick_sort(array, i, end); //大于中间值的数组 } int main(void){ int array[] = {1,2,3,4,5,8,5,1,6,5}; int end_index = sizeof(array) / sizeof(int); quick_sort(array, 0, end); for (int i = 0; i < 10; i++){ printf("%d ", array[i]); } return 0; }
tip1:
不能将
array[i] < center写成array[i] <= center;
或者
array[j] > center写成array[j] >= center;
这是因为当中间值为最大值时,i直接超出数组最大索引
加入判断i是否超过最大值也不可以,
因为要保证中间值左边都是小于中间值的
中间值右边都是大于中间值的
中间值也应该被移动
对于j来说,当中间值为最小值时同理
tip2:
if (i <= j)的判断一定要加,这步是最难想到的
i <= j的条件不能写成i < j;
因为当 i = j 时,会进入死循环
加入if (i <= j)的原因是
当中间值为最大值或者是最小值时
以最大值为例

tip3
加入两个if判断是为了避免i和j超出索引范围
***完***
本文来自博客园,作者:口乞厂几,转载请注明原文链接:https://www.cnblogs.com/laijianwei/p/14545004.html

浙公网安备 33010602011771号