zoukankan      html  css  js  c++  java
  • python 算法 day11 图 词梯

    顶点 vertex:是图的基础部分

    边 edge:如果一个边连接两个点,则表示两者具有联系,边可以是单向的也可以是双向的,如果一个图中的边都是单向的,我们就说这个图是有向图

    权重 weight:一个顶点到另一个顶点的“代价”,可以给边赋权

    路径 path:由边依次连接起来的顶点序列

    圈 cycle:有向图里的圈是首尾顶点相同的路径

    邻接矩阵

    用邻接矩阵表示图的优点是简单,很容易看出节点之间的联系状态,然而会有大量的矩阵分量是空的

    邻接表

    一个更高效的方法是使用邻接表,我们采用维护一个包含所有顶点的主列表,再关联一个与自身有边连接的所以顶点的列表,我们采用字典来实现顶点类,字典中的key对应连接的顶点标示,value对应与自身连接的点的权重

    class Vertex:
        def __init__(self,key):
            self.id = key
            self.connectedTo = {}
        def addNeighbor(self,nbr,weight=0):
            self.connectedTo[nbr] = weight  #添加邻居节点以及权重
        def __str__(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]

    图的类是把刚才的顶点类和顶点重新构造成一个字典

    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, item):   重载in
            return item 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())

     词梯问题

    • 以图的形式描绘出单词间的关系
    • 利用广度优先搜索(BFS)的图算法找到一条从开始单词到目标单词的最短路径

    首先解决如何将大量单词组成的集合转变成图,我们先假设我们有一个单词列表,其中的单词都一样长,我们可以为列表里的每个单词在图中创建一个顶点,而为将这些单词连接起来,我们可以将列表中的每个词与所有其他单词比较,当我们比较的时候,我们要看到单词中有多少字母是不同的,如果两个词中只有一个字母不同,我们就可以在图中创建一条连接他们的边 算法复杂度是O(n2)

    优化方法:假设我们有非常多的桶,每个桶外都贴有一个四字字母的单词标签,并且标签上有且只有一个字母被通配符-代替

    我们可以通过一个字典来实现 桶上的标签作为一个key值  value对应桶里单词的列表

    from pythonds.graphs import Graph
    def buildGraph(wordFile):
        d = {}
        g = Graph()
        w_file = open(wordFile,'r')  这里文件每行只有一个单词
        for line in w_file:
            word = line[:-1] 去除行结尾的回车字符
            for i in range(len(word)):
                bucket = word[:i]+'_'+word[i+1:]  将第i个字符去除作为通配符
                if bucket in d:
                    d[bucket].append(word)
                else:
                    d[bucket] = [word]
        for bucket in d.keys():
            for word1 in d[bucket]:
                for word2 in d[bucket]:
                    if word1 !=word2:
                        g.addEdge(word1,word2)

     实现广度优先搜索(BFS)

    已知一个图G和它的一个起始节点s,广度优先搜索(BFS)通过搜索图中的边来找到图G中所有和s有路径相连的顶点,其显著特点是在搜索达到距离k+1的顶点之前,BFS会找全部距离为k的顶点 为了追踪这一过程,会给每一个搜索过的顶点染成灰色 每一个顶点在被构建时都初始化为白色,当完全搜索完一个层级的节点时 它会被染成黑色,这意味着一旦一个节点染成黑色,它就没有邻近的白色节点,而另一方面,如果一个顶点被标示成了灰色,这就意味这附近还可能存在为探索的顶点等待被探索

     节点类中还需添加三个新的实例变量:距离 父顶点和颜色

    对于起始节点 设置0和None 随后起始节点加入一个队列中,下一步便是系统地搜索队首顶点,这个过程通过迭代遍历队首顶点的邻接列表来完成,每检查邻接表中的一个顶点,便会维护这个顶点的颜色变量:

    1. 新的未探索的顶点nbr 标记成灰色
    2. nbr的父顶点被设置成当前节点currentVert
    3. nbr的距离被设置成当前节点的距离+1
    4. nbr被加入队尾,这一操作使得直到nbr在当前顶点的邻接列表中所有节点被搜索完后,才能进行下一层次的探索操作
    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)
                    nbr.setPred(currentVert)
                    vertQueue.enqueue(nbr)  每次标记完节点 再把下一层节点 为白色的加入队列  直到最后没有元素

     对BFS的分析:

    当while循环被执行时,图的顶点集合|v|的每个顶点最多被访问一次,因而while循环拥有O(V)的复杂度 while中的for循环语句,对于图的边集|E|中的每一条边至多会被执行一次,这是因为每个顶点只会最多被出队一次,对于从顶点u到顶点v的边,只有当顶点u出队的时候我们才会检查,所以每条边最多被检查一次 算法复杂度为O(V+E)

  • 相关阅读:
    REST Security with JWT using Java and Spring Security
    UserMapper.selectByPrimaryKey-Inline 报错的解决办法
    Nginx反向代理,负载均衡,redis session共享,keepalived高可用
    HTML 5 Web 存储-localStorage
    Android之自定义checkbox样式
    android fragment传递参数_fragment之间传值的两种方法
    linux常用基本命令
    fragment点击跳转到外部Activity后,怎么通过返回按钮返回
    android 中FragmentActivity中模拟返回键返回上一个Activity效果
    Fragment与Activity相互传递数据:
  • 原文地址:https://www.cnblogs.com/suizhixxie/p/10449880.html
Copyright © 2011-2022 走看看