Permutation of array

后端 未结 11 1024
我寻月下人不归
我寻月下人不归 2020-11-22 07:56

For example I have this array:

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

I need list of all permutations such that if one is like this, {3,2,1

11条回答
  •  粉色の甜心
    2020-11-22 08:12

    Here is how you can print all permutations in 10 lines of code:

    public class Permute{
        static void permute(java.util.List 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);
        }
    }
    

    You take first element of an array (k=0) and exchange it with any element (i) of the array. Then you recursively apply permutation on array starting with second element. This way you get all permutations starting with i-th element. The tricky part is that after recursive call you must swap i-th element with first element back, otherwise you could get repeated values at the first spot. By swapping it back we restore order of elements (basically you do backtracking).

    Iterators and Extension to the case of repeated values

    The drawback of previous algorithm is that it is recursive, and does not play nicely with iterators. Another issue is that if you allow repeated elements in your input, then it won't work as is.

    For example, given input [3,3,4,4] all possible permutations (without repetitions) are

    [3, 3, 4, 4]
    [3, 4, 3, 4]
    [3, 4, 4, 3]
    [4, 3, 3, 4]
    [4, 3, 4, 3]
    [4, 4, 3, 3]
    

    (if you simply apply permute function from above you will get [3,3,4,4] four times, and this is not what you naturally want to see in this case; and the number of such permutations is 4!/(2!*2!)=6)

    It is possible to modify the above algorithm to handle this case, but it won't look nice. Luckily, there is a better algorithm (I found it here) which handles repeated values and is not recursive.

    First note, that permutation of array of any objects can be reduced to permutations of integers by enumerating them in any order.

    To get permutations of an integer array, you start with an array sorted in ascending order. You 'goal' is to make it descending. To generate next permutation you are trying to find the first index from the bottom where sequence fails to be descending, and improves value in that index while switching order of the rest of the tail from descending to ascending in this case.

    Here is the core of the algorithm:

    //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;
        }
    }
    

    Here is the full code of iterator. Constructor accepts an array of objects, and maps them into an array of integers using HashMap.

    import java.lang.reflect.Array;
    import java.util.*;
    class Permutations implements  Iterator{
    
        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 hm = new HashMap();
            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() {
    
        }
    }
    

    Usage/test:

        TCMath.Permutations perm = new TCMath.Permutations(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);
    

    Prints out all 7!/(2!*3!*2!)=210 permutations.

提交回复
热议问题