leetcode专题训练127. Word Ladder

本文详细介绍了如何将Word Ladder问题的解空间从O(n²l)优化到O(nl),通过添加虚拟节点降低复杂度。讨论了C++11和Python3实现可变节点数邻接表的方法,并提到了nonlocal修饰符在Python中的应用。最后,提供了解题代码作为示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最先想出的方法的时间复杂度是 O ( n 2 l ) O(n^2l) O(n2l)的,其中n是wordList长度,l是单词长度,果然超时了。

import queue

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        lWord = len(beginWord)
        lList = len(wordList)

        # 连接状况
        Graph = [[-1 for i in range(lList+1)] for j in range(lList+1)]
        # 是否已经访问过某个单词节点
        vis = [0 for i in range(lList+1)]
        # 到某个位置的路径长度
        le = [0 for i in range(lList+1)]

        # 将beginWord放到WordList中,方便取值
        wordList.append(beginWord)

        def existPath(ind1, ind2):
            if Graph[ind1][ind2] != -1:
                return Graph[ind1][ind2]
            else:
                word1 = wordList[ind1]
                word2 = wordList[ind2]
                difcount = 0
                for i in range(lWord):
                    if word1[i] != word2[i]:
                        difcount += 1
                    if difcount > 1:
                        break

                if difcount == 1:
                    Graph[ind1][ind2] = 1
                    Graph[ind2][ind1] = 1
                    return 1
                else:
                    Graph[ind1][ind2] = 0
                    Graph[ind2][ind1] = 0
                    return 0
        
        result = 0
        q = queue.Queue()
        vis[lList] = 1
        q.put(lList)
        le[lList] = 1
        while not q.empty():
            now = q.get()
            for i in range(lList):
                if vis[i] != 0:
                    continue
                if existPath(now, i):
                    vis[i] = 1
                    le[i] = le[now]+1
                    # print(wordList[i], le[i])
                    if wordList[i] == endWord:
                        result = le[i]
                        break
                    q.put(i)
        
        return result

之后看题解,发现可以用添加虚拟节点的方式来使得时间复杂度变为 O ( n l ) O(nl) O(nl)。添加虚拟节点的方式主要是把当前单词能转移到的位置全部添加上虚拟节点。比如单词"abc",那么可以转移到的位置是"*bc", “a*c"和"ab*”,那么就创建这三个虚拟节点即可。若两个单词之间有路径,那两个单词一定会连到同一个虚拟节点,就不用再挨个比对任意两个单词是否相连通了。时间复杂度变为了先构建所有虚拟节点和单词到虚拟节点的边, O ( n l ) O(nl) O(nl),之后再用BFS找到从初始节点到结束节点的路径,所有节点最多弹出或弹入队列一次,所以为 O ( n l ) O(nl) O(nl),整体的复杂度为 O ( n l ) O(nl) O(nl)
需要注意最后的结果为(边数//2+1),因为虚拟节点的引入,使得边数变为之前的两倍,而题目所求的其实是节点个数,节点个数为边数加1。
需要学习的就是C++和python的可变节点数邻接表创建。
C++11

vector<vector<int> > edge;
edge.emplace_back(); // 可创建新节点
edge[id1].push_back(id2); // 直接用索引获取新节点

python3

edge = collections.defaultdict(list)
edge[id1].append(id2)

还有nonlocal修饰符。

num = 0
def f(x):
	nonlocal num
	a = num

解题代码如下:

import queue
import collections

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        lWord = len(beginWord)
        lList = len(wordList)

        # 邻接表
        edge = collections.defaultdict(list)
        # 单词和索引对应的字典
        Index = {}
        # 当前index
        now_index = 0

        # 构建单词
        def AddWord(word):
            nonlocal now_index
            if word not in Index.keys():
                Index[word] = now_index
                now_index += 1

            return Index[word]

        # 添加到虚拟节点的边
        def AddEdge(word):
            id1 = AddWord(word)
            for i in range(lWord):
                virWord = word[:i]+'*'+word[i+1:]
                # virWord[i] = '*'
                id2 = AddWord(virWord)
                edge[id1].append(id2)
                edge[id2].append(id1)
    
        begInd = AddWord(beginWord)
        endInd = AddWord(endWord)
        AddEdge(beginWord)
        for i in range(lList):
            AddEdge(wordList[i])

        # now_index为所有节点个数
        dis = [-1 for i in range(now_index)]
        
        result = 0
        
        q = queue.Queue()
        q.put(begInd)
        dis[begInd] = 0
        while not q.empty():
            now = q.get()
            if now == endInd:
                # 总共的边数为dis[now]//2,题目所要的节点数为边数+1
                result = dis[now]//2+1
                break
            for ind in edge[now]:
                if dis[ind] == -1:
                    dis[ind] = dis[now]+1
                    q.put(ind)

        return result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值