(1) 全排列:
将数组看为一个集合,将集合分为两部分:0~s和s~e,其中0~s表示已经选出来的元素,而s~e表示还没有选择的元素。
perm(set, s, e)
{
顺序从s~e中选出一个元素与s交换(即选出一个元素)
调用perm(set, s + 1, e)
直到s>e,即剩余集合已经为空了,输出set
}
1 void perm(int list[], int s, int e, void (*cbk)(int list[])) 2 { 3 int i; 4 if(s > e) 5 { 6 (*cbk)(list); 7 } 8 else 9 { 10 for(i = s; i <= e; i++)11 { 12 swap(list, s, i); 13 perm(list, s + 1, e, cbk); 14 swap(list, s, i); 15 }16 }17 }
排列的非递归算法:
L算法作用于有序序列a_1 < a_2 < a_3 < ... < a_n 上(可以改写成≤的形式),其算法步骤如下:
- 输出序列本身
- 从右至左找到第一个a_j,使得a_j < a_{j+1}且a_{j+1} > a_{j+2} > ... > a_n
- 在a[j+1..n]中找到最小的a_k使得a_j < a_k,交换a_j和a_k
- 将a[j+1..n]逆序,输出当前序列
- 重复第2~5步,直到找不到第二步中合适的a_j
这种算法可以生成字典序的排列序列。
1 public static IEnumerableFullPermutations (IEnumerable iter) where T : IComparable 2 { 3 var pool = iter.ToArray(); 4 while (true) 5 { 6 yield return pool.ToArray(); 7 int j = pool.Length - 2; 8 while (j >= 0 && pool[j].CompareTo(pool[j + 1]) >= 0) 9 {10 j--;11 }12 if (j < 0)13 yield break;14 int k = pool.Length - 1;15 while (k > j && pool[k].CompareTo(pool[j]) <= 0)16 {17 k--;18 }19 SwapItem(pool, j, k);20 Reverse(pool, j + 1, pool.Length - 1);21 }22 }23 private static void Reverse (IList lst, int s, int e)24 {25 while (s < e)26 {27 Swap(lst, s, e);28 s++;29 e--;30 }31 }
(2)组合
组合指从n个不同元素中取出m个元素来合成的一个组,这个组内元素没有顺序。使用C(n, k)表示从n个元素中取出k个元素的取法数。
每一次从集合中选出一个元素,然后对剩余的集合(n-1)进行一次k-1组合。反向地选取。
1 class Solution { 2 public: 3 vector> combine(int n, int k) { 4 vector r(k, 0); 5 recursive(n, k, r); 6 return ret; 7 } 8 9 void recursive(int n, int k, vector &r) {10 if (k <= 0) {11 ret.push_back(r);12 return;13 }14 for (int i = n; i >= k; --i) {15 r[k - 1] = i;16 recursive(i - 1, k - 1, r);17 }18 }19 20 private:21 vector > ret;22 };
组合的非递归实现:
首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。
然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为 “01”组合,同时将其左边的所有“1”全部移动到数组的最左端。 当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得 到了最后一个组合。 例如求5中选3的组合: 1 1 1 0 0 //1,2,3 1 1 0 1 0 //1,2,4 1 0 1 1 0 //1,3,4 0 1 1 1 0 //2,3,4 1 1 0 0 1 //1,2,5 1 0 1 0 1 //1,3,5 0 1 1 0 1 //2,3,5 1 0 0 1 1 //1,4,5 0 1 0 1 1 //2,4,5 0 0 1 1 1 //3,4,5