Word Ladder|leetcode题解

这道题很容易想到使用广度优先搜索的方式,从起始单词start搜索到结束单词end,中间产生状态的产生有两种,一种是通过改变单词的一个字母并其是否在字典中来产生新的状态;另一种是通过以距离为1借助字典构建单词间的邻接关系。第二种状态产生的方式,太天真,由于字典有可能很大,构建邻接关系图的复杂度为单词个数的平方,无法接受。我第一个版本的代码使用此种方法生成新的状态超时。

在选用通过改变单词中的一个字母来产生新的状态之后,在广度优先搜索的时候,需要记录已经搜索过的顶点,以防止重复搜索,开始时,我使用额外的visitd结合表示此单词是否被访问过,运行时间是1466ms,而直接通过在字典中删除此单词,使得判定此单词不在字典中标志此单词被访问过的方法,运行时间只有396ms,如此大的提升,为什么,现在推断的原因是由于set访问比较慢。

接着,我使用双向广度优先搜索对程序进行优化,结果运行时间下降到了105ms,相对广度优先搜索提高了2倍。

下面是3个版本的程序:

1.单向广搜,使用visited记录访问过的状态(1466ms)

int ladderLength(string start, string end, unordered_set<string> &dict) {
        int n=dict.size();
        if(0==n)return 0;  
        set<string>visited;
        visited.insert(start);
        queue<string>words;
        words.push(start);
        int level=1;
        int cnt=1;
        while(!words.empty()){
            string word=words.front();
            words.pop();
            for(string::size_type i=0;i<word.size();i++){
                char t=word[i];
                for(char c='a';c<='z';c++){
                    if(t==c)continue;
                    string t=word;
                    word[i]=c;
                    if(word==end)return level+1;
                    if(!visited.count(word)&&dict.count(word)){
                        words.push(word);
                        visited.insert(word);
                    }
                    word=t;
                }
            }
            if(--cnt==0){
                level++;
                cnt=words.size();
            }
        }   
     
        return 0;  
    }

2.单向广搜,使用删除单词标志此单词被访问(396ms)

int ladderLength(string start, string end, unordered_set<string> &dict) {
        int n=dict.size();
        if(0==n)return 0;
        queue<string>words;
        words.push(start);
        dict.erase(start);
        int level=1;
        int cnt=1;
        while(!words.empty()){
            string word=words.front();
            words.pop();
            for(string::size_type i=0;i<word.size();i++){
                char t=word[i];
                for(char c='a';c<='z';c++){
                    if(t==c)continue;
                    string t=word;
                    word[i]=c;
                    if(word==end)return level+1;
                    if(dict.count(word)){
                        words.push(word);
                        dict.erase(word);
                    }
                    word=t;
                }
            }
            if(--cnt==0){
                level++;
                cnt=words.size();
            }
        }
    
        return 0;
    }
3.双向广搜(105ms)

 int ladderLength(string start, string end, unordered_set<string> &dict) {
        int n=dict.size();
        if(0==n)return 0;  
    
        queue<string>firstQueue;
        map<string,int>firstVisited;
        firstQueue.push(start);firstVisited.insert(pair<string,int>(start,1));
        queue<string>secondQueue;
        map<string,int>secondVisited;
        secondQueue.push(end);secondVisited.insert(pair<string,int>(end,1));
        int firstLevel=1,secondLevel=1;
        int firstCnt=1,secondCnt=1;
        dict.erase(start);dict.erase(end);
        bool first=true;
        while(!firstQueue.empty()||!secondQueue.empty()){
            string word=first?firstQueue.front():secondQueue.front();
            if(first)firstQueue.pop();
            else secondQueue.pop();
            for(string::size_type i=0;i<word.size();i++){
                char t=word[i];
                for(char c='a';c<='z';c++){
                    if(t==c)continue;
                    string t=word;
                    word[i]=c;
                    if(first&&secondVisited.count(word))return firstLevel+secondVisited[word];
                    if(!first&&firstVisited.count(word))return secondLevel+firstVisited[word];
                    if(dict.count(word)){
                        if(first){
                            firstQueue.push(word);
                            firstVisited.insert(pair<string,int>(word,firstLevel+1));
                        }
                        else{
                            secondQueue.push(word);
                            secondVisited.insert(pair<string,int>(word,secondLevel+1));
                        }
                        dict.erase(word);
                    }
                    word=t;
                }
            }
          if(first){
                if(--firstCnt==0){
                    firstLevel++;
                    firstCnt=firstQueue.size();
                }
            }
            else{
                if(--secondCnt==0){
                    secondLevel++;
                    secondCnt=secondQueue.size();
                }
            }
            first=!first;
        }
        return 0;
    }

双向广搜需要添加额外的firstVisited,secondVisited用来记录访问过的节点所在的层次,用于计算整个变换的长度。

因为在单向广搜中,搜索到最终状态,直接用当前节点的层次就能计算变换长度,而在双向广搜中,要根据当前节点

是否在另一个队列中被搜索过,以标志结束,但是此节点在另外一个队列中被搜索的路径长度,并不一定是其正在搜索

的层次,也有可能是新加入顶点的层次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值