Jak określić, czy lista połączona ma cykl, używając tylko dwóch miejsc pamięci

Czy ktoś zna algorytm do znajdowania, czy lista połączona pętli na siebie za pomocą tylko dwóch zmiennych do przejścia listy. Powiedzmy, że masz połączoną listę obiektów, nie ma znaczenia, jaki typ obiektu. Mam wskaźnik do głowy połączonej listy w jednej zmiennej i mam tylko jedną inną zmienną, aby przejść listę z.

Więc moim planem jest porównanie wartości wskaźników, aby sprawdzić, czy jakiekolwiek wskaźniki są takie same. Lista jest skończona, ale może być ogromna. Mogę ustawić oba zmienna do głowicy, a następnie przemierzać listę z inną zmienną, zawsze sprawdzając, czy jest równa drugiej zmiennej, ale, jeśli nie hit pętli nigdy się z niej. Myślę, że ma to związek z różnymi szybkościami przechodzenia listy i porównywania wartości wskaźnika. Jakieś pomysły?

Author: Bill the Lizard, 2009-01-30

7 answers

Proponuję użyć Floyd's Cycle-Finding Algorithm aka The Tortoise and the Hare Algorithm. Ma złożoność O (n) i myślę, że pasuje do Twoich wymagań.

Przykładowy kod:

function boolean hasLoop(Node startNode){
  Node slowNode = Node fastNode1 = Node fastNode2 = startNode;
  while (slowNode && fastNode1 = fastNode2.next() && fastNode2 = fastNode1.next()){
    if (slowNode == fastNode1 || slowNode == fastNode2) return true;
    slowNode = slowNode.next();
  }
  return false;
}

Więcej informacji na Wikipedii: algorytm wyszukiwania cyklu Floyda.

 46
Author: Baishampayan Ghose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-30 08:01:32

Możesz użyć algorytmu żółwia i królika.

Wikipedia też ma wyjaśnienie i nazywają to "algorytmem znajdowania cyklu Floyda " lub "żółw i zając"

 17
Author: martinus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-04 22:39:11

Absolutnie. Jednym z rozwiązań rzeczywiście może być przemierzanie listy z obu wskaźników, jeden podróżuje z dwukrotnym tempem drugiego.

Zacznij od "powolnego" i "szybkiego" wskaźnika wskazującego dowolne miejsce na liście. Uruchom pętlę trawersową. Jeśli wskaźnik "szybki" w dowolnym momencie pokrywa się ze wskaźnikiem wolnym, masz okrągłą listę połączoną.

int *head = list.GetHead();
if (head != null) {
    int *fastPtr = head;
    int *slowPtr = head;

    bool isCircular = true;

    do 
    {
        if (fastPtr->Next == null || fastPtr->Next->Next == null) //List end found
        {
            isCircular = false;
            break;
        }

        fastPtr = fastPtr->Next->Next;
        slowPtr = slowPtr->Next;
    } while (fastPtr != slowPtr);

    //Do whatever you want with the 'isCircular' flag here
}
 9
Author: Frederick The Fool,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-30 09:02:22

Próbowałem rozwiązać ten sam i znalazłem inne (mniej efektywne, ale nadal optymalne) rozwiązanie.

Idea opiera się na odwróceniu listy pojedynczo połączonej w czasie liniowym. Można to zrobić, wykonując dwa swapy na każdym kroku iteracji listy. Jeśli q jest poprzednim elementem (początkowo null), A P jest bieżącym, swap(q,p->next) swap(P,q) odwróci łącze i przesunie Dwa wskaźniki w tym samym czasie. Swapy można wykonać za pomocą XOR, aby zapobiec konieczności korzystania z trzeciej pamięci miejsce.

Jeśli lista ma cykl, to w pewnym momencie podczas iteracji dotrzesz do węzła, którego wskaźnik został już zmieniony. Nie możesz wiedzieć, który to węzeł, ale kontynuując iterację, zamieniając kilka elementów dwukrotnie, ponownie trafiasz na początek listy.

Odwracając listę dwukrotnie, lista pozostaje niezmieniona w wyniku i możesz stwierdzić, czy miała cykl na podstawie tego, czy dotarłeś do pierwotnego początku listy, czy nie.

 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-05 17:52:17
int isListCircular(ListNode* head){
    if(head==NULL)
        return 0;
    ListNode *fast=head, *slow=head;
    while(fast && fast->next){
        if(fast->next->next==slow)
            return 1;
        fast=fast->next->next;
        slow=slow->next;
    }
    return 0;
}
 2
Author: rajya vardhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-09 20:21:18

Przejście tego problemu do następnego kroku będzie oznaczeniem cyklu (czyli nie tylko tego, że cykl istnieje, ale gdzie dokładnie znajduje się na liście). Algorytm żółwia i zająca może być używany do tego samego, jednak będziemy wymagać śledzenia głowy listy przez cały czas. Ilustrację tego algorytmu można znaleźć tutaj .

 0
Author: ND_27,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-11 20:15:06
boolean findCircular(Node *head)
{
Node *slower, * faster;
slower = head;
faster = head->next;
while(true) {


 if( !faster || !faster->next)
   return false;

 else if (faster == slower || faster->next == slower)
    return true;
 else{

    faster = faster->next->next;
   }
}
}
 0
Author: user5297378,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-03 14:59:34