연결된 목록에서 루프를 감지하는 방법?


434

Java로 연결된 목록 구조가 있다고 가정하십시오. 노드로 구성됩니다.

class Node {
    Node next;
    // some user data
}

각 노드는 다음 노드를 가리키는 마지막 노드를 제외하고 다음 노드를 가리 킵니다. 리스트가 루프를 포함 할 가능성이 있다고하자. 즉, 널 (null) 대신 최종 노드가리스트의 앞에있는 노드 중 하나에 대한 참조를 갖는다.

쓰는 가장 좋은 방법은 무엇입니까

boolean hasLoop(Node first)

true주어진 노드가 루프가있는 목록의 첫 번째 경우 반환 하고 false그렇지 않으면? 일정한 공간과 적당한 시간이 걸리도록 어떻게 쓸 수 있습니까?

다음은 루프가있는 목록의 모습입니다.

대체 텍스트


50
와우 .. 나는이 고용주를 위해 일하고 finite amount of space and a reasonable amount of time?
싶다

10
@SLaks-루프는 첫 번째 노드로 루프백 할 필요가 없습니다. 반쯤 반복 될 수 있습니다.
jjujuma

109
아래의 답변은 읽을 가치가 있지만 이와 같은 인터뷰 질문은 끔찍합니다. 당신은 답을 알고 있거나 (즉, Floyd의 알고리즘에서 변형을 보았거나) 그렇지 않으며, 추론이나 설계 능력을 테스트하기 위해 아무 것도하지 않습니다.
GaryF

3
공정하게 말하면, "알고있는 알고리즘"의 대부분은 연구 수준의 일을하지 않는 한 이와 같습니다!
Larry

12
@GaryF 그리고 아직 답을 몰랐을 때 그들이 무엇을할지 아는 것이 드러날 것입니다. 예를 들어 알고리즘 지식 부족을 극복하기 위해 어떤 조치를 취하고 누구와 함께 작업 할 것인가?
Chris Knight

답변:


538

Floyd의 사이클 찾기 알고리즘 ( 거북 및 토끼 알고리즘 이라고도 함)을 사용할 수 있습니다 .

아이디어는 목록에 대한 두 개의 참조를 가지고 서로 다른 속도 로 이동하는 것 입니다. 1노드 별로 하나씩 이동하고 노드 별로 이동하십시오 2.

  • 연결된 목록에 루프가 있으면 분명히 충족됩니다.
  • 그렇지 않으면 두 참조 중 하나 nextnull됩니다.

알고리즘을 구현하는 Java 함수 :

boolean hasLoop(Node first) {

    if(first == null) // list does not exist..so no loop either
        return false;

    Node slow, fast; // create two references.

    slow = fast = first; // make both refer to the start of the list

    while(true) {

        slow = slow.next;          // 1 hop

        if(fast.next != null)
            fast = fast.next.next; // 2 hops
        else
            return false;          // next node null => no loop

        if(slow == null || fast == null) // if either hits null..no loop
            return false;

        if(slow == fast) // if the two ever meet...we must have a loop
            return true;
    }
}

29
또한 다시 fast.next전화하기 전에 null 검사를 수행해야 next합니다.if(fast.next!=null)fast=fast.next.next;
cmptrgeekken

12
당신은 (느리게 == 빠른)뿐만 아니라 : (느리게 == 빠른 || slow.next == 빠른) 점검하여 느리게 빠른 점프를 막아야합니다.
Oleg Razgulyaev

13
나는 틀렸다 : 빠른 다음 단계에서 천천히 뛰어 넘기 때문에 빠른 속도로 천천히 뛰어 넘을 수 없다 :)
Oleg Razgulyaev

4
목록에 노드가 하나만 있지 않으면 slow == null 검사는 중복됩니다. Node.next에 대한 하나의 호출을 제거 할 수도 있습니다. 간단하고 빠른 루프 버전은 다음과 같습니다. pastie.org/927591
Kay Sarraute

22
당신은 정말 당신의 참고 문헌을 인용해야합니다. 이 알고리즘은 60 년대 로버트 플로이드 (Robert Floyd)가 발명 한 것으로 플로이드 (Floyd)의 사이클 찾기 알고리즘으로 알려져 있습니다. 거북이와 토끼 알고리즘.
joshperry

127

홀수 길이 목록을 올바르게 처리하고 선명도를 향상시키는 Fast / Slow 솔루션이 개선되었습니다.

boolean hasLoop(Node first) {
    Node slow = first;
    Node fast = first;

    while(fast != null && fast.next != null) {
        slow = slow.next;          // 1 hop
        fast = fast.next.next;     // 2 hops 

        if(slow == fast)  // fast caught up to slow, so there is a loop
            return true;
    }
    return false;  // fast reached null, so the list terminates
}

2
좋고 간결합니다. 이 코드는 slow == fast || (fast.next! = null && slow = fast.next); :)
arachnode.net 3

11
@ arachnode.net 최적화가 아닙니다. 만약 slow == fast.next다음은 slow동일합니다 fast바로 다음 반복에; 매 반복마다 추가 테스트를 희생하면서 최대 한 번의 반복 만 저장합니다.
Jason C

@ ana01 slowfast동일한 참조 경로를 따르기 전에 null이 될 수 없습니다 (모든 베팅이 해제 된 목록을 동시에 수정하지 않는 한).
Dave L.

호기심에서 홀수에 대해 어떻게 작동합니까? 토끼가 홀수 길이의 연결된 목록에서 거북이를 여전히 통과 할 수 없습니까?
theGreenCabbage

1
@theGreenCabbage 토끼의 루프가 반복 될 때마다 거북이보다 한 발 더 앞서갑니다. 따라서 토끼가 3 단계 뒤에 있으면 다음 반복에는 두 번의 홉이 걸리고 거북이는 한 번의 홉이 걸리고 이제 토끼는 2 단계가 걸립니다. 다음 반복 후 토끼는 1 홉 뒤에 있으며 정확하게 잡히고 있습니다. 거북이가 하나를 잡는 동안 토끼가 3 홉을 가져 가면 매번 2 씩 증가하기 때문에 건너 뛸 수 있지만 반복마다 1 씩 증가하기 때문에 건너 뛸 수 없습니다.
Dave L.

52

플로이드 알고리즘보다 우수

Richard Brent는 대체 사이클 감지 알고리즘을 설명했습니다. 했는데, 이것은 토끼와 거북이 [Floyd 's cycle]와 비슷하지만 여기서 느린 노드는 움직이지 않지만 나중에 고정 된 빠른 노드의 위치로 "텔레포트"된다는 점을 제외하고 간격.

설명은 여기에 있습니다 : http://www.siafoo.net/algorithm/11 자신의 알고리즘이 24 ~ 36 % 빠른 플로이드의 사이클 알고리즘보다 것을 브렌트 주장. O (n) 시간 복잡성, O (1) 공간 복잡성

public static boolean hasLoop(Node root){
    if(root == null) return false;

    Node slow = root, fast = root;
    int taken = 0, limit = 2;

    while (fast.next != null) {
        fast = fast.next;
        taken++;
        if(slow == fast) return true;

        if(taken == limit){
            taken = 0;
            limit <<= 1;    // equivalent to limit *= 2;
            slow = fast;    // teleporting the turtle (to the hare's position) 
        }
    }
    return false;
}

이 답변은 굉장합니다!
valin077

1
답변이 정말 마음에 들었습니다. 내 블로그 ( k2code.blogspot.in/2010/04/…) 에 포함되어 있습니다 .
kinshuk4

왜 확인해야 slow.next != null합니까? 내가 볼 수있는 한 slow항상 뒤에 또는 같습니다 fast.
TWiStErRob

알고리즘을 배우기 시작한이 오래 전에 했었습니다. 코드를 편집했습니다. 감사 :)
Ashok Bijoy Debnath

50

내가 목록을 일시적으로 변경했을 때 거북이와 토끼에 대한 대안 솔루션은 그리 좋지는 않습니다.

아이디어는 목록을 걷고 가고 갈수록 뒤집는 것입니다. 그런 다음 이미 방문한 노드에 처음 도달하면 다음 포인터가 "뒤로"를 가리키며 반복이 first다시 진행 되어 종료됩니다.

Node prev = null;
Node cur = first;
while (cur != null) {
    Node next = cur.next;
    cur.next = prev;
    prev = cur;
    cur = next;
}
boolean hasCycle = prev == first && first != null && first.next != null;

// reconstruct the list
cur = prev;
prev = null;
while (cur != null) {
    Node next = cur.next;
    cur.next = prev;
    prev = cur;
    cur = next;
}

return hasCycle;

테스트 코드 :

static void assertSameOrder(Node[] nodes) {
    for (int i = 0; i < nodes.length - 1; i++) {
        assert nodes[i].next == nodes[i + 1];
    }
}

public static void main(String[] args) {
    Node[] nodes = new Node[100];
    for (int i = 0; i < nodes.length; i++) {
        nodes[i] = new Node();
    }
    for (int i = 0; i < nodes.length - 1; i++) {
        nodes[i].next = nodes[i + 1];
    }
    Node first = nodes[0];
    Node max = nodes[nodes.length - 1];

    max.next = null;
    assert !hasCycle(first);
    assertSameOrder(nodes);
    max.next = first;
    assert hasCycle(first);
    assertSameOrder(nodes);
    max.next = max;
    assert hasCycle(first);
    assertSameOrder(nodes);
    max.next = nodes[50];
    assert hasCycle(first);
    assertSameOrder(nodes);
}

루프가 처음이 아닌 다른 노드를 가리킬 때 역방향이 올바르게 작동합니까? 초기 연결리스트가이 1-> 2-> 3-> 4-> 5-> 2와 같으면 (5에서 2 사이의 주기로), 반대로 된리스트는 1-> 2 <-3 <-4처럼 보입니다. <-5? 그리고 그 반대 인 경우 최종 재구성 목록이 망가질까요?
Zenil

1
@ Zenil : 마지막 노드가 목록의 중간을 가리키는 마지막 테스트 케이스를 작성한 이유입니다. 재구성이 작동하지 않으면 해당 테스트는 실패합니다. 예를 들어, 1-> 2-> 3-> 5-> 2의 반전은 1-> 2-> 5-> 4-> 3-> 2가됩니다. 왜냐하면 루프는 목록의 끝에 한 번만 중지하기 때문입니다 루프의 끝에 도달했을 때 (쉽게 감지 할 수 없음)에 도달하지 않았습니다.
meriton

28

거북이와 토끼

Pollard의 rho 알고리즘 살펴보기 . 그것은 똑같은 문제는 아니지만 논리를 이해하고 링크 된 목록에 적용 할 수 있습니다.

(게으 르면 사이클 감지를 확인할 수 있습니다. 확인하십시오. 거북과 토끼에 대한 부분을 확인하십시오.)

여기에는 선형 시간과 2 개의 추가 포인터 만 필요합니다.

자바에서 :

boolean hasLoop( Node first ) {
    if ( first == null ) return false;

    Node turtle = first;
    Node hare = first;

    while ( hare.next != null && hare.next.next != null ) {
         turtle = turtle.next;
         hare = hare.next.next;

         if ( turtle == hare ) return true;
    }

    return false;
}

(대부분의 솔루션은 null nextnext.nextnull 을 모두 확인하지 않습니다 . 또한 거북이가 항상 뒤에 있기 때문에 null을 확인하지 않아도됩니다. 토끼는 이미 그렇게했습니다.)


13

사용자 unicornaddict 는 위의 멋진 알고리즘을 가지고 있지만 불행히도 홀수 길이> = 3의 비 루프 목록에 대한 버그가 포함되어 있습니다. 문제는 fast목록이 끝나기 직전에 "고착"될 수 있다는 것 입니다.slow 것입니다. 루프가 잘못 감지되었습니다.

올바른 알고리즘은 다음과 같습니다.

static boolean hasLoop(Node first) {

    if(first == null) // list does not exist..so no loop either.
        return false;

    Node slow, fast; // create two references.

    slow = fast = first; // make both refer to the start of the list.

    while(true) {
        slow = slow.next;          // 1 hop.
        if(fast.next == null)
            fast = null;
        else
            fast = fast.next.next; // 2 hops.

        if(fast == null) // if fast hits null..no loop.
            return false;

        if(slow == fast) // if the two ever meet...we must have a loop.
            return true;
    }
}

10

이러한 맥락에서, 텍스트 자료는 어디에나 존재합니다. 개념을 이해하는 데 실제로 도움이되는 다이어그램 표현을 게시하고 싶었습니다.

지점 p에서 빠르고 느리게 만나면

빠른 이동 거리 = a + b + c + b = a + 2b + c

느리게 이동 한 거리 = a + b

빠름이 느림보다 2 배 빠르기 때문입니다. 따라서 a + 2b + c = 2 (a + b) 이면 a = c가 됩니다.

따라서 다른 느린 포인터가 head에서 q로 다시 실행될 때 빠른 포인터가 p에서 q로 실행 되므로 포인트 q에서 함께 만나게 됩니다.

여기에 이미지 설명을 입력하십시오

public ListNode detectCycle(ListNode head) {
    if(head == null || head.next==null)
        return null;

    ListNode slow = head;
    ListNode fast = head;

    while (fast!=null && fast.next!=null){
        fast = fast.next.next;
        slow = slow.next;

        /*
        if the 2 pointers meet, then the 
        dist from the meeting pt to start of loop 
        equals
        dist from head to start of loop
        */
        if (fast == slow){ //loop found
            slow = head;
            while(slow != fast){
                slow = slow.next;
                fast = fast.next;
            }
            return slow;
        }            
    }
    return null;
}

2
그림은 수천 단어 이상의 가치가 있습니다. 깔끔하고 간단한 설명 감사합니다!
Calios

1
인터넷에 대한 최고의 설명. 이것은 선형 시간 이후에 빠르고 느린 포인터가 수렴한다는 것을 증명할뿐입니다
VarunPandey

경우 a루프 길이보다 큰 고속으로 여러 루프를 만들 것입니다 및 수식이 distance (fast) = a + b + b + c변경됩니다 a + (b+c) * k + b추가 매개 변수 도입 klopps의 카운트 번호가 빠른 하나에 의해 만들어진 것으로합니다.

9

연산

public static boolean hasCycle (LinkedList<Node> list)
{
    HashSet<Node> visited = new HashSet<Node>();

    for (Node n : list)
    {
        visited.add(n);

        if (visited.contains(n.next))
        {
            return true;
        }
    }

    return false;
}

복잡성

Time ~ O(n)
Space ~ O(n)

공간 복잡도 O (2n)는 어떻습니까?
Programmer345

@ user3543449 당신이 맞아요, 그것은 n고정되어 있어야합니다
Khaled.K

1
이것은 각각 ArrayList에 대한 검사를 포함하고 O (n)이 있고 O (n)이 있기 때문에 실제로는 ~ O (n ^ 2)입니다. 선형 시간 대신 HashSet을 사용하십시오.
Dave L.

3
사이클을 테스트하지 않고 equals및 및 요소를 사용하여 중복 값을 테스트합니다 hashCode. 같은 것이 아닙니다. 그리고 null마지막 요소를 역 참조 합니다. 그리고 질문은에 노드를 저장하는 것에 대해 아무 말도하지 않았습니다 LinkedList.
Lii

2
@Lii 그것은 의사 코드이며 자바 코드가 아닙니다. 그래서 Algorithm
Khaled로

8

다음은 가장 좋은 방법이 아닐 수 있습니다. O (n ^ 2)입니다. 그러나 (최종적으로) 작업을 수행하는 것이 좋습니다.

count_of_elements_so_far = 0;
for (each element in linked list)
{
    search for current element in first <count_of_elements_so_far>
    if found, then you have a loop
    else,count_of_elements_so_far++;
}

for ()를 수행하기 위해 얼마나 많은 요소가 목록에 있는지 어떻게 알 수 있습니까?
Jethro Larson

@JethroLarson : 링크 된 목록의 마지막 노드는 알려진 주소를 가리 킵니다 (많은 구현에서 이것은 NULL 임). 알려진 주소에 도달하면 for-loop를 종료하십시오.
스파키

3
public boolean hasLoop(Node start){   
   TreeSet<Node> set = new TreeSet<Node>();
   Node lookingAt = start;

   while (lookingAt.peek() != null){
       lookingAt = lookingAt.next;

       if (set.contains(lookingAt){
           return false;
        } else {
        set.put(lookingAt);
        }

        return true;
}   
// Inside our Node class:        
public Node peek(){
   return this.next;
}

내 무지를 용서하십시오 (나는 여전히 Java와 프로그래밍에 익숙하지 않습니다). 왜 위의 방법이 효과가 없습니까?

나는 이것이 일정한 공간 문제를 해결하지 못한다고 생각하지만 ... 적당한 시간 안에 적어도 거기에 도착합니까? 링크 된 목록의 공간과 n 개의 요소가있는 집합의 공간 (n은 링크 된 목록의 요소 수 또는 루프에 도달 할 때까지의 요소 수) 만 차지합니다. 그리고 시간이 지남에 따라 최악의 분석은 O (nlog (n))을 제안한다고 생각합니다. contains ()에 대한 SortedSet 조회는 log (n)입니다 (javadoc을 확인하십시오.하지만 TreeSet의 기본 구조는 TreeMap이며, 차례로 빨강-검은 나무입니다). 또는 끝까지 루프), n 조회를 수행해야합니다.


2
예, 일종의 Set이있는 솔루션은 잘 작동하지만 목록의 크기에 비례하는 공간이 필요합니다.
jjujuma

3

클래스를 포함시킬 수 있다면 Node아래에서 구현했을 때 문제를 해결할 것입니다. hasLoop()O (n) 시간에 실행되며의 공간 만 사용합니다 counter. 이것이 적절한 해결책처럼 보입니까? 아니면 포함하지 않고 할 수있는 방법이 Node있습니까? (실제로 구현할 때와 같은 더 많은 메소드가있을 것입니다 RemoveNode(Node n).)

public class LinkedNodeList {
    Node first;
    Int count;

    LinkedNodeList(){
        first = null;
        count = 0;
    }

    LinkedNodeList(Node n){
        if (n.next != null){
            throw new error("must start with single node!");
        } else {
            first = n;
            count = 1;
        }
    }

    public void addNode(Node n){
        Node lookingAt = first;

        while(lookingAt.next != null){
            lookingAt = lookingAt.next;
        }

        lookingAt.next = n;
        count++;
    }

    public boolean hasLoop(){

        int counter = 0;
        Node lookingAt = first;

        while(lookingAt.next != null){
            counter++;
            if (count < counter){
                return false;
            } else {
               lookingAt = lookingAt.next;
            }
        }

        return true;

    }



    private class Node{
        Node next;
        ....
    }

}

1

일정한 O (1) 시간 안에도 할 수 있습니다 (매우 빠르거나 효율적이지는 않지만) : 컴퓨터의 메모리가 보유 할 수있는 노드의 수가 제한되어 있습니다 (N 레코드). N 개 이상의 레코드를 순회하면 루프가있는 것입니다.


이것은 O (1)이 아니며,이 알고리즘은 big-O 표기법에서 의미있는 시간 복잡성을 갖지 않습니다. 큰 O 표기법 은 입력 크기가 무한대로됨에 따라 한계 에서의 성능에 대해서만 알려줍니다 . 따라서 알고리즘 이 일부 큰 N에 대해 N 개보다 많은 요소가있는 목록 없다고 가정 하면 목록 크기가 무한대에 가까워 질 때 런타임의 한계는 정의되지 않습니다. 따라서 복잡성은 "O (anything)"이 아닙니다.
fgp

1
 // To detect whether a circular loop exists in a linked list
public boolean findCircularLoop() {
    Node slower, faster;
    slower = head;
    faster = head.next; // start faster one node ahead
    while (true) {

        // if the faster pointer encounters a NULL element
        if (faster == null || faster.next == null)
            return false;
        // if faster pointer ever equals slower or faster's next
        // pointer is ever equal to slower then it's a circular list
        else if (slower == faster || slower == faster.next)
            return true;
        else {
            // advance the pointers
            slower = slower.next;
            faster = faster.next.next;
        }
    }
}

1
boolean hasCycle(Node head) {

    boolean dec = false;
    Node first = head;
    Node sec = head;
    while(first != null && sec != null)
    {
        first = first.next;
        sec = sec.next.next;
        if(first == sec )
        {
            dec = true;
            break;
        }

    }
        return dec;
}

위의 함수를 사용하여 java의 linkedlist에서 루프를 감지하십시오.


2
위의 답변과 거의 동일하지만 문제가 있습니다. 홀수 길이 목록이있는 목록 (루프없이)에 대해 NullPointerException이 발생합니다. 예를 들어 head.next가 null이면 sec.next.next는 NPE를 발생시킵니다.
Dave L.

1

연결된 목록에서 루프를 검색하는 가장 간단한 방법 중 하나를 사용하여 해시 맵을 사용하는 O (N) 복잡성 또는 정렬 기반 방법을 사용하는 O (NlogN)을 수행 할 수 있습니다.

헤드에서 시작하여 목록을 탐색 할 때 정렬 된 주소 목록을 작성하십시오. 새 주소를 삽입 할 때 주소가 정렬 된 목록에 있는지 확인하십시오.이 경우 O (logN) 복잡성이 필요합니다.


이 접근 방식의 복잡성은 O (N log N)
fgp

0

고정 시간이나 공간을 차지하는 방법을 볼 수 없으며 목록의 크기에 따라 둘 다 증가합니다.

IdentityHashMap을 사용하고 (아직 IdentityHashSet이없는 경우) 각 노드를 맵에 저장합니다. 노드가 저장되기 전에 containsKey를 호출합니다. 노드가 이미 존재하면주기가 있습니다.

ItentityHashMap은 .equals 대신 ==를 사용하므로 객체의 내용이 같은 것이 아니라 메모리의 위치를 ​​확인하고 있습니다.


3
목록의 맨 끝에 루프가있을 수 있으므로 전체 목록을 방문해야하므로 고정 된 시간이 걸리는 것은 확실히 불가능합니다. 그러나 Fast / Slow 알고리즘은 고정 된 양의 메모리를 사용하는 솔루션을 보여줍니다.
Dave L.

그것은 점근 적 인 행동을 의미하지 않습니까, 즉 선형 O (n)이며 여기서 n은 목록의 길이입니다. (1) O 것 고정
마크 롭슨

0

이 스레드를 처리하는 데 너무 늦었고 새롭습니다. 그래도 ..

노드의 주소와 "다음"노드가 테이블에 저장되지 않는 이유

우리가 이런 식으로 표를 만들 수 있다면

node present: (present node addr) (next node address)

node 1: addr1: 0x100 addr2: 0x200 ( no present node address till this point had 0x200)
node 2: addr2: 0x200 addr3: 0x300 ( no present node address till this point had 0x300)
node 3: addr3: 0x300 addr4: 0x400 ( no present node address till this point had 0x400)
node 4: addr4: 0x400 addr5: 0x500 ( no present node address till this point had 0x500)
node 5: addr5: 0x500 addr6: 0x600 ( no present node address till this point had 0x600)
node 6: addr6: 0x600 addr4: 0x400 ( ONE present node address till this point had 0x400)

따라서 사이클이 형성됩니다.


솔루션이 "일정한 공간"요구 사항을 통과하지 못합니다.
Arnaud

0

실행 가능한 코드는 다음과 같습니다.

내가 한 것은 O(1)링크를 추적 하는 3 개의 임시 노드 (공간 복잡성 )를 사용하여 링크 된 목록을 존중하는 것입니다 .

이 작업에 대한 흥미로운 사실은 연결 목록에서주기를 감지하는 데 도움이되는 것입니다. 앞으로 나아갈 때 시작점 (루트 노드)으로 돌아갈 것으로 예상하지 않고 임시 노드 중 하나가 null이 아닌 경우 루트 노드를 가리키는주기를가집니다.

이 알고리즘의 시간 복잡도는 O(n)공간 복잡도입니다 O(1).

다음은 링크 된 목록의 클래스 노드입니다.

public class LinkedNode{
    public LinkedNode next;
}

다음은 마지막 노드가 두 번째 노드를 가리키는 세 개의 노드로 구성된 간단한 테스트 사례가있는 기본 코드입니다.

    public static boolean checkLoopInLinkedList(LinkedNode root){

        if (root == null || root.next == null) return false;

        LinkedNode current1 = root, current2 = root.next, current3 = root.next.next;
        root.next = null;
        current2.next = current1;

        while(current3 != null){
            if(current3 == root) return true;

            current1 = current2;
            current2 = current3;
            current3 = current3.next;

            current2.next = current1;
        }
        return false;
    }

다음은 마지막 노드가 두 번째 노드를 가리키는 세 개의 노드에 대한 간단한 테스트 사례입니다.

public class questions{
    public static void main(String [] args){

        LinkedNode n1 = new LinkedNode();
        LinkedNode n2 = new LinkedNode();
        LinkedNode n3 = new LinkedNode();
        n1.next = n2;
        n2.next = n3;
        n3.next = n2;

        System.out.print(checkLoopInLinkedList(n1));
    }
}

0

이 코드는 최적의 답변으로 선택된 것보다 더 빠르게 결과를 생성합니다. answer 'method. 다음의 드라이 런을 살펴보면 내가 말하려는 내용을 알 수 있습니다. 그런 다음 아래 주어진 방법을 통해 문제를보고 no를 측정하십시오. 답을 찾기 위해 취한 단계.

1-> 2-> 9-> 3 ^ -------- ^

코드는 다음과 같습니다.

boolean loop(node *head)
{
 node *back=head;
 node *front=head;

 while(front && front->next)
 {
  front=front->next->next;
  if(back==front)
  return true;
  else
  back=back->next;
 }
return false
}

이것이 모든 상황에서 올바른 결과를 낳을 것이라고 확신합니까? 목록 1-> 2-> 3-> 4-> 5-> 6-> 7-> 3-> ... 에서이 알고리즘을 실행하면 4로 머리를 반환한다고 생각하지만 원하는 경우 3.
Sunreef

문제는 루프가 있는지 여부를 찾는 것입니다.이 경우, 질문은 절대적으로 잘 작동하고 사례에 대해 원하는 부울 결과를 얻습니다. 루프가 시작된 곳에서 정확한 노드를 원하면 코드에 더 많은 것을 추가해야하지만 결과를 생성하는 한 더 빠른 결론을 내릴 수 있습니다.
Sarthak Mehra

boolean hasLoop(Node first)주어진 질문이 루프가있는 목록 중 첫 번째이면 true를 반환하고 그렇지 않으면 false를 반환 하는 가장 좋은 작성 방법은 무엇입니까 ?
Sunreef

첫 번째 값은 뒤로 포인터를 의미하고 두 번째 부분은 앞으로 포인터를 의미합니다. (1,1)-(1,3)-(2,3)-(2,5)-(3,5) -(3,7)-(4,7)-(4,4).
Sarthak Mehra

실제로, 나는 지금 질문을 이해하는 두 가지 방법이 있다는 것을 알고 있습니다. 루프가 있는지 검색하는 경우 알고리즘이 올바른 것입니다. 그러나 루프가 어디에서 시작되는지 묻는 질문이 있다고 생각했습니다.
Sunreef

0

여기 내 자바 솔루션이 있습니다.

boolean detectLoop(Node head){
    Node fastRunner = head;
    Node slowRunner = head;
    while(fastRunner != null && slowRunner !=null && fastRunner.next != null){
        fastRunner = fastRunner.next.next;
        slowRunner = slowRunner.next;
        if(fastRunner == slowRunner){
            return true;
        }
    }
    return false;
}

0

위의 답변에서 제안한대로 Floyd의 거북이 알고리즘을 사용할 수도 있습니다.

이 알고리즘은 단일 연결 목록에 닫힌주기가 있는지 확인할 수 있습니다. 이것은 다른 속도로 이동할 두 개의 포인터로 목록을 반복하여 달성 할 수 있습니다. 이런 식으로 사이클이 있으면 두 포인터가 나중에 어느 시점에서 만날 것입니다.

블로그 게시물 을 확인하시기 바랍니다링크 된 목록 데이터 구조에서 . 여기서 위에서 언급 한 알고리즘을 Java 언어로 구현 한 코드 스 니펫도 포함했습니다.

문안 인사,

안드레아스 (@Andreas)


0

사이클을 감지하는 솔루션은 다음과 같습니다.

public boolean hasCycle(ListNode head) {
            ListNode slow =head;
            ListNode fast =head;

            while(fast!=null && fast.next!=null){
                slow = slow.next; // slow pointer only one hop
                fast = fast.next.next; // fast pointer two hops 

                if(slow == fast)    return true; // retrun true if fast meet slow pointer
            }

            return false; // return false if fast pointer stop at end 
        }

0

// 링크리스트 찾기 루프 함수

int findLoop(struct Node* head)
{
    struct Node* slow = head, *fast = head;
    while(slow && fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
            return 1;
    }
 return 0;
}

-1

이 방법은 공간 오버 헤드가 있지만 구현이 더 간단합니다.

루프는 맵에 노드를 저장하여 식별 할 수 있습니다. 그리고 노드를 놓기 전에; 노드가 이미 존재하는지 확인하십시오. 노드가 이미 맵에 존재하면 링크 된 목록에 루프가 있음을 의미합니다.

public boolean loopDetector(Node<E> first) {  
       Node<E> t = first;  
       Map<Node<E>, Node<E>> map = new IdentityHashMap<Node<E>, Node<E>>();  
       while (t != null) {  
            if (map.containsKey(t)) {  
                 System.out.println(" duplicate Node is --" + t  
                           + " having value :" + t.data);  

                 return true;  
            } else {  
                 map.put(t, t);  
            }  
            t = t.next;  
       }  
       return false;  
  }  

이것은 질문에 주어진 일정한 양의 공간 제한을 충족시키지 않습니다 !
dedek

공간 오버 헤드가 있음에 동의합니다. 이 문제를 해결하는 또 다른 방법입니다. 명백한 접근법은 거북이와 하르 즈 알고리즘입니다.
rai.skumar

@ downvoter, 이유를 설명 할 수 있다면 도움이 될 것입니다.
rai.skumar

-2
public boolean isCircular() {

    if (head == null)
        return false;

    Node temp1 = head;
    Node temp2 = head;

    try {
        while (temp2.next != null) {

            temp2 = temp2.next.next.next;
            temp1 = temp1.next;

            if (temp1 == temp2 || temp1 == temp2.next) 
                return true;    

        }
    } catch (NullPointerException ex) {
        return false;

    }

    return false;

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.