簡體   English   中英

while內部for循環的時間復雜度?

[英]Time complexity of while inside for loop?

在 Java 應用程序中,我有以下算法用於“具有 K 個不同字符的最長 Substring”,如下所示:

輸入:String="araaci", K=2 Output: 4 解釋:不超過 '2' 個不同字符的最長子串是“araa”。

輸入:String="cbbebi", K=3 Output: 5 解釋:不同字符不超過“3”的最長子串是“cbbeb”和“bbebi”。

這是代碼:

public static int longestSubstring(String str, int k) {
    Map<Character, Integer> map = new HashMap<>();
    int maxLength = 0;
    int l = 0;

    for (int r = 0; r < str.length(); r++) {
        char cRight = str.charAt(r);
        map.put(cRight, map.getOrDefault(cRight, 0) + 1);

        while (map.size() > k) {
            char cLeft = str.charAt(l);
            map.put(cLeft, map.getOrDefault(cLeft, 0) - 1);
            if (map.get(cLeft) == 0) {
                map.remove(cLeft);
            }
            l++;
        }
        maxLength = Math.max(maxLength, r - l + 1);
    }
    return maxLength;
}

我無法理解以下定義中的時間復雜度:

時間復雜度 上述算法的時間復雜度為 O(N),其中“N”是輸入字符串中的字符數。 外層 for 循環針對所有字符運行,內層 while 循環只處理每個字符一次,因此算法的時間復雜度為 O(N+N),漸近等價於 O(N)。

所以,我認為當另一個 for 循環中有一個 while 循環時,我認為時間復雜度是 O(n^2)。 但是在這里我無法理解“內部 while 循環只處理每個字符一次”。 你能解釋一下這個 state 是否正確嗎?

為了分析算法的復雜性,大多數時候您需要詳細了解代碼的作用(您不需要了解代碼的作用是否正確)。 使用代碼的結構(即循環是否嵌套)或只看大圖通常不是一個好主意。 換句話說,計算算法的復雜性會花費(您的)大量時間。

正如您所說的“內部 while 循環只處理每個字符一次”,這確實很重要,但我認為這還不夠。

循環本身並不重要,重要的是您的程序將根據輸入大小運行的指令總數。 您可以將“指令”理解為“以恆定時間運行的 function”(與輸入大小無關)。

確保所有 function 調用都在 O(1) 中

我們先看一下所有function調用的復雜度:

  • 我們有幾個 map 讀取,全部在 O(1) 中(讀作“恆定時間讀取”):
    • map.getOrDefault(cRight, 0)
    • map.getOrDefault(cLeft, 0)
  • 還有幾個 map 插入,也都在 O(1) 中:
    • map.put(cRight, ...)
    • map.put(cLeft, ...)
  • 和 map 項目刪除map.remove(cLeft) ,也在 O(1)
  • Math.max(..., ...)也在 O(1) 中
  • str.charAt(..)也在 O(1) 中
  • 還有循環變量的遞增/遞減並檢查它們的值以及其他一些+1-1都在 O(1) 中。

好的,現在我們可以有把握地說所有外部函數都是“指令”(或者更准確地說,所有這些 function 使用“恆定大小的指令數”)。 請注意,所有 hashmap 的復雜性並不完全准確,但這是您可以單獨查看的細節

這意味着我們現在只需要調用調用了多少個這些函數。

分析function來電次數

評論中的論點是准確的,但使用char cLeft = str.charAt(l)將使程序崩潰,如果l > N在我看來不是很令人滿意。 但這是有道理的,內循環不可能總共執行超過l次(這直接導致預期的O(N)時間復雜度)。

如果這是作為練習給出的,我懷疑這是預期的答案。 讓我們像使用char cLeft = str.charAt(l % str.length())編寫的程序一樣分析程序,而不是讓它更有趣一點。

我覺得主要論點應該基於存儲在map中的“字符計數總數”(一個 map 字符對計數器)。 以下是一些事實,主要是:

  1. 外部循環總是將單個計數器恰好增加一個。
  2. 內部循環總是將單個計數器恰好減少一個。

還:

  1. 內部循環確保所有計數器都為正(當計數器等於 0 時刪除計數器)。
  2. 只要(正)計數器的數量> k ,內部循環就會運行。

引理對於總共要執行 C 次的內循環,總共需要至少執行 C 次外循環。

證明假設外層循環執行C次,內層循環至少執行了C + 1次,即存在外層循環r次迭代,其中內層循環執行r + 1次。 在外循環的r迭代期間,在某個點(通過 1. 和 2.), map中所有字符計數器的總和將等於0 根據事實 3.,這意味着沒有剩余的計數器( map.size()等於 0)。 由於k > 0 ,在迭代r期間不可能進入內循環r + 1次,因為 4.. 證明引理成立的矛盾。

不太正式的是,如果計數器總和為 0,則內循環將永遠不會執行,因為k ( > 0 ) 個正計數器的總和大於0 換句話說,消費者(內循環)消費的東西不能超過(外循環)生產的東西。

由於這個引理,並且因為外層循環恰好執行了 N 次,所以內層循環至多執行N次。 總共我們將在外部循環中執行最多A * N function 次調用,在內部循環中執行B * N function 次調用, AB都是常量並且所有函數都在O(1)中,因此(A + B) * N ∈ O(N)

另請注意,寫O(N + N)是一種重復(或沒有意義),因為大 O 應該忽略所有常數因子(乘法和加法)。 通常人們不會使用大 O 符號來寫方程,因為很難寫出正確和正式的東西(適用於明顯的集合包含,如O(log N) ∈ O(N) )。 通常你會說“所有操作都在O(N)中,因此算法在O(N)中”。

暫無
暫無

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

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