写在前面:
对递归这部分,终觉得要下些功夫,因为最初学习这部分的时候理解起来就有些抽象。机缘巧合在这本书中将递归用几个例子说得明明白白,所以决定耐心下来,类似单步执行般,将这个例子仔细梳理一遍,务求理解。
P28页用C++写的递归过程分析
#include <iostream>
#include <iterator>
using namespace std;
template <class T>
void perm(T list[], int k, int m) // list is array name, m is number of elements, k is count of elements has already been picked out to front (equals ei of ei.perm(X))
{
if(k == m) // base component
copy(list, list + m + 1, ostream_iterator<T>(cout, "")); // list is begin(),list+m+1 is end()
else // recursion component
{
for(int i = k; i <= m; i++)
{
/* 需要将list[k:m]中的每一个元素分别提到最前面,并与余下的 “不含此元素的集合的所有组合” 进行组合;
* 因要使用递归,元素数需要减少一个,从list[k]这个元素开始,将它提出,余下的再组合;
*
* list[k]这个位置很特殊,它是下一级递归所不包含的位置,所以提取元素出来时就可以放在这个位置,
* 那能不能放在list[k-1]或list[k-x]只要不超出list的边界即可呢?不能,因为list[k]这个位置的元素,
* 是要参与到当前perm()层的排列组合中的,而list[k-1]却不参与
*
* 于是就出现了这样的效果,list[k]这个位置,分别被list[k+1],list[k+2],……,list[m]所取代,
* 在执行完中间的perm()后,在随后的第2个swap()中又被复原,
* 也就是说,假设初始list[k]的值为AA,则过程是,AA与list[k+1]交换,然后换回,AA再与list[k+2]交换,
* 然后换回,依次类推,直到AA与list[m]交换并再换回;
*
* 所以,第1个swap()的任务,是完成对各元素的“提取并放在首位”,第2个swap()的任务,是将提出的元素放回到集合中
*/
swap(list[k], list[i]);
perm(list, k + 1, m);
// cout << endl;
swap(list[k], list[i]);
}
}
}
int main()
{
char arr[5] {'a', 'b', 'c', 'd', 'e'};
perm<char>(arr, 0, 4);
system("pause");
return 0;
}
分析for的过程:
-
进入最外层for之后,第1个swap()将list[k]与list[i]交换,随着i的增加,相当于逐个将list[k:m]中的元素提出来放在最前面
-
随后进入下一级perm(),注意,这里不是马上执行第2个swap(),而是需要执行完这个perm()
-
最新进入的perm()中,又要涉及,对减少一个元素后的集合,分别提取出元素放在最前面的过程
-
可以发现,每层for中第2个swap()要等到它上面一句的perm()全部执行完毕后,才会被执行,于是就有了书中P28图1-3中的顺序
对书中图1-3进行的过程分析:
必须要理解的一个点:这个permutations()
,就是通过修改list数组中元素的位置,来到达表达每个不同组合的,每个组合都对应着一个全新的list,而这个变化后的list就是要在base component中遍历显示的。要区别于最初始状态的list数组。
下面分析过程:(第1层指最外层)
-
1-2: 第1层for中第1个swap()将a与a调换 (注意,此时list[k]==a),list此时为{a,b,c},进入下一层perm()
-
2-3: 第2层for中第1个swap()将b与b调换 (注意,此时list[k+1]==b),list此时为{a,b,c},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素
-
3-2: 第2层for中第2个swap()将b与b恢复调换,list此时为{a,b,c}
-
2-4: 第2层for中第1个swap()将c与b调换,list此时为{a,c,b},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素
-
4-2: 第2层for中第2个swap()将c与b恢复调换,list此时为{a,b,c}
-
2-1: 第1层for中第2个swap()将a与a恢复调换,list此时为{a,b,c}
-
截止此时,第1层for中i=0的情况,已经完成
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%(分隔线)
-
下面进行,第1层for中i=1的情况,即list[i]=b的情况
-
1-5: 第1层for中第1个swap()将b与a调换 (注意,此时list[k]==a,list[i]==b),list此时为{b,a,c},进入下一层perm()
-
5-6: 第2层for中第1个swap()将a与a调换,相当于没变,list变为{b,a,c},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素
-
6-5: 第2层for中第2个swap()将a与a恢复调换,相当于没变,list仍为{b,a,c}
-
5-7: 第2层for中第1个swap()将a与c调换,list变为{b,c,a},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素
-
7-5: 第2层for中第2个swap()将a与c恢复调换,list恢复为{b,a,c}
-
5-1: 第1层for中第2个swap()将b与a恢复调换,list此时为{a,b,c}
-
截止此时,最外层for的i=1的情况完成
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%(分隔线)
-
下面进行,第1层for中i=2的情况,即list[i]=c的情况
-
1-8: 第1层for中第1个swap()将a与c调换 (注意,此时list[k]==a,list[i]==c),list此时为{c,b,a},进入下一层perm()
-
8-9: 第2层for中第1个swap()将b与b调换,list仍为{c,b,a},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素
-
9-8: 第2层for中第2个swap()将b与b恢复调换,lise仍为{c,b,a}
-
9-10: 第2层for中第1个swap()将b与a调换,list变为{c,a,b},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素
-
10-9: 第2层for中第2个swap()将b与a恢复调换,list变为{c,b,a}
-
9-1: 第1层for中第2个swap()将a与c恢复调换,list此时为{a,b,c}
-
截止目前,所有层的perm()全部得到了完整的执行