簡體   English   中英

用迭代計算遞歸函數的時間復雜度

[英]Calculating Time Complexity of recursive function with iteration

我試圖了解這段代碼的時間復雜度,它通過將字符串分成 4 部分來計算給定字符串的 IP 地址。 每個部分由句點分隔,即.

public List<String> restoreIpAddresses(String s, int parts) {

    List<String> result = new ArrayList<>();
    if (parts == 1) {
        if (isValidPart(s)) result.add(s);
        return result;
    }
    
    for (int i = 0; i < s.length(); i++) {
        String first = s.substring(0, i);
        if (!isValidPart(first)) {
            continue;
        }
        
        List<String> previous = restoreIpAddresses(s.substring(i, s.length()), parts - 1);
        
        for (String str: previous) {
            StringBuilder sb = new StringBuilder();
            result.add(first + "." + str);
        }
    }        
    
    return result;
    
}

private boolean isValidPart(String part) {
    if ( (part.length() > 1 && part.startsWith("0")) || 
         (part.length() > 3) || (part.length() == 0)
         (Integer.valueOf(part) > 255) ) return false;
      return true;
    }
}

由於 for 循環是O(n) ,n 是字符串的長度,並且在每次迭代中,for 循環都會為在父 for 循環中傳遞的子字符串執行,所以O(n - 1) 所以按照這個邏輯,時間復雜度應該是n(n-1)(n-2) ....1n! 在最壞的情況下,對吧?

但是,如果我環顧四周(例如herehere ),我會看到人們發布恆定的時間復雜度。 我無法理解。 有人可以幫我分解一下嗎?

考慮到從上述算法生成 IP 地址,我們有兩個約束。

  1. 有效 IP 為 0->255。 這可以在恆定時間內進行評估。
  2. 將有 4 個八位字節。 所以問題字符串應該分為4部分。

現在考慮一個長度為12的字符串111 111 111 111

  1. 你有多少種方式可以形成第一個八位字節? => 最少 1 ,最多 3 種方式,共 12 個字符。 complexity:- O(3)

  2. 你有多少種方式可以形成第二個八位字節? => 9 個字符中的最小 0 最大 3 種方式,考慮到第一個八位字節使用了 3 個字符。 complexity:- O(3)

  3. 你有多少種方式可以形成第三個八位字節? => 最少 0 最多 3 種方式,從 6 個字符開始,考慮到第一個和第二個八位字節使用 6 個字符。 complexity:- O(3)

  4. 你有多少種方法可以用剩余的字符組成第四個八位字節? => 只有一種方法可以從剩余的 3 個字符形成一個八位字節。 考慮到第一個、第二個和第三個八位字節使用了 9 個字符。 O(1)

時間復雜度計算。

Time Complexity = product of complexities of each recursive function
                = O(3)*O(3)*O(3)*O(1)
                = 3*O(3) = O(1) = [constant-time] complexity

因此,無論您提供什么字符串作為輸入,所有有效 IP 地址都可以計算為27次迭代。 因此這個算法是一個常數時間O(1)

考慮到以上理解,代碼可以按照以下方式重寫


public static List<String> restoreIpAddresses(String s, int position, int parts) {

        List<String> result = new ArrayList<>();
        // O(1) time complexity
        if (parts == 1) {
            if (position < s.length() && isValidPart(s.substring(position))) {
                result.add(s.substring(position));
            }
            return result;
        }

        // Iterate only thrice in each recursive function. O(3) time complexity
        for (int i = position; i <= position + 3; i++) {
            if (i > s.length()) {
                continue;
            }

            String first = s.substring(position, i);
            if (!isValidPart(first)) {
                continue;
            }

            List<String> previous = restoreIpAddresses(s, i , parts - 1);

            for (String str : previous) {
                StringBuilder sb = new StringBuilder();
                result.add(first + "." + str);
            }
        }

        return result;

    }

請注意,上述算法是經典backtracking problems一個例子。 來自維基。

回溯是一種通用算法,用於尋找某些計算問題的解決方案,特別是約束滿足問題,它逐步構建解決方案的候選者,並在確定候選者不可能完成到有效的時立即放棄候選者(“回溯”)解決方案

PS:- 示例111 111 111 111是一個極端示例,只有一個有效的 IP 地址111.111.111.111可以從該字符串中形成。 但是,循環/遞歸評估最多會發生 81 次。

給定n ,輸入字符串的長度和i ,部分的數量。 代碼的time-complexity由以下公式獲得:

在此處輸入圖片說明

該公式是遞歸的,因為代碼是遞歸的。 在右側,第二個 Sigma 表示每個for-loop (子字符串)的time-complexity 。此外,通過求解后一個 Sigma,如下所示:

在此處輸入圖片說明

我們得出這個等式:

在此處輸入圖片說明

考慮我們在Level-0並且我們移動到Level-1 因此,方程變為:

在此處輸入圖片說明

我們大約有(有關更多信息,請參閱此鏈接):

在此處輸入圖片說明

方程變為:

在此處輸入圖片說明

如果我們進入下一個級別,即Level-2 (我只寫簡單部分):

在此處輸入圖片說明

方程變為:

在此處輸入圖片說明

借助mathematical induction我們可以說:

在此處輸入圖片說明

我的假設和表述可能是錯誤的。 不過,我喜歡玩數學。

暫無
暫無

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

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