实现BFS以找到从一个单词到另一个单词的最短转换时出错(Word Ladder Challenge)

时间:2017-11-15 08:44:57

标签: python-3.x graph undirected-graph

我正在尝试实现梯形图问题,我必须在最短的路径中将一个单词转换为另一个单词。显然我们可以使用广度优先搜索(BFS)来解决它但在此之前我们必须首先绘制图形。我已经实现了桶的概念,如果它们与桶类型相匹配,某些词落在桶中。但是我的图表没有正确实现。

给定的单词列表是[&#34; CAT&#34;,&#34; BAT&#34;,&#34; COT&#34;,&#34; COG&#34;,&#34; COW& #34;,&#34; RAT&#34;,&#34; BUT&#34;,&#34; CUT&#34;,&#34; DOG&#34;,&#34; WED&#34;] < / p>

因此,对于每个单词,我都可以创建一个存储桶。例如对于单词&#39; CAT&#39;,我可以有三个存储桶_AT,C_T,CA_。同样地,我可以为剩下的单词创建存储桶,并且与存储桶类型匹配的单词将属于这些存储桶。

我在图表中表达问题的代码工作正常,我得到一个这样的图表(理论上的) enter image description here

现在我需要找到最短的操作来转换CAT&#39;为了实现它,我使用了BFS的改进方法。当我制作自己的样本图时,它工作正常。例如

graph = {'COG': ['DOG', 'COW', 'COT'], 'CAT': ['COT', 'BAT', 'CAT', 'RAT'], 'BUT': ['CUT', 'BAT'], 'DOG': ['COG']}

代码工作正常,我得到了正确的结果。但是如果我有一个很大的单词列表说1500,那么输入和创建一个很长的字典是不可行的。所以我做了一个函数来取这些列表中的单词,实现了我上面讨论的技术并为我创建了图形,直到这里工作正常。但是当我试图获得两个单词之间的最短距离时,我得到以下错误

for neighbour in neighbours:
TypeError: 'Vertex' object is not iterable

以下是我的代码

class Vertex:
    def __init__(self,key):
        self.id = key
        self.connectedTo = {}

    # add neighbouring vertices to the current vertex along with the edge weight
    def addNeighbour(self,nbr,weight=0):
        self.connectedTo[nbr] = weight

    #string representation of the object
    def __str__(self):
        return str(self.id) + " is connected to " + str([x.id for x in self.connectedTo])

    def getConnections(self):
        return self.connectedTo.keys()

    def getId(self):
        return self.id

    def getWeight(self,nbr):
        return self.connectedTo[nbr]

class Graph:
    def __init__(self):
        self.vertList = {}
        self.numVertices = 0

    def addVertex(self,key):
        self.numVertices += 1
        newVertex = Vertex(key)
        self.vertList[key] = newVertex
        return newVertex

    def getVertex(self,n):
        if n in self.vertList:
            return self.vertList[n]
        else:
            return None

    def addEdge(self,f,t,cost=0):
        if f not in self.vertList:
            nv = self.addVertex(f)

        if t not in self.vertList:
            nv = self.addVertex(t)

        self.vertList[f].addNeighbour(self.vertList[t],cost)

    def getVertices(self):
        return self.vertList.keys()

    def __iter__(self):
        return iter(self.vertList.values())

# I have only included few words in the list to focus on the implementation
wordList = ["CAT", "BAT", "COT", "COG", "COW", "RAT", "BUT", "CUT", "DOG", "WED"]

def buildGraph(wordList):
    d = {} #in this dictionary the buckets will be the keys and the words will be their values
    g = Graph()
    for i in wordList:
        for j in range(len(i)):
            bucket = i[:j] + "_" + i[j+1:]
            if bucket in d:
                #we are storing the words that fall under the same bucket in a list 
                d[bucket].append(i)
            else:
                d[bucket] = [i]

    # create vertices for the words under the buckets and join them
    #print("Dictionary",d)
    for bucket in d.keys():
        for word1 in d[bucket]:
            for word2 in d[bucket]:
                #we ensure same words are not treated as two different vertices
                if word1 != word2:
                    g.addEdge(word1,word2)
    return g

def bfs_shortest_path(graph, start, goal):
    explored = []
    queue = [[start]]
    if start == goal:
        return "The starting node and the destination node is same"
    while queue:
        path = queue.pop(0)
        node = path[-1]
        if node not in explored:
            neighbours = graph[node] # it shows the error here
            for neighbour in neighbours:
                new_path = list(path)
                new_path.append(neighbour)
                queue.append(new_path)
                if neighbour == goal:
                    return new_path

            explored.append(node)

    return "No connecting path between the two nodes"

# get the graph object
gobj = buildGraph(wordList)
# just to check if I am able to fetch the data properly as mentioned above where I get the error (neighbours = graph[node])
print(gobj["CAT"]) # ['COT', 'BAT', 'CUT', 'RAT']
print(bfs_shortest_path(gobj, "CAT", "DOG"))

要检查每个顶点的相邻顶点,我们可以

for v in gobj:
    print(v)

输出如下所示,正确描述了上图。

CAT is connected to ['COT', 'BAT', 'CUT', 'RAT']
RAT is connected to ['BAT', 'CAT']
COT is connected to ['CUT', 'CAT', 'COG', 'COW']
CUT is connected to ['COT', 'BUT', 'CAT']
COG is connected to ['COT', 'DOG', 'COW']
DOG is connected to ['COG']
BUT is connected to ['BAT', 'CUT']
BAT is connected to ['BUT', 'CAT', 'RAT']
COW is connected to ['COT', 'COG']
CAT is connected to ['COT', 'BAT', 'CUT', 'RAT']

那可能会出现什么问题?

1 个答案:

答案 0 :(得分:0)

好的,所以我想出了问题。问题在于这行代码

neighbours = graph[node]

基本上它正在尝试获取特定节点的邻域。因此它需要访问被声明为vertList类的属性的Graph字典。因此,对象可以访问dictonary值,必须实现__getitem__特殊方法。所以我在Graph类下声明如下

# returns the value for the key which will be an object    
def __getitem__(self, key):
    return self.vertList[key]

现在graph[node]将能够获取节点的对象表示,因为vertList字典的值是顶点对象(vertList将顶点名称存储为键,顶点对象存储为值因此,我必须明确告诉它获取对象的邻居而不是对象本身。所以我可以在getConnections()类下调用Vertex方法,进一步调用{{1}获取特定顶点对象的邻居对象的属性。 (connectedTo字典将顶点对象作为键,边缘权重作为值。)

所以现在这些邻居对象将拥有自己的ID,我可以访问它并将其用于BFS操作。下面的行是修改后的代码(在connectedTo方法下)完成上述工作。

bfs_shortest_path

现在我获取特定节点的neigbours列表并使用它。其余代码保持不变。