簡單圖模板 Graph

來源:互聯網
上載者:User

標籤:圖論   python   演算法   

仿寫 networkx 的功能

# -*- coding: cp936 -*-'''                            簡單圖 Graph:要求:    關於節點:        功能1.add_node:            通過 add_node 一次性加一個節點            字串,數字,任何可以被雜湊的 python 對象都可以當做節點            包括自訂的類型或者另一個圖( 構成超圖 )        格式:            >>> G.add_node( 1 )            功能2.add_nodes_from:            通過其他容器加入節點,比如list, dict,set,檔案或者另一個圖        格式:            >>> G.add_nodes_from( [2, 3] )            >>> H = Graph()            >>> H.add_path( range( 10 ) )            >>> G.add_nodes_from( H ) # 構成超圖                關於邊:        功能1.add_edge:            加入一條邊        格式:            >>> G.add_edge( 1, 2 ) # 在節點 1 和節點 2 之間加一條邊                    功能2.add_edges_from:            加入邊集        格式:            >>> G.add_edges_from( [ ( 2, 4 ), ( 1, 3 ) ] ) # 通過邊集來加邊            >>> G.add_edges_from( H.edges() ) # 通過另一個容器來加邊        註:            當加邊操作的兩個節點不存在時,會自動建立出來    關於屬性:        每個圖,邊,節點都可以擁有 '索引值對' 屬性        初始時都為空白,但是允許被動態添加和修改屬性        可以通過 add_node, add_edge 操作,或者直接對節點,邊,圖的字典進行修改        功能1.節點屬性:            >>> G = Graph( name = 'scheme' )            >>> G.graph            { 'name': 'scheme' }                        >>> G.add_node( 1, city = 'Nanjing' )            >>> G.nodes[1]            { 'city': 'Nanjing' }                        >>> G.node            { 1: {'city': 'Nanjing'} }                        >>> G.add_node( 1 ) # 但是再加入相同的節點時,屬性不會消失掉            >>> G.node            {1: {'city': 'Nanjing'}}            >>> G.add_node( 1, nowtime = '8:00' ) # 加入相同的節點,帶上新的屬性時,原來的屬性會被更新            >>> G.node            {1: { 'city': 'Nanjing', 'nowtime': '8:00' } }            >>> G.add_node( 1, nowtime = '8:10' )            >>> G.node            {1: { 'city': 'Nanjing', 'nowtime': '8:10' } }                        >>> G.nodes[1]['nowtime'] = '10:00' # 但是當節點 1 不存在時,這樣寫屬性需要報錯            >>> del G.nodes[1]['nowtime']            >>> G.add_nodes_from( range( 7, 9 ), city = 'shanghai' )            註:                pass                        功能2.邊的屬性:            >>> G.add_edge( 1, 2 weight = 100 )            >>> G.edge            { 1: { 2: { 'weight': 100 } }, 2: { 1: { 'weight': 100 } } }                        >>> G.add_edges_from( [ ( 1, 3 ), ( 2, 3 ) ], weight = 111 )            >>> G.edge            { 1: { 2: { 'weight': 100 }, 3: { 'weight': 111 } },            2: { 1: { 'weight': 100 }, 3: { 'weight': 111 } },            3: { 1: { 'weight': 111 }, 2: { 'weight': 111 } } }                        >>> G.add_edges_from( [ ( 1, 3, { 'weight': 1 } ), ( 3, 4, { 'weight': 2 } ) ] )            >>> G.edge            { 1: { 2: { 'weight': 100 }, 3: { 'weight': 1 } },            2: { 1: { 'weight': 100 }, 3: { 'weight': 111 } },            3: { 1: { 'weight': 1 }, 2: { 'weight': 111 },            4: { 'weight': 2 } }, 4: { 3: { 'weight': 2 } } }            >>> G.edge[1][2]['weight'] = 111111 # 允許直接操作            或者            >>> G[1][2]['weight'] = 1111    利用 python 的特性提供快捷操作:        比如:            >>> 1 in G            True            >>> [ n for n in G if n < 3 ] # 節點迭代            [1, 2]            >>> len(G) # 節點個數            5            >>> G[1] # 與該點相鄰的所有點的屬性            { 2: { 'weight': 100 }, 3: { 'weight': 1 } }        提供 adjacency_iter 來遍曆邊:            >>> for node, nbrsdict in G.adjacency_iter():                    for nbr, attr in nbrsdict.items():                        if 'weight' in attr:                            ( node, nbr, attr['weight'] )            (1, 2, 100)            (1, 3, 1)            (2, 1, 100)            (2, 3, 111)            (3, 1, 1)            (3, 2, 111)            (3, 4, 2)            (4, 3, 2)            >>> [ ( start, end, attr['weight'])             for start, end, attr in G.edges( data = True ) if 'weight' in attr ]            [(1, 2, 100), (1, 3, 1), (2, 3, 111), (3, 4, 2)]   其他一些功能:       pass'''class Graph( object ):    def __init__( self, data = None, **attr ):        self.graph = {}        self.node  = {}        self.adj   = {}        if data is not None:            pass        self.graph.update( attr )        self.edge = self.adj    @property    def name( self ):        return self.graph.get( 'name', '' )    @name.setter    def name( self, newname ):        self.graph['name'] = newname    def __str__( self ):        return self.name    def __iter__( self ):        return iter( self.node )    def __contains__( self, node ):        try:            return node in self.node        except TypeError:            return False    def __len__( self ):        return len( self.node )    def __getitem__( self, node ):        return self.adj[node]    def add_node( self, node, attr_dict = None, **attr ):        if attr_dict is None:            attr_dict = attr        else:            try:                attr_dict.update( attr )            except AttributeError:                raise Exception( "The attr_dict argument must be a dictionary." )        if node not in self.node:            self.adj[node] = {}            self.node[node] = attr_dict        else:            self.node[node].update( attr_dict )            def add_nodes_from( self, nodes, **attr ):        for node in nodes:            try:                newnode = node not in self.node            except TypeError:                node_, node_dict = node                if node_ not in self.node:                    self.adj[node_] = {}                    newdict = attr.copy()                    newdict.update( node_dict )                    self.node[node_] = newdict                else:                    olddict = self.node[node_]                    olddict.update( attr )                    olddict.update( node_dict )                continue            if newnode:                self.adj[node] = {}                self.node[node] = attr.copy()            else:                self.node[node].update( attr )                                def remove_node( self, start ):        try:            nbrs = list( adj[start].keys() )            del self.node[start]        except KeyError:            raise Exception( "The node %s is not in the graph."%( start ) )        for end in nbrs:            del self.adj[start][end]        del self.adj[start]            def remove_nodes_from( self, nodes ):        for start in nodes:            try:                del self.node[start]                for end in list( self.adj[start].keys() ):                    del self.adj[end][start]                del self.adj[start]            except KeyError:                pass    def nodes( self, show_info = False ):        def nodes_iter( show_info = False ):            if show_info:                return iter( self.node.item() )            return iter( self.node )                return list( nodes_iter( show_info = show_info ) )    def number_of_nodes( self ):        return len( self.node )    def order( self ):        return len( self.node )        def has_node( self, node ):        try:            return node in self.node        except TypeError:            return False    def add_edge( self, start, end, attr_dict = None, **attr ):        if attr_dict is None:            attr_dict = attr        else:            try:                attr_dict.update( attr )            except AttributeError:                raise Exception( "The attr_dict argument must be a dictionary." )        if start not in self.node:            self.adj[start]  = {}            self.node[start] = {}                    if end not in self.node:            self.adj[end]  = {}            self.node[end] = {}        data_dict = self.adj[start].get( end, {} )        data_dict.update( attr_dict )        self.adj[start][end] = data_dict        self.adj[end][start] = data_dict            def add_edges_from( self, edges, attr_dict = None, **attr ):        if attr_dict is None:            attr_dict = attr        else:            try:                attr_dict.update( attr )            except AttributeError:                raise Exception( "The attr_dict argument must be a dictionary." )        for edge in edges:            elem_num = len( edge )            if elem_num == 3:                start, end, start_end_dict = edge            elif elem_num == 2:                start, end = edge            else:                raise Exception( "Edge tuple %s must be a 2-tuple or 3-tuple."%( edge ) )            attr_dict.update( start_end_dict )            self.add_edge( start, end, attr_dict )                    def remove_edge( self, start, end ):        try:            del self.adj[start][end]            if start != end:                del adj[end][start]        except KeyError:            raise Exception( "The edge %s-%s is not in the graph"%( start,end ) )    def remove_edges_from( self, edges ):        for edge in edges:            start, end = edge[:2]            if start in self.adj and end in self.adj[start]:                del self.adj[start][end]                if start != end:                    del self.adj[end][start]    def has_edge( self, start, end ):        try:            return end in self.adj[start]        except KeyError:            return False            def neighbors( self, node ):        try:            return list( self.adj[node] )        except KeyError:            raise Exception( "The node %s is not in the graph."%( node,) )            def neighbors_iter( self, node ):        try:            return iter( self.adj[n] )        except KeyError:            raise Exception( "The node %s is not in the graph."%( node,) )            def edges( self, nodes = None, show_info = False ):        def edges_iter( nodes, show_info = False ):            seen = {}            if nodes is None:                nodes_nbrs = self.adj.items()            else:                nodes_nbrs = ( ( node, self.adj[node] ) for node in self.nodes_container( nodes ) )                            if show_info:                for node, nbrs in nodes_nbrs:                    for nbr, data in nbrs.items():                        if nbr not in seen:                            yield ( node, nbr, data )                    seen[node] = 1            else:                for node, nbrs in nodes_nbrs:                    for nbr, data in nbrs.items():                        if nbr not in seen:                            yield ( node, nbr )                    seen[node] = 1            del seen                return list( edges_iter( nodes, show_info = show_info ) )                def get_edge_data( self, start, end, default = None ):        try:            return self.adj[start][end]        except KeyError:            return default            def adjacency_list( self ):        return list( map( list, iter( self.adj.values() ) ) )        def adjacency_iter( self ):        return iter( self.adj.items() )            def degree( self, nodes_container = None, weight = None ):        '''            功能:                返回一個節點或者一系列節點的度( degree )                            參數:                nodes_container: 可以被迭代的容器,可選,預設值為所有節點                weight: 字串或者為空白,可選,預設為空白,                        若是為None,則所有的邊的權重都設為 1,                        degree 則是所有與節點相鄰的 weight 權重的邊的個數之和                                    傳回值:                字典或者數字                字典: 一系列節點與它們的索引值對                數字: 一個指定節點的度                            例如:                >>> G = nx.Graph()   # or DiGraph, MultiGraph, MultiDiGraph, etc                >>> G.add_path( [ 0, 1, 2, 3 ] )                >>> G.degree(0)                1                >>> G.degree( [ 0, 1 ] )                { 0: 1, 1: 2 }                >>> list( G.degree( [ 0, 1 ] ).values() )                [ 1, 2 ]        '''        if nodes_container in self:            return next( self.degree_iter( nodes_container, weight ) )[1]        else:            return dict( self.degree_iter( nodes_container, weight ) )            def degree_iter( self, nodes_container = None, weight = None ):        '''            功能:                返回一個( node, degree ) 的迭代對象                            參數:                nodes_container: 可以被迭代的容器,可選,預設值為所有節點                weight: 字串或者為空白,可選,預設為空白,                        若是為None,則所有的邊的權重都設為 1,                        degree 則是所有與節點相鄰的 weight 權重的邊的個數之和            傳回值:                返回一個( node, degree ) 的迭代對象        '''        if nodes_container is None:            nodes_nbrs = self.adj.items()        else:            nodes_nbrs = ( ( node, self.adj[node] )                            for node in self.nodes_container_iter(                               nodes_container ) )        if weight is None:            for node, nbrs in nodes_nbrs:                yield ( node, len( nbrs ) + ( node in nbrs ) )        else:            for node, nbrs in nodes_nbrs:                yield (node, sum( ( nbrs[nbr].get( weight, 1 ) for nbr in nbrs ) ) +                              ( node in nbrs and nbrs[node].get( weight, 1 ) ) )                    def clear( self ):        self.name = ''        self.adj.clear()        self.node.clear()        self.graph.clear()            def copy( self ):        from copy import deepcopy        return deepcopy( self )        def is_multigraph( self ):        return False        def is_directed( self ):        return False        def subgraph( self, nodes ):                from copy import deepcopy                nodes = self.nodes_container_iter( nodes )        H = self.__class__()        H.graph = deepcopy( self.graph )        for node in nodes:            H.node[node] = deepcopy( self.node[node] )        for node in H.node:            H_nbrs = {}            H.adj[node] = H_nbrs            for nbr, attr in self.adj[node].items():                if nbr in H.adj:                    H_nbrs[nbr] = attr                    H.adj[nbr][node] = attr        return H    def nodes_with_selfloops( self ):        return [ node for node, nbrs in self.adj.items() if node in nbrs ]    def selfloop_edges( self, show_info = False ):        if show_info:            return [ ( node, node, nbrs[node] )                     for node, nbrs in self.adj.items() if node in nbrs ]        else:            return [ ( node, node )                     for node, nbrs in self.adj.items() if node in nbrs ]        def number_of_selfloops( self ):        return len( self.selfloop_edges() )        def size( self, weight = None ):        s = sum( self.degree( weight = weight ).values() ) / 2        if weight is None:            return int( s )        else:            return float( s )    def number_of_edges( self, start = None, end = None ):        if start is None:            return int( self.size() )        if end in self.adj[start]:            return 1        else:            return 0    def add_path( self, nodes, **attr ):        node_list = list( nodes )        edges = zip( node_list[:-1], node_list[1:] )        self.add_edges_from( edges, **attr )            def add_cycle( self, nodes, **attr ):        node_list = list( nodes )        edges = zip( node_list, node_list[1:]  + [node_list[0]] )        self.add_edges_from( edges, **attr )            def nodes_container_iter( self, nodes_container = None ):        '''            功能:                返回存在圖中且在 nodes_container 中的節點的迭代對象            參數:                nodes_container: 可以被迭代的容器,可選,預設值為所有節點            傳回值:                nodes_iter: iterator                        '''        if nodes_container is None:            container = iter( self.adj.keys() )                    elif nodes_container in self:            container = iter( [nodes_container] )                    else:            def container_iter( node_list, adj ):                try:                    for node in node_list:                        if node in adj:                            yield node                except TypeError as e:                    message = e.args[0]                    import sys                    sys.stdout.write( message )                    if 'iter' in message:                        raise Exception( "nodes_container is not a node or a sequence of nodes." )                    elif 'hashable' in message:                        raise Exception( "Node %s in the sequence nbunch is not a valid node."%n )                    else:                        raise            container = container_iter( nodes_container, self.adj )        return container


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.