2015-12-13 84 views
4

假設你正在和你的朋友玩下面的翻轉游戲:給定一個只包含這兩個字符的字符串:+-,你和你的朋友輪流翻轉兩個連續的"++""--"。遊戲結束時,一個人不能再行動,因此另一個人將成爲贏家。如何分析時間複雜性?

寫一個函數來確定起始玩家是否可以保證勝利。

例如,給出s = "++++",返回true。首發球員可以通過翻轉中間"++"來保證贏得成爲"+--+"

這裏是我的代碼:

public boolean canWin(String s) { 

    if(s==null || s.length()<2) return false; 
    char[] arr=s.toCharArray(); 
    return canWinHelper(arr); 
} 

public boolean canWinHelper(char[] arr){ 
    for(int i=0; i<arr.length-1; i++){ 
     if(arr[i]=='+' && arr[i+1]=='+'){ 
      arr[i]='-'; 
      arr[i+1]='-'; 
      boolean win=!canWinHelper(arr); 
      arr[i]='+'; 
      arr[i+1]='+'; 
      if(win) return true; 
     } 
    } 
    return false; 
} 

它的工作原理,但我不知道這裏怎麼計算時間複雜性,因爲該功能將保持自稱,直到返回一個錯誤。任何人都有一些想法嗎?

另外在搜索期間,我們會遇到重複計算,所以我想我可以使用散列圖來避免這些重複。鍵:字符串,值:布爾值。使用一個HashMap

我更新的代碼:

public boolean canWin(String s){ 

    if(s==null || s.length()<2) return false; 
    HashMap<String,Boolean> map=new HashMap<String,Boolean>(); 
    return helper(s,map); 
} 

public boolean helper(String s, HashMap<String,Boolean> map){ 
    if(map.containsKey(s)) return map.get(s); 
    for(int i=0; i<s.length()-1; i++){ 
     if(s.charAt(i)=='+' && s.charAt(i+1)=='+'){ 
      String fliped=s.substring(0,i)+"--"+s.substring(i+2); 
      if(!helper(fliped,map)){ 
       map.put(s,true); 
       return true; 
      } 
     } 
    } 
    map.put(s,false); 
    return false; 
} 

不過,我想知道如何在這裏分析的時間和空間複雜度?

回答

2

取爲n = arr.length - 1個

第一遍您有n個遞歸調用。對於每個你刪除了兩個+,所以每個最多有n-2個遞歸調用,等等。所以你最多有n + n(n-2)+ n(n-2)(n-4)+ ...遞歸調用。 (1 + 1/2 + 1 /(2 * 4)+ 1 /(2 * 4 * 8)+ ...)因爲1 + 1/2 + 1 /( 2 * 4)+ 1 /(2 * 4 * 8)+ ...是會聚的,≤2,你有O(n !!)

關於內存,你有一個長度爲n的數組,用於遞歸調用,所以你有n + n n + n n n + n ...(n/2次)... * n = n(n ^(n/2)-1)/(n-1 )並且這是O(n ^(n/2))

這顯然指向比徹底搜索沒有更好的性能。

對於散列改進,您要求使用代碼創建的所有可能的組合。然而,你的代碼與實際創建所有組合的代碼沒有多大區別,除了你用兩個代替兩個+的事實,這是通過一些因素來降低複雜度,但不是它的級別。總體而言,最壞的情況與在n/2個位置中的比特組合的數目是2 ^(n/2)相同。觀察哈希函數本身可能有一些隱藏日誌,因此總的複雜度將是搜索O(2 ^(n/2)* ln(n/2))和內存O(2 ^(n/2))。

這是最糟糕的情況。但是,如果有一些安排你不能贏,那麼當沒有獲勝策略時,上面的這些確實是你需要依賴的複雜性。

那麼平均情景的問題就是你可以/不可以獲勝的情況的數量以及它們在所有安排中的分佈情況的問題。這個問題與你的算法沒有多大關係,需要一套完全不同的工具才能解決。

經過一段時間檢查上述推理是否正確無誤,我對結果很滿意,因爲它告訴我所有我需要知道的信息。你不能指望你會有一個有利的安排,我真的懷疑你只有0.01%的最壞情況安排,所以你需要準備最糟糕的情況,除非這是一個特殊的項目,信封計算是你的朋友。

無論如何,這些類型的計算都有正確準備的測試用例,而不是正確和最終的實現。使用這些測試,您可以在考慮編譯器,內存消耗,分頁等情況下找到O()中隱藏的因素。

儘管如此,我們仍然可以不斷改進信封推理。例如,你實際上在每一步都沒有n-2,因爲它取決於奇偶性。例如,對於++++++++ ...如果您替換第三個+++ - +++++ ...顯然您將有n-3個,而不是n-2個遞歸調用,甚至n-4。因此,呼叫的半數可能具有n-3次遞歸調用,其將是n/2(n-3)+ n/2(n-2)= n(n-5/2)

觀察到, != N!(N-1)!我們可以拿n !!≈√n!,再次n!= n !!!(n-1)!!!(n-2)!!!或者n !!!≈∛n!這可能會導致我們應該有類似O((n!)^(5/2))的結論。測試會告訴我在O((n!)^(x))中可以減少x = 3多少。 (雖然它可以用不同的方式表達,但在一種特殊形式下尋找複雜性是非常正常的,所以我會繼續使用複雜形式O((n!)^(x) ((n!)^(x)),1≤x≤3)

+0

對於散列版本,這是怎麼來的2 ^(n/2)? –