用于生成字符串排列的复杂性

时间:2016-11-24 02:22:29

标签: java algorithm time-complexity permutation

我试图弄清楚如何生成给定字符串的所有排列的代码(来自Cracking the Coding Interview book)的复杂性为O(n!)。

我知道这是最好的复杂性,因为我们有n!排列,但我想以编码方式理解它,因为并非每个执行此操作的算法都是O(n!)。

守则:

import java.util.*;

public class QuestionA {

    public static ArrayList<String> getPerms(String str) {
        if (str == null) {
            return null;
        }
        ArrayList<String> permutations = new ArrayList<String>();
        if (str.length() == 0) { // base case
            permutations.add("");
            return permutations;
        }

        char first = str.charAt(0); // get the first character
        String remainder = str.substring(1); // remove the first character
        ArrayList<String> words = getPerms(remainder);
        for (String word : words) {
            for (int j = 0; j <= word.length(); j++) {
                String s = insertCharAt(word, first, j);
                permutations.add(s);
            }
        }
        return permutations;
    }

    public static String insertCharAt(String word, char c, int i) {
        String start = word.substring(0, i);
        String end = word.substring(i);
        return start + c + end;
    }

    public static void main(String[] args) {
        ArrayList<String> list = getPerms("abcde");
        System.out.println("There are " + list.size() + " permutations.");
        for (String s : list) {
            System.out.println(s);
        }
    }

}

这是我一直以来的想法,直到现在: 在任何函数调用中,可用的单词数是(n-1);假设我们在一个剩余长度(n-1)的地方。现在在所有这些(n-1)个单词的所有可能位置插入第n个元素需要(n-1)*(n-1)个时间。

所以跨执行,应该是(n-1)^ 2 +(n-2)^ 2 +(n-3)^ 2 + .... 2 ^ 2 + 1 ^ 2操作,其中我不要以为是n!。

我在这里想念的是什么?

2 个答案:

答案 0 :(得分:1)

我认为getPerms的时间复杂度为O((n + 1)!)

我们将getPerms的运行时间表示为T(n),其中n是输入的长度。

=============================================== ====================

两个if分支和行char first = str.charAt(0)需要O(1)次。以下行需要O(n)时间:

String remainder = str.substring(1); // remove the first character

下一行需要时间T(n - 1)

ArrayList<String> words = getPerms(remainder);

现在我们考虑嵌套for-loops的运行时间。外for-loop的大小为(n-1)!

for (String word : words) {

内部for-loop的大小为n + 1

for (int j = 0; j <= word.length(); j++) {

insertCharAt的复杂性也是O(n)

因此,嵌套for-loops的总运行时间为(n + 1) * (n - 1)! * O(n) = O((n + 1)!)

因此我们有以下关系:

T(n) = T(n - 1) + O(n) + O((n + 1)!)
     = T(n - 1) + O(n) + O((n + 1)!)
     = (T(n - 2) + O(n - 1) + O(n!) + O(n) + O((n + 1)!)
     = T(n - 2) + ( O(n - 1) + O(n) ) + ( O(n!) + O((n + 1)!) )
     = ...
     = O(n2) + (1 + ... + O(n!) + O((n + 1)!) )
     = O((n + 1)!)

答案 1 :(得分:1)

如果你正在研究这个,那么最好学习一般的解决方案,而不仅仅是你的例子中提供的实现。 Sedgewick做了我所知道的最好的分析。我在课堂上教它。

https://www.cs.princeton.edu/~rs/talks/perms.pdf

生成函数的每次调用的复杂性是O(n)。因此成本是O(n!)。

您提供的代码效率极低。有一个巨大的常数因素,因为你创建了许多字符串对象和数组,这是你可以用Java做的最低效的事情之一。

如果您只想浏览所有排列,请置换单个实体,不要生成列表。这是一个更快的实现:

public class Permute {
    private int[] a;
  private void swap(int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
    public Permute(int n) {
        a = new int[n];
        for (int i = 0; i < n; i++)
            a[i] = i+1;
        this.generate(n);
    }
    public void generate(int N) {
        //      System.out.println("generate(" + N + ")");
        if (N == 0) doit();
        for (int c = 0; c < N; c++) {
            //          System.out.print("Swapping: " + c + "," + N);
            swap(c, N-1);                         //swap(0, 7)
            generate(N-1);
            //          System.out.print("Swapping: " + c + "," + N);
            swap(c, N-1);
        }
    }
    public void doit() {
        for (int i = 0; i < a.length; i++)
            System.out.print(a[i] + " ");
        System.out.println();
    }

    public static void main(String[] args) {
        Permute p = new Permute(4);
    }
}

Sedgewick显示的另一种方法是Heaps,每个排列只有一个交换而不是2.这是一个C ++实现:

#include <vector>
#include <iostream>

using namespace std;
class Heaps {
private:
    vector<int> p;
public:
    Heaps(int n) {
        p.reserve(n);
        for (int i = 0; i < n; i++)
            p.push_back(i+1);
        generate(n);
    }
  void doit() {
        cout << "doit size=" << p.size() << '\n';
        for (int i = 0; i < p.size(); i++)
            cout << p[i];
        cout << '\n';
    }
    void generate(int N) {
        //      cout << "generate(" << N << ")\n";
    if (N == 0)
            doit();
    for (int c = 0; c < N; c++) {
            generate(N-1);
            swap(p[N % 2 != 0 ? 0 : c], p[N-1]);
        }
    }
};


int main() {
    Heaps p(4);
}