BAEL-1123 - Testing Linked List for Cycles - deep20jain@gmail.com (#2525)
* Adding node and cycle detection by hashing * Adding implementation for all algorithms for cycle detection and removal * Applying formatting rules
This commit is contained in:
parent
4baaf9f984
commit
d8398caaa2
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
public class CycleDetectionBruteForce {
|
||||
|
||||
public static <T> boolean detectCycle(Node<T> head) {
|
||||
if (head == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<T> it1 = head;
|
||||
int nodesTraversedByOuter = 0;
|
||||
while (it1 != null && it1.next != null) {
|
||||
it1 = it1.next;
|
||||
nodesTraversedByOuter++;
|
||||
|
||||
int x = nodesTraversedByOuter;
|
||||
Node<T> it2 = head;
|
||||
int noOfTimesCurrentNodeVisited = 0;
|
||||
|
||||
while (x > 0) {
|
||||
it2 = it2.next;
|
||||
|
||||
if (it2 == it1) {
|
||||
noOfTimesCurrentNodeVisited++;
|
||||
}
|
||||
|
||||
if (noOfTimesCurrentNodeVisited == 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
x--;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
public class CycleDetectionByFastAndSlowIterators {
|
||||
|
||||
public static <T> boolean detectCycle(Node<T> head) {
|
||||
if (head == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<T> slow = head;
|
||||
Node<T> fast = head;
|
||||
|
||||
while (fast != null && fast.next != null) {
|
||||
slow = slow.next;
|
||||
fast = fast.next.next;
|
||||
|
||||
if (slow == fast) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CycleDetectionByHashing {
|
||||
|
||||
public static <T> boolean detectCycle(Node<T> head) {
|
||||
if (head == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<Node<T>> set = new HashSet<>();
|
||||
Node<T> node = head;
|
||||
|
||||
while (node != null) {
|
||||
if (set.contains(node)) {
|
||||
return true;
|
||||
}
|
||||
set.add(node);
|
||||
node = node.next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
public class CycleRemovalBruteForce {
|
||||
|
||||
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||
if (head == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<T> slow = head;
|
||||
Node<T> fast = head;
|
||||
|
||||
while (fast != null && fast.next != null) {
|
||||
slow = slow.next;
|
||||
fast = fast.next.next;
|
||||
|
||||
if (slow == fast) {
|
||||
removeCycle(slow, head);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> void removeCycle(Node<T> loopNodeParam, Node<T> head) {
|
||||
Node<T> it = head;
|
||||
|
||||
while (it != null) {
|
||||
if (isNodeReachableFromLoopNode(it, loopNodeParam)) {
|
||||
Node<T> loopStart = it;
|
||||
findEndNodeAndBreakCycle(loopStart);
|
||||
break;
|
||||
}
|
||||
it = it.next;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> boolean isNodeReachableFromLoopNode(Node<T> it, Node<T> loopNodeParam) {
|
||||
Node<T> loopNode = loopNodeParam;
|
||||
|
||||
while (loopNode.next != loopNodeParam) {
|
||||
if (it == loopNode) {
|
||||
return true;
|
||||
}
|
||||
loopNode = loopNode.next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> void findEndNodeAndBreakCycle(Node<T> loopStartParam) {
|
||||
Node<T> loopStart = loopStartParam;
|
||||
|
||||
while (loopStart.next != loopStartParam) {
|
||||
loopStart = loopStart.next;
|
||||
}
|
||||
|
||||
loopStart.next = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
public class CycleRemovalByCountingLoopNodes {
|
||||
|
||||
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||
if (head == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<T> slow = head;
|
||||
Node<T> fast = head;
|
||||
|
||||
while (fast != null && fast.next != null) {
|
||||
slow = slow.next;
|
||||
fast = fast.next.next;
|
||||
|
||||
if (slow == fast) {
|
||||
int cycleLength = calculateCycleLength(slow);
|
||||
removeCycle(head, cycleLength);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> void removeCycle(Node<T> head, int cycleLength) {
|
||||
Node<T> cycleLengthAdvancedIterator = head;
|
||||
Node<T> it = head;
|
||||
|
||||
for (int i = 0; i < cycleLength; i++) {
|
||||
cycleLengthAdvancedIterator = cycleLengthAdvancedIterator.next;
|
||||
}
|
||||
|
||||
while (it.next != cycleLengthAdvancedIterator.next) {
|
||||
it = it.next;
|
||||
cycleLengthAdvancedIterator = cycleLengthAdvancedIterator.next;
|
||||
}
|
||||
|
||||
cycleLengthAdvancedIterator.next = null;
|
||||
}
|
||||
|
||||
private static <T> int calculateCycleLength(Node<T> loopNodeParam) {
|
||||
Node<T> loopNode = loopNodeParam;
|
||||
int length = 1;
|
||||
|
||||
while (loopNode.next != loopNodeParam) {
|
||||
length++;
|
||||
loopNode = loopNode.next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
public class CycleRemovalWithoutCountingLoopNodes {
|
||||
|
||||
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||
if (head == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<T> slow = head;
|
||||
Node<T> fast = head;
|
||||
|
||||
while (fast != null && fast.next != null) {
|
||||
slow = slow.next;
|
||||
fast = fast.next.next;
|
||||
|
||||
if (slow == fast) {
|
||||
removeCycle(slow, head);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> void removeCycle(Node<T> meetingPointParam, Node<T> head) {
|
||||
Node<T> loopNode = meetingPointParam;
|
||||
Node<T> it = head;
|
||||
|
||||
while (loopNode.next != it.next) {
|
||||
it = it.next;
|
||||
loopNode = loopNode.next;
|
||||
}
|
||||
|
||||
loopNode.next = null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
public class Node<T> {
|
||||
T data;
|
||||
Node<T> next;
|
||||
|
||||
public static <T> Node<T> createNewNode(T data, Node<T> next) {
|
||||
Node<T> node = new Node<T>();
|
||||
node.data = data;
|
||||
node.next = next;
|
||||
return node;
|
||||
}
|
||||
|
||||
public static <T> void traverseList(Node<T> root) {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node<T> node = root;
|
||||
while (node != null) {
|
||||
System.out.println(node.data);
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Node<T> getTail(Node<T> root) {
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Node<T> node = root;
|
||||
while (node.next != null) {
|
||||
node = node.next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CycleDetectionBruteForceTest extends CycleDetectionTestBase {
|
||||
|
||||
@Test
|
||||
public void givenNormalList_dontDetectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
Assert.assertFalse(CycleDetectionBruteForce.detectCycle(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCyclicList_detectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
createLoop(root);
|
||||
Assert.assertTrue(CycleDetectionBruteForce.detectCycle(root));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CycleDetectionByFastAndSlowIteratorsTest extends CycleDetectionTestBase {
|
||||
|
||||
@Test
|
||||
public void givenNormalList_dontDetectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCyclicList_detectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
createLoop(root);
|
||||
Assert.assertTrue(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CycleDetectionByHashingTest extends CycleDetectionTestBase {
|
||||
|
||||
@Test
|
||||
public void givenNormalList_dontDetectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
Assert.assertFalse(CycleDetectionByHashing.detectCycle(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCyclicList_detectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
createLoop(root);
|
||||
Assert.assertTrue(CycleDetectionByHashing.detectCycle(root));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
public class CycleDetectionTestBase {
|
||||
|
||||
public static Node<Integer> createList() {
|
||||
Node<Integer> root = Node.createNewNode(10, null);
|
||||
|
||||
for (int i = 9; i >= 1; --i) {
|
||||
Node<Integer> current = Node.createNewNode(i, root);
|
||||
root = current;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public static void createLoop(Node<Integer> root) {
|
||||
Node<Integer> tail = Node.getTail(root);
|
||||
|
||||
Node<Integer> middle = root;
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
middle = middle.next;
|
||||
}
|
||||
|
||||
tail.next = middle;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CycleRemovalBruteForceTest extends CycleDetectionTestBase {
|
||||
|
||||
@Test
|
||||
public void givenNormalList_dontDetectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
Assert.assertFalse(CycleRemovalBruteForce.detectAndRemoveCycle(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCyclicList_detectAndRemoveLoop() {
|
||||
Node<Integer> root = createList();
|
||||
createLoop(root);
|
||||
Assert.assertTrue(CycleRemovalBruteForce.detectAndRemoveCycle(root));
|
||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CycleRemovalByCountingLoopNodesTest extends CycleDetectionTestBase {
|
||||
|
||||
@Test
|
||||
public void givenNormalList_dontDetectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
Assert.assertFalse(CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCyclicList_detectAndRemoveLoop() {
|
||||
Node<Integer> root = createList();
|
||||
createLoop(root);
|
||||
Assert.assertTrue(CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(root));
|
||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.baeldung.algorithms.linkedlist;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CycleRemovalWithoutCountingLoopNodesTest extends CycleDetectionTestBase {
|
||||
|
||||
@Test
|
||||
public void givenNormalList_dontDetectLoop() {
|
||||
Node<Integer> root = createList();
|
||||
Assert.assertFalse(CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCyclicList_detectAndRemoveLoop() {
|
||||
Node<Integer> root = createList();
|
||||
createLoop(root);
|
||||
Assert.assertTrue(CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(root));
|
||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue