图-实现, 2020.02.26

(2020.02.26)
图数据结构有如下四种实现方式。

实现方式对比

Edge list structure边列表

用非排序list分别保存一个图的顶点(V)和边(E)。V保存(n个)顶点名。E保存(m个)边名,并且每个边对象包含两个指针,分别是该边的两个顶点。

复杂度分析:

添加顶点,O(1);添加一条边,O(1);删除一个顶点(考虑到要在E中删除所有相关边),O(m),删除一个边,O(1).

顶点用set表示更快。

Adjacent list structure邻近列表

用一个list保存图的顶点V,每个vertex对应一个collection I(v),这个I(v)中保存顶点v的incident edge(无无向图)。传统上I(v)是个list,所以这种表达方式成为adjacent list.

2020.02.27

Adjacent map structure(all-round optimal性能最佳)

用list/dict保存顶点V,每个顶点v对应了一个map/dict,其中的key是与v相连的其他顶点,value是两个顶点相连的边。(we let the opposite endpoint of each incident edge serve as a key in the map, with the edge structure serving as the value.)

{..., v : {u: e, w: g}, ...}

该表达在graph的4种表达方式里全方面性能最优。

adjacent matrix structure(适用于dense graph)

根据顶点个数n生成一个n*n的矩阵M,vertices are indiced from 0 to n-1,矩阵中的M(i, j)点保存的顶点i到顶点j的信息。如果用boolean表达,1则表示两点间有边,否则为0;对有向图,1和-1可用于表达incoming和outgoing。如果非boolean表达,矩阵中的值可保存边的权值。该矩阵是对称阵。对于sparse graph,这种表达过于redundant。该表达适用于dense graph.

复杂度分析对比

image

(2022.07.01 Fri)

图的实现

这里按adjacent map形式实现图结构。图由类Graph实现,其中的顶点和边分别由类Vertex和类Edge实现。顶点和边类中都加入了__hash__方法,便于分别做为映射或set的键。图的ADT包含如下方法:

  • vertices/vertex_count:图的所有顶点,以及顶点个数
  • edges/edge_count:图的所有边,以及边的个数
  • get_edge(u, v):根据给定顶点,判断顶点间有无边
  • degree(v):计算顶点v的度
  • incident_edge(v, outgoing=True):与顶点v相邻的所有边
  • insert_vertex(v):插入顶点v
  • insert_edge(u, v, x=None):插入由顶点u和v组成的边
class Vertex:
    __slots__ = '_element'
    def __init__(self, x):
        self._element = x
    def element(self):
        return self._element
    def __hash__(self):
        return hash(id(self))

class Edge:
    __slots__ = '_origin', '_destination', '_element'
    def __init__(self, a, b, c):
        self._origin = a
        self._destination = b
        self._element = c
    def element(self):
        return self._element
    def endpoints(self):
        return (self._origin, self._destination)
    def opposite(self, x):
        return self._destination if x is self._origin else self._origin
    def __hash__(self):
        return hash((self._origin, self._destination))

class Graph:
    def __init__(self, directed=False):
        self._outgoing = {}
        self._incoming = {} if directed else self._outgoing
    def is_directed(self):
        return self._incoming is not self._outgoing
    def vertex_counting(self):
        return len(self._outgoing)
    def vertices(self):
        return self._outgoing.keys()
    def edge_counting(self):
        total = sum(len(self._outgoing[v] for v in self._outgoing))
        return total if self.is_directed() else total // 2
    def edges(self):
        result = set()
        for v in self._outgoing.values():
            result.update(v.values())
        return result
    def get_edge(self, u, v):
        return self._outgoing[u].get(v) # returns None is v not adjacent
    def degree(self, v, outgoing=True):
        adj = self._outgoing if outgoing else self._incoming
        return len(adj[v])
    def incident_edges(self, v, outgoing=True):
        adj = self._outgoing if outgoing else self._incoming
        for k in adj[v].values():
            yield k
    def insert_vertex(self, v, outgoing=True):
        v = Vertex(v)
        self._outgoing[v] = {}
        if self.is_directed():
            self._incoming = {}
        return v
    def insert_edge(self, u, v, x=None):
        e = Edge(u, v, x)
        self._outgoing[u][v] = e
        self._incoming[v][u] = e
        return 'done'

Reference:

1 M. Goodrich and etc, Data Structures and Algorithms in Python.

你可能感兴趣的:(图-实现, 2020.02.26)