例如我有这个数组:
int a[] = new int[]{3,4,6,2,1};
我需要列出所有排列,以便如果一个像这样{3,2,1,4,6},则其他排列一定不能相同。我知道,如果数组的长度为n,那么就有n!可能的组合。该算法如何编写?
{3,2,1,4,6}
更新:谢谢,但是我需要一个伪代码算法,例如:
for(int i=0;i<a.length;i++){ // code here }
只是算法。是的,API函数很好,但是对我没有太大帮助。
如果使用的是C ++,则可以std::next_permutation从头<algorithm>文件使用:
<algorithm>
int a[] = {3,4,6,2,1}; int size = sizeof(a)/sizeof(a[0]); std::sort(a, a+size); do { // print a's elements } while(std::next_permutation(a, a+size));
这是您可以在10行代码中打印所有排列的方法:
public class Permute{ static void permute(java.util.List<Integer> arr, int k){ for(int i = k; i < arr.size(); i++){ java.util.Collections.swap(arr, i, k); permute(arr, k+1); java.util.Collections.swap(arr, k, i); } if (k == arr.size() -1){ System.out.println(java.util.Arrays.toString(arr.toArray())); } } public static void main(String[] args){ Permute.permute(java.util.Arrays.asList(3,4,6,2,1), 0); } }
您获取数组的第一个元素(k = 0),并将其与数组的任何元素(i)交换。然后,从第二个元素开始对数组递归应用置换。这样,您可以从第i个元素开始获得所有排列。棘手的部分是,在递归调用之后,您必须将第i个元素与第一个元素交换回去,否则您可能会在第一个位置获得重复的值。通过将其交换回来,我们可以恢复元素的顺序(基本上,您可以进行回溯)。
迭代器和扩展到重复值的情况
先前算法的缺点是它是递归的,并且在迭代器中不能很好地发挥作用。另一个问题是,如果您在输入中允许重复的元素,那么它将无法按原样工作。
例如,给定输入[3,3,4,4],所有可能的排列(无重复)为
[3,3,4,4]
[3, 3, 4, 4] [3, 4, 3, 4] [3, 4, 4, 3] [4, 3, 3, 4] [4, 3, 4, 3] [4, 4, 3, 3]
(如果permute仅从上方应用函数,您将得到[3,3,4,4]的四倍,在这种情况下,这自然不是您想要看到的;这样的排列数为4!/(2! * 2!)= 6)
permute
可以修改上面的算法来处理这种情况,但是看起来不太好。幸运的是,有一个更好的算法(我在这里找到了它)可以处理重复的值,并且不是递归的。
首先要注意的是,通过以任意顺序枚举整数,可以将任何对象的数组置换减少为整数的置换。
要获取整数数组的排列,请从以升序排序的数组开始。您的“目标”是使其下降。为了生成下一个排列,您尝试从序列未能降序的底部开始寻找第一个索引,并在该尾巴的其余部分从降序转换为升序的同时提高该索引的值。
这是算法的核心:
//ind is an array of integers for(int tail = ind.length - 1;tail > 0;tail--){ if (ind[tail - 1] < ind[tail]){//still increasing //find last element which does not exceed ind[tail-1] int s = ind.length - 1; while(ind[tail-1] >= ind[s]) s--; swap(ind, tail-1, s); //reverse order of elements in the tail for(int i = tail, j = ind.length - 1; i < j; i++, j--){ swap(ind, i, j); } break; } }
这是迭代器的完整代码。构造函数接受一个对象数组,并使用将它们映射为整数数组HashMap。
import java.lang.reflect.Array; import java.util.*; class Permutations<E> implements Iterator<E[]>{ private E[] arr; private int[] ind; private boolean has_next; public E[] output;//next() returns this array, make it public Permutations(E[] arr){ this.arr = arr.clone(); ind = new int[arr.length]; //convert an array of any elements into array of integers - first occurrence is used to enumerate Map<E, Integer> hm = new HashMap<E, Integer>(); for(int i = 0; i < arr.length; i++){ Integer n = hm.get(arr[i]); if (n == null){ hm.put(arr[i], i); n = i; } ind[i] = n.intValue(); } Arrays.sort(ind);//start with ascending sequence of integers //output = new E[arr.length]; <-- cannot do in Java with generics, so use reflection output = (E[]) Array.newInstance(arr.getClass().getComponentType(), arr.length); has_next = true; } public boolean hasNext() { return has_next; } /** * Computes next permutations. Same array instance is returned every time! * @return */ public E[] next() { if (!has_next) throw new NoSuchElementException(); for(int i = 0; i < ind.length; i++){ output[i] = arr[ind[i]]; } //get next permutation has_next = false; for(int tail = ind.length - 1;tail > 0;tail--){ if (ind[tail - 1] < ind[tail]){//still increasing //find last element which does not exceed ind[tail-1] int s = ind.length - 1; while(ind[tail-1] >= ind[s]) s--; swap(ind, tail-1, s); //reverse order of elements in the tail for(int i = tail, j = ind.length - 1; i < j; i++, j--){ swap(ind, i, j); } has_next = true; break; } } return output; } private void swap(int[] arr, int i, int j){ int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } public void remove() { } }
用法/测试:
TCMath.Permutations<Integer> perm = new TCMath.Permutations<Integer>(new Integer[]{3,3,4,4,4,5,5}); int count = 0; while(perm.hasNext()){ System.out.println(Arrays.toString(perm.next())); count++; } System.out.println("total: " + count);
打印出所有7!/(2!*3!*2!)=210排列。
7!/(2!*3!*2!)=210