通过上一篇的内容,我们可以得到一颗递归解答树。
假设n=4.序列为{1,2,3,4},可以用一颗树显示出了。这颗树和前面介绍的二叉树不同。第0层(根)结点有n个子结点,第1层结点各有n-1个子结点,第2层结点各有n-2个子结点,第3层结点各有n-3个子结点,....,第n层结点都没有子结点(即都是叶子),而每个叶子对应于一个排列,共有n!个叶子。由于这棵树展示的是从什么都没做逐步生成完整解的过程,因此将其称为解答树。
如果某问题的解可以由多个步骤得到,而每个步骤都有若干种选择(这些候选方案集可能会依赖于先前做出的选择),且可以用递归枚举法实现,则它的工作方式可以用解答树来描述。
这颗解答树一共有多少个结点呢?可以逐层查看,第0层有1个结点,第1层n个,第2层有n*(n-1)个结点(因为第1层的每个结点都有n-1结点),第3层有n(n-1)*(n-2)个(因为第2层的每个结点都有n-2个结点),....,第n层有n*(n-1)*(n-2)*.....*2*1 = n!个。
下面把它们加起来,为了推导方便,把n*(n-1)*(n-2)*...*(n-k)写成n! / (n-k-1)!,则所有结点之和为:
根据高等数学中的泰勒展开公式,,因此T(n) < n! e = O(n!) 。由于叶子有n!个,倒数第二层也有n!个结点,因此上米娜的各层全部加起来也不到n!,这是一个很重要的结论:在多数情况下,解答数上的结点集合全部来源于最后一两层。和它们相比,上米娜的结点数可以忽略不计。
不熟悉泰勒展开公式也没有关系,可以写一个程序,输出随着n增大时的变化,并发现它能很快收敛。这就是计算机的优点之一-可以通过模拟避开数学推导。即使无法严密而精确的求解,也可以找到令人信服的实验数据。
下一个排列。
枚举所有排列的另一个方法是慧聪字典序最小排列开始,不停调用 求下一个排列的过程。如何求下一个排列呢?C++的STL中提供了一个库函数 next_permutation。
运行结果:
void STL_print_repeat_permutation(int n,int *P)
{
//从字典序的最小排列开始,不停调用求下一个排列的过程
do{
for(int i=0;i<n;i++) //输出排列P
printf("%d ",P[i]);
printf("\n");
}while(next_permutation(P,P+n)); //求下一个排列
}
需啊哟注意的是,上述代码同样使用于可重集。记得在此之前对P数组进行排序。