[Изменить вопрос и тему было перефразировано, чтобы уточнить, что мы проверяем циклы в двусвязном списке, а не проверяем, является ли двусвязный список просто круглым, поэтому части этого поста могут быть неактуальными.]
Это список двойных ссылок, каждый узел имеет указатели 'next' и 'previous'.
Списки с двойной связью обычно реализуются с указанием заголовка и хвоста списка.в NULL, чтобы указать, где они заканчиваются.
[Редактировать] Как указывалось, проверяется только то, является ли список круглым в целом, не содержит ли он циклы в нем, но это была формулировкаисходный вопрос. Если список является круглым, то tail-> next == head и / или head-> prev == tail.Если у вас нет доступа как к хвостовому, так и к головному узлу, и у вас есть только один из них, но не оба, тогда достаточно просто проверить, является ли head-> prev! = NULL или tail-> next! = NULL.
Если это не достаточный ответ, потому что нам дан только какой-то случайный узел [и мы ищем циклы в любом месте списка], тогда все, что вам нужно сделать, это взять этот случайный узел и продолжать обход списка, пока выдостичь узла, который соответствует (в этом случае это круговой) или нулевой указатель (в этом случае это не так).
Однако, это по сути то же самое, что и ответ, который вы уже предоставили, который интервьюер не сделал 'не нравится.Я совершенно уверен, что без какого-либо магического взлома невозможно найти цикл в связанном списке при условии случайного узла без алгоритма линейной сложности.
[Редактировать] Мой разум переключил передачи сейчасс акцентом на обнаружение циклов в списке, а не на определение того, является ли связанный список круглым.
Если у нас есть такой случай, как: 1 <-> 2 <-> 3 <-> [2]
Единственный способ, которым я вижу, что мы можем обнаруживать циклы, - это отслеживать все элементы, которые мы прошли до сих пор, и искать любые совпадения на этом пути.
Конечно, это может быть дешево.Если бы нам было разрешено изменять узлы списка, мы могли бы сохранять флаг простого обхода для каждого узла, который мы установили, когда делаем это.Если мы встречаем узел с уже установленным флагом, то мы нашли цикл.Тем не менее, это не сработает для параллелизма.
Здесь предложено решение [которое я украл из другого ответа], называемое «Алгоритм нахождения циклов Флойда».Давайте посмотрим на него (модифицированный, чтобы мне было легче его читать).
function boolean hasLoop(Node startNode)
{
Node fastNode2 = startNode;
Node fastNode1 = startNode;
Node slowNode = startNode;
while ( slowNode && (fastNode1 = fastNode2.next()) && (fastNode2 = fastNode1.next()) )
{
if (slowNode == fastNode1 || slowNode == fastNode2)
return true;
slowNode = slowNode.next();
}
return false;
}
В основном это предполагает использование 3 итераторов вместо 1. Мы можем рассмотреть случай, подобный следующему: 1->2-> 3-> 4-> 5-> 6 -> [2] case:
Сначала мы начнем с [1] с быстрого итератора до [2], а другого с [3] или [1, 2, 3].Мы останавливаемся, когда первый итератор совпадает с любым из двух вторых итераторов.
Переходим к [2, 4, 5] (первый быстрый итератор пересекает следующий узел второго быстрого итератора, а второй быстрый итераторпосле этого проходит следующий узел первого быстрого итератора).Затем [3, 6, 2] и, наконец, [4, 3, 4].
Да, мы нашли совпадение и, таким образом, определили список, содержащий цикл из 4 итераций.