数组的排列

dat*_*ili 61 c++ java algorithm

例如,我有这个数组:

int a[] = new int[]{3,4,6,2,1};
Run Code Online (Sandbox Code Playgroud)

我需要所有排列的列表,如果一个像这样{3,2,1,4,6},其他的必须不一样.我知道如果数组的长度是n那么就有n!可能的组合.如何编写这个算法?

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

for(int i=0;i<a.length;i++){
    // code here
}
Run Code Online (Sandbox Code Playgroud)

只是算法.是的,API函数很好,但它对我没有多大帮助.

Yev*_*kiy 121

以下是如何在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您获取数组的第一个元素(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]
Run Code Online (Sandbox Code Playgroud)

(如果您只是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是迭代器的完整代码.构造函数接受一个对象数组,并使用它们将它们映射到整数数组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() {

    }
}
Run Code Online (Sandbox Code Playgroud)

用法/测试:

    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);
Run Code Online (Sandbox Code Playgroud)

打印出所有7!/(2!*3!*2!)=210排列.

  • 首先,我知道答案是 6(来自我的 [3,3,4,4] 示例)。要推导该公式,请将 [3,3,4,4] 视为两个蓝色和两个红色球。问题是有多少种放置球的方式(相同颜色的球是相同的)。如果你以某种方式放置球,那么交换蓝色球(2!种方法)或两个红球(2!种方法)不会改变任何东西。现在,我们有 4 个!放置 4 个球的方法,但排列蓝色球(2!种)或红球(2!种)不会改变球的位置。所以你得到 4!/(2!*2!) 作为最终答案 (2认同)

rek*_*o_t 21

如果您使用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));
Run Code Online (Sandbox Code Playgroud)


小智 17

以下是Java中Permutation的实现:

排列 - Java

你应该检查它!

编辑:下面粘贴的代码,以防止链接死亡:

// Permute.java -- A class generating all permutations

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.lang.reflect.Array;

public class Permute implements Iterator {

   private final int size;
   private final Object [] elements;  // copy of original 0 .. size-1
   private final Object ar;           // array for output,  0 .. size-1
   private final int [] permutation;  // perm of nums 1..size, perm[0]=0

   private boolean next = true;

   // int[], double[] array won't work :-(
   public Permute (Object [] e) {
      size = e.length;
      elements = new Object [size];    // not suitable for primitives
      System.arraycopy (e, 0, elements, 0, size);
      ar = Array.newInstance (e.getClass().getComponentType(), size);
      System.arraycopy (e, 0, ar, 0, size);
      permutation = new int [size+1];
      for (int i=0; i<size+1; i++) {
         permutation [i]=i;
      }
   }

   private void formNextPermutation () {
      for (int i=0; i<size; i++) {
         // i+1 because perm[0] always = 0
         // perm[]-1 because the numbers 1..size are being permuted
         Array.set (ar, i, elements[permutation[i+1]-1]);
      }
   }

   public boolean hasNext() {
      return next;
   }

   public void remove() throws UnsupportedOperationException {
      throw new UnsupportedOperationException();
   }

   private void swap (final int i, final int j) {
      final int x = permutation[i];
      permutation[i] = permutation [j];
      permutation[j] = x;
   }

   // does not throw NoSuchElement; it wraps around!
   public Object next() throws NoSuchElementException {

      formNextPermutation ();  // copy original elements

      int i = size-1;
      while (permutation[i]>permutation[i+1]) i--;

      if (i==0) {
         next = false;
         for (int j=0; j<size+1; j++) {
            permutation [j]=j;
         }
         return ar;
      }

      int j = size;

      while (permutation[i]>permutation[j]) j--;
      swap (i,j);
      int r = size;
      int s = i+1;
      while (r>s) { swap(r,s); r--; s++; }

      return ar;
   }

   public String toString () {
      final int n = Array.getLength(ar);
      final StringBuffer sb = new StringBuffer ("[");
      for (int j=0; j<n; j++) {
         sb.append (Array.get(ar,j).toString());
         if (j<n-1) sb.append (",");
      }
      sb.append("]");
      return new String (sb);
   }

   public static void main (String [] args) {
      for (Iterator i = new Permute(args); i.hasNext(); ) {
         final String [] a = (String []) i.next();
         System.out.println (i);
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

  • +1请将相关代码添加到您的帖子中,以防链接出现故障 (4认同)

Mos*_*our 7

根据维基https://en.wikipedia.org/wiki/Heap%27s_algorithm

\n\n

堆的算法生成 n 个对象的所有可能的排列。它由 BR Heap 于 1963 年首次提出。该算法最大限度地减少了移动:它通过交换一对元素来从前一个排列生成每个排列;其他 n\xe2\x88\x922 个元素不受影响。在 1977 年对排列生成算法的回顾中,Robert Sedgewick 得出的结论是,它是当时计算机生成排列最有效的算法。

\n\n

因此,如果我们想以递归方式执行此操作,Sudo 代码如下。

\n\n
procedure generate(n : integer, A : array of any):\n    if n = 1 then\n          output(A)\n    else\n        for i := 0; i < n - 1; i += 1 do\n            generate(n - 1, A)\n            if n is even then\n                swap(A[i], A[n-1])\n            else\n                swap(A[0], A[n-1])\n            end if\n        end for\n        generate(n - 1, A)\n    end if\n
Run Code Online (Sandbox Code Playgroud)\n\n

java代码:

\n\n
public static void printAllPermutations(\n        int n, int[] elements, char delimiter) {\n    if (n == 1) {\n        printArray(elements, delimiter);\n    } else {\n        for (int i = 0; i < n - 1; i++) {\n            printAllPermutations(n - 1, elements, delimiter);\n            if (n % 2 == 0) {\n                swap(elements, i, n - 1);\n            } else {\n                swap(elements, 0, n - 1);\n            }\n        }\n        printAllPermutations(n - 1, elements, delimiter);\n    }\n}\n\nprivate static void printArray(int[] input, char delimiter) {\n    int i = 0;\n    for (; i < input.length; i++) {\n        System.out.print(input[i]);\n    }\n    System.out.print(delimiter);\n}\n\nprivate static void swap(int[] input, int a, int b) {\n    int tmp = input[a];\n    input[a] = input[b];\n    input[b] = tmp;\n}\n\npublic static void main(String[] args) {\n    int[] input = new int[]{0,1,2,3};\n    printAllPermutations(input.length, input, \',\');\n}\n
Run Code Online (Sandbox Code Playgroud)\n


ami*_*che 5

这是包装在迭代器中的列表的2置换

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/* all permutations of two objects 
 * 
 * for ABC: AB AC BA BC CA CB
 * 
 * */
public class ListPermutation<T> implements Iterator {

    int index = 0;
    int current = 0;
    List<T> list;

    public ListPermutation(List<T> e) {
        list = e;
    }

    public boolean hasNext() {
        return !(index == list.size() - 1 && current == list.size() - 1);
    }

    public List<T> next() {
        if(current == index) {
            current++;
        }
        if (current == list.size()) {
            current = 0;
            index++;
        }
        List<T> output = new LinkedList<T>();
        output.add(list.get(index));
        output.add(list.get(current));
        current++;
        return output;
    }

    public void remove() {
    }

}
Run Code Online (Sandbox Code Playgroud)


Voi*_*oid 5

n!对于给定的数组大小,存在总排列n。这是使用 DFS 用 Ja​​va 编写的代码。

public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> results = new ArrayList<List<Integer>>();
    if (nums == null || nums.length == 0) {
        return results;
    }
    List<Integer> result = new ArrayList<>();
    dfs(nums, results, result);
    return results;
}

public void dfs(int[] nums, List<List<Integer>> results, List<Integer> result) {
    if (nums.length == result.size()) {
        List<Integer> temp = new ArrayList<>(result);
        results.add(temp);
    }        
    for (int i=0; i<nums.length; i++) {
        if (!result.contains(nums[i])) {
            result.add(nums[i]);
            dfs(nums, results, result);
            result.remove(result.size() - 1);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于输入数组[3,2,1,4,6],总共有5个!= 120 种可能的排列,它们是:

[[3,4,6,2,1],[3,4,6,1,2],[3,4,2,6,1],[3,4,2,1,6],[3,4,1,6,2],[3,4,1,2,6],[3,6,4,2,1],[3,6,4,1,2],[3,6,2,4,1],[3,6,2,1,4],[3,6,1,4,2],[3,6,1,2,4],[3,2,4,6,1],[3,2,4,1,6],[3,2,6,4,1],[3,2,6,1,4],[3,2,1,4,6],[3,2,1,6,4],[3,1,4,6,2],[3,1,4,2,6],[3,1,6,4,2],[3,1,6,2,4],[3,1,2,4,6],[3,1,2,6,4],[4,3,6,2,1],[4,3,6,1,2],[4,3,2,6,1],[4,3,2,1,6],[4,3,1,6,2],[4,3,1,2,6],[4,6,3,2,1],[4,6,3,1,2],[4,6,2,3,1],[4,6,2,1,3],[4,6,1,3,2],[4,6,1,2,3],[4,2,3,6,1],[4,2,3,1,6],[4,2,6,3,1],[4,2,6,1,3],[4,2,1,3,6],[4,2,1,6,3],[4,1,3,6,2],[4,1,3,2,6],[4,1,6,3,2],[4,1,6,2,3],[4,1,2,3,6],[4,1,2,6,3],[6,3,4,2,1],[6,3,4,1,2],[6,3,2,4,1],[6,3,2,1,4],[6,3,1,4,2],[6,3,1,2,4],[6,4,3,2,1],[6,4,3,1,2],[6,4,2,3,1],[6,4,2,1,3],[6,4,1,3,2],[6,4,1,2,3],[6,2,3,4,1],[6,2,3,1,4],[6,2,4,3,1],[6,2,4,1,3],[6,2,1,3,4],[6,2,1,4,3],[6,1,3,4,2],[6,1,3,2,4],[6,1,4,3,2],[6,1,4,2,3],[6,1,2,3,4],[6,1,2,4,3],[2,3,4,6,1],[2,3,4,1,6],[2,3,6,4,1],[2,3,6,1,4],[2,3,1,4,6],[2,3,1,6,4],[2,4,3,6,1],[2,4,3,1,6],[2,4,6,3,1],[2,4,6,1,3],[2,4,1,3,6],[2,4,1,6,3],[2,6,3,4,1],[2,6,3,1,4],[2,6,4,3,1],[2,6,4,1,3],[2,6,1,3,4],[2,6,1,4,3],[2,1,3,4,6],[2,1,3,6,4],[2,1,4,3,6],[2,1,4,6,3],[2,1,6,3,4],[2,1,6,4,3],[1,3,4,6,2],[1,3,4,2,6],[1,3,6,4,2],[1,3,6,2,4],[1,3,2,4,6],[1,3,2,6,4],[1,4,3,6,2],[1,4,3,2,6],[1,4,6,3,2],[1,4,6,2,3],[1,4,2,3,6],[1,4,2,6,3],[1,6,3,4,2],[1,6,3,2,4],[1,6,4,3,2],[1,6,4,2,3],[1,6,2,3,4],[1,6,2,4,3],[1,2,3,4,6],[1,2,3,6,4],[1,2,4,3,6],[1,2,4,6,3],[1,2,6,3,4],[1,2,6,4,3]]
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。