LeetCode 127. 单词接龙(图的BFS)

2020年9月26日 周六 天气晴 【不悲叹过去,不荒废现在,不惧怕未来】



解题要点

  • 无向图中两个顶点之间的最短路径的长度,可以通过广度优先遍历得到;
  • 借助哈希表判断是否单词之间是否存在路径(只有一个字母不同);
  • 已知目标顶点的情况下,可以分别从起点和目标顶点(终点)执行广度优先遍历(每次都从节点数比较小的那一方开始遍历),直到遍历的部分有交集,这是双向广度优先遍历的思想。

1. 图的BFS(单向)

class Solution{
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList){
        // 用原始数组初始化dict,访问过之后要删除,避免重复访问
        unordered_set<string> dict(wordList.begin(),wordList.end());
        if (dict.find(endWord) == dict.end()) return 0;

        queue<pair<string,int>> q; // BFS需要的队列
        q.push(make_pair(beginWord,1)); // 加入beginWord

        string tmp; // 当前访问的节点
        int step; // 抵达该节点时的step

        while(!q.empty()){
            auto p = q.front();
            // 寻找到endWord了
            if(p.first==endWord) return p.second;
            tmp = p.first;
            step = p.second;
            q.pop();

            // 开始寻找下一个节点
            char old_ch;
            for(int i=0;i<tmp.size();++i){
                old_ch= tmp[i];
                // 从'a'-'z'都尝试一次
                for(char c ='a';c<='z';++c){
                    // 字符相同,提前结束
                    if(c==old_ch) continue;
                    tmp[i] = c;
                    // 如果能找到,说明存在路径(转换)
                    if(dict.find(tmp)!=dict.end()){
                        q.push(make_pair(tmp,step+1)); // 加入到下一层中
                        dict.erase(tmp); // 删除该节点
                    }
                }
                tmp[i] = old_ch; // 复原 
            }
        }
        return 0;
    }
};
  • 时间复杂度: O ( m n ) O\left( {mn} \right) O(mn),其中 m m m是单词的长度, n n n是单词表中单词的总数。
  • 空间复杂度: O ( m n ) O\left( {mn} \right) O(mn)

2. 图的BFS(双向)

具体做法:

  1. 使用两个set,分别从start和end两头开始BFS;
  2. 每次选择较小的set开始BFS,也就是将小的作为start,大的作为end;
  3. 如果end中能找到start,说明出现了交集,遍历结束;否则,在访问set中加入访问记录,并加入到tmp中,作为子节点。
class Solution{
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList){
        // 用原始数组初始化dict
        unordered_set<string> dict(wordList.begin(),wordList.end());
        if (dict.find(endWord) == dict.end()) return 0;

        // 从起点和终点同时进行遍历
        unordered_set<string> begSet, endSet, tmp, visited;
        begSet.insert(beginWord);
        endSet.insert(endWord);
        int len = 1;

        // 两头同时进行遍历,直到有一个set为空,说明不存在beginWord到endWord的路径(转换),返回0
        while(!begSet.empty() && !endSet.empty()){
            // 始终让begSet里的单词个数最小
            if(begSet.size() > endSet.size()){
                tmp = begSet;
                begSet = endSet;
                endSet = tmp;
            }
            tmp.clear();
            // 遍历单词个数较小的begSet
            for(string word:begSet){
                // 更改每个位置的字母,看dict中是否有匹配的word,有的话说明能建立路径
                for(int i=0;i<word.size();++i){
                    char old_ch = word[i];
                    for(char c='a';c<='z';++c){
                        if(c==old_ch) continue;
                        word[i] = c;
                        // 如果当前这个word出现在endSet,说明出现了交集,遍历结束,返回结果:len+1
                        if(endSet.find(word)!=endSet.end())
                            return len+1;
                        // 没有出现的话,根据是否遍历过、是否存在于dict进行处理
                        if(dict.find(word)!=dict.end() && visited.find(word)==visited.end()){
                            visited.insert(word);
                            tmp.insert(word);
                        }
                    }
                    word[i] = old_ch; // 还原
                }
            }
            ++len; // 又进行了一层遍历,len自加1
            begSet = tmp; // 将新的一层节点赋值给begSet
        }
        return 0;
    }
};
  • 时间复杂度: O ( m n ) O\left( {mn} \right) O(mn),其中 m m m是单词的长度, n n n是单词表中单词的总数。虽然渐进时间复杂度和前一个解法相同,但是因为是双向遍历,实际运行时间更短,空间复杂度分析类似。
  • 空间复杂度: O ( m n ) O\left( {mn} \right) O(mn)

参考文献

https://leetcode-cn.com/problems/word-ladder/solution/cpp-yi-ge-si-lu-de-zhuan-bian-cong-1156msjia-su-da/

https://leetcode-cn.com/problems/word-ladder/solution/yan-du-you-xian-bian-li-shuang-xiang-yan-du-you-2/

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值