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