Какова временная сложность получения соседей узла в сетиx?
>>> G = nx.Graph()
>>> G.add_edges_from([('a', 'b'), ('a', 'c')])
>>> G.neighbors('a')
Короткий ответ: получение соседей принимает линейное время O (m) (с m число соседей или в маловероятных сценариях O (m + n) с n числом узлов). Добавление ребер к сети обычно также приводит к линейному времени, а также O (n) в количестве добавляемых ребер, поэтому постоянным для одного края, но в (очень) маловероятном сценарии может принимать квадратичное время O (n 2). Вы можете получить доступ к словарю соседей в постоянное время (ну и в худшем случае - линейное время) с помощью 'G [' a '].
Мы можем проверить исходный код и посмотреть:
def neighbors(self, n): # ... try: return list(self.adj[n]) except KeyError: raise NetworkXError("The node %s is not in the graph." % (n,))
Теперь, если проверить исходный код, мы видим, что (по умолчанию) adj
является словарем, действительно:
node_dict_factory = dict adjlist_dict_factory = dict edge_attr_dict_factory = dict def __init__(self, data=None, **attr): # ... self.node_dict_factory = ndf = self.node_dict_factory # ... self.node = ndf() # empty node attribute dict # ...
Таким образом, даже если поиск в словаре происходит в худшем случае, но очень маловероятен - линейное время O (n) в количестве узлов, тогда у нас есть только одно линейное время для преобразования списка ключей в список.
С другой стороны, метод add_edges_from
реализуется как:
def add_edges_from(self, ebunch, attr_dict=None, **attr): # ... for e in ebunch: ne = len(e) if ne == 3: u, v, dd = e elif ne == 2: u, v = e dd = {} # doesnt need edge_attr_dict_factory else: raise NetworkXError( "Edge tuple %s must be a 2-tuple or 3-tuple." % (e,)) if u not in self.node: self.adj[u] = self.adjlist_dict_factory() self.node[u] = {} if v not in self.node: self.adj[v] = self.adjlist_dict_factory() self.node[v] = {} datadict = self.adj[u].get(v, self.edge_attr_dict_factory()) datadict.update(attr_dict) datadict.update(dd) self.adj[u][v] = datadict self.adj[v][u] = datadict
Таким образом, мы в основном итерации каждый элемент в списке, сделать некоторые распаковки, а затем добавить словарь данных дважды в adj
(один раз в одном направлении, один раз в другом). Если поиск словаря выполняется быстро (обычно это постоянное время), то этот алгоритм имеет линейное время (в количестве добавляемых кортежей). Наихудший случай (но очень маловероятный) поиск в словаре может занять линейное время, поэтому вставка может масштабироваться до O (n 2).
Однако класс Graph
позволяет получить доступ к подзадаче с помощью G['a']
:
>>> G['a']
AtlasView({'b': {}, 'c': {}})
AtlasView
представляет собой представление по словарю, чтобы предотвратить редактирование базового словаря.
G['a']
, это вернет вам подчиненное значение этого узла, и ключи, таким образом, содержат соседей. Таким образом, это (при нормальных обстоятельствах) даст вам словарь в постоянном времени. Однако обратите внимание, что тогда вы получили доступ к «данным» графика, и изменение словаря окажет влияние на ваш графикG