和很多学习刚学习算法的同学来说,递归是摆在我们面前的一道坎
结合数据结构中的二叉树和图的遍历,逛了逛51cto和csdn还有一些大牛博客,总算能摸着一些头绪了。
递归--传递和回归
1:传递
将规模为n的问题,传递为n-1或者n-2 更小也行
这要求解决A问题依赖于解决小A问题
也可以是,解决A问题=解决一个小菜问题+解决小A问题
如遍历一颗二叉树,可以是遍历左子树+遍历右子树
如遍历一幅图,打印一个节点,再从它的临节点开始遍历一副少一个结点的图
不要以为递归只是执行一次,f(n)不能解决,f(n-1)当然也难以解决。当问题规模不断的缩小,直到n=1or n=2这样规模的问题基本就可以解决了
举例 f(n)=f(n-1)+f(n-2) 未知
f(5)=f(4)+f(3) 未知
f(4)=f(3)+f(2)已知
f(3)=f(2)已知+f(1)已知
(感谢 Ewarder 提供)
2:回归
当问题的规模中出现已知,如上图,就可以将 解 带回
总结:引用一张觉得概括性很高的图片
(来自Wiki—
怎样解递归题目
根据题目获取递归表达式(一般采用数学归纳法,即带值慢慢找规律)
递归的表达式(表现形式:自己定义自己)
这里用斐波那契的例子来说明
f(n)=
- 1 (n=1) (回归用) 递归终止条件 即当执行f(1) 终止
- 2 ( n=2) (回归用) 递归终止条件 即当执行f(2)时 终止
- f(n-1)+f(n-1) (n>2) (传递用)
可以看成是分段函数的编程(if语句就行了)
递归的作用
分解(依赖传递)Divide
递归与分治 Recurison <->Divide&Conqur
Ex.1 归并排序(递归)
归并排序(递归)
1 #include<stdio.h> 2 3 4 5 void mergesort(int *num,int start,int end);// 声明一个递归函数--Divide 形参为(数组指针,数组头指针,数组尾指针) 6 7 8 void merge(int *num,int start,int middle,int end);// 这个函数用来将两个排好序的数组进行合并---Conqur 9 10 int main() 11 { 12 // 测试数组 13 int num[10]= {12,54,12,67,86,45,97,32,14,65}; 14 int i; 15 // 排序之前 16 printf("Before sorting:\n"); 17 for (i=0; i<10; i++) 18 { 19 printf("%3d",num[i]); 20 } 21 printf("\n"); 22 // 进行合并排序 23 mergesort(num,0,9); 24 printf("After sorting:\n"); 25 // 排序之后 26 for (i=0; i<10; i++) 27 { 28 printf("%3d",num[i]); 29 } 30 printf("\n"); 31 return 0; 32 } 33 34 35 36 //这个函数用来将问题细分 37 38 void mergesort(int *num,int start,int end) 39 { 40 int middle; 41 if(start<end) 42 { 43 middle=(start+end)/2; //数组中间位置的那个数 (区间上的中点) 44 // 归并的基本思想 45 // 排左边 46 mergesort(num,start,middle); 47 // 排右边 48 mergesort(num,middle+1,end); 49 // 合并 50 merge(num,start,middle,end); 51 } 52 } 53 54 55 56 //这个函数用于将两个已排好序的子序列合并 57 58 void merge(int *areadysort,int start,int middle,int end) 59 { int * temparry=new int[end-start+1]; //动态分配内存(注意不能用a[num]的形式 60 int indexA=start; 61 int indexB=middle+1; 62 int I=0; 63 while((indexA<=middle)&&(indexB<=end)) //扫描两个序列 64 { 65 if (areadysort[indexA]<=areadysort[indexB]) 66 67 temparry[I++]=areadysort[indexA++]; 68 69 else 70 if(areadysort[indexB]<=areadysort[indexA]) 71 temparry[I++]=areadysort[indexB++]; 72 73 } 74 while(indexA<=middle)//复制没有比较完子表中的元素 75 76 temparry[I++]=areadysort[indexA++]; 77 while(indexB<=end) 78 temparry[I++]=areadysort[indexB++]; 79 80 I=0; 81 int tmp = end-start; 82 while(I<=tmp) 83 areadysort[start++]=temparry[I++]; //aready 84 printf("%5d\n",I); 85 86 87 }
Ex.2 全排列
全排列
1 #include <stdio.h> 2 3 int n = 0; //全局计数器,全排列的个数 4 5 void swap(int *a, int *b) 6 { 7 int m; 8 m = *a; 9 *a = *b; 10 *b = m; 11 } 12 void perm(int list[], int k, int m) //m是数组最大下标,k是当前递归的标记 13 { 14 int i; 15 if(k > m) //当计数器大于最大下标时, 16 { 17 for(i = 0; i <= m; i++) 18 printf("%d ", list[i]); //输出的全排列是交换后的临时结果 19 printf("\n"); 20 n++; 21 } 22 else 23 { 24 for(i = k; i <= m; i++) 25 { 26 swap(&list[k], &list[i]); //把选好的元素放到最前面来 27 perm(list, k + 1, m); 28 swap(&list[k], &list[i]); //排列好之后,再换回来,等着下次交换 29 } 30 } 31 } 32 int main() 33 { 34 int list[] = {1, 2, 3}; 35 perm(list, 0, 2); 36 printf("total:%d\n", n); 37 return 0; 38 }
Ex.3 快速排序
快速排序
1 #include<stdio.h> 2 #include<iostream.h> 3 4 int count = 0; 5 6 void Swap(int *a,int *b) 7 { 8 printf("Swap begin\n");int t; 9 t = *a; 10 *a = *b; 11 *b =t; 12 } 13 int Partition( int a[],int p,int r) 14 { 15 printf("Partition begin\n"); 16 int i = p; //一個指針指向數組頭部 17 int j = r+1; //一個指針指向數組尾部 (+1是為了下面方便操作) 18 int x = a[p]; //设置pivot 19 while(true) 20 { 21 while(a[++i]<x&&i<r); 22 while(a[--j]>x); //(得益于+1) 23 if(i>=j) break; 24 Swap(&a[i],&a[j]); 25 } 26 a[p]=a[j]; 27 a[j]=x;//pivot落到最终位置; 28 return j; 29 } 30 void QuickSort( int a[],int p,int r) 31 { 32 printf("%d QickSort begin\n", count++); 33 int q ; //划分位置 34 if(p<r) //待排数组中 只剩下 35 { q = Partition(a,p,r); 36 QuickSort(a,p,q-1); 37 QuickSort(a,q+1,r); 38 } 39 } 40 41 42 43 44 void main() 45 { int a[]={8,6,3,1,5,6,4,2,7}; 46 47 cout<<"The array before sort:"<<endl; 48 for(int i=0;i<9;i++) 49 printf("%2d",a[i]); 50 printf(" OK \n"); 51 52 QuickSort(a,0,8); 53 printf(" OK\n"); 54 55 cout<<"The array after sort:"<<endl; 56 for(i=0;i<9;i++) 57 printf("%2d",a[i]); 58 printf("\n"); 59 }
递归与动态规划 Recurison <->Dinamic Programming
Ex.2 爬楼梯(DP)
爬楼梯之动态规划
1 #include<iostream.h> 2 int table[6]; // 表格,儲存全部問題的答案。 3 bool solve[6]; // 紀錄問題是否已經計算完畢 4 5 int f(int n) 6 { 7 if (n == 0 || n == 1) 8 return 1; 9 10 if (solve[n]) 11 return table[n]; 12 13 table[n] = f(n-1) + f(n-2); // 將答案存入表格 14 solve[n] = true; // 已經計算完畢 15 16 return table[n]; 17 } 18 19 void main() //Stair_Climbing 5阶楼梯 20 { 21 // initial 22 for (int i=0; i<=5; i++) 23 solve[i] = false; 24 25 // compute 26 cout << "爬完五階的踏法有" << f(5) << "種\n"; 27 28 29 int n; //Stair_Climbing 5阶以下楼梯 30 31 cout<<"请输入:一个五阶以下的楼梯\n"; 32 while (cin >> n && (n >= 0 && n <= 5)) 33 { 34 cout << "爬完" << n << "階的踏法" 35 << "有" << f(n) << "種\n"; 36 cout<<"请输入:一个五阶以下的楼梯\n"; //这句必须放在最后,即下一个循环之前 37 } 38 }
递归与回溯
Ex1 n皇后迭代回溯
迭代回溯
1 #include <stdio.h> 2 #include <iostream.h> 3 #include <iomanip.h> 4 #include <math.h> 5 6 class Queen{ 7 friend int nQueen(int); 8 private: 9 bool Place(int k); 10 void Backtrack(void); 11 void display(); 12 int n, //皇后个数 13 * x; //当前解 14 long sum; //当前已找到的可行方案 15 }; 16 17 bool Queen::Place(int k) 18 { 19 for (int j=1;j<k;j++) 20 if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) //不满足隐约束 21 return false; 22 return true; 23 } 24 25 void Queen::Backtrack(void) 26 { 27 x[1] = 0; 28 int k = 1; 29 while (k>0) { 30 x[k]+=1; //遍历所有孩子 31 while((x[k]<=n)&&!(Place(k))) x[k]+=1; //对每个孩子进行尝试 32 if (x[k]<=n) //执行到这一行只有种可能,1。当前行没有可行解 2.找到一个活节点 33 if (k==n){ //已经搜索到第n行(在第n行已经找到一个活节点) 34 sum++; 35 display(); 36 cout<<"\n"; 37 } 38 else{ 39 k++; //深度搜素 40 x[k]=0; 41 } 42 else k--; //回溯 43 } 44 } 45 46 void Queen::display( ){ 47 //输出n个皇后在棋盘图形上的位置 48 int i,j; 49 int queenq[100][100]; 50 for ( i=1; i<=n; i++) 51 for ( j=1; j<=n; j++) 52 queenq[i][j] =0; //初始化棋盘图形矩阵 53 for ( i=1; i<=n; i++) 54 if (x[i]) queenq[i][x[i]] =1; //为棋盘图形矩阵赋值 55 56 for( i=1;i<=n;i++){ //输出棋盘图形 57 cout << "(" <<" "; 58 for ( j = 1; j<=n; j++) 59 if(queenq[i][j]) cout << "Q" << " "; 60 else cout << "*" << " "; 61 cout << ")" << endl; 62 } 63 } 64 65 66 int nQueen(int n) 67 { Queen X; 68 X.n = n; 69 X.sum = 0; 70 int *p = new int [n+1]; 71 for (int i = 0; i<=n; i++) 72 p[i] = 0; 73 X.x = p; 74 X.Backtrack(); 75 delete [] p; 76 return X.sum; 77 } 78 79 void main( ) 80 { 81 int n,sum; 82 while(1){ 83 cout << "用迭代回溯法解n后问题"<<"\n"<<endl; 84 cout << "请输入皇后的数目(不超过16个): "; 85 cin >> n; 86 sum = nQueen(n); 87 cout << "一共有"<<sum<<"种情况"<<endl; 88 cout<<"\n"<<endl; 89 } 90 }
n皇后递归回溯
递归回溯
1 #include <stdio.h> 2 #include <iostream.h> 3 #include <iomanip.h> 4 #include <math.h> 5 6 class Queen{ 7 friend int nQueen(int); 8 private: 9 bool Place(int k); 10 void Backtrack(int t); 11 void display(); 12 int n, //皇后个数 13 * x; //当前解 14 long sum; //当前已找到的可行方案 15 }; 16 17 bool Queen::Place(int k) 18 { 19 for (int j=1;j<k;j++) 20 if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) //不满足隐约束 21 return false; 22 return true; 23 } 24 25 void Queen::Backtrack(int t) //搜索解空间中的第t层子树(先序遍历空间树) 26 { 27 if (t>n) { 28 sum++; //到达叶结点,搜索到一个解 29 display(); 30 cout<<'\n'; 31 } 32 else 33 for (int i=1;i<=n;i++) { 34 x[t]=i; //在第 35 if (Place(t)) Backtrack(t+1); 36 } 37 } 38 39 void Queen::display( ){ 40 //输出n个皇后在棋盘图形上的位置 41 int i,j; 42 int queenq[100][100]; 43 for ( i=1; i<=n; i++) 44 for ( j=1; j<=n; j++) 45 queenq[i][j] =0; //初始化棋盘图形矩阵 46 for ( i=1; i<=n; i++) 47 if (x[i]) queenq[i][x[i]] =1; //为棋盘图形矩阵赋值 48 49 for( i=1;i<=n;i++){ //输出棋盘图形 50 cout << "(" <<" "; 51 for ( j = 1; j<=n; j++) 52 if(queenq[i][j]) cout << "Q" << " "; 53 else cout << "*" << " "; 54 cout << ")" << endl; 55 } 56 } 57 58 59 int nQueen(int n) 60 { Queen X; 61 X.n = n; 62 X.sum = 0; 63 int *p = new int [n+1]; 64 for (int i = 0; i<=n; i++) 65 p[i] = 0; 66 X.x = p; 67 X.Backtrack(1); 68 delete [] p; 69 return X.sum; 70 } 71 72 void main( ) 73 { 74 int n,sum; 75 while(1){ 76 cout << "用递归回溯法解n后问题"<<"\n"<<endl; 77 cout << "请输入皇后的数目(不超过16个): "; 78 cin >> n; 79 sum = nQueen(n); 80 cout << "一共有"<<sum<<"种情况"<<endl; 81 cout<<"\n"<<endl; 82 } 83 }