簡體   English   中英

理解示例 12 來自大 O 符號的字符串的所有排列 - 破解編碼面試

[英]Understanding Example 12 All Permutations of a string from Big O notation - Cracking the Coding Interview

我一直不明白作者是如何得到以下過程的O(n^2 * n!)復雜度的,該過程生成字符串的所有排列。

void permutation(String str){
   permutation(str,"");
}
void permutation(String str, String prefix){
  if(str.length()==0){
    System.out.println(prefix);
  } else{
    for(int i=0;i<str.length();i++){
        String rem=str.substring(0,i)+str.substring(i+1);
         permutation(rem,prefix+str.charAt(i));
    }
  }
}

由於 else 路徑成本,該方法的復雜度為O(n^2 *n!)

首先注意每次調用String rem=str.substring(0,i)+str.substring(i+1); O(n) ,在else路徑中,我們將它計算n次,並調用具有復雜度T(n-1) permutation

計算這個的復雜度等價於求解: T(n) = n[n+T(n-1)] n次(for 循環)一個(n+T(n-1))

解決這個重復問題並不是那么容易,如果我沒記錯的話,它應該歸結為解決:

foo+bar

但讓我們嘗試近似。 每個排列(基本情況)代表遞歸樹中的一個節點。 這棵樹有n! 葉子。 每個葉子都有一條到長度為n的根的路徑。 所以可以安全地假設不超過n*n! 樹中的節點。

這是對permutation調用次數的上限。 由於每個調用都花費n ,因此復雜度的總體上限為O(n^2*n!)

希望這可以幫助。

我遲到了,但我仍然會發布我的答案。

我是這樣向自己解釋的。 采用簡化的方法,讓我們忽略函數的所有步驟: permutation(String str, String prefix)除了遞歸步驟。

記住上面的內容,函數的時間復雜度可以用遞推關系表示: T(N) = N*T(N-1) ,其中N是輸入字符串的長度。 展開后, T(N) = N*(N-1)*(N-2)*...3*2*1*T(0) --- (*)

現在, T(0) = O(N)因為在基本情況下我們打印前綴字符串,打印長度為 N 的字符串是一個 O(N) 操作。

以封閉形式表示上面的 (*): N*N! --- (1)

現在考慮permutation函數的以下行: String rem = str.substring(0, i) + str.substring(i + 1);

這又是一次O(N)操作,並且對N! 遞歸調用。 因此考慮以上並與上面的表達式(1)乘積,總運行時間復雜度T(N)結果為

N*N*N! = N^2*N!

時間復雜度來自 for 循環執行的次數。 在書中這用n * n!表示n * n! 這是一種簡化。 此運行時來自圖中的估計節點數。 如果您開始繪制圖形,您會看到最低級別的節點數為n! 在每個級別中,它是較低級別的節點數除以2, 3, .., n util 頂層有一個節點。

在書中,他們沒有計算節點的確切數量,而是將圖中的級別數乘以級別中的最大分支數,因此n * n! . 這個數字將始終高於或等於節點的確切數量,因此它可以很好地作為上限。

然后如書中所述,for 循環體中的字符串連接將采用O(n)因此總體時間復雜度為O(n * n * n!) = O(n^2 * n!)

出於兩個原因,我們沒有添加 if body 所花費的時間復雜度。 一個是主體的時間復雜度是O(1) - 因為它只是一個打印語句。 如果主體將依賴於 n 或其他相關變量,那么我們將不得不添加該不變量和 if 主體執行次數的乘法。 其次, if 主體被執行n! 次,但我們已經將其計入 for 循環體執行的次數(有關 for 循環體的確切調用次數,請參見下面的等式)。

如果在 else 語句中但在 for 循環之外有更多代碼行,我們將不得不通過將不變量和 else 循環的執行次數相乘來增加時間復雜度。

我相信我們可以使用下面的等式獲得圖中節點的確切數量:

在此處輸入圖片說明

然后可以通過以下方式確定 for 循環體調用的數量:

在此處輸入圖片說明

我們必須加n! 因為 for 循環另外執行了 n! 長度為零的 rem 的次數。 我們必須減去 1,因為沒有為第一個節點執行 for 循環體(其中rem.length() == str.length() )。

解決這個問題的一個簡單技巧是 O(節點數 * 每個節點的計算量)。

每個節點的計算是 O(n)。

節點數可以通過對每個級別的節點求和來計算。

即(n!/1!)+(n!/2!)+(n!/3!)...+(n!/n!)

等於 n! * (1/1! + 1/2! + ... + 1/n!)

該系列的總和 (1/1! + 1/2! + ... + 1/n!) 被視為n (作為上限)

這導致節點數為 n * n!

所以,O(節點數*每個節點的計算)是O(n * n! * n),也就是O(n 2 * n!)


但是,如果我們進行真正的數學運算, (1/1! + 1/2! + ... + 1/n!) 是 1.7182 (其中 n > 6 )

因此,節點數為 1.7182 * n!。 復雜度必須是 O(1.7182 * n! * n) 即O(n * n!)


當我在代碼之間使用計數時,我總是得到 n 階乘作為步數。

public class example12 {
    int count=0;

    public static void main(String args[])
    {
        example12 a= new example12();
        a.permutation("12345678", "test");
    }

    void permutation(String str){
           permutation(str,"");
        }

        void permutation(String str, String prefix){
          if(str.length()==0){
            System.out.println(prefix);
            System.out.println(count+"at  print");

          } else{
            for(int i=0;i<str.length();i++){
                String rem=str.substring(0,i)+str.substring(i+1);
                permutation(rem,prefix+str.charAt(i));
                System.out.println(count);
                count= count+1;

            }
          }
        }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM