图---广度优先搜索(BFS)

图—广度优先搜索(BFS)

Breadth First Search是搜索图的最简单算法之一,

  1. 给定图G,以及开始搜索的起始顶点s
  • BFS搜索所有从s可到达顶点的边,而且在达到更远的距离k+1的顶点之前,BFS会找到全部距离为k的顶点
  • 可以想象为以s为根,构建一棵树的过程,从顶部向下逐步增加层次
  • 广度优先搜索能保证在增加层次之前,添加了所有兄弟结点到树中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OJuXE9FK-1614326400529)(C:\Users\93623\AppData\Roaming\Typora\typora-user-images\image-20210219144349084.png)]

  1. 从Fool开始搜索

  2. 为了跟踪顶点的加入过程,并避免重复顶点,要为顶点增加3个属性

  • 距离distance:从起始顶点到此顶点路径长度
  • 前驱顶点predecessor:可反向追溯到起点
  • 颜色:标识了此顶点是尚未发现(白色),已经发现(灰色), 还是已经完成探索, 还是已经完成探索(黑色)
  1. 还需要用一个队列queue来对已发现的顶点进行排列

  2. 决定下一个要探索的顶点(队首顶点)

  3. 从起始顶点s开始,作为刚发现的顶点,标注为灰色,距离为0,前驱为None,加入队列,下面是循环迭代过程:

  • 从队首取出一个顶点作为当前顶点;
  • 遍历当前顶点的邻接顶点,如果是尚未发现的白色顶点,则将其颜色改为灰色(已发现),距离增加1,前驱顶点为当前顶点,加入到队列中遍历完成后,将当前顶点设置为黑色,循环回到步骤1的队首取当前顶点
def bfs(g, start):
    start.setDistance(0)
    start.setPred(None)
    vertQueue = Queue()
    vertQueue.enqueue(start)
    while vertQueue.size() > 0:
        currentVert = vertQueue.dequeue()
        for nbr in currentVert.getConnections():
            if nbr.getColor() == 'white':
                nbr.setColor('gray')
                nbr.setDistance(currentVert.getDistance()+1)
                vertQueue.enqueue(nbr)
        currentVert.setColor('black')
  1. BFS算法主体是两个循环的嵌套

    while循环对每个顶点访问一次,所以是 O ( ∣ v ∣ ) \mathbf{O}(|\mathbf{v}|) O(v),而嵌套在while中的for,由于每条边只有在其其实顶点u出队的时候才会被检查一次,而每个顶点最多出队1次,所以边最多被检查1次,一共是 O ( ∣ E ∣ ) \mathbf{O}(|\mathbf{E}|) O(E),综合起来BFS的事件复杂度为 0 ( ∣ v ∣ + ∣ E ∣ ) 0(|v|+|E|) 0(v+E)

PS:

词梯文件被存在的地址是 : http://gis4g.pku.edu.cn/course/pythonds/#%E8%AF%BE%E7%A8%8B%E8%B5%84%E6%BA%90 你可以复制网址.在浏览器中打开上面的网址.并在打开的网页中搜索(CTRL+F)关键词 : 字母单词表. 之后,就可以找到一个压缩包.下载到本地并解压它就会得到文章代码所需要的词梯文件.希望回复及时

下面代码建立图和顶点的类:

# coding: utf-8

from collections.abc import *
import sys


class Vertex:
    def __init__(self, key):
        self.id = key
        self.connectedTo = {
    
    }
        # 宽度优先遍历增加以下三个属性START----
        self.color = 'white'
        self.dist = sys.maxsize
        self.pred = None
        # 宽度优先遍历增加以下三个属性END----
        # 深度优先遍历增加两个属性start------------
        self.disc = 0
        self.fin = 0
        # 深度优先遍历增加两个属性end---------------

    def addNeighbor(self, nbr, weight=0):
        self.connectedTo[nbr] = weight

    # 宽度优先增加方法start----------------
    def setColor(self, color):
        self.color = color

    def setDistance(self, d):
        self.dist = d

    def setPred(self, p):
        self.pred = p

    def getPred(self):
        return self.pred

    def getDistance(self):
        return self.dist

    def getColor(self):
        return self.color

    # 宽度优先增加方法end----------------

    # 深度优先增加方法 start---------------
    def setDiscovery(self, dtime):
        self.disc = dtime

    def setFinish(self, ftime):
        self.fin = ftime

    def getFinish(self):
        return self.fin

    def getDiscovery(self):
        return self.disc

    # 深度优先增加方法 end---------------
    # def __str__(self):
    def __repr__(self):
        return str(self.id) + 'connectedTo: ' + 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]


# v1 = Vertex("good")
# v2 = Vertex("morning")
# v1.addNeighbor(v2)
# print(v2.getConnections())
# print(v1.getWeight(v2))

class Graph:
    def __init__(self):
        # 顶点形成的集合
        self.vertList = {
    
    }
        # 顶点的数目
        self.numVertices = 0

    def addVertex(self, key):
        self.numVertices = 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 __contains__(self, n):
        return n in self.vertList

    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].addNeighbor(self.vertList[t], cost)

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

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


if __name__ == '__main__':
    g = Graph()
    # print(isinstance(g, Iterator))
    # print(isinstance(g, Iterable))
    for i in range(6):
        g.addVertex(i)
    print(g.vertList)
    g.addEdge(0, 1, 5)
    g.addEdge(1, 2, 4)
    g.addEdge(2, 3, 9)
    g.addEdge(3, 4, 7)

    g.addEdge(4, 0, 1)
    g.addEdge(0, 5, 2)
    g.addEdge(5, 4, 8)
    g.addEdge(3, 5, 3)
    g.addEdge(5, 2, 1)
    # 下面的for调用__iter__函数
    for v in g:
        for w in v.getConnections():
            print("(%s, %s)" % (v.getId(), w.getId()))

# print(g.vertList)

接下来利用图的广度优先搜索解决词梯问题:

# coding: utf-8
# from queue import Queue
# from pythonds.graphs import Graph, Vertex
import queue_orderdlist_ccc
import graph_ccc


def traverse(y):
    x = y
    # 当前驱不为None
    print(x.getPred())
    while x.getPred() is not None:
        # 打印当前节点的id,就是这个单词
        print(x.getId())
        x = x.getPred()
    print(x.getId())


def buildGraph(wordFile):
    d = {
    
    }
    g = graph_ccc.Graph()
    wfile = open(wordFile, 'r')
    # 采用字典建立桶
    for line in wfile:
        word = line[:-1]
        # print("word++",word)
        for i in range(len(word)):
            bucket = word[:i] + '_' + word[i + 1:]
            # print("bbbb", bucket)
            if bucket in d:
                d[bucket].append(word)
            else:
                d[bucket] = [word]

    # add vertices and edges for words in the same bucket
    for bucket in d.keys():
        for word1 in d[bucket]:
            for word2 in d[bucket]:
                if word1 != word2:
                    g.addEdge(word1, word2)
    return g


def bfs(g, start):
    # print("kkkkkkkk", start)
    start.setDistance(0)
    start.setPred(None)
    # print("ddddd",start.getDistance())
    vertQueue = queue_orderdlist_ccc.Queue()
    vertQueue.enqueue(start)
    # 下面的while循环所有的顶点都放进去过一次
    while vertQueue.size() > 0:
        currentVert = vertQueue.dequeue()
        for nbr in currentVert.getConnections():
            # 说明还没有访问过
            if nbr.getColor() == 'white':
                nbr.setColor('gray')
                nbr.setDistance(currentVert.getDistance() + 1)
                nbr.setPred(currentVert)
                vertQueue.enqueue(nbr)
        currentVert.setColor('black')


if __name__ == '__main__':
    # g = buildGraph('fourletterwords.txt')
    # for item in g.vertList.values():
    #     print(item)
    wordgraph = buildGraph("fourletterwords.txt")
    bfs(wordgraph, wordgraph.getVertex('FOOL'))
    traverse(wordgraph.getVertex('SAGE'))

猜你喜欢

转载自blog.csdn.net/weixin_46129834/article/details/114137158