簡體   English   中英

為什么哈希集的性能比列表更快?

[英]Why the hashset's performance is way faster than list?

這個問題來自leetcode( https://leetcode.com/problems/word-ladder/ )!

給定兩個單詞(beginWord和endWord)以及字典的單詞列表,找到從beginWord到endWord的最短轉換序列的長度,例如:

一次只能更改一個字母。 每個轉換的單詞都必須存在於單詞列表中。 注意beginWord不是轉換后的單詞。 注意:

如果沒有這樣的轉換序列,則返回0。 所有單詞的長度相同。 所有單詞僅包含小寫字母字符。 您可以假設單詞列表中沒有重復項。 您可以假設beginWord和endWord為非空並且不相同。

這是我的代碼,需要800毫秒才能運行:

class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList){
    if(!wordList.contains(endWord))
        return 0;
    int ret = 1;
    LinkedList<String> queue = new LinkedList<>();
    Set<String> visited = new HashSet<String>();
    queue.offer(beginWord);
    queue.offer(null);
    while(queue.size() != 1 && !queue.isEmpty()) {
        String temp = queue.poll();
        if(temp == null){
            ret++;
            queue.offer(null);
            continue;                
        }
        if(temp.equals(endWord)) {
            //System.out.println("succ ret = " + ret);
            return ret;
        }
        for(String word:wordList) {           
            if(diffOf(temp,word) == 1){
                //System.out.println("offered " + word);
                //System.out.println("ret =" + ret);
                if(!visited.contains(word)){
                visited.add(word);
                queue.offer(word); 
                }
            }
        }
    }
    return 0;
}
private int diffOf(String s1, String s2) {
    if(s1.length() != s2.length())
        return Integer.MAX_VALUE;
    int dif = 0;
    for(int i=0;i < s1.length();i++) {
        if(s1.charAt(i) != s2.charAt(i))
            dif++;
    }
    return dif;    
}
}

這是另一個需要100毫秒才能運行的代碼:

class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    Set<String> set = new HashSet<>(wordList);
    if (!set.contains(endWord)) {
        return 0;
    }

    int distance = 1;
    Set<String> current = new HashSet<>();
    current.add(beginWord);

    while (!current.contains(endWord)) {
        Set<String> next = new HashSet<>();

        for (String str : current) {
            for (int i = 0; i < str.length(); i++) {
                char[] chars = str.toCharArray();

                for (char c = 'a'; c <= 'z'; c++) {
                    chars[i] = c;
                    String s = new String(chars);

                    if (s.equals(endWord)) {
                        return distance + 1;
                    }

                    if (set.contains(s)) {
                        next.add(s);
                        set.remove(s);
                    }
                }
            }
        }
        distance++;

        if (next.size() == 0) {
            return 0;
        }
        current = next;
    }

    return 0;
}
}

我認為第二個代碼效率較低,因為它每個單詞測試26個字母。 為什么這么快?

簡短的答案:您的“呼吸優先”搜索與“單詞距離單位”(以下稱為“迭代”)相比要多幾個數量級。

  • 您將每個候選人與每個剩余的單詞進行比較。 每次迭代的時間復雜度T(N×n),
  • 他們將每個候選人與人為構建的“下一個”候選人進行比較。 而且由於他們構造了候選者,所以不必“計算”距離。 為簡單起見,我假設兩者(構造或檢查)具有相同的運行時間。 每次迭代的時間復雜度為T(26×l×n)。

(N =單詞列表大小,n =此迭代的候選數,l =單詞長度)

當然26×l×n比N×n小得多,因為單詞長度很小,但單詞列表很大。

我在("and","has",[List of 2M English words])上嘗試了您的例程,並在30秒后將其殺死,因為我認為它已崩潰。 它沒有崩潰,只是很慢。 我轉到另一個50K的單詞列表,您的單詞列表現在需要8秒,而實現它們需要0.04秒。

對於我的N = 51306的單詞列表,有2167個3個字母的單詞。 這意味着平均每個單詞有3個cbrt(2167)個可能的候選詞,即n≈38.82。

  • 他們的預期性能:每次迭代T(26×l×n)≈T(3027)工作,
  • 您的預期性能:每次迭代T(N×n)≈T(1991784)工作。

(假設單詞列表不會變短;但是有了這么多單詞,差異可以忽略不計)


順便說一句,基於隊列的循環緩沖區實現可能比其兩個交替集實現更快,因此您可以使混合實現更快。

暫無
暫無

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

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