算法-全排列问题
2014-12-22 13:43
176 查看
背景-有一个字符串数组abcd,求这是个字母的所有排列.
有数学知识可知全排列的结果为n!
那么用程序如何列出这些排列呢?
1.递归
我们知道手工排列的话,肯定是按照顺序一步一变的.
我们用(a,b)表示ab的全排列
那么(a,b)=a(b)+b(a);
同理:
(a,b,c)=a(b,c)+b(a,c)+c(a,b)
于是递归思想就出现了,具体参看下列算法(java版):
----------------------
本算法有一个很巧妙的做法就是,将下一个前缀提到序列的首部,不参与递归.完成后再还原,提取下一个前缀.
递归的方法虽然很好理解,但是在序列很长时非常耗时,因为额外需要使用栈.
下面介绍非递归的方法.
=======================================
2.
我先描述可以重复的情形,最后去掉重复就成全排列了,客官可要耐心一点.
引子:
考虑一种情况,有三个字符a b c ,要求这三个字符的有顺序的全部组合,可以重复
例如aaa,aab,aac ,...
很容易知道,每列均有3种字母可写,一共的组合是3*3*3=27种
用程序的迭代来看,一共有三列,每列有三种情形.把每列看做一层大循环,每个大循环内部有三个小循环.
推论:已知字符序列长度为len,那么一共有len层大循环和len层小循环.
我们用字符数组chs表示原始序列abcdefg...一共是len个字符.
对于每一层我们不可能都列出来,所以用一个数组表示:
line[len] :每一个元素表示一列,其值表示这一列进行到了第几次循环,同时它还表示这一列的取哪个字符(每一次小循环对应取一个不同的字符).
对应: line中的每一个元素对应一个大循环,它的值对应大循环内部的第几次小循环.
啊!你别说了!我晕了!!!!好吧,我们手工写一下,更直观
字符序列 对应数组值
aaa line[0]=0,line[1]=0,line[2]=0
aab line[0]=0,line[1]=0,line[2]=1
aac line[0]=0,line[1]=0,line[2]=2
aba line[0]=0,line[1]=1,line[2]=0
abb line[0]=0,line[1]=1,line[2]=1
abc line[0]=0,line[1]=1,line[2]=2
看懂了没?好了,如何表示循环层数理解了,那么如何控制它们循环呢?
很简单的问题,我们用一个循环控制整个程序的流程,每循环一次后,当前列的值+1,当值>=len时,就要退出当前列的小循环了.和for循环是一样的.例如line[2]=3时,line[1]必须+1,同时line[2]要置0
这里需要用一个变量表示正在执行大循环的是第几列.就用currentLine表示吧
问题,什么时候程序该结束了??
当然是第一列的循环执行完毕了,即line[0]>len-1
好了,废话多,搞个程序运行一下呗!
char[] chs=new char[]{'1','2','3','4','5'};
运行permutation(chs,3);
num=27;
-------------------------
看了这么多,没解决实际问题啊!不允许重复怎么办??
只要理解了上面的方法,这个问题就很简单了.
首先:为什么会重复??
因为前面有一列已经取走了pos的值,当前列还会取.检查一下当前pos值有没有被取过不就行了
代码:
//假设没有取过
find=false;
for(int i=0;i<len;i++){
if(pos+1==line[len]){
find=true;
break;
}
}
只要取过,就进行下一次循环,否则就赋值.下面的最终的代码.用一个变量repeat表示是否考虑重复问题
char[] chs=new char[]{'1','2','3','4','5'};
运行permutation(chs,3,true);
num=6;
-------------------------
运行方式:
有数学知识可知全排列的结果为n!
那么用程序如何列出这些排列呢?
1.递归
我们知道手工排列的话,肯定是按照顺序一步一变的.
我们用(a,b)表示ab的全排列
那么(a,b)=a(b)+b(a);
同理:
(a,b,c)=a(b,c)+b(a,c)+c(a,b)
于是递归思想就出现了,具体参看下列算法(java版):
static int pc=0;//统计总数 public static void permutation(char[] chs,int low,int high){ int i,j; //枚举完毕 if(low==high){pc++; ombination(chs,9,value); /*for(i=0;i<high;i++){ System.out.print(chs[i]); } System.out.println();*/ }else{ //提取前缀,递归子序列 for(j=low;j<=high;j++){ swap(chs,low,j); permutation(chs,low+1,high); swap(chs,low,j); } } } public static void swap(char[] chs,int i,int j){ char tmp=chs[i]; chs[i]=chs[j]; chs[j]=tmp; }
----------------------
本算法有一个很巧妙的做法就是,将下一个前缀提到序列的首部,不参与递归.完成后再还原,提取下一个前缀.
递归的方法虽然很好理解,但是在序列很长时非常耗时,因为额外需要使用栈.
下面介绍非递归的方法.
=======================================
2.
我先描述可以重复的情形,最后去掉重复就成全排列了,客官可要耐心一点.
引子:
考虑一种情况,有三个字符a b c ,要求这三个字符的有顺序的全部组合,可以重复
例如aaa,aab,aac ,...
很容易知道,每列均有3种字母可写,一共的组合是3*3*3=27种
用程序的迭代来看,一共有三列,每列有三种情形.把每列看做一层大循环,每个大循环内部有三个小循环.
推论:已知字符序列长度为len,那么一共有len层大循环和len层小循环.
我们用字符数组chs表示原始序列abcdefg...一共是len个字符.
对于每一层我们不可能都列出来,所以用一个数组表示:
line[len] :每一个元素表示一列,其值表示这一列进行到了第几次循环,同时它还表示这一列的取哪个字符(每一次小循环对应取一个不同的字符).
对应: line中的每一个元素对应一个大循环,它的值对应大循环内部的第几次小循环.
啊!你别说了!我晕了!!!!好吧,我们手工写一下,更直观
字符序列 对应数组值
aaa line[0]=0,line[1]=0,line[2]=0
aab line[0]=0,line[1]=0,line[2]=1
aac line[0]=0,line[1]=0,line[2]=2
aba line[0]=0,line[1]=1,line[2]=0
abb line[0]=0,line[1]=1,line[2]=1
abc line[0]=0,line[1]=1,line[2]=2
看懂了没?好了,如何表示循环层数理解了,那么如何控制它们循环呢?
很简单的问题,我们用一个循环控制整个程序的流程,每循环一次后,当前列的值+1,当值>=len时,就要退出当前列的小循环了.和for循环是一样的.例如line[2]=3时,line[1]必须+1,同时line[2]要置0
这里需要用一个变量表示正在执行大循环的是第几列.就用currentLine表示吧
问题,什么时候程序该结束了??
当然是第一列的循环执行完毕了,即line[0]>len-1
好了,废话多,搞个程序运行一下呗!
static int num=0;//统计组合数 public static void permutation(char[] chs,int n){ //用一个临时字符数组保存组合 char[] tmp=new char ; int[] line=new int ; int len=n; int currentLine=0; int pos=0;//当前列要取那个值 while(true){ //结束条件 if(currentLine==0&&line[currentLine]==len)break; pos=line[currentLine]; if(currentLine<len-1){ //继续循环 if(pos<len){ //保存值 tmp[currentLine]=chs[pos]; //进行当前列的下一次循环 line[currentLine]++; //循环下一列 currentLine++; }else{ //该返回了 line[currentLine]=0; currentLine--; } //循环完了,返回 }else{ //到了最后一列,每次小循环都是一种组合 if(pos<len){ tmp[currentLine]=chs[pos]; line[currentLine]++; //注意这里,没有下一列了**** //打印出组合 for(int i=0;i<len;i++){ System.out.print(tmp[i]); } System.out.println(); num++; }else{ //该返回了 line[currentLine]=0; currentLine--; } } } }
char[] chs=new char[]{'1','2','3','4','5'};
运行permutation(chs,3);
num=27;
-------------------------
看了这么多,没解决实际问题啊!不允许重复怎么办??
只要理解了上面的方法,这个问题就很简单了.
首先:为什么会重复??
因为前面有一列已经取走了pos的值,当前列还会取.检查一下当前pos值有没有被取过不就行了
代码:
//假设没有取过
find=false;
for(int i=0;i<len;i++){
if(pos+1==line[len]){
find=true;
break;
}
}
只要取过,就进行下一次循环,否则就赋值.下面的最终的代码.用一个变量repeat表示是否考虑重复问题
static int num=0;//统计组合数 public static void permutation(char[] chs,int n,boolean repeat){ //用一个临时字符数组保存组合 char[] tmp=new char ; int[] line=new int ; int len=n; int currentLine=0; int pos=0;//当前列要取那个值 boolean find; while(true){ //结束条件 if(currentLine==0&&line[currentLine]==len)break; pos=line[currentLine]; if(currentLine<len-1){ //继续循环 if(pos<len){ //检查重复 find=false; if(repeat){ for(int i=0;i<len;i++){ if(pos+1==line[i]){ find=true; break; } } } if(find){ //进行当前列的下一次循环 line[currentLine]++; continue; } //保存值 tmp[currentLine]=chs[pos]; //进行当前列的下一次循环 line[currentLine]++; //循环下一列 currentLine++; }else{ //该返回了 line[currentLine]=0; currentLine--; } //循环完了,返回 }else{ //到了最后一列,每次小循环都是一种组合 if(pos<len){ //检查重复 find=false; if(repeat){ for(int i=0;i<len;i++){ if(pos+1==line[i]){ find=true; break; } } } if(find){ //进行当前列的下一次循环 line[currentLine]++; continue; } tmp[currentLine]=chs[pos]; line[currentLine]++; //注意这里,没有下一列了**** //打印出组合 for(int i=0;i<len;i++){ System.out.print(tmp[i]); } System.out.println(); num++; }else{ //该返回了 line[currentLine]=0; currentLine--; } } } }
char[] chs=new char[]{'1','2','3','4','5'};
运行permutation(chs,3,true);
num=6;
-------------------------
运行方式:
public static void main(String[] args) { char[] chs=new char[]{'1','2','3','4','5'}; //递归法 //permutation(chs,0,chs.length-1); //考虑重复 permutation(chs,5,true); //不考虑重复 //permutation(chs,5,false); System.out.println(num); }
相关文章推荐
- 算法之排列问题
- 【算法分析与设计】全排列问题
- 算法java实现--回溯法--圆排列问题--排列树
- c语言实现排列组合算法问题
- 0002算法笔记——【递归】排列问题,整数划分问题,Hanoi问题
- 排列组合问题的通用算法
- 【算法设计与分析】递归与分治----2.4 排列问题
- c语言实现排列组合算法问题
- 0039算法笔记——【分支限界法】电路板排列问题
- 算法实现(2)有重复元素的排列问题
- 求全排列问题的算法
- 算法题-排列组合问题
- 排列组合问题的通用算法
- 算法 全排列问题,组合问题,子集问题
- 全排列算法递归及STL实现,八皇后问题
- 排列组合之排列问题的算法实现
- 恶补算法与数据结构(一)——排列问题
- 〖編程·C++〗回溯算法:排列树 - 工作分配问题
- 数据结构与算法问题 [NOIP2001]求先序排列
- c语言实现排列组合算法问题