数据结构(五)图

图的存储结构相对于线性表和树来说更为复杂,因为图中的顶点具有相对概念,没有固定的位置图是由(V, E)来表示的,V是顶点的集合,E是边的集合。
图看起来就像下图这样:
图有各种形状和大小。边可以有权重(weight),即每一条边会被分配一个正数或者负数值。边是可以有方向的。在上面提到的例子中,边是没有方向的。

 

1.为什么要使用图?

例如,假设你有一系列任务需要完成,但是有的任务必须等待其他任务完成后才可以开始。你可以通过非循环有向图来建立模型:
每一个顶点代表一个任务。两个任务之间的边表示目的任务必须等到源任务完成后才可以开始(先决条件)。比如,在任务B和任务D都完成之前,任务C不可以开始。在任务A完成之前,任务B和D都不能开始。
现在这个问题就通过图描述清楚了,你可以使用深度优先搜索算法来执行执行拓扑排序。这样就可以将所有的任务排入最优的执行顺序,保证等待任务完成的时间最小化。(这里可能的顺序之一是:A, B, D, E, C, F, G, H, I, J, K
2.图的存储结构
图的结构有多种形式,这里介绍两种常用的结构:邻接矩阵(数组表示法)和邻接表
 
(1)邻接矩阵
        用两个数组分别存储数据元素信息和数据元素之间的关系信息。无向图和邻接矩阵如下:
    0代表两点之间没有关联;1代表有关联
需要注意的是,当边上有权值的时候,称之为网图。邻接矩阵中的元素是两点间的权值,若两点间没有联系,元素为∞
 
代码实现
class Graph:
    def __init__(self, gdict={}):
        self.gdict = gdict
    # 获取所有结点
    def Node(self):
        return list(self.gdict.keys())
    # 计算出邻接矩阵(二维数组)
    def Matrix(self):
        binary_matrix = []
        node = self.Node()
        for v1 in node:
            binary_matrix.append([])
            for v2 in node:
                if v2 in self.gdict[v1]:
                    binary_matrix[-1].append(1)
                else:
                    binary_matrix[-1].append(0)
        return binary_matrix
 
 
(2)邻接表
邻接表是图的一种链式存储结构。在邻接表中对图的每个顶点建立一个单链表左边的节点称为顶点节点,其结构体包含顶点元素和指向第一条边的指针;右边的为边节点。 对于有权值的网图,只需要在边节点增加一个权值的成员变量即可。
 
代码实现
class Graph:
    def __init__(self, gdict={}):
        self.gdict = gdict
    # 生成N个链表
    def LinkedList(self):
        # 链表的数据结构
        class ListNode:
            def __init__(self, x):
                self.val = x
                self.next = None
        # 填充每一个链表
        res = []
        node = list(self.gdict.keys())
        for v1 in node:
            temp = ListNode(v1)
            cur = temp
            for v2 in node:
                if v2 in self.gdict[v1]:
                    cur.next = ListNode(v2)
                    cur = cur.next
            res.append(temp)
        return res
 
 
*3.拓扑排序
针对于DAG图(有向无环图)。按照某种规则将这个图的顶点取出来,这些顶点能够表示什么或者有什么联系。 顶点的顺序是保证所有指向它的下个节点在被指节点前面! 拓扑排序序列不一定唯一「拓扑排序」的一个附加效果是:能够顺带检测有向图中是否存在环
步骤:
  • 从DGA图中找到一个没有前驱的顶点输出。(可以遍历,也可以用优先队列维护)
  • 删除以这个点为起点的边。(它的指向的边删除,为了找到下个没有前驱的顶点)
  • 重复上述,直到最后一个顶点被输出。如果还有顶点未被输出,则说明有环!
 
代码实现
class Graph:
    def __init__(self, gdict={}):
        self.gdict = gdict
        self.linkedlist = self.LinkedList()
    # 生成图的邻接表结构
    def LinkedList(self):
        class ListNode:  # 链表结构
            def __init__(self, x):
                self.val = x
                self.next = None
        links = []
        node = list(self.gdict.keys())   # 图的原始数据是以“前驱”为键,“后继”为值的哈希表
        for v1 in node:
            temp = ListNode(v1)
            cur = temp
            for v2 in node:
                if v2 in self.gdict[v1] and v1 != v2:
                    cur.next = ListNode(v2)
                    cur = cur.next
            links.append(temp)
        return links
    # 拓扑排序(用邻接表排序)
    def TopoSort(self):
        indegree = {}    # 存储每个结点的入度的哈希表
        edge = {}   # 存储每个结点的后继(也就是结点的关系)
        # “拆解”每个链表,填充 入度哈希表 和 后继哈希表
        for link in self.linkedlist:
            node = link.val
            edge[node] = []
            if not indegree.get(link.val):
                indegree[link.val] = 0    
            link = link.next
            while link: # 表头之后
                edge[node].append(link.val)
                if indegree.get(link.val):
                    indegree[link.val] += 1
                else:
                    indegree[link.val] = 1
                link = link.next
        # 下面就是针对邻接表的拓扑排序
        res = []
        while indegree:
            for n in indegree:
                if indegree[n] == 0:    # 找到度为0的结点
                    res.append(n)
                    for e in edge[n]:
                        indegree[e] -= 1
                    del indegree[n]
                    break
            else:  # 遍历入度哈希表后没有找到入度为0的结点,则一定存在环
                return "别TM搞笑了,这是个带环图!"
        return res
'''
graph = {
    "a": {"b"},
    "b": {"c"},
    "c": {"d"},
    "d": {"e"},
    "e": {}
    }
obj = Graph(graph)
out = obj.TopoSort()
print(out)
'''
 
 
*4.关键路径
 

猜你喜欢

转载自www.cnblogs.com/xiaoqichaoren/p/12951665.html