Как я могу заказать список соединений

В настоящее время у меня есть список соединений, хранящихся в списке, где каждое соединение представляет собой направленную ссылку, которая соединяет две точки, и ни одна точка никогда не связывается с более чем одной точкой или связана более чем с одной точкой. Например:

connections = [ (3, 7), (6, 5), (4, 6), (5, 3), (7, 8), (1, 2), (2, 1) ]

Должен производить:

ordered = [ [ 4, 6, 5, 3, 7, 8 ], [ 1, 2, 1 ] ]

Я пытаюсь сделать это, используя алгоритм, который берет точку ввода и список соединений и рекурсивно вызывает себя, чтобы найти следующую точку и добавить ее в растущий упорядоченный список. Тем не менее, мой алгоритм выходит из строя, когда я не начинаю с правильной точки (хотя это просто вопрос повторения того же алгоритма в обратном порядке), но также и при наличии нескольких не связанных нитей

Как лучше написать эффективный алгоритм для упорядочения этих соединений?

3 ответа

Решение

Алгоритм решения

Вы ищете топологический алгоритм сортировки:

from collections import defaultdict

def topological_sort(dependency_pairs):
    'Sort values subject to dependency constraints'
    num_heads = defaultdict(int)   # num arrows pointing in
    tails = defaultdict(list)      # list of arrows going out
    for h, t in dependency_pairs:
        num_heads[t] += 1
        tails[h].append(t)

    ordered = [h for h in tails if h not in num_heads]
    for h in ordered:
        for t in tails[h]:
            num_heads[t] -= 1
            if not num_heads[t]:
                ordered.append(t)
    cyclic = [n for n, heads in num_heads.iteritems() if heads]
    return ordered, cyclic

if __name__ == '__main__':
    connections = [(3, 7), (6, 5), (4, 6), (5, 3), (7, 8), (1, 2), (2, 1)]
    print topological_sort(connections)

Вот вывод для ваших образцов данных:

([4, 6, 5, 3, 7, 8], [1, 2])

Время выполнения линейно пропорционально количеству ребер (пар зависимостей).

КАК ЭТО УСТРОЕНО

Алгоритм организован вокруг таблицы поиска под названием num_heads, которая ведет подсчет количества предшественников (входящих стрелок). Рассмотрим пример со следующими подключениями: a->h b->g c->f c->h d->i e->d f->b f->g h->d h->e i->b, количество:

node  number of incoming edges
----  ------------------------
 a       0
 b       2
 c       0
 d       2
 e       1
 f       1  
 g       2
 h       2
 i       1

Алгоритм работает путем "посещения" узлов без предшественников. Например, узлы a а также c не имеют входящих ребер, поэтому они посещаются первыми.

Посещение означает, что узлы выводятся и удаляются из графика. Когда узел посещается, мы зацикливаемся на его преемниках и уменьшаем их входящий счетчик на единицу.

Например, в гостевом узле a Идем к его преемнику h уменьшить его входящий счетчик на единицу (так, чтобы h 2 становится h 1,

Аналогично, при посещении узла c Зациклились на его преемниках f а также h, уменьшая их количество на один (так что f 1 становится f 0 а также h 1 становится h 0).

Узлы f а также h у нас больше нет входящих ребер, поэтому мы повторяем процесс их вывода и удаления из графика, пока все узлы не будут посещены. В примере порядок посещения (топологический вид):

a c f h e d i b g

Если num_heads когда-либо приходит в состояние, когда нет узлов без входящих ребер, это означает, что существует цикл, который не может быть топологически отсортирован, и алгоритм завершает работу, чтобы показать запрошенные результаты.

Что-то вроде этого:

from collections import defaultdict
lis = [ (3, 7), (6, 5), (4, 6), (5, 3), (7, 8), (1, 2), (2, 1) ]
dic = defaultdict(list)

for k,v in lis:
    if v not in dic:
        dic[k].append(v)
    else:
        dic[k].extend([v]+dic[v])
        del dic[v]

for k,v in dic.items():
    for x in v:
        if x in dic and x!=k:
            dic[k].extend(dic[x])
            del dic[x]

print dic
print [[k]+v for k,v in dic.items()]

выход:

defaultdict(<type 'list'>, {2: [1, 2], 4: [6, 5, 3, 7, 8]})
[[2, 1, 2], [4, 6, 5, 3, 7, 8]]

Я думаю, что вы можете сделать это в O(n) с чем-то вроде этого:

ordered = {}

for each connection (s,t):
  if t exists in ordered :
     ordered[s] = [t] + ordered[t]
     del ordered[t]
  else:
     ordered[s] = [t]

# Now merge...
for each ordered (s : [t1, t2, ... tN]):
  ordered[s] = [t1, t2, ... tN] + ordered[tN]
  del ordered[tN]

В конце у вас останется что-то вроде этого, но вы можете легко это преобразовать

ordered = { 
  4 : [6, 5, 3, 7, 8],
  2 : [1, 2]
}
Другие вопросы по тегам