阅读之前的忠告:程序在看的时候一定要自己在草稿纸上画一画,理解了就好写了
无重复项的全排列
1、一个数的全排列,如排列{1},就是这个数本身这一种情况
2、两个数的全排列,如排列{1,2}:
第一步:将{1}放在第零个位置,剩下的{2}进行一个数的全排列,结果为{1,2}
第二步:将{2}放在第零个位置,剩下的{1}进行一个数的全排列,结果为{2,1}
即两个数的全排列为以上2种情况。
3、三个数的全排列,如排列{1,2,3}:
第一步:将{1}放在第零个位置,剩下的{2,3}进行两个数的全排列,结果为{1,2,3} {1,3,2}
第二步:将{2}放在第零个位置,剩下的{1,3}进行两个数的全排列,结果为{2,1,3} {2,3,1}
第三步:将{3}放在第零个位置,剩下的{1,2}进行两个数的全排列,结果为{3,1,2} {3,2,1}
即三个数的全排列为以上6种情况。
4、即m个数(无重)的全排列,就是将m个数分别放在第零个位置,再将剩下的m-1个数的全排列加在后面,当m-1=1时为递归的出口。
实现方式一:
1 //无重复项 2 //arr为待排列的数组 3 //n标记数组当前位置,即n=0时,对arr[0]到arr[length-1]全排列;n=1时,对arr[1]到arr[length-1]全排列...以此类推,length为数组长度 4 private static void test(int[] arr,int n){ 5 int length = arr.length; 6 if(n>=length-1){//当n定位到最后一个数的时候,即递归出口 7 for(int i:arr){ 8 System.out.print(i); 9 } 10 System.out.println(); 11 }else{ 12 for(int i=n;i<length;i++){//将length-n个数分别放在n~length-1位置,(length-n)对应需要全排列数的数目 13 {int temp = arr[n]; 14 arr[n] = arr[i]; 15 arr[i] = temp;}//以交换位置来实现 16 test(arr,n+1);//对剩下的length-1-n个数全排列 17 {int temp = arr[n]; 18 arr[n] = arr[i]; 19 arr[i] = temp;}//恢复原来的顺序,进行下一次交换 20 } 21 } 22 } 23 24 public static void main(String args[]){ 25 int[] arr = {1,2,3,4}; 26 test(arr,0); 27 }
运行结果:
实现方式二(stack实现):
1 public static void main(String args[]){ 2 perm(new int[]{1,1,3,4},new Stack<Integer>()); 3 } 4 5 private static void perm(int[] array, Stack<Integer> stack) { 6 if(array.length <= 0) { 7 //进入了叶子节点,输出栈中内容 8 System.out.println(stack); 9 } else { 10 for(int i = 0; i < array.length; i++) { 11 //tmepArray是一个临时数组,用于就是Ri 12 //1,2,3,4的全排列,先取出1,那么这时tempArray中就是2,3,4 13 int[] tempArray = new int[array.length-1]; 14 System.arraycopy(array,0,tempArray,0,i); 15 System.arraycopy(array,i+1,tempArray,i,array.length-1-i); 16 stack.push(array[i]); 17 perm(tempArray,stack); 18 stack.pop(); 19 } 20 } 21 }
运行结果:
有重复项的全排列
与上一种无重复项排列基本思路一致。只不过需要保证每次结果不重复。就是需要添加一个判断,判断每次把一个数放到那个位置之前有没有相同的数已经放过那个位置了,如果有,不必重复放置。
程序如下:
1 private static void test2(int[] arr,int n){ 2 int length = arr.length; 3 List<Integer> exis = new ArrayList<Integer>();//存放已经有过的数字 4 if(n>=length-1){ 5 for(int i:arr){ 6 System.out.print(i); 7 } 8 System.out.println(); 9 }else{ 10 for(int i=n;i<length;i++){ 11 if(!exis.contains(arr[i])){ //如果arr[i]不在exis中,则可以把arr[i]放在那个位置 12 exis.add(arr[i]); //将arr[i]放进exis 13 { 14 int temp = arr[n]; 15 arr[n] = arr[i]; 16 arr[i] = temp; 17 } 18 test2(arr,n+1); 19 { 20 int temp = arr[n]; 21 arr[n] = arr[i]; 22 arr[i] = temp; 23 } 24 } 25 } 26 } 27 } 28 29 public static void main(String args[]){ 30 int[] arr = {1,1,2,2}; 31 test2(arr,0); 32 }
运行结果:
列出数组的所有子集
利用了状态压缩
关于状态压缩推荐文章:https://blog.csdn.net/u011077606/article/details/43487421
1 public static void f(int[] a) { 2 int length = a.length; 3 for(int i = 1; i < (1 << length); i++) { 4 for(int j = 0; j < length; j++) { 5 if((i & (1 << j)) > 0) { 6 System.out.print(a[j] + " "); 7 } 8 } 9 System.out.println(); 10 } 11 } 12 13 public static void main(String args[]){ 14 int[] a = {1,2,3,4,5}; 15 f(a); 16 }
代码结果:
帮助理解添加了一些代码:
public static void f(int[] a) { int len = a.length; for(int i = 1; i < (1 << len); i++) { //1 << len指的是将1左移五位=100000=32 System.out.print("i的值"+i+" "); for(int j = 0; j < len; j++) { System.out.print("按位与"+(i & (1 << j))+" "); if((i & (1 << j)) > 0) { System.out.print(a[j] + " "); } } System.out.println(); } } public static void main(String args[]){ int[] a = {1,2,3,4,5}; System.out.println(1 << a.length); f(a); }
上面代码运行结果,看这个结果会更好理解一些: