最近对数学方面颇有兴趣,周末和同窗去大学蹭课,其中在讲排列组合的时候讲到了全排列的字典序生成算法,我以为这个想法真的挺好,去网上找了找,貌似都是递归求全排列,没有讲到这个算法的,今天我将这个算法写出来了,发在这里,之后学习。算法
非递归方法(字典序法):ide
这种算法被用在了C++的STL库中。学习
对给定的字符集中的字符规定了一个前后关系,在此基础上规定两个全排列的前后是从左到右逐个比较对应的字符的前后。spa
[例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是: 123,132,213,231,312,321
※ 一个全排列可看作一个字符串,字符串可有前缀、后缀。code
生成给定全排列的下一个排列.所谓一个的下一个就是这一个与下一个之间没有其余的。这就要求这一个与下一个有尽量长的共同前缀,也即变化限制在尽量短的后缀上。blog
[例]839647521是1--9的排列。1—9的排列最前面的是123456789,最后面的987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。不然找出第一次出现降低的位置。
【例】 通常而言,设P是[1,n]的一个全排列。 P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn find: j=max{i|Pi<Pi+1}
k=max{i|Pi>Pj} 1, 对换Pj,Pk,
2, 将Pj+1…Pk-1PjPk+1…Pn翻转
P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个
【例】 如何获得346987521的下一个 1,从尾部往前找第一个P(i-1) < P(i)的位置 3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1 最终找到6是第一个变小的数字,记录下6的位置i-1 2,从i位置日后找到最后一个大于6的数 3 4 6 -> 9 -> 8 -> 7 5 2 1 最终找到7的位置,记录位置为m 3,交换位置i-1和m的值 3 4 7 9 8 6 5 2 1 4,倒序i位置后的全部数据 3 4 7 1 2 5 6 8 9 则347125689为346987521的下一个排列
依照上面的讲述不难将代码写出来,以下:排序
private static void PermutationList() { int fromIndex, endIndex, changeIndex; Sort(0, length - 1); do { // 输出一种全排列 Output(); fromIndex = endIndex = length - 1; // 向前查找第一个变小的元素 while (fromIndex > 0 && words[fromIndex] < words[fromIndex - 1]) --fromIndex; changeIndex = fromIndex; if (fromIndex == 0) break; // 向后查找最后一个大于words[fromIndex-1]的元素 while (changeIndex + 1 < length && words[changeIndex + 1] > words[fromIndex - 1]) ++changeIndex; Swap(fromIndex - 1, changeIndex); // 交换两个值 InvertArray(fromIndex, endIndex); // 对后面的全部值进行反向处理 } while (true); }
递归方法求全排列:递归
递归方法很容易理解:分别将每一个位置交换到最前面位,以后全排列剩下的位。字符串
【例】递归全排列 1 2 3 4 5 1,for循环将每一个位置的数据交换到第一位 swap(1,1~5) 2,按相同的方式全排列剩余的位
因为递归方法很容易理解,并且网上也有不少的资料,因此不过多讲述,代码以下:input
/// <summary> /// 递归方式生成全排列的方法 /// </summary> /// <param name="fromIndex">全排列的起始位置</param> /// <param name="endIndex">全排列的终止位置</param> private static void PermutationList(int fromIndex, int endIndex) { if (fromIndex == endIndex) Output(); else { for (int index = fromIndex; index <= endIndex; ++index) { // 此处排序主要是为了生成字典序全排列,不然递归会打乱字典序 Sort(fromIndex, endIndex); Swap(fromIndex, index); PermutationList(fromIndex + 1, endIndex); Swap(fromIndex, index); } } }
以上代码只截取了部分关键代码,全部代码以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Algorithms { class Permutation { private static int MAXLENGTH = 20; private static int length; private static char[] words = new char[MAXLENGTH]; private static void Swap(int indexX, int indexY) { if (indexX != indexY) { char ch = words[indexX]; words[indexX] = words[indexY]; words[indexY] = ch; } } private static void InvertArray(int fromIndex, int endIndex) { for (; fromIndex < endIndex; ++fromIndex, --endIndex) Swap(fromIndex, endIndex); } private static void Sort(int fromIndex, int endIndex) { Array.Sort(words, fromIndex, endIndex - fromIndex + 1); } private static void Output() { for (int index = 0; index < length; ++index) Console.Write(words[index] + " "); Console.WriteLine(); } private static void PermutationList() { int fromIndex, endIndex, changeIndex; Sort(0, length - 1); do { // 输出一种全排列 Output(); fromIndex = endIndex = length - 1; // 向前查找第一个变小的元素 while (fromIndex > 0 && words[fromIndex] < words[fromIndex - 1]) --fromIndex; changeIndex = fromIndex; if (fromIndex == 0) break; // 向后查找最后一个大于words[fromIndex-1]的元素 while (changeIndex + 1 < length && words[changeIndex + 1] > words[fromIndex - 1]) ++changeIndex; Swap(fromIndex - 1, changeIndex); // 交换两个值 InvertArray(fromIndex, endIndex); // 对后面的全部值进行反向处理 } while (true); } /// <summary> /// 递归方式生成全排列的方法 /// </summary> /// <param name="fromIndex">全排列的起始位置</param> /// <param name="endIndex">全排列的终止位置</param> private static void PermutationList(int fromIndex, int endIndex) { if (fromIndex == endIndex) Output(); else { for (int index = fromIndex; index <= endIndex; ++index) { // 此处排序主要是为了生成字典序全排列,不然递归会打乱字典序 Sort(fromIndex, endIndex); Swap(fromIndex, index); PermutationList(fromIndex + 1, endIndex); Swap(fromIndex, index); } } } public static void PermutationTest() { Console.Write("please input the permutation words:"); words = Console.ReadLine().ToCharArray(); length = words.Count(); PermutationList(); Console.ReadLine(); } } }
运行结果: