回溯递归 (写给我的寝室长 曹旭)
来源:互联网 发布:传奇霸业武魂数据 编辑:程序博客网 时间:2024/05/22 08:17
下面是很久前我在一论坛看到一帖子,是这篇文章让我回溯递归入门的,上次上一门无聊课时,和寝室长突发雅兴论及递归和回溯来,才想起多年前曾拜读过此文,现整理出来,送与寝室长。望旭哥学术有成,下次再来和小生畅叙。
作为一个程序的初学者,我曾经有一段时间不大理解回朔递归思想。
当然现在也是不知皮毛的菜鸟。本贴,仅仅给刚入门的学生朋友一点参考。
希望能让你我对回朔有更进一步的理解。
下面开始吧 ^-^:)
int n = 3;
void f(int k)
...{
if(k==r)
...{
cout << "hello,world!" << endl;
return;
}
for(int i=0; i<n; i++)
f(k+1);
}
int main()
...{
f(0);
cin.get();
}
以上程序会打印多少次: hello world!答案是:n^r 次。
首先:程序从f(0) 进入。在此,我们称f(0)为根结点,并且有n个孩子为f(1)。
同时每个f(1)结点,又拥有n个孩子结点f(2),依次递归下去。
但是,我更愿意称,f(i)为第i层,并且该第i层上有n^i个结点(当然f(0)为根,只有一个,为第0层),
每个结点又有n个孩子结点。所以该题打印的次数就是求f(k)层上结点的个数
以上就是简单的所谓的解空间树。(下面一段来参考自课本 ·@_@)
回朔法是系统地搜索问题的所有解的算法。在问题的解空间树中,
回朔法按照深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意结点时,先判断该结点是否包含问题的解。如果肯定不包含,那么跳过以该结点为根的子树的搜索,回朔到其双亲,并对该结点的尚未分析的兄弟(如果所有兄弟都分析过,那么则回溯到双亲的尚未分析的兄弟),应用以上的过程。
否则进入该子树,继续深度优先策略搜索。
如果要求所有问题的解时,要回朔到根,并且对所有子树都搜索完才结束.
上面第一个程序幸运的是,所有结点都是包含问题的解。根本不用判断。
下面来个有点意思的题目。
problem1:P(n,r). 从n个数中选取r个数进行全排列(r<=n)。并输出所有解。
分析:我们可以把n个数看成是一个结点的所有孩子结点。
因此问题的求解,变成是:从根结点出发搜索所有长度为r的路径。并且要求该路径上任意结点互不相同,因此任意一条该路径上结点的集合就是一个问题的解。
const int n=4;
const int r=3;
int flag[n]=...{0};
int result[r]=...{0};
void f(int k)
...{
if(k==r)//路径长度达到r,输出路径结点。
...{
for(int i=0;i<k;i++)
cout<<result[i];
cout<<endl;
return;
}
for(int j=0;j<n;j++)//搜索所有孩子结点
...{
if(!flag[j])//该结点在路径上尚未被选入
...{
result[k]=j+1;//记录该结点。
flag[j]=1;//表示该结点已经被选中,为了不让其孩子结点与它相同。
f(k+1);//进入下一层。
flag[j]=0;//以f(k+1)层一结点为根的子树搜索完毕。要回溯到右边的兄弟,要求对做过标记的兄弟恢复。
}
}
}
void main()
...{
f(0);
cin.get();
}
problem2:C(n,r). 从n个数中选取r个数进行组合(r<=n)。并且输出结果。阿哈。怎么样?如何运用以上的策列呢?
我们来认真思考一下:首先,路径长度也要为r,不仅路径上的结点要互不相同,同时还要求任意两条路径上的所有结点的集合都要互不相同。
const int n=5;
const int r=3;
int flag[n] = ...{0};
int result[r]=...{0};
void f(int k)
...{
if(k == r)
...{
for(int i=0;i<k;i++)
cout<<result[i];
cout<<endl;
return;
}
for(int i=0; i<n; i++)
...{
if(!flag[i])
...{
result[k] = i+1;
flag[i] = 1;
f(k+1);
if(i==n-1) //以f(k+1)层一结点为根的子树搜索完毕。并且此根是其双亲的最右边的孩子。
...{ //因此要回朔到其双亲的尚未分析的兄弟结点,要求对已经做过标志的右边的兄弟进行恢复。
int s = result[k]-result[k-1];//表示要恢复的个数。
for(int j=n-1; j>=n-s; j--)
flag[j] = 0;
}
}
}
}
void main()
...{
f(0);
cin.get();
}
problem3:求n个数的所有子集。有了上面两题的分析,这题应该可以轻松解决,:) (其实这道题在数据结构严蔚敏版有的 刺猬注)
const int n=5;
int flag[n]=...{0};
int result[n]=...{0};
void f(int k)
...{
if(k<=n)
...{
for(int i=0;i<k;i++)
cout<<result[i];
cout<<endl;
}
else return;
for(int j=0;j<n;j++)
...{
if(!flag[j]&&(j+1>result[k-1]))
...{
result[k]=j+1;
flag[j]=1;
f(k+1);
flag[j]=0;
}
}
}
void main()
...{
f(0);
cin.get();
}
原文地址: http://topic.csdn.net/t/20040810/23/3261786.html
刺猬注:其实我们数据结构教材上专门开辟了一小节叙述回溯与递归的,那里面论述的在看了上面这位前辈所总结之后再去反复咀嚼,才发现其中的奥妙之处,可惜,这节内容太过于“难”,老师也考虑到应试也不多讲。所以对于我们计算机专业,应试基本来说是毫无意义的,拿了一等奖学金其实是骗骗自己,哄哄父母。
高等教育=学校+老师+学生一起来骗世人,或是民族未来?
- 回溯递归 (写给我的寝室长 曹旭)
- 寝室记-------《我的绿野仙踪》
- 我的寝室-------我的大一
- 递归的应用-回溯
- 关于 typedef & typedef struct & typedef union理解 --写给不长脑子的我
- N皇后问题的非递归回溯和递归回溯
- 写给我的老婆
- 写给我的兄弟
- 写给浮躁的我
- 写给我的朋友
- 写给我的父亲
- 写给未来的我
- 写给我的女朋友
- 写给我的你
- 写给我的2013
- 写给我的2016
- 写给未来的我
- poj Matrix 回溯,递归,虽然对于很多人是水题,但我感觉这道题听好的嘛!!!!!!!!!!!
- 什么是java的守护线程
- 客户化FreeTextBox;汉化ImageGallery
- c将大写字母转换成小写字母
- Java中 static/transient,final/volatile 说明
- Active Directory扩展脚本的编写
- 回溯递归 (写给我的寝室长 曹旭)
- Undo/Redo框架的一种实现
- cadence中cell属性值的传递
- FLEX&BISON学习笔记(一:语法)
- 看书
- problem 1082
- FCKeditor 2.3 在ASP.NET中的设置和使用
- 团队管理101招
- 团队沟通方面的技巧