题目:输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
方法一 : 非递归方法(字典序,这种方法被用在STL库中) 转载自:转载博客
对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。
[例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是: 123,132,213,231,312,321
一个全排列可看做一个字符串,字符串可有前缀、后缀。生成给定全排列的下一个排列:所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀。 例如:如何得到346987521的下一个1,从尾部往前找第一个P(i-1) < P(i)的位置,最终找到6是第一个,记录下6的位置i-1 3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1 2,从i位置往后找到最后一个大于6的数,最终找到7的位置,记录位置为m 3 4 6 -> 9 -> 8 -> 7 5 2 1 3,交换位置i-1和m的值 : 3 4 7 9 8 6 5 2 1 4,倒序i-1位置后的所有数据: 3 4 7 1 2 5 6 8 9
bool cmp(char a, char b){ if (a < b) return true; else return false; } void print(char *str, int len) { if (str == NULL) return ; sort(str, str + len, cmp); // 按某种规则排序 while (1) { printf("%s \n", str); // 一定要先打印,不然排序后的第一个序列打印不了 int first = len - 1; while (first > 0 && str[first] < str[first - 1]) --first; // 倒序找第一个比后面小的数字 if (first == 0) break; // 如果倒序结束,都没有找到,说明序列完全递减,结束 int minBigger = first; // 从first位置出发,找比first-1大的数字里面的最小的 while (minBigger + 1 < len && str[minBigger + 1] > str[first - 1]) ++minBigger; swap(str, minBigger, first - 1); // 交换两个值 reverse(str + first, str + len); // 倒置 } }方法2:递归 递归全排列 1 2 3 4 5 1,for循环将每个位置的数据交换到第一位 swap(1,1~5) 2,按相同的方式全排列剩余的位
关于循环里面有递归可以这么想:在最外层的需求是要把当前序列的第一个数和后面的每一个都交换,显然递归是做不到的,只能用循环。在交换之后,要对除了当前子序列的第一个元素外的剩余元素做全排列,这显然是递归过程。两者是分层的嵌套关系
void prem(char *str, int start, int end) { if (start >= end) // 起始位置等于终止位置,自然不往下递归交换了,结束并输出 printf("%s \n", str); else { for (int i = start; i <= end; ++i) { // 把起始位置和后面的位置依此交换 // sort(str + i, str + end + 1,cmp); 如果要按某种顺序输出,这里排序 swap(str, i, start); prem(str, start + 1, end); // 递归下一个位置 swap(str, i, start); } } } void premation(char *str, int len) { // 主调函数 if (str == NULL) return; prem(str, 0, len - 1); // 全排列的起始位置,终止位置 }扩展: 求组合数(子集)
vector< vector<char> > zuhe(char *str) { // 主调函数,返回二维list 存储所有子集 if (str == NULL) return NULL; vector< vector<char> > ans ; vector <char> t; // 存储当前遍历到的一个子集 DFS(str, 0, t, ans); return ans; } void DFS(char* str, int index, vector <char> &t, vector< vector<char> > &ans) { ans.push_back(t); // 第一次调用,存的空集 for (int j = index; j < strlen(str); ++j) { t.push_back(str[j]); // 当前数字加入当前子集中 DFS(str, j + 1, t, ans); // 递归下一个位置的数 t.pop_back(); // 回溯,当前子集舍弃当前数 } }扩展: 1. 8个数放正方体的8个顶点,要求任意一个面的4个顶点和相等 2. 8皇后问题,8个皇后放在8*8棋盘,不能同行,同列,或者对角线 P200