图-Traversal

图的两种遍历

何谓遍历:从图中某一顶点出发,访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程谓之图的遍历(Traversing Graph)

深度优先遍历(Depth First Search)

该方法相当于一根筋走到底,以图1左边为例。假设每次都选择右边的路口,当右边无路可走或者已经走过时,返回上一个节点,选择左边的路口。遍历路线为:
A-B-C-D-E-F-G-H-I

图1-DFS遍历过程

邻接矩阵

思路:
1.需要一个数组存储哪些顶点遍历过,哪些顶点没有遍历。visitied = []
2.其实是一个递归的过程,每次改变的只是“起始节点”

关注点:
1.dfsTravese()函数中,第一个for循环,可看成对邻接矩阵的行循环,避免一些“孤立”(在其他分支上)的点被遗漏,如图1-DFS遍历过程中的 I 节点。
2.每次调用dfs()函数时都需要检查一遍该节点是否已经被遍历过。
3.dfs(nodeIndex)函数中的for循环可看做是邻接矩阵中的的循环

class adjacencyMatrix(object):
    def __init__(self, nodeNum, edgeNum):
        # 存储邻接矩阵中节点的个数
        self.nodeNum = nodeNum
        # 存储邻接矩阵中节点边的数量
        self.edgeNum = edgeNum
        # 在遍历时检查节点是够被使用过
        self.visited = None
        # 记录每个节点实际对应的名字
        self.nodeNameList = ['v'+str(_) for _ in range(nodeNum)]
        # 记录每个节点下标
        self.NodeList = []
        # 记录每条边的下标,(定位权重用的)
        self.EdgeList = []
        for i in range(nodeNum):
            self.EdgeList.append([NULLFLAG] * edgeNum)

    '''
    使用邻接矩阵创建图的时候,其实就是构造一个长宽均为节点数量的正方形。
    行和列的交汇点代表边的权重
    通过 头结点和尾节点的下标,可以定位连接边的权重值。
    '''
    def createGraph(self, prevIndex, weight, nextIndex):
        if prevIndex not in self.NodeList:
            self.NodeList.append(prevIndex)
            self.EdgeList[prevIndex][prevIndex] = 0
        if nextIndex not in self.NodeList:
            self.NodeList.append(nextIndex)
            self.EdgeList[nextIndex][nextIndex] = 0
        self.EdgeList[prevIndex][nextIndex] = weight
        self.EdgeList[nextIndex][prevIndex] = weight

    '''
    递归调用节点:遍历行节点,然后递归调用行节点内对应的所有节点
    需要记录遍历过程
    '''
    def dfsTravese(self):
        self.visited = [False] * self.nodeNum
        for inode in range(self.nodeNum):
            if self.visited[inode] is False:
                self.dfs(inode)

    def dfs(self, nodeIndex):
        print(self.nodeNameList[nodeIndex])
        self.visited[nodeIndex] = True
        for j in range(self.nodeNum):
            if (self.EdgeList[nodeIndex][j] != NULLFLAG) and (self.visited[j] is False):
                self.dfs(j)


邻接表

class AdjacencyList(object):
    def __init__(self):
        self.vertex = {}
        self.visited = None

    def createGraph(self, startIndex, weight, endIndex):
        if startIndex not in self.vertex.keys():
            vertex_node = VertexNode(startIndex, 'v' + str(startIndex), None)
            self.vertex[startIndex] = vertex_node
        if endIndex not in self.vertex.keys():
            vertex_node = VertexNode(endIndex, 'v' + str(endIndex), None)
            self.vertex[endIndex] = vertex_node

        edgeNode = EdgeNode(endIndex, 'v' + str(endIndex), weight=weight, nextNode=None)
        tmp = self.vertex[startIndex].firstEdge
        edgeNode.nextNode = tmp
        self.vertex[startIndex].firstEdge = edgeNode



    def dfsTravese(self):
        self.visited = [False]*len(self.vertex)
        for i in range(len(self.vertex)):
            if self.visited[i] == False:
                self.dfs(i)

    def dfs(self, i):
        print("node name is ", self.vertex[i].name)
        self.visited[i] = True
        node = self.vertex[i].firstEdge
        while node:
            if self.visited[node.index] == False:
                self.dfs(node.index)
            node = node.nextNode

广度优先遍历

在广度优先遍历中,最直观的理解是:从第一行(邻接矩阵),依次遍历每一行对应的所有列,操作(可以为输出等)与该行对应的所有点。具体可参考树的层序遍历,与之不同的是,广度优先遍历借助了《队列》先进先出的思想来实现。

广度优先遍历
邻接矩阵

1、第一层循环:遍历举矩阵中的所有行,防止遗漏点,若改点没有被访问过,则加入队列中,并将改点标记为已经访问。
2、当对列不为空时,依次取出队列中的点,并将与该点相连的所有点进行检查,若无访问记录,则加入队列。

class adjacencyMatrix(object):
    def __init__(self, nodeNum, edgeNum):
        # 存储邻接矩阵中节点的个数
        self.nodeNum = nodeNum
        # 存储邻接矩阵中节点边的数量
        self.edgeNum = edgeNum
        # 在遍历时检查节点是够被使用过
        self.visited = None
        # 记录每个节点实际对应的名字
        self.nodeNameList = ['v'+str(_) for _ in range(nodeNum)]
        # 记录每个节点下标
        self.NodeList = []
        # 记录每条边的下标,(定位权重用的)
        self.EdgeList = []
        for i in range(nodeNum):
            self.EdgeList.append([NULLFLAG] * edgeNum)
    '''
    借助了队列
    把每一行对应的节点加入队列中,若队列空则下一行。
    另外,需要遍历队列中的元素对应的行中的所有元素
    '''
    def bfsTravese(self):
        q = Queue()
        self.visited = [True] * self.nodeNum
        for i in range(self.nodeNum):
            if self.visited[i] is True:
                q.put(i)
                print('node name is ', self.nodeNameList[i])
                self.visited[i] = False
                while q:
                    if q.empty():
                        return
                    nodeIndex = q.get()
                    # print('node name is ', self.nodeNameList[nodeIndex])
                    for j in range(self.nodeNum):
                        if (self.visited[j] is True) and (self.EdgeList[nodeIndex][j] != NULLFLAG):
                            q.put(j)
                            self.visited[j] = False
                            print('node name is ', self.nodeNameList[j])

邻接表
class AdjacencyList(object):
    def __init__(self):
        self.vertex = {}
        self.visited = None

    def bfs(self):
        self.visited = [0] * len(self.vertex)

        q = Queue()
        for i in range(len(self.vertex)):
            if self.visited[i] == 0:
                self.visited[i] = 1
                q.put(self.vertex[i].index)
                while q:
                    if q.empty():
                        break
                    visitIndex = q.get()
                    print(self.vertex[visitIndex].name)

                    edgeNode = self.vertex[visitIndex].firstEdge
                    while edgeNode is not None:
                        if self.visited[edgeNode.index] == 0:
                            self.visited[edgeNode.index] = 1
                            q.put(edgeNode.index)
                        edgeNode = edgeNode.nextNode

参考文献

  • 《大话数据结构》
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容

  • 一、动态规划 找到两点间的最短路径,找最匹配一组点的线,等等,都可能会用动态规划来解决。 参考如何理解动态规划中,...
    小碧小琳阅读 24,811评论 2 20
  • 数据结构学不好,c++就到后面会很迷,数据结构真滴很重要啊,上机题一定要认真做,紧密的和实际操作的代码联系在一起是...
    Nancy_Shi阅读 720评论 0 4
  • 图的定义与术语 1、图按照有无方向分为无向图和有向图。无向图由顶点和边构成,有向图由顶点和弧构成。弧有弧尾和弧头之...
    unravelW阅读 402评论 0 0
  • 这两天在读刘同的《我在未来等你》,感触颇深。 36岁的郝回归遇到了17岁的自己,他和他自己成为了好朋友,帮着17岁...
    后知后觉_6293阅读 920评论 0 0
  • 【原创】富有的十大习惯 一、坚持每天读书自学,可以利用软件也可以读纸质书籍。 二、至少每天30分钟的有氧运动,利用...
    我心我愿秀阅读 14,539评论 19 501