以递归方式生成列表的所有可能排列

时间:2012-04-24 20:00:11

标签: java algorithm permutation

我正在尝试以递归方式递归生成列表中的所有项目。我已经看到了类似问题的一些解决方案,但我无法让我的代码工作。有人可以指出我如何修复我的代码吗?

这对所有S / O'ers开放,而不仅仅是Java用户。

(另外我应该注意它因SO异常而崩溃)。

示例输入: [1,2,3]

输出: [1,2,3] [1,3,2] [2,1,3] [2,3,1] [3,1,2] [3,2,1]

//allPossibleItems is an AL of all items 

//this is called with generatePerm(null, new ArrayList<Item>);

private void generatePerm(Item i, ArrayList<Item> a) {
      if(i != null) { a.add(i); }
      if (a.size() == DESIRED_SIZE){
          permutations.add(a);
          return;
      }
      for(int j = 0; j < allPossibleItems.size(); j ++) {
          if(allPossibleItems.get(j) != i)
            generatePerm(allPossibleItems.get(j), a);
      }
  }

7 个答案:

答案 0 :(得分:21)

如果allPossibleItems包含两个不同的元素x和y,那么您将x和y连续写入列表,直到它达到DESIRED_SIZE。那是你真正想要的吗?如果你选择DESIRED_SIZE足够大,你将在堆栈上有太多的递归调用,因此SO异常。

我要做的是(如果原版没有重复/重复):

  public <E> List<List<E>> generatePerm(List<E> original) {
     if (original.size() == 0) {
       List<List<E>> result = new ArrayList<List<E>>(); 
       result.add(new ArrayList<E>()); 
       return result; 
     }
     E firstElement = original.remove(0);
     List<List<E>> returnValue = new ArrayList<List<E>>();
     List<List<E>> permutations = generatePerm(original);
     for (List<E> smallerPermutated : permutations) {
       for (int index=0; index <= smallerPermutated.size(); index++) {
         List<E> temp = new ArrayList<E>(smallerPermutated);
         temp.add(index, firstElement);
         returnValue.add(temp);
       }
     }
     return returnValue;
   }

答案 1 :(得分:2)

问题是在进行递归调用之前必须clone ArrayList。否则,您将始终添加到相同的ArrayList。

//allPossibleItems is an AL of all items 

//this is called with generatePerm(null, new ArrayList<Item>);

private void generatePerm(Item i, ArrayList<Item> a) {
      if(i != null) { a.add(i); }
      if (a.size() == DESIRED_SIZE){
          permutations.add(a);
          return;
      }
      for(int j = 0; j < allPossibleItems.size(); j ++) {
          if(!a.contains(allPossibleItems.get(j))){
            ArrayList<Item> b = clone(a);
            generatePerm(allPossibleItems.get(j), b);
          }
      }
  }

答案 2 :(得分:1)

谷歌搜索引导我到这个问题。我发现下面的方法比其他方法更快。

基本上我使用Set来递归生成排列。为了说明,第一个位置可以包含所有可能的值,第二个位置除了第一个值之外的所有可能值,依此类推。当我们到达最后一个位置时,只有一种可能性。

就递归函数的参数而言,(1)我们传递已经记录为currenttring的内容。 (2)我们传递保存结果的Arraylist - list_of_permutes(3)我们通过set来选择当前的数字 - currentnums。 在最后一级,我们有一个完整的排列,然后将其添加到arraylist - list_of_permutes中,然后向上返回。

public static ArrayList permute_array(int[] arr){
    Set<Integer> currentnums = new HashSet<>();
    for (int i = 0; i < arr.length; i++) {currentnums.add(arr[i]);}
    ArrayList permutations = new ArrayList();
    recurse_nums(currentnums,"",permutations);
    return permutations;
}

可以通过以下方式调用:

Sub CopyFIN() 'copies FIN from account above if E is empty and AJ is anything other than empty

Dim lr As Long
Dim rcell As Range
Dim col As Range

Application.ScreenUpdating = False

lr = Cells(Rows.Count, 6).End(xlUp).Row
Set col = Range("E12:E" & lr)
Set col2 = Range("AJ12:AJ" & lr)

    For Each rcell In col2
        If rcell.Value <> "" Then
        End If
    Next

    For Each rcell In col
        If rcell.Value = "" Then
            rcell.Offset(-1, 0).Copy rcell
        End If
    Next

Application.ScreenUpdating = True

End Sub

答案 3 :(得分:0)


private List generatePerm(List a, int depth) {
   // this is the method definition you want
   // to generate all permutations, you need cycle thru all elements in your list and for each element
   // add that element to each member of generatePerm(a, depth - 1);
   // if you want combinations, you need to remove the element and then call
   /// generateCombinations with the remaining list 
}

答案 4 :(得分:0)

您可以固定起点,然后继续交换。这是最容易理解的方法之一。

public class PermutationListRecursion {

    private Set<List<Integer>> permList = new HashSet<>();

    public static void main(String[] args) {
        PermutationListRecursion pt = new PermutationListRecursion();
        Integer[] nums = { 1, 2, 3 };
        pt.permute(nums);
        System.out.println(pt.permList);
    }

    public void permute(Integer[] nums) {
        permutation(0, nums.length - 1, nums);
    }

    public void permutation(int start, int end, Integer[] nums) {
        if (start == end) {
            permList.add(new ArrayList<Integer>(Arrays.asList(nums)));
        }
        for (int i = start; i <= end; i++) {
            permList.add(swap(nums, start, i));
            permutation(start + 1, end, nums);
            permList.add(swap(nums, start, i));
        }
    }

    private List<Integer> swap(Integer[] arr, int a, int b) {
        if (a == b) {
            return new ArrayList<>(Arrays.asList(arr));
        }
        Integer temp = arr[b];
        arr[b] = arr[a];
        arr[a] = temp;
        return new ArrayList<>(Arrays.asList(arr));
    }
}

答案 5 :(得分:0)

我调查了此线程,并分析了正确的解决方案。不幸的是,我需要为大量输入使用此递归,这将导致创建很多不需要存储的对象,我想对每个排列应用一个方法,并仅保留那些满足我的算法的对象,因此我想到了这个方法解。希望对别人有帮助。

public static <E> void iteratePermutations(List<E> original, Consumer<List<E>> consumer) {
    Objects.requireNonNull(original);
    consumer.accept(original);
    iteratePermutationsRecursively(original, 0, consumer);
}

public static <E> void iteratePermutationsRecursively(List<E> original, int start, Consumer<List<E>> consumer) {
    Objects.requireNonNull(original);
    for (int i = start; i < original.size() - 1; i++) {
        for (int j = i + 1; j < original.size(); j++) {
            List<E> temp = new ArrayList<>(original);
            E tempVal = temp.get(i);
            temp.set(i, temp.get(j));
            temp.set(j, tempVal);
            consumer.accept(temp);
            iteratePermutationsRecursively(temp, i + 1, consumer);
        }
    }
}

我可以这样称呼:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<List<Integer>> result = new ArrayList<>();
iteratePermutations(list, result::add);

或:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
iteratePermutations(list, System.out::println);

答案 6 :(得分:0)

map 和 reduce 方法

  • 输入列表,可能包含重复项。

    List<String> list = Arrays.asList("?", "?", "?");
    
  • map 方法将列表的每个元素表示为一个置换映射列表

    1: [{0=?}, {1=?}, {2=?}]
    2: [{0=?}, {1=?}, {2=?}]
    3: [{0=?}, {1=?}, {2=?}]
    
  • reduce 方法按顺序对这些列表对求和,并将映射对连接成一个排列映射列表

    {0=?, 1=?, 2=?}
    {0=?, 2=?, 1=?}
    {1=?, 0=?, 2=?}
    {1=?, 2=?, 0=?}
    {2=?, 0=?, 1=?}
    {2=?, 1=?, 0=?}
    

Try it online!

public static void main(String[] args) {
    // input list
    List<String> list = Arrays.asList("?", "?", "?");
    // possible permutations
    List<Map<Integer, String>> pp = possiblePermutations(list);
    // output
    pp.forEach(System.out::println);
}
/**
 * @param list the input list, may contain duplicates
 * @param <E>  the type of the element of the list
 * @return the list of possible permutations
 */
public static <E> List<Map<Integer, E>> possiblePermutations(List<E> list) {
    // check if the list is non-null and non-empty
    if (list == null || list.size() == 0) return Collections.emptyList();
    return IntStream.range(0, list.size())
            // represent each list element as a list of permutation maps
            .mapToObj(i -> IntStream.range(0, list.size())
                    // key - element position, value - element itself
                    .mapToObj(j -> Collections.singletonMap(j, list.get(j)))
                    // Stream<List<Map<Integer,E>>>
                    .collect(Collectors.toList()))
            // reduce a stream of lists to a single list
            .reduce((list1, list2) -> list1.stream()
                    .flatMap(map1 -> list2.stream()
                            // filter out those keys that are already present
                            .filter(map2 -> map2.keySet().stream()
                                    .noneMatch(map1::containsKey))
                            // concatenate entries of two maps, order matters
                            .map(map2 -> new LinkedHashMap<Integer, E>() {{
                                putAll(map1);
                                putAll(map2);
                            }}))
                    // list of combinations
                    .collect(Collectors.toList()))
            // otherwise an empty collection
            .orElse(Collections.emptyList());
}

另见:String permutations using recursion in Java