力扣:127. 单词接龙 题解

Problem: 127. 单词接龙

127. 单词接龙

  • 前言
  • 预备知识
  • 解题思路
  • 复杂度
  • Code
  • 其它细节
  • 推荐博客或题目
    • 博客
    • 题目
      • 深广度优先搜索
      • 哈希表

前言

好好好!又做出来一道困难题,逐渐在进步,莫西莫西!!!

预备知识

力扣:127. 单词接龙 题解_第1张图片

此题用到了广度优先搜索遍历,以及哈希表的运用。c++中哈希表是unordered_map。如果对此不了解的uu,建议查看相关介绍博客和更简单的题目!!!

解题思路

该题解法为:广度优先搜索遍历 + 哈希表的运用。

  1. 我们先用一个哈希集合去存储wordlist所有字符串,让之后的查询复杂度降为O(1)。
    力扣:127. 单词接龙 题解_第2张图片
  2. 再次遍历wordlist,对每个s的下一个状态进行查询,复杂度为260,并且将每个s的下一个状态存储到一个哈希表mm(unordered_map)中,具有记忆化功能,之后查询为O(1)。
    力扣:127. 单词接龙 题解_第3张图片
  3. 在ffdd查询s下一个状态的函数中主要遍历有以下功能:
    比如s为hot,那么我选择遍历每个位置的26个字母的话最坏复杂度只有s.size() * 26,即10 * 26,但是我如果选择遍历wordlist中的话,就麻烦多了,首先是5000的长度,况且找到之后怎么确定是s的下一个状态,时间复杂度肯定不是O(1)。因此如果用一个哈希表记录wordlist,在用260的复杂度便可以找到s的下一个状态的所有字符串。
    力扣:127. 单词接龙 题解_第4张图片
  4. 对于beginword,我单独进行了一次查找下一个状态的操作。
    力扣:127. 单词接龙 题解_第5张图片
  5. 最后使用广度优先搜索进行所有下一个状态的处理与弹出,最多5000下一个状态。
    力扣:127. 单词接龙 题解_第6张图片

复杂度

时间复杂度:

O(260 * n)

空间复杂度:

O(5000 * 3),只计算了mm,us,fd。

Code

class Solution {
public:
    unordered_map<string, vector<string>> mm;
    vector<string> ffdd(string& s, unordered_set<string>& us) {
        vector<string> v;
        //比如s为hot,那么我选择遍历每个位置的26个字母的话最坏复杂度只有s.size() * 26,即10 * 26,但是我如果选择遍历wordlist中的话,就麻烦多了,首先是5000的长度,况且找到之后怎么确定是s的下一个状态,时间复杂度肯定不是O(1)。因此如果用一个哈希表记录wordlist,在用260的复杂度便可以找到s的下一个状态的所有字符串。
        for (int i = 0; i < s.size(); i++) {    //260
            for (int j = 0; j < 26; j++) {
                if ('a' + j != s[i]) {
                    string ss(s);
                    ss[i] = 'a' + j;
                    if (us.count(ss)) {
                        v.push_back(ss);
                    }
                }
            }
        }
        return v; 
    }

    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> us, fd;
        for (auto& s : wordList) {   //n
            //我们先用一个哈希集合去存储wordlist所有字符串,让之后的查询复杂度降为O(1)。
            us.insert(s);
        }
        for (auto& s : wordList) {   // 260 * n
            //再次遍历wordlist,对每个s的下一个状态进行查询,复杂度为260,并且将每个s的下一个状态存储到一个哈希表mm(unordered_map>)中,具有记忆化功能,之后查询为O(1)。
            mm.insert({s, ffdd(s, us)});
        }
        int cnt = 0;
        //进行广度优先搜索的存储节点的队列
        queue<string> q;
        //对于beginword,我单独进行了一次查找下一个状态的操作
        for (int i = 0; i < beginWord.size(); i++) {    //260
            for (int j = 0; j < 26; j++) {
                if ('a' + j != beginWord[i]) {
                    string ss(beginWord);
                    ss[i] = 'a' + j;
                    if (us.count(ss)) {
                        q.push(ss);
                        //再使用一个哈希集合确保是beginword的下一个状态并且进入队列的字符串不会再次被放入队列中,这样的话push的操作最多执行5000次。
                        fd.insert(ss);
                    }
                }
            }
        }
        cnt++;
        while (!q.empty()) {
            int size = q.size();
            //5000
            cnt++;
            for (int ii = 0; ii < size; ii++) {
                string s = q.front();q.pop();
                //cnt必须先++,因为这找到endword就会return了
                if (s == endWord) return cnt;
                for (int i = 0; i < mm[s].size(); i++) {
                    if (fd.count(mm[s][i]) == 0) {
                        q.push(mm[s][i]);   
                        //再使用一个哈希集合确保是mm[s][i]的下一个状态并且进入队列的字符串不会再次被放入队列中,这样的话push的操作最多执行5000次。
                        fd.insert(mm[s][i]);
                    }
                }
            }
            
        }
        return 0;
    }
};

其它细节

可以尝试用输出日志的方式来获得局部代码的正确性。对于比较长的代码,我们应该在写完整个代码之前,已经完成多个地方的日志输出。多加练习能够提高自己写代码的正确性。

for (auto& [a, b] : pp) {   
    //调试bug的时候可以用输出的方法
    cout << a << b << endl;
}

推荐博客或题目

博客

  1. 深度优先搜索
  2. 广度优先搜索
  3. 哈希表理论基础

题目

深广度优先搜索

  1. 所有可能的路径 难度:++
  2. 钥匙和房间 难度:++
  3. 岛屿的周长 难度:+++
  4. 岛屿数量 难度:+++
  5. 岛屿的最大面积 难度:+++
  6. 被围绕的区域 难度:+++
  7. 太平洋大西洋水流问题 难度:++++
  8. 最大人工岛 难度:++++

哈希表

  1. 两数之和 难度:++
  2. 三数之和 难度:+++
  3. 四数之和 难度:++++
  4. 四数相和II 难度:++++

你可能感兴趣的:(数据结构与算法学习,leetcode,哈希算法,散列表,c++,图搜索算法,图搜索,广度优先)