Skip to content

Commit d8398ca

Browse files
deep20jainzhendrikse
authored andcommitted
BAEL-1123 - Testing Linked List for Cycles - [email protected] (eugenp#2525)
* Adding node and cycle detection by hashing * Adding implementation for all algorithms for cycle detection and removal * Applying formatting rules
1 parent 4baaf9f commit d8398ca

14 files changed

Lines changed: 432 additions & 0 deletions
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
public class CycleDetectionBruteForce {
4+
5+
public static <T> boolean detectCycle(Node<T> head) {
6+
if (head == null) {
7+
return false;
8+
}
9+
10+
Node<T> it1 = head;
11+
int nodesTraversedByOuter = 0;
12+
while (it1 != null && it1.next != null) {
13+
it1 = it1.next;
14+
nodesTraversedByOuter++;
15+
16+
int x = nodesTraversedByOuter;
17+
Node<T> it2 = head;
18+
int noOfTimesCurrentNodeVisited = 0;
19+
20+
while (x > 0) {
21+
it2 = it2.next;
22+
23+
if (it2 == it1) {
24+
noOfTimesCurrentNodeVisited++;
25+
}
26+
27+
if (noOfTimesCurrentNodeVisited == 2) {
28+
return true;
29+
}
30+
31+
x--;
32+
}
33+
}
34+
35+
return false;
36+
}
37+
38+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
public class CycleDetectionByFastAndSlowIterators {
4+
5+
public static <T> boolean detectCycle(Node<T> head) {
6+
if (head == null) {
7+
return false;
8+
}
9+
10+
Node<T> slow = head;
11+
Node<T> fast = head;
12+
13+
while (fast != null && fast.next != null) {
14+
slow = slow.next;
15+
fast = fast.next.next;
16+
17+
if (slow == fast) {
18+
return true;
19+
}
20+
}
21+
22+
return false;
23+
}
24+
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
import java.util.HashSet;
4+
import java.util.Set;
5+
6+
public class CycleDetectionByHashing {
7+
8+
public static <T> boolean detectCycle(Node<T> head) {
9+
if (head == null) {
10+
return false;
11+
}
12+
13+
Set<Node<T>> set = new HashSet<>();
14+
Node<T> node = head;
15+
16+
while (node != null) {
17+
if (set.contains(node)) {
18+
return true;
19+
}
20+
set.add(node);
21+
node = node.next;
22+
}
23+
24+
return false;
25+
}
26+
27+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
public class CycleRemovalBruteForce {
4+
5+
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
6+
if (head == null) {
7+
return false;
8+
}
9+
10+
Node<T> slow = head;
11+
Node<T> fast = head;
12+
13+
while (fast != null && fast.next != null) {
14+
slow = slow.next;
15+
fast = fast.next.next;
16+
17+
if (slow == fast) {
18+
removeCycle(slow, head);
19+
return true;
20+
}
21+
}
22+
23+
return false;
24+
}
25+
26+
private static <T> void removeCycle(Node<T> loopNodeParam, Node<T> head) {
27+
Node<T> it = head;
28+
29+
while (it != null) {
30+
if (isNodeReachableFromLoopNode(it, loopNodeParam)) {
31+
Node<T> loopStart = it;
32+
findEndNodeAndBreakCycle(loopStart);
33+
break;
34+
}
35+
it = it.next;
36+
}
37+
}
38+
39+
private static <T> boolean isNodeReachableFromLoopNode(Node<T> it, Node<T> loopNodeParam) {
40+
Node<T> loopNode = loopNodeParam;
41+
42+
while (loopNode.next != loopNodeParam) {
43+
if (it == loopNode) {
44+
return true;
45+
}
46+
loopNode = loopNode.next;
47+
}
48+
49+
return false;
50+
}
51+
52+
private static <T> void findEndNodeAndBreakCycle(Node<T> loopStartParam) {
53+
Node<T> loopStart = loopStartParam;
54+
55+
while (loopStart.next != loopStartParam) {
56+
loopStart = loopStart.next;
57+
}
58+
59+
loopStart.next = null;
60+
}
61+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
public class CycleRemovalByCountingLoopNodes {
4+
5+
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
6+
if (head == null) {
7+
return false;
8+
}
9+
10+
Node<T> slow = head;
11+
Node<T> fast = head;
12+
13+
while (fast != null && fast.next != null) {
14+
slow = slow.next;
15+
fast = fast.next.next;
16+
17+
if (slow == fast) {
18+
int cycleLength = calculateCycleLength(slow);
19+
removeCycle(head, cycleLength);
20+
return true;
21+
}
22+
}
23+
24+
return false;
25+
}
26+
27+
private static <T> void removeCycle(Node<T> head, int cycleLength) {
28+
Node<T> cycleLengthAdvancedIterator = head;
29+
Node<T> it = head;
30+
31+
for (int i = 0; i < cycleLength; i++) {
32+
cycleLengthAdvancedIterator = cycleLengthAdvancedIterator.next;
33+
}
34+
35+
while (it.next != cycleLengthAdvancedIterator.next) {
36+
it = it.next;
37+
cycleLengthAdvancedIterator = cycleLengthAdvancedIterator.next;
38+
}
39+
40+
cycleLengthAdvancedIterator.next = null;
41+
}
42+
43+
private static <T> int calculateCycleLength(Node<T> loopNodeParam) {
44+
Node<T> loopNode = loopNodeParam;
45+
int length = 1;
46+
47+
while (loopNode.next != loopNodeParam) {
48+
length++;
49+
loopNode = loopNode.next;
50+
}
51+
52+
return length;
53+
}
54+
55+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
public class CycleRemovalWithoutCountingLoopNodes {
4+
5+
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
6+
if (head == null) {
7+
return false;
8+
}
9+
10+
Node<T> slow = head;
11+
Node<T> fast = head;
12+
13+
while (fast != null && fast.next != null) {
14+
slow = slow.next;
15+
fast = fast.next.next;
16+
17+
if (slow == fast) {
18+
removeCycle(slow, head);
19+
return true;
20+
}
21+
}
22+
23+
return false;
24+
}
25+
26+
private static <T> void removeCycle(Node<T> meetingPointParam, Node<T> head) {
27+
Node<T> loopNode = meetingPointParam;
28+
Node<T> it = head;
29+
30+
while (loopNode.next != it.next) {
31+
it = it.next;
32+
loopNode = loopNode.next;
33+
}
34+
35+
loopNode.next = null;
36+
}
37+
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
public class Node<T> {
4+
T data;
5+
Node<T> next;
6+
7+
public static <T> Node<T> createNewNode(T data, Node<T> next) {
8+
Node<T> node = new Node<T>();
9+
node.data = data;
10+
node.next = next;
11+
return node;
12+
}
13+
14+
public static <T> void traverseList(Node<T> root) {
15+
if (root == null) {
16+
return;
17+
}
18+
19+
Node<T> node = root;
20+
while (node != null) {
21+
System.out.println(node.data);
22+
node = node.next;
23+
}
24+
}
25+
26+
public static <T> Node<T> getTail(Node<T> root) {
27+
if (root == null) {
28+
return null;
29+
}
30+
31+
Node<T> node = root;
32+
while (node.next != null) {
33+
node = node.next;
34+
}
35+
return node;
36+
}
37+
38+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
import org.junit.Assert;
4+
import org.junit.Test;
5+
6+
public class CycleDetectionBruteForceTest extends CycleDetectionTestBase {
7+
8+
@Test
9+
public void givenNormalList_dontDetectLoop() {
10+
Node<Integer> root = createList();
11+
Assert.assertFalse(CycleDetectionBruteForce.detectCycle(root));
12+
}
13+
14+
@Test
15+
public void givenCyclicList_detectLoop() {
16+
Node<Integer> root = createList();
17+
createLoop(root);
18+
Assert.assertTrue(CycleDetectionBruteForce.detectCycle(root));
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
import org.junit.Assert;
4+
import org.junit.Test;
5+
6+
public class CycleDetectionByFastAndSlowIteratorsTest extends CycleDetectionTestBase {
7+
8+
@Test
9+
public void givenNormalList_dontDetectLoop() {
10+
Node<Integer> root = createList();
11+
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
12+
}
13+
14+
@Test
15+
public void givenCyclicList_detectLoop() {
16+
Node<Integer> root = createList();
17+
createLoop(root);
18+
Assert.assertTrue(CycleDetectionByFastAndSlowIterators.detectCycle(root));
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.baeldung.algorithms.linkedlist;
2+
3+
import org.junit.Assert;
4+
import org.junit.Test;
5+
6+
public class CycleDetectionByHashingTest extends CycleDetectionTestBase {
7+
8+
@Test
9+
public void givenNormalList_dontDetectLoop() {
10+
Node<Integer> root = createList();
11+
Assert.assertFalse(CycleDetectionByHashing.detectCycle(root));
12+
}
13+
14+
@Test
15+
public void givenCyclicList_detectLoop() {
16+
Node<Integer> root = createList();
17+
createLoop(root);
18+
Assert.assertTrue(CycleDetectionByHashing.detectCycle(root));
19+
}
20+
}

0 commit comments

Comments
 (0)