python数据结构教程 Day16

本章内容

  1. 通用深度优先DFS算法
  2. 单源最短路径问题
  3. 最小生成树

一、通用深度优先DFS算法

一般的深度优先搜索目标是在图上进行尽量深的搜索,连接尽量多的顶点,必要时可以进行分支(创建了树) 有时候深度优先搜索会创建多棵树,称为“深度优先森林”。深度优先搜索同样要用到顶点的“前驱” 属性,来构建树或森林,另外要设置“发现时间”和“结束时间”属性。

  • 前者是在第几步访问到这个顶点(设置灰色)
  • 后者是在第几步完成了此顶点探索(设置黑色)

与BFS的区别在于,在添加兄弟节点之前,先添加层次

代码实现:
class DFSGraph(Graph):
    def __init__(self):
        super().__init__()
        self.time = 0

    def dfs(self):
        '''
        可能会生成多个深度优先搜索森林
        '''
        #颜色初始化
        for aVertex in self:#父类已经实现了iter()方法
            aVertex.setColor('White')
            aVertex.setPred(-1)
        #如果还有未包括的顶点,则建森林
        for aVertex in self:
            if aVertex.getColor() == 'White':
                self.dfsvisit(aVertex)

    def dfsvisit(self, rootVertex):
        '''
        不同于BTS中的队列,这里的递归隐式使用了栈
        '''
        rootVertex.setColor('Gray')
        self.time += 1
        rootVertex.setDiscovery(self.time)
        for nextVertex in rootVertex.getconnections():
            if nextVertex.getColor == 'White':
                nextVertex.setPred(rootVertex)
                self.dfsvisit(nextVertex)
        rootVertex.setColor('Black')
        self.time += 1
        rootVertex.setFinish(self.time)

即一个顶点的“发现时间”总小于所有子顶点的“发现时间” 而“结束时间”则大于所有子顶点“结束时间” 比子顶点更早被发现,更晚被结束探索,类比括号和子括号的关系。

DFS算法分析:

dfs函数中有两个循环,每个都是|V|次,所以 是O(|V|)。而dfsvisit函数中的循环则是对当前顶点所连接的顶点进行,而且仅有在顶点为白色的情况下才进行递归调用,所以对每条边来说只会运行一 步,所以是O(|E|) 加起来就是和BFS一样的O(|V|+|E|)。

DFS算法的应用:
1、拓扑排序

拓扑排序处理一个DAG,输出顶点的线性序列,使得两个顶点v,w,如果G中有(v,w)边,在线性序列中v就出现在w之前。

在如上所示的DFS之中,进行拓扑排序非常方便按照vertex中finish从大到小的顺序,即可快速完成拓扑排序。

2、强连通分支
定义:

图G的一个子集C,C中的任意两个顶点v,w之间都有路径来回,即 (v,w)(w,v)都是C的路径, 而且C是具有这样性质的最大子集。

在图中发现高度聚集节点群的算法,即寻找“强连通分支Strongly Connected Components”算法。一旦找到强连通分支,可以据此对图的顶点进行分类,并对图进行化简。

示例:
先熟悉一个概念:Transposition转置

一个有向图G的转置GT,定义为将图G的所有边的顶点交换次序,如将(v,w)转换为(w,v) 可以观察到图和转置图在强连通分支的数量和划分上,是相同的。

Kosaraju算法:
对G进行DFS,获得每个vertex的finish
G进行转置得到GT
对GT使用DFS(注意在每个顶点的搜索循环中,要以顶点的结束时间倒序顺序进行搜索)得到深度优先森林,那么其中的每一棵树都是一个强连通分支
示例:

第一趟:

第二趟:

结果:

二、单源最短路径问题

目标:获得从一个节点出发到达图中任意顶点的最短距离。

解决带权图的最短路径问题,与使用BFS解决词梯问题非常相似,只不过边中带了权重。这里使用Dijkstra算法,迭代得出一个顶点到所有顶点的最短路径。实现上在Vertex类中添加dist来存储起点到本结点的最短权重之和,顶点的访问次序由优先队列控制。队列中作为优先级的是dist,具有最小dist的结点出队,计算其它结点的权重和,引起堆的重排,随后根据更新dist优先级再依次出队。注意Dijkstra只能处理权值为正的带权图。

代码实现:
def dijkstra(graph, start):
    #对所有顶点建堆形成优先队列
    pq = PriorityQueue()
    start.setDistance(0)
    pq.buildHeap([(v.getDistance(), v) for v in graph])
    while not pq.empty():
        #优先队列出队
        currentVert = pq.delmin()
        #修改邻接顶点dist,并逐个重排队列
        for nextVertex in currentVert.getconnections():
            newDist = currentVert.getDistance() + currentVert.getweight(nextVertex)
            if newDist < nextVertex.getDistance():
                nextVertex.setDistance(newDist)
                nextVertex.setPred(currentVert)
                pq.decreaseKey(nextVertex, newDist)

Dijkstra算法需要具备整个图的数据,涉及巨大数据量的问题,同时动态变化的数据特性也使得保存全图缺乏现实性。

Dijkstra算法分析:

首先,将所有顶点加入优先队列并建堆,时间复杂度为O(|V|) 。其次,每个顶点仅出队1次,每次delMin 花费O(log|V|),一共就是O(|V|log|V|) 。另外,每个边关联到的顶点会做一次 decreaseKey操作(O(log|V|)),一共是O(|E|log|V|) 。上面三个加在一起,数量级就是O((|V|+|E|)log|V|)。

三、最小生成树

生成树:

拥有图中所有的顶点和最少数量的边, 以保持连通的子图。

图G(V,E)的最小生成树,指包含所有节点v,以及E的无圈子集,并且边的权重之和最小。

解决:

最小生成树问题的Prim算法,属于 “贪心算法” ,即每步都沿着最小权重的边向前搜索。

思路:
if T还不是生成树:
    则反复做:
        找到一条最小权重的可以安全添加的边
        将边添加到树T

“可以安全添加”的边:

定义为一端顶点在树中,另一端不在树中的边,以便保持树的无圈特性。

代码实现:
def prim(G,start):
    pq = PriorityQueue()
    # 节点初始化
    for v in G:
        v.setDistance(sys.maxsize)
        v.setPred(None)
    start.setDistance(0)
    pq.buildHeap([(v.getDistance(), v) for v in G])
    while not pq.empty():
        currentVert = pq.delmin()
        # distsance仅代表从current到next的距离,根据distance进行重排
        for nextVert in currentVert.getconnections():
            newCost = currentVert.getweight(nextVert)
            if nextVert in pq and newCost < nextVert.getDistance():
                nextVert.setPred(currentVert)
                nextVert.setDistance(newCost)
                pq.decreasekey(nextVert, newCost)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345