Космическая сложность Дейкстры

Я работал над некоторыми вопросами Leetcode для алгоритма Дейкстры и не совсем понимаю его пространственную сложность. Я поискал в Интернете, но нашел разные ответы, некоторые из которых были довольно сложными, поэтому мне хотелось узнать, правильно ли я их понял.

# initialize maxheap
maxHeap = [(-1, start_node)]
heapq.heapify(maxHeap)

# dijkstras algorithm
visit = set()
res = 0
while maxHeap:
    prob1,cur = heapq.heappop(maxHeap)
    visit.add(cur)

    # update result
    if cur == end_node: 
        res = max(res, -1 * prob1)

    # add the neighbors to the priority queue
    for nei,prob2 in adj_list[cur]:
        if nei not in visit: heapq.heappush(maxHeap, (prob1 * prob2, nei))

Поскольку я использую набор visit и priority queue для отслеживания узлов, будет ли сложность пространства просто равна O (V), где V — количество вершин в графе? А если бы мне пришлось создать список смежности в Python с помощью dict, имела бы пространственная сложность O(E), где E — количество ребер?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Отвечать:

  1. Ваш набор ни в коем случае не может доминировать в приоритетной очереди.
  2. Если список смежности имеет значение на вашей стороне (т. е. вы получаете необработанные данные, скажем, описанные на человеческом языке и вам нужно самостоятельно построить список смежности), то он может доминировать, а может и не доминировать, в зависимости от реализации приоритета. очередь и свойство графа.

Объяснение:

Heapq Python использует список в качестве базовой структуры данных, вы уже купили его, как и maxHeap = [(-1, start_node)]. Таким образом, его пространственная сложность равна Θ(n), где n — количество элементов в списке.

Какой тогда номер? это зависит от ребер и вершин в графе, краткий ответ: если вершины доминируют над ребрами, то O(V), если наоборот, то O(E), но для общего случая мы можем обозначить это как O(V+E) ). это O(V+E) вместо O(V), учитывая асимптотическое соотношение между V и E, его можно дополнительно переписать как O(V^2).

Почему? поскольку вы использовали heapq Python, он не обеспечивает операцию increamentKey/decreamentKey, при обработке графа вам может потребоваться включить много узлов, которые вы еще не можете определить, что с ним делать.

Таким образом, набор никогда не может доминировать, поскольку он равен O(V), в лучшем случае, когда вершины доминируют над ребрами, он может связываться только с heapq. Однако для списка смежности, если вы использовали версию приоритетной очереди DIY, где разрешена операция decrementKey, следовательно, вы получаете сложность пространства O (V), тогда, если E доминирует над V, вы получаете O (E) списка смежности вместо O очереди приоритетов. (В).

Почему минусуют? Хотите комментировать и учить?

PkDrew 02.08.2024 09:04

«если вершины доминируют над ребрами, то O (V)»: это не учитывает сценарий несвязного графа, большинство ребер которого находится в искомом компоненте, в то время как большинство его вершин не находятся в этом компоненте.

trincot 02.08.2024 09:32

Да, это правда, поскольку OP использует Python heapq, здесь будет храниться только исходный узел, по моему незнанию, отредактированный.

PkDrew 02.08.2024 09:51
Ответ принят как подходящий

Поскольку я использую набор посещений и очередь приоритетов для отслеживания узлов, будет ли сложность пространства просто равна O (V), где V — количество вершин в графе?

Что касается комплекта: да. В худшем случае целью является последний узел, который можно посетить, и тогда set в конце концов будет иметь запись для каждого узла, достижимого из начального узла, поэтому O(𝑉'), где 𝑉' - количество узлы в связном компоненте, где вы начинаете поиск. Это O(𝑉), когда граф связен.

Что касается приоритетной очереди: это не гарантируется. Поскольку вы помечаете узлы как посещенные только тогда, когда извлекаете их из очереди, вы потенциально можете иметь несколько вхождений одного и того же узла в очереди в определенный момент. Ограничение на количество вызовов heappush определяется количеством ребер. Таким образом, мы имеем наихудшую космическую сложность O(𝐸) для приоритетной очереди.

И если бы мне пришлось создать список смежности в Python с помощью dict, имела бы пространственная сложность O(E), где E — количество ребер?

Это зависит от того, создаете ли вы dict-keys для узлов, у которых нет исходящих ребер (с пустыми списками в качестве значений). Если да, то это Θ(𝑉+𝐸). Если вы опустите ключи, которые представляли бы такие узлы (без исходящих ребер), то это действительно Θ(𝐸), но тогда ваша программа должна иметь проверку, существует adj_list[cur] или нет.

Другие замечания

  • Вам не нужно вызывать heapify, если в вашем списке всего один элемент. Список с одним элементом уже является допустимой кучей.

  • Когда вы извлекаете узел из очереди, вам следует проверить, был ли он уже посещен. Это могло произойти, когда узел был помещен в очередь несколько раз (поскольку он встречался по разным путям), прежде чем из него было извлечено первое вхождение. В этом случае вы пометите его как посещенный, поскольку первое вхождение будет извлечено из очереди, но затем оно может в конечном итоге быть извлечено во второй раз: и тогда вам не следует его обрабатывать.

  • Когда вы найдете целевой узел и у вас есть res, вам следует выйти из цикла.

  • Вместо присвоения res было бы неплохо обернуть этот код в функцию и вернуть это значение.

Так:

def dijkstra(adj, start_node, end_node):
    # initialize maxheap
    maxHeap = [(-1, start_node)]

    # dijkstra's algorithm
    visit = set()
    while maxHeap:
        prob1, cur = heapq.heappop(maxHeap)
        if cur in visit:
            continue
        visit.add(cur)

        # update result
        if cur == end_node: 
            return max(res, -1 * prob1)

        # add the neighbors to the priority queue
        for nei,prob2 in adj_list[cur]:
            if nei not in visit: 
                heapq.heappush(maxHeap, (prob1 * prob2, nei))

    return 0  # end_node not reachable

Другие вопросы по теме