我一直在阅读一本书中的示例,该示例向您展示如何将String置换为所有可能的组合,但是由于我还是一般的编程初学者,因此我实际上无法理解代码的工作原理!

有人可以分析一下我提供的代码,并对所有功能及其执行方式进行详尽的解释吗?

非常感谢,

亚历克斯

class PermuteString{
    String word;
    int index;
    PermuteString substringGenerator;
    public PermuteString(String s){
        word = s;
        index = 0;
        if(s.length() > 1){
              substringGenerator = new PermuteString(s.substring(1));
    }
}

public String nextPermutation(){
    if(word.length() == 1){
        ++index;
        return word;
    }
    else{
        String r = word.charAt(index) + substringGenerator.nextPermutation();
        if(!substringGenerator.morePermutations()){
            ++index;
            if(index < word.length()){
                String tailString = word.substring(0, index) + word.substring(index + 1);
                substringGenerator = new PermuteString(tailString);
            }
        }
        return r;
    }
}

public boolean morePermutations(){
    return index < word.length();
}

}

public class PermuteStringDemo {
public static void main(String[] args){
    PermuteString p = new PermuteString("opyn");
    while(p.morePermutations())
        System.out.println(p.nextPermutation());
}
}

最佳答案

请记住,这不是置换字符串的最清晰或最简单的方法。实际上,就编程风格而言,它简直是可悲的。而是一个教科书示例来说明递归的形式。理解正在发生的事情的最简单方法是一步一步地进行。

从构造函数开始。它保存单词,将其索引设置为0,然后(如有必要)从单词的第二个字符到最后一个字符创建一个新的PermuteString对象。完成此操作后,您将具有相当于PermuteString对象的链表的功能:首先是“ opyn”,然后是“ pyn”,“ yn”,“ n”。很简单。

现在让我们看一下迭代器。 morePermutations()与标准Iterator中的next()等效;如果其索引仍小于其字的长度,则返回true。因此,您知道当PermuteString索引达到其单词长度时,它就完成了。

最后是nextPermutation()。


对于一个字母的单词,它将返回其单词并将其索引加1。这意味着从后开始对morePermutations()的后续调用将返回false。这是有道理的:一个字母的单词本身就是一个排列。到现在为止还挺好。
N等于2或更大的N个字母的单词呢?在这里,请考虑PermuteString对象的任务:逐个返回其字母的每个排列,并在不再剩余排列时通知其调用者。这样做的方法是将一个字母指定为“当前”字母,并使用子PermuteString生成其“其他”字母的所有可能排列。在每次迭代中,它将返回一个字符串,该字符串首先由当前字母组成,然后再由其他字母的某种组合组成。
事情变得如此糟糕的地方实际上是如何进行的。回想一下,在构造时,每个PermuteString对象都有一个指向其第一个字母的索引以及一个子PermuteString,用于生成其第二个到最后一个字母的排列。因此,每次调用nextPermutation()


首先,它将计算并将返回的下一个排列存储在“ r”中。这就是当前索引字母加上其“其他”字母的下一个排列。很简单。
然后,它窥视其子PermuteString,以查看是否有更多的子字符串排列可提供给我们。如果是这样,则对nextPermutation()的此调用实质上已完成:它返回“ r”并退出。
但是,如果说孩子PermuteString没有子弹,可以这么说,那么父母就知道已经生成了其当前起始字母的所有排列。它将索引前进到下一个字母。如果它已经到了单词的末尾,那么它的所有排列就都用光了。请注意,所有对morePermutations()的后续调用现在都将返回false。如果不是,则该对象知道它必须开始以其新的起始字母生成排列。为此,它需要创建一个全新的子字符串生成器,其中包括原始单词中的所有其他字母-从位置0到刚结束的索引字母的那些字母,再加上从下一个字母到单词末尾的那些字母。这就是“ tailString”(一个可怕的命名变量)的计算结果,下一行将为其他字母创建PermuteString置换符。



这是自我修改递归对象的一个​​很好的例子。为什么这个糟糕的实词代码?嘘,有很多原因。其变量的名称非常倾斜。它在(递归)循环的中间(如果条件检查是否已完成)更改substringGenerator的值。到达末尾后再调用nextPermutation()将导致随机越界异常,而不是有意义的异常。等等等等。但是,递归本身是合乎逻辑的,值得理解它是如何工作的。干杯!

09-12 11:33