Skip to content

Commit 88699f6

Browse files
committed
Loop detection improved
1 parent f8a51ba commit 88699f6

File tree

3 files changed

+44
-45
lines changed

3 files changed

+44
-45
lines changed

Diff for: src/main/java/com/ctci/linkedlists/LoopDetection.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class LoopDetection {
1414
* EXAMPLE
1515
* Input: A -> B -> C -> D -> E -> C [the same C as earlier]
1616
* Output: C
17+
* <p>
18+
* See {@link com.rampatra.linkedlists.DetectAndRemoveLoop} for a slightly more complex problem.
1719
*
1820
* @param head the starting node of the linked list
1921
* @return the {@code Node} where the loop starts, {@code null} otherwise.
@@ -62,4 +64,4 @@ public static void main(String[] args) {
6264
l1.next.next.next.next.next.next = l1.next.next.next;
6365
System.out.println(findLoopStartNode(l1).val);
6466
}
65-
}
67+
}

Diff for: src/main/java/com/rampatra/linkedlists/DetectAndRemoveLoop.java

+32-35
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,56 @@
44
import com.rampatra.base.SingleLinkedNode;
55

66
/**
7-
* Created by IntelliJ IDEA.
7+
* See this <a href="https://stackoverflow.com/a/32190575/1385441">Stackoverflow post</a> to understand
8+
* how to find the starting node of the loop.
9+
* <p>
10+
* Proof for Flyod's Loop Detection Algorithm:
11+
* <p>
12+
* Suppose fastRunner had just skipped over slowRunner. fastRunner would only be 1 node ahead of slowRunner, since their
13+
* speeds differ by only 1. So we would have something like this:
14+
* <p>
15+
* [ ] -> [s] -> [f]
16+
* <p>
17+
* What would the step right before this "skipping step" look like? fastRunner would be 2 nodes back, and slowRunner
18+
* would be 1 node back. But wait, that means they would be at the same node! So fastRunner didn't skip over slowRunner!
19+
* (This is a proof by contradiction.)
820
*
921
* @author rampatra
1022
* @since 7/1/15
11-
* @time: 12:39 PM
1223
*/
1324
public class DetectAndRemoveLoop {
1425

1526
/**
16-
* Detects loop if any in {@param list} and removes it.
17-
* <p>
18-
* Algorithm:
27+
* Detects loop, if any, in {@code list} and removes it.
1928
* <p>
29+
* Approach:
2030
* 1) Use Floyd's cycle detection algorithm to detect loop.
2131
* 2) Acc. to FCD, once the fast pointer meets the slow pointer we conclude that there is a loop.
22-
* 3) Now compute the length 'l' of the loop.
23-
* 4) Move the fast pointer length 'l' from head.
24-
* 5) Now move both slow and fast pointer at same pace and where they meet is the starting point of the loop.
25-
* 6) Lastly, to remove the loop make the next of the node (before the starting point of loop) to null.
32+
* 4) Now that we have concluded there is a loop, let's detect the starting node and remove the loop:
33+
* i. Move the slow pointer to head.
34+
* ii. Now, move both slow and fast pointer at same pace and where they meet is the starting point of the loop.
35+
* iii. Lastly, to remove the loop make the next of the node (before the starting point of loop) to null.
2636
*
2737
* @param list
2838
* @param <E>
2939
* @return {@code true} if loop exists {@code false} otherwise.
3040
*/
3141
public static <E extends Comparable<E>> boolean detectAndRemoveLoop(SingleLinkedList<E> list) {
32-
int i = 0, length = 0;
3342
boolean isLoopPresent = false;
34-
SingleLinkedNode<E> slow = list.head, fast = slow.next;
43+
SingleLinkedNode<E> slow = list.head, fast = list.head;
3544

3645
while (fast != null && fast.next != null) {
46+
slow = slow.next;
47+
fast = fast.next.next;
3748
if (slow == fast) {
3849
isLoopPresent = true;
3950
break;
4051
}
41-
slow = slow.next;
42-
fast = fast.next.next;
4352
}
4453

45-
if (isLoopPresent == false) return false;
46-
47-
// compute length of loop
48-
while (fast.next != slow) {
49-
fast = fast.next;
50-
length++;
51-
}
52-
53-
// move fast pointer from head by the length of loop
54-
slow = fast = list.head;
55-
while (i <= length) {
56-
fast = fast.next;
57-
i++;
58-
}
54+
if (!isLoopPresent) return false;
5955

56+
slow = list.head;
6057
// move both pointers at same pace to determine the starting node of loop
6158
while (true) {
6259
slow = slow.next;
@@ -72,14 +69,14 @@ public static <E extends Comparable<E>> boolean detectAndRemoveLoop(SingleLinked
7269

7370
public static void main(String[] args) {
7471
SingleLinkedList<Integer> linkedList = new SingleLinkedList<>();
75-
linkedList.add(00);
76-
linkedList.add(11);
77-
linkedList.add(22);
78-
linkedList.add(33);
79-
linkedList.add(44);
80-
linkedList.add(55);
81-
linkedList.getNode(1).next = linkedList.getNode(0);
72+
linkedList.add(0);
73+
linkedList.add(1);
74+
linkedList.add(2);
75+
linkedList.add(3);
76+
linkedList.add(4);
77+
linkedList.add(5);
78+
linkedList.getNode(4).next = linkedList.getNode(2);
8279
System.out.println(detectAndRemoveLoop(linkedList));
8380
linkedList.printList();
8481
}
85-
}
82+
}

Diff for: src/main/java/com/rampatra/linkedlists/DetectLoop.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
*
1111
* @author rampatra
1212
* @since 6/19/15
13-
* @time: 9:24 AM
1413
*/
1514
public class DetectLoop {
1615

@@ -24,29 +23,30 @@ public class DetectLoop {
2423
* there is a loop. If pointers do not meet
2524
* then linked list does not have loop.
2625
* <p/>
27-
* Time: O(n)
28-
* Space: O(1)
26+
* Level: Easy
27+
* Time Complexity: O(n)
28+
* Space Complexity: O(1)
2929
*
3030
* @param list
3131
* @return
3232
*/
3333
public static <E extends Comparable<E>> boolean isLoopPresent(SingleLinkedList<E> list) {
34-
SingleLinkedNode<E> slow = list.head, fast = slow.next;
34+
SingleLinkedNode<E> slow = list.head, fast = list.head;
3535
while (fast != null && fast.next != null) {
36+
slow = slow.next;
37+
fast = fast.next.next;
3638
if (slow == fast) {
3739
return true;
3840
}
39-
slow = slow.next;
40-
fast = fast.next.next;
4141
}
4242
return false;
4343
}
4444

4545
/**
4646
* Uses HashMap to store visited nodes.
4747
* <p/>
48-
* Time: O(n)
49-
* Space: O(n)
48+
* Time Complexity: O(n)
49+
* Space Complexity: O(n)
5050
*
5151
* @param node
5252
* @return
@@ -75,4 +75,4 @@ public static void main(String[] args) {
7575
linkedList.getNode(4).next = linkedList.getNode(3);
7676
System.out.println(isLoopPresent(linkedList));
7777
}
78-
}
78+
}

0 commit comments

Comments
 (0)