Изучение методов обнаружения цикла в связанном списке: решения LeetCode

В этой статье блога мы углубимся в различные методы обнаружения циклов в связанном списке. Мы рассмотрим различные подходы, а также примеры кода, для решения проблемы цикла связанных списков в LeetCode. К концу этой статьи вы получите полное представление о различных методах идентификации циклов в связанном списке.

Метод 1: два указателя (алгоритм черепахи и зайца Флойда)
В этом алгоритме используются два указателя, часто называемые «черепахой» и «зайцем». Черепаха делает один шаг за раз, а заяц делает два шага за раз. Если цикл существует, черепаха и заяц в конечном итоге встретятся в одном узле.

def hasCycle(head):
    if not head or not head.next:
        return False

    slow = head
    fast = head.next

    while slow != fast:
        if not fast or not fast.next:
            return False
        slow = slow.next
        fast = fast.next.next

    return True

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

def hasCycle(head):
    visited = set()

    while head:
        if head in visited:
            return True
        visited.add(head)
        head = head.next

    return False

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

class ListNode:
    def __init__(self, val):
        self.val = val
        self.next = None
        self.visited = False
def hasCycle(head):
    while head:
        if head.visited:
            return True
        head.visited = True
        head = head.next

    return False

Метод 4: алгоритм «Черепаха и заяц» Флойда с длиной цикла
Этот вариант алгоритма «Черепаха и заяц» Флойда не только обнаруживает наличие цикла, но и вычисляет его длину.

def detectCycle(head):
    if not head or not head.next:
        return None

    slow = head
    fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

        if slow == fast:
            cycle_length = get_cycle_length(slow)
            return find_cycle_start(head, cycle_length)

    return None
def get_cycle_length(slow):
    current = slow
    length = 0

    while True:
        current = current.next
        length += 1
        if current == slow:
            break

    return length
def find_cycle_start(head, cycle_length):
    pointer1 = head
    pointer2 = head

    for _ in range(cycle_length):
        pointer2 = pointer2.next

    while pointer1 != pointer2:
        pointer1 = pointer1.next
        pointer2 = pointer2.next

    return pointer1

В этой статье мы рассмотрели различные методы обнаружения циклов в связанном списке, приведя примеры кода для каждого подхода. Мы рассмотрели алгоритм двух указателей (алгоритм черепахи и зайца Флойда), реализацию хэш-набора, модификацию структуры узла и вариант алгоритма Флойда для расчета длины цикла. Понимая эти методы, вы сможете уверенно решать проблему цикла связанных списков на LeetCode и аналогичных платформах кодирования.

Не забудьте проанализировать требования вашей конкретной проблемы и выбрать наиболее подходящий метод. Приятного кодирования!