一尘不染

Java数组置换

java

例如我有这个数组:

int a[] = new int[]{3,4,6,2,1};

我需要列出所有排列,以便如果一个像这样{3,2,1,4,6},则其他排列一定不能相同。我知道,如果数组的长度为n,那么就有n!可能的组合。该算法如何编写?

更新:谢谢,但是我需要一个伪代码算法,例如:

for(int i=0;i<a.length;i++){
    // code here
}

只是算法。是的,API函数很好,但是对我没有太大帮助。


阅读 532

收藏
2020-03-01

共2个答案

一尘不染

如果使用的是C ++,则可以std::next_permutation从头<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));
2020-03-01
一尘不染

这是您可以在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, 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)

可以修改上面的算法来处理这种情况,但是看起来不太好。幸运的是,有一个更好的算法(我在这里找到了它)可以处理重复的值,并且不是递归的。

首先要注意的是,通过以任意顺序枚举整数,可以将任何对象的数组置换减少为整数的置换。

要获取整数数组的排列,请从以升序排序的数组开始。您的“目标”是使其下降。为了生成下一个排列,您尝试从序列未能降序的底部开始寻找第一个索引,并在该尾巴的其余部分从降序转换为升序的同时提高该索引的值。

这是算法的核心:

//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排列。

2020-03-01