全排列算法
全排列算法是有多种算法,目前我们只考虑其中的一种,如果后续遇到了,我也会继续更新我的博客。
递归算法核心
分治法全排列实现逻辑
定义集合 R = {r1,r2,r3 … rn} 表示含有n个元素的集合,也就是我们需要排列的数据
定义集合Ri = R - {ri}; Ri表示n个元素中去掉ri的元素的集合
定义含有X集合中所有元素的全排列Perm(X)
定义N(Perm(X))表示集合X的全排列的数目
定义元素rPerm(X)表示以r开头的X中所有元素的全排列
比如 r = 2 X = { 1,3}
rPerm(X) = 2Perm(X)
N(rPerm(X)) = N(Perm(X)) 以2开头,后续字符串是集合X的全排列 rPerm(X)在这里全排列的数目 与Perm(X)的数目是一致的
定义全排列
if n == 1
r1Perm(R1) R1为空 r1的全排列是r1 N(Perm(X)) = 1;
if n > 1
Perm(R) = r1Perm(R1) + r2Perm(R2) + r3Perm(R3) + ... + rnPerm(Rn) Rn都是n-1
这里将全排列的问题规模已经缩减到了n-1的规模
然后继续对Perm(R1),Perm(R2),Perm(R3)... Perm(Rn)进行问题分解,直到问题规模变成1
如果你被上方的数学算法所绕晕了,那么换一种直观的数据表达方式
如果R = { 1,2,3,4}
Perm(R) = 1Perm(R1) + 2Perm(R2) + 3Perm(R3) + 4Perm(R4)
这里我们将定义Xi = Ri 这个只是问题规模缩小后,无法再使用R表示当前的集合
X1 = {2,3,4}
X2 = {1, 3, 4}
X3 = {1, 2, 4}
X4 = {1, 2, 3}
这里我们定义Xii =(X- xi) - xii 表示去掉元素xi和xii的元素
Perm(R1) = 2Perm(X11) + 3Perm(X12) + 4Perm(13)
X11 = {3,4}
X12 = {2,4}
X12 = {2,3}
//全排列输出
Perm(X11) = 3Perm(X111) + 4Perm(X112) = {1,2,3,4} + {1,2,4,3}
N(PermX11) = 2
如果对上述内容存在疑问
我们之前的描述是针对这副图的抽象。
在我们进行Perm(X)的全排列时,我们保持一个元素不变,求Perm(X-1)规模的全排列。
核心代码如下
void FullPermutationDivideConquerMethod::permutation(char *t, int start, int len) {
if (start == len -1) {
std::cout << t << std::endl;
times++;
return;
}
if (len < 0) {
std::cerr << "the len is illegal" << std::endl;
return;
}
for (int i = start; i < len; i++) { //广度遍历,将所有元素与第一个元素交换
Tools::swap(t, i, start, len);
permutation(t, start+1, len); //将问题规模缩小
Tools::swap(t, i, start, len);
}
}
调用
permutation(t, 0, len);