我们老师说的好:只要你会了for和DFS,就能拿省一。于是我就开始思考DFS和for之间的关系,总结了一下改写的小技巧。
简单介绍:
for循环其实绝大部分可以改写成递归形式,在不考虑空间复杂度的形况下,把for改写成递归形式的好处就是可以很方便的调试代码(不然你发现你代码写错了,拖着一撮for上下乱跑,大括号你都对应不起来了就),还能便捷的把for改成n层for(forrer,比for还for)
比如,我下面写下一个简单的for的代码来求前缀和->
#include<iostream>
#include<cstdio>
using namespace std;
int sum=0;
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
printf("%d",sum);
sum+=i;
printf("加上%d之后是 %d\n",i,sum);
}
printf("%d",sum);
return 0;
}
运行结果如下:
那么我怎么给他改写成递归形式呢?来看for里面,一个printf,一个求和,一个printf。我们需要什么?只需要递归往下加数就行了啊。接下来开始想怎么写代码。
万物之源归于定义:void forrer(int k);
为什么要定义一个k呢?这里面的k就相当于是for里面的i,也就是作为加数。
接下来考虑怎么写。递归,都要有结束条件,for的结束条件来自于第二个参数是否为真,上面的例子里面,for的第二个参数是i<=n,也就是k的结束条件是k>n的时候结束而在k<=n的时候继续执行。
void forrer(int k){
if(k==n){
sum+=k;
return;
}
}
//或者下面形式
void forrer(int k){
if(k>n) return;
}
接下来要做的就是抄代码了。我可以完全把for里面的内容照搬下来,变成下面这样。
void forrer(int k){
if(k>n) return;
printf("%d",sum);//先打印sum
sum+=k;//求和
printf("加上%d之后是 %d\n",k,sum);//再打印sum
forrer(k+1);//让“for”进入下一层
}
运行结果和for完全相同。
递归形式中的递归下一层,可以理解为for里的第三个表达式。
到了现在,for改写成了递归形式,我们总结一下知识点:
1.for(int i=1;i<=n;i++) 中int i对应递归中的void forrer(int k)
2.递归开始的初始值相当于for里的第一个表达式,上例的forrer(1) == for(i=1;..;..);
3.递归里的返回条件是for里面i<=n的补集;
4.递归里进入下一层的条件对应for里面的i++;
5.递归里的处理代码直接照搬for里面代码。
for循环嵌套怎么改写?
例子:
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("i = %d ; j = %d\n",i,j);
}
}
return 0;
}
运行结果:
你会发现for循环嵌套了哎233。而且第二个表达式还不一样,要知道,上面第一个例子的for到n终止,在递归里就可以到相应的位置直接停止,但是这次有两个循环次数,而我只想用一个递归怎么办。
思考一个问题,为什么递归里可以直接写入 if(k>n) return;
就是因为只循环了n次,但是我这次需要内部额外循环的截止条件是个变量怎么办,很简单,函数嘛,里面可以传N多参数。再开一个参数记录次数,这里不是循环次数,而是类似i,j一样的才能在,就完了。
因此可以这么改:
void forrer(int i,int j){
if(i>n) return;
if(j>m){
forrer(i+1,1);
return;
}
printf("i = %d ; j = %d\n",i,j);
forrer(i,j+1);
}
首先祖传返回写最上面,实现抄在下面,遍历最内层变量,到边界就遍历第二层。因此三层for循环可以这么改:
void forrer(int i,int j,int k){
if(i>n) return;
if(j>m){
forrer(i+1,1,1);
return;
}
if(k>o){
forrer(i,j+1,1);
return;
}
printf("i = %d ; j = %d ; k = %d\n",i,j,k);
forrer(i,j,k+1);
}
在知道循环次数的条件下,可以随便搞。
那么新的问题就是,我不知道循环嵌套了几层怎么办(DFS),比如说,我输入了一个n,要求n层for循环,每层循环n次(复杂度
,N=11就会炸),这里有个问题(不是全排列),描述如下:
输入一个N;
要求有N层for循环,每层循环N次;
输出遍历顺序;
代码如下:
int arr[114514];
void forrer(int k){
for(int i=1;i<=n;i++){
arr[k]=i;
if(k==n){
for(int j=1;j<=n;j++) cout<<arr[j]<<" ";
cout<<endl;
}
else forrer(k+1);
}
}
只需要告诉函数一共有几层,就可以跑出来了。
(建议背下来)
int arr[114514];
void forrer(int k){//k表示第k层
for(int i=1;i<=3;i++){
arr[k]=i;
if(k==3){
for(int j=1;j<=3;j++) cout<<arr[j]<<" ";
cout<<endl;
}
else forrer(k+1);
}
}