简体   繁体   English

在 Java 中使用递归的字符串排列

[英]String permutations using recursion in Java

I came across THIS post which tried very hard to explain the recursive solution to print all string.我遇到了这篇文章,它非常努力地解释了打印所有字符串的递归解决方案。

public class Main {
    private static void permutation(String prefix, String str) {
        int n = str.length();
        if (n == 0)
            System.out.println(prefix);
        else {
            for (int i = 0; i < n; i++)
                permutation(prefix + str.charAt(i),
                        str.substring(0, i) + str.substring(i + 1));
        }
    }

    public static void main(String[] args) {
        permutation("", "ABCD");
    }
}

But still I am not able to get the part when we start popping off the stack.但是当我们开始从堆栈中弹出时,我仍然无法获得零件。 For example, recursion goes all the way till permutation("ABCD","") , where base case meets and it prints ABCD .例如,递归一直到permutation("ABCD","") ,基本情况相遇并打印ABCD But what happens now?但是现在会发生什么? We pop off permutation("ABC","D") from function call stack.我们从 function 调用堆栈中弹出permutation("ABC","D") What we do with this and so on?我们用这个等等做什么?

Can someone please help explain a bit?有人可以帮忙解释一下吗?

Also, I need some pointers on the time complexity of this.另外,我需要一些关于时间复杂度的指示。 Not like complete calculation but some hints.不像完整的计算,而是一些提示。

Easier example: permutation("", "ABC") , representing empty string as * :更简单的示例: permutation("", "ABC") ,将空字符串表示为*

* ABC + A BC + AB C - ABC *
      |      |
      |      ` AC B - ACB *
      |
      + B AC + BA C - BAC *
      |      |
      |      ` BC A - BCA *
      |
      ` C AB + CA B - CAB *
             |
             ` CB A - CBA *

Note that this looks like a tree laid on its side.请注意,这看起来像一棵横着放的树。 Indeed, it called a tree.的确,它叫做一棵树。 When we start, we will enter ("A", "BC") state;当我们开始时,我们会进入("A", "BC")状态; we proceed to call ("AB", "C") , and finally ("ABC", "") .我们继续调用("AB", "C") ,最后调用("AB", "C") ("ABC", "") Here we print our output.在这里我们打印我们的输出。 Then we remember we have unfinished business, we return to a loop, but the loop in the previous level only had one cycle.然后我们记得我们有未完成的事情,我们回到一个循环,但是上一层的循环只有一个循环。 So we're done there as well, and return back to ("A", "BC") level;所以我们在那里也完成了,并返回到("A", "BC")级别; there are two elements in "BC" and we've only done the "B" , so it's now "C" 's turn: we call ("AC", "B") , which then calls ("ACB", "") . "BC"有两个元素,我们只完成了"B" ,所以现在轮到"C" :我们调用("AC", "B") ,然后调用("ACB", "") Now all the loops under ("A", "BC") are done... But wait, still work to do!现在("A", "BC")下的所有循环都完成了......但是等等,还有工作要做! Because ("", "ABC") still has two more letters to process.因为("", "ABC")还有两个字母要处理。 And so it goes, branch by branch, in what we usually call "depth-first search".就这样,一个分支一个分支,在我们通常所说的“深度优先搜索”中。

In each level is a loop.在每个级别是一个循环。 The loop shrinks (we iterate 3 times in the thick branch at the left, then 2 in the next level, then only one) but still there is a loop.循环缩小了(我们在左边的粗分支中迭代了 3 次,然后在下一层迭代了 2 次,然后只有一次)但仍然存在一个循环。 So in total, we do n * (n - 1) * (n - 2) * ... * 2 * 1 iterations.因此,我们总共进行了n * (n - 1) * (n - 2) * ... * 2 * 1次迭代。 O(N!) ? O(N!)

As a kind of recursion , you can use Stream.reduce method.作为一种递归,您可以使用Stream.reduce方法。 First prepare a list of possible combinations of characters for each character-position , and then consecutively reduce the stream of these lists to a single list, by summing the pairs of list elements.先准备的每个字符位置的字符的可能组合的列表,然后将这些列表的流连续地减少到一个列表,通过对列表中的元素求和。

As a list element, you can use Map<Integer,String> , where key - the position of the character in the string, value - the character itself, and summ the contents of these maps, excluding those keys that are already present.作为列表元素,您可以使用Map<Integer,String> ,其中key - 字符串中字符的位置, value - 字符本身,并对这些映射的内容求和,不包括那些已经存在的

And you get a list of permutations of characters.你会得到一个字符排列列表。

For example, if you have a four-character string 𝗔𝗕𝗖𝗗 , then you have to pass through three steps of reduction , consecutively accumulating the results:举例来说,如果你有一个四字符串𝗔𝗕𝗖𝗗 ,那么你必须经过三个步骤减少,连续积累的结果:

str1 + str2 = sum1 str1 + str2 = sum1 sum1 + str3 = sum2 sum1 + str3 = sum2 sum2 + str4 = total sum2 + str4 = 总计
𝗔 + 𝗕 = 𝗔𝗕 𝗔 + 𝗕 = 𝗔𝗕
𝗔 + 𝗖 = 𝗔𝗖 𝗔 + 𝗖 = 𝗔𝗖
𝗔 + 𝗗 = 𝗔𝗗 𝗔 + 𝗗 = 𝗔𝗗
 𝗔𝗕 + 𝗖 = 𝗔𝗕𝗖 𝗔𝗕 + 𝗖 = 𝗔𝗕𝗖
𝗔𝗕 + 𝗗 = 𝗔𝗕𝗗 𝗔𝗕 + 𝗗 = 𝗔𝗕𝗗
𝗔𝗖 + 𝗕 = 𝗔𝗖𝗕 𝗔𝗖 + 𝗕 = 𝗔𝗖𝗕
𝗔𝗖 + 𝗗 = 𝗔𝗖𝗗 𝗔𝗖 + 𝗗 = 𝗔𝗖𝗗
𝗔𝗗 + 𝗕 = 𝗔𝗗𝗕 𝗔𝗗 + 𝗕 = 𝗔𝗗𝗕
𝗔𝗗 + 𝗖 = 𝗔𝗗𝗖 𝗔𝗗 + 𝗖 = 𝗔𝗗𝗖
 𝗔𝗕𝗖 + 𝗗 = 𝗔𝗕𝗖𝗗 𝗔𝗕𝗖 + 𝗗 = 𝗔𝗕𝗖𝗗
𝗔𝗕𝗗 + 𝗖 = 𝗔𝗕𝗗𝗖 𝗔𝗕𝗗 + 𝗖 = 𝗔𝗕𝗗𝗖
𝗔𝗖𝗕 + 𝗗 = 𝗔𝗖𝗕𝗗 𝗔𝗖𝗕 + 𝗗 = 𝗔𝗖𝗕𝗗
𝗔𝗖𝗗 + 𝗕 = 𝗔𝗖𝗗𝗕 𝗔𝗖𝗗 + 𝗕 = 𝗔𝗖𝗗𝗕
𝗔𝗗𝗕 + 𝗖 = 𝗔𝗗𝗕𝗖 𝗔𝗗𝗕 + 𝗖 = 𝗔𝗗𝗕𝗖
𝗔𝗗𝗖 + 𝗕 = 𝗔𝗗𝗖𝗕 𝗔𝗗𝗖 + 𝗕 = 𝗔𝗗𝗖𝗕

15 sums for each of the 4 characters are 60 sums, resulting in 4! = 24 4字符中每个字符的15总和是60总和,结果为4! = 24 4! = 24 permutations. 4! = 24个排列。

Try it online! 在线试试吧!

// original string
String str = "𝗔𝗕𝗖𝗗";
// array of characters of the string
int[] codePoints = str.codePoints().toArray();
// contents of the array
System.out.println(Arrays.toString(codePoints));
//[120276, 120277, 120278, 120279]
// list of permutations of characters
List<Map<Integer, String>> permutations = IntStream.range(0, codePoints.length)
        // Stream<List<Map<Integer,String>>>
        .mapToObj(i -> IntStream.range(0, codePoints.length)
                // represent each character as Map<Integer,String>
                .mapToObj(j -> Map.of(j, Character.toString(codePoints[j])))
                // collect a list of maps
                .collect(Collectors.toList()))
        // intermediate output
        //[{0=𝗔}, {1=𝗕}, {2=𝗖}, {3=𝗗}]
        //[{0=𝗔}, {1=𝗕}, {2=𝗖}, {3=𝗗}]
        //[{0=𝗔}, {1=𝗕}, {2=𝗖}, {3=𝗗}]
        //[{0=𝗔}, {1=𝗕}, {2=𝗖}, {3=𝗗}]
        .peek(System.out::println)
        // reduce a stream of lists to a single list
        .reduce((list1, list2) -> list1.stream()
                // summation of pairs of maps from two lists
                .flatMap(map1 -> list2.stream()
                        // filter out those keys that are already present
                        .filter(map2 -> map2.keySet().stream()
                                .noneMatch(map1::containsKey))
                        // join entries of two maps
                        .map(map2 -> new LinkedHashMap<Integer, String>() {{
                            putAll(map1);
                            putAll(map2);
                        }}))
                // collect into a single list
                .collect(Collectors.toList()))
        // List<Map<Integer,String>>
        .orElse(List.of(Map.of(0, str)));
// number of permutations
System.out.println(permutations.size()); // 24

// number of rows (factorial of string length - 1)
int rows = IntStream.range(1, codePoints.length)
        .reduce((a, b) -> a * b).orElse(1);

// column-wise output
IntStream.range(0, rows)
        .mapToObj(i -> IntStream.range(0, permutations.size())
                .filter(j -> j % rows == i)
                .mapToObj(permutations::get)
                .map(map -> map.toString().replace(" ", ""))
                .collect(Collectors.joining(" ")))
        .forEach(System.out::println);
//{0=𝗔,1=𝗕,2=𝗖,3=𝗗} {1=𝗕,0=𝗔,2=𝗖,3=𝗗} {2=𝗖,0=𝗔,1=𝗕,3=𝗗} {3=𝗗,0=𝗔,1=𝗕,2=𝗖}
//{0=𝗔,1=𝗕,3=𝗗,2=𝗖} {1=𝗕,0=𝗔,3=𝗗,2=𝗖} {2=𝗖,0=𝗔,3=𝗗,1=𝗕} {3=𝗗,0=𝗔,2=𝗖,1=𝗕}
//{0=𝗔,2=𝗖,1=𝗕,3=𝗗} {1=𝗕,2=𝗖,0=𝗔,3=𝗗} {2=𝗖,1=𝗕,0=𝗔,3=𝗗} {3=𝗗,1=𝗕,0=𝗔,2=𝗖}
//{0=𝗔,2=𝗖,3=𝗗,1=𝗕} {1=𝗕,2=𝗖,3=𝗗,0=𝗔} {2=𝗖,1=𝗕,3=𝗗,0=𝗔} {3=𝗗,1=𝗕,2=𝗖,0=𝗔}
//{0=𝗔,3=𝗗,1=𝗕,2=𝗖} {1=𝗕,3=𝗗,0=𝗔,2=𝗖} {2=𝗖,3=𝗗,0=𝗔,1=𝗕} {3=𝗗,2=𝗖,0=𝗔,1=𝗕}
//{0=𝗔,3=𝗗,2=𝗖,1=𝗕} {1=𝗕,3=𝗗,2=𝗖,0=𝗔} {2=𝗖,3=𝗗,1=𝗕,0=𝗔} {3=𝗗,2=𝗖,1=𝗕,0=𝗔}

See also: How do you check if a word has an anagram that is a palindrome?另请参阅:如何检查单词是否有回文字谜?

You can generate an array of permutations of the string using map and reduce methods.您可以使用mapreduce方法生成字符串的排列数组。

This solution assumes that the original string does not contain duplicate characters, otherwise the permutation maps Map<Integer,String> should be used instead of the permutation arrays String[] .此解决方案假定原始字符串不包含重复字符,否则应使用置换映射Map<Integer,String>而不是置换数组String[]

The reduce method takes a pair of permutation arrays and sums their elements in pairs , accumulating the results. reduce方法采用一对置换数组并将它们的元素成对求和,累积结果。 For example, four steps of reduction for a five-character string 𝖠𝖡𝖢𝖣𝖤 :例如,一个五字符的字符串𝖠𝖡𝖢𝖣𝖤四步约简:

0 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
1 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
---
sum1: [𝖠𝖡, 𝖠𝖢, 𝖠𝖣, 𝖠𝖤, ...]
2 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
---
sum2: [𝖠𝖡𝖢, 𝖠𝖡𝖣, 𝖠𝖡𝖤, ...]
3 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
---
sum3: [𝖠𝖡𝖢𝖣, 𝖠𝖡𝖢𝖤, ...]
4 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
---
total: [𝖠𝖡𝖢𝖣𝖤, 𝖠𝖡𝖢𝖤𝖣, ...]

Try it online! 在线试试吧!

// original string
String str = "𝖠𝖡𝖢𝖣𝖤";
// array of characters of the string
int[] codePoints = str.codePoints().toArray();
String[] permutations = IntStream.range(0, codePoints.length)
        // intermediate output, character-position
        .peek(i -> System.out.print(i + " "))
        // prepare an array of possible permutations for each character-position
        .mapToObj(i -> Arrays.stream(codePoints).mapToObj(Character::toString)
                // array of characters as strings
                .toArray(String[]::new))
        // intermediate output, array of possible permutations
        .peek(arr -> System.out.println(Arrays.deepToString(arr)))
        // reduce a stream of arrays to a single array
        .reduce((arr1, arr2) -> Arrays.stream(arr1)
                // summation of pairs of strings from two arrays
                .flatMap(str1 -> Arrays.stream(arr2)
                        // filter out those characters that are already present
                        .filter(str2 -> !str1.contains(str2))
                        // concatenate two strings
                        .map(str2 -> str1 + str2))
                // collect into a single array
                .toArray(String[]::new))
        // otherwise an empty array
        .orElse(new String[0]);
// final output
System.out.println("Number of permutations: " + permutations.length);
// number of rows
int rows = permutations.length / 10;
// permutations by columns
IntStream.range(0, rows).forEach(i -> System.out.println(
        IntStream.range(0, permutations.length)
                .filter(j -> j % rows == i)
                .mapToObj(j -> permutations[j])
                .collect(Collectors.joining(" "))));

Intermediate output, arrays of possible permutations for each character-position:中间输出,每个字符位置的可能排列数组:

0 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
1 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
2 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
3 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]
4 [𝖠, 𝖡, 𝖢, 𝖣, 𝖤]

Final output, permutations by columns:最终输出,按列排列:

Number of permutations: 120
𝖠𝖡𝖢𝖣𝖤 𝖠𝖣𝖡𝖢𝖤 𝖡𝖠𝖢𝖣𝖤 𝖡𝖣𝖠𝖢𝖤 𝖢𝖠𝖡𝖣𝖤 𝖢𝖣𝖠𝖡𝖤 𝖣𝖠𝖡𝖢𝖤 𝖣𝖢𝖠𝖡𝖤 𝖤𝖠𝖡𝖢𝖣 𝖤𝖢𝖠𝖡𝖣
𝖠𝖡𝖢𝖤𝖣 𝖠𝖣𝖡𝖤𝖢 𝖡𝖠𝖢𝖤𝖣 𝖡𝖣𝖠𝖤𝖢 𝖢𝖠𝖡𝖤𝖣 𝖢𝖣𝖠𝖤𝖡 𝖣𝖠𝖡𝖤𝖢 𝖣𝖢𝖠𝖤𝖡 𝖤𝖠𝖡𝖣𝖢 𝖤𝖢𝖠𝖣𝖡
𝖠𝖡𝖣𝖢𝖤 𝖠𝖣𝖢𝖡𝖤 𝖡𝖠𝖣𝖢𝖤 𝖡𝖣𝖢𝖠𝖤 𝖢𝖠𝖣𝖡𝖤 𝖢𝖣𝖡𝖠𝖤 𝖣𝖠𝖢𝖡𝖤 𝖣𝖢𝖡𝖠𝖤 𝖤𝖠𝖢𝖡𝖣 𝖤𝖢𝖡𝖠𝖣
𝖠𝖡𝖣𝖤𝖢 𝖠𝖣𝖢𝖤𝖡 𝖡𝖠𝖣𝖤𝖢 𝖡𝖣𝖢𝖤𝖠 𝖢𝖠𝖣𝖤𝖡 𝖢𝖣𝖡𝖤𝖠 𝖣𝖠𝖢𝖤𝖡 𝖣𝖢𝖡𝖤𝖠 𝖤𝖠𝖢𝖣𝖡 𝖤𝖢𝖡𝖣𝖠
𝖠𝖡𝖤𝖢𝖣 𝖠𝖣𝖤𝖡𝖢 𝖡𝖠𝖤𝖢𝖣 𝖡𝖣𝖤𝖠𝖢 𝖢𝖠𝖤𝖡𝖣 𝖢𝖣𝖤𝖠𝖡 𝖣𝖠𝖤𝖡𝖢 𝖣𝖢𝖤𝖠𝖡 𝖤𝖠𝖣𝖡𝖢 𝖤𝖢𝖣𝖠𝖡
𝖠𝖡𝖤𝖣𝖢 𝖠𝖣𝖤𝖢𝖡 𝖡𝖠𝖤𝖣𝖢 𝖡𝖣𝖤𝖢𝖠 𝖢𝖠𝖤𝖣𝖡 𝖢𝖣𝖤𝖡𝖠 𝖣𝖠𝖤𝖢𝖡 𝖣𝖢𝖤𝖡𝖠 𝖤𝖠𝖣𝖢𝖡 𝖤𝖢𝖣𝖡𝖠
𝖠𝖢𝖡𝖣𝖤 𝖠𝖤𝖡𝖢𝖣 𝖡𝖢𝖠𝖣𝖤 𝖡𝖤𝖠𝖢𝖣 𝖢𝖡𝖠𝖣𝖤 𝖢𝖤𝖠𝖡𝖣 𝖣𝖡𝖠𝖢𝖤 𝖣𝖤𝖠𝖡𝖢 𝖤𝖡𝖠𝖢𝖣 𝖤𝖣𝖠𝖡𝖢
𝖠𝖢𝖡𝖤𝖣 𝖠𝖤𝖡𝖣𝖢 𝖡𝖢𝖠𝖤𝖣 𝖡𝖤𝖠𝖣𝖢 𝖢𝖡𝖠𝖤𝖣 𝖢𝖤𝖠𝖣𝖡 𝖣𝖡𝖠𝖤𝖢 𝖣𝖤𝖠𝖢𝖡 𝖤𝖡𝖠𝖣𝖢 𝖤𝖣𝖠𝖢𝖡
𝖠𝖢𝖣𝖡𝖤 𝖠𝖤𝖢𝖡𝖣 𝖡𝖢𝖣𝖠𝖤 𝖡𝖤𝖢𝖠𝖣 𝖢𝖡𝖣𝖠𝖤 𝖢𝖤𝖡𝖠𝖣 𝖣𝖡𝖢𝖠𝖤 𝖣𝖤𝖡𝖠𝖢 𝖤𝖡𝖢𝖠𝖣 𝖤𝖣𝖡𝖠𝖢
𝖠𝖢𝖣𝖤𝖡 𝖠𝖤𝖢𝖣𝖡 𝖡𝖢𝖣𝖤𝖠 𝖡𝖤𝖢𝖣𝖠 𝖢𝖡𝖣𝖤𝖠 𝖢𝖤𝖡𝖣𝖠 𝖣𝖡𝖢𝖤𝖠 𝖣𝖤𝖡𝖢𝖠 𝖤𝖡𝖢𝖣𝖠 𝖤𝖣𝖡𝖢𝖠
𝖠𝖢𝖤𝖡𝖣 𝖠𝖤𝖣𝖡𝖢 𝖡𝖢𝖤𝖠𝖣 𝖡𝖤𝖣𝖠𝖢 𝖢𝖡𝖤𝖠𝖣 𝖢𝖤𝖣𝖠𝖡 𝖣𝖡𝖤𝖠𝖢 𝖣𝖤𝖢𝖠𝖡 𝖤𝖡𝖣𝖠𝖢 𝖤𝖣𝖢𝖠𝖡
𝖠𝖢𝖤𝖣𝖡 𝖠𝖤𝖣𝖢𝖡 𝖡𝖢𝖤𝖣𝖠 𝖡𝖤𝖣𝖢𝖠 𝖢𝖡𝖤𝖣𝖠 𝖢𝖤𝖣𝖡𝖠 𝖣𝖡𝖤𝖢𝖠 𝖣𝖤𝖢𝖡𝖠 𝖤𝖡𝖣𝖢𝖠 𝖤𝖣𝖢𝖡𝖠

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM