BAEL 1123 - deep20jain@gmail.com (#2572)
* Adding node and cycle detection by hashing * Adding implementation for all algorithms for cycle detection and removal * Applying formatting rules * Refactoring methods and adding more tests * Fixing infinite loop corner case
This commit is contained in:
parent
35ac22c947
commit
cccd6a3eab
@ -2,9 +2,9 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
public class CycleDetectionBruteForce {
|
public class CycleDetectionBruteForce {
|
||||||
|
|
||||||
public static <T> boolean detectCycle(Node<T> head) {
|
public static <T> CycleDetectionResult<T> detectCycle(Node<T> head) {
|
||||||
if (head == null) {
|
if (head == null) {
|
||||||
return false;
|
return new CycleDetectionResult<>(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node<T> it1 = head;
|
Node<T> it1 = head;
|
||||||
@ -25,14 +25,14 @@ public class CycleDetectionBruteForce {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (noOfTimesCurrentNodeVisited == 2) {
|
if (noOfTimesCurrentNodeVisited == 2) {
|
||||||
return true;
|
return new CycleDetectionResult<>(true, it1);
|
||||||
}
|
}
|
||||||
|
|
||||||
x--;
|
x--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return new CycleDetectionResult<>(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
public class CycleDetectionByFastAndSlowIterators {
|
public class CycleDetectionByFastAndSlowIterators {
|
||||||
|
|
||||||
public static <T> boolean detectCycle(Node<T> head) {
|
public static <T> CycleDetectionResult<T> detectCycle(Node<T> head) {
|
||||||
if (head == null) {
|
if (head == null) {
|
||||||
return false;
|
return new CycleDetectionResult<>(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node<T> slow = head;
|
Node<T> slow = head;
|
||||||
@ -15,11 +15,11 @@ public class CycleDetectionByFastAndSlowIterators {
|
|||||||
fast = fast.next.next;
|
fast = fast.next.next;
|
||||||
|
|
||||||
if (slow == fast) {
|
if (slow == fast) {
|
||||||
return true;
|
return new CycleDetectionResult<>(true, fast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return new CycleDetectionResult<>(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class CycleDetectionByHashing {
|
public class CycleDetectionByHashing {
|
||||||
|
|
||||||
public static <T> boolean detectCycle(Node<T> head) {
|
public static <T> CycleDetectionResult<T> detectCycle(Node<T> head) {
|
||||||
if (head == null) {
|
if (head == null) {
|
||||||
return false;
|
return new CycleDetectionResult<>(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Node<T>> set = new HashSet<>();
|
Set<Node<T>> set = new HashSet<>();
|
||||||
@ -15,13 +15,13 @@ public class CycleDetectionByHashing {
|
|||||||
|
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
if (set.contains(node)) {
|
if (set.contains(node)) {
|
||||||
return true;
|
return new CycleDetectionResult<>(true, node);
|
||||||
}
|
}
|
||||||
set.add(node);
|
set.add(node);
|
||||||
node = node.next;
|
node = node.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return new CycleDetectionResult<>(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class CycleDetectionResult<T> {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<T> node;
|
||||||
|
|
||||||
|
public CycleDetectionResult(boolean cycleExists, Node<T> node) {
|
||||||
|
super();
|
||||||
|
this.cycleExists = cycleExists;
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
}
|
@ -3,26 +3,21 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
public class CycleRemovalBruteForce {
|
public class CycleRemovalBruteForce {
|
||||||
|
|
||||||
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||||
if (head == null) {
|
CycleDetectionResult<T> result = CycleDetectionByFastAndSlowIterators.detectCycle(head);
|
||||||
return false;
|
|
||||||
|
if (result.cycleExists) {
|
||||||
|
removeCycle(result.node, head);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node<T> slow = head;
|
return result.cycleExists;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param loopNodeParam - reference to the node where Flyods cycle
|
||||||
|
* finding algorithm ends, i.e. the fast and the slow iterators
|
||||||
|
* meet.
|
||||||
|
* @param head - reference to the head of the list
|
||||||
|
*/
|
||||||
private static <T> void removeCycle(Node<T> loopNodeParam, Node<T> head) {
|
private static <T> void removeCycle(Node<T> loopNodeParam, Node<T> head) {
|
||||||
Node<T> it = head;
|
Node<T> it = head;
|
||||||
|
|
||||||
@ -39,12 +34,12 @@ public class CycleRemovalBruteForce {
|
|||||||
private static <T> boolean isNodeReachableFromLoopNode(Node<T> it, Node<T> loopNodeParam) {
|
private static <T> boolean isNodeReachableFromLoopNode(Node<T> it, Node<T> loopNodeParam) {
|
||||||
Node<T> loopNode = loopNodeParam;
|
Node<T> loopNode = loopNodeParam;
|
||||||
|
|
||||||
while (loopNode.next != loopNodeParam) {
|
do {
|
||||||
if (it == loopNode) {
|
if (it == loopNode) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
loopNode = loopNode.next;
|
loopNode = loopNode.next;
|
||||||
}
|
} while (loopNode.next != loopNodeParam);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3,28 +3,17 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
public class CycleRemovalByCountingLoopNodes {
|
public class CycleRemovalByCountingLoopNodes {
|
||||||
|
|
||||||
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||||
if (head == null) {
|
CycleDetectionResult<T> result = CycleDetectionByFastAndSlowIterators.detectCycle(head);
|
||||||
return false;
|
|
||||||
|
if (result.cycleExists) {
|
||||||
|
removeCycle(result.node, head);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node<T> slow = head;
|
return result.cycleExists;
|
||||||
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) {
|
private static <T> void removeCycle(Node<T> loopNodeParam, Node<T> head) {
|
||||||
|
int cycleLength = calculateCycleLength(loopNodeParam);
|
||||||
Node<T> cycleLengthAdvancedIterator = head;
|
Node<T> cycleLengthAdvancedIterator = head;
|
||||||
Node<T> it = head;
|
Node<T> it = head;
|
||||||
|
|
||||||
|
@ -3,24 +3,13 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
public class CycleRemovalWithoutCountingLoopNodes {
|
public class CycleRemovalWithoutCountingLoopNodes {
|
||||||
|
|
||||||
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||||
if (head == null) {
|
CycleDetectionResult<T> result = CycleDetectionByFastAndSlowIterators.detectCycle(head);
|
||||||
return false;
|
|
||||||
|
if (result.cycleExists) {
|
||||||
|
removeCycle(result.node, head);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node<T> slow = head;
|
return result.cycleExists;
|
||||||
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) {
|
private static <T> void removeCycle(Node<T> meetingPointParam, Node<T> head) {
|
||||||
|
@ -2,19 +2,22 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
public class CycleDetectionBruteForceTest extends CycleDetectionTestBase {
|
public class CycleDetectionBruteForceTest extends CycleDetectionTestBase {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<Integer> head;
|
||||||
|
|
||||||
@Test
|
public CycleDetectionBruteForceTest(Node<Integer> head, boolean cycleExists) {
|
||||||
public void givenNormalList_dontDetectLoop() {
|
super();
|
||||||
Node<Integer> root = createList();
|
this.cycleExists = cycleExists;
|
||||||
Assert.assertFalse(CycleDetectionBruteForce.detectCycle(root));
|
this.head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCyclicList_detectLoop() {
|
public void givenList_detectLoop() {
|
||||||
Node<Integer> root = createList();
|
Assert.assertEquals(cycleExists, CycleDetectionBruteForce.detectCycle(head).cycleExists);
|
||||||
createLoop(root);
|
|
||||||
Assert.assertTrue(CycleDetectionBruteForce.detectCycle(root));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,22 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
public class CycleDetectionByFastAndSlowIteratorsTest extends CycleDetectionTestBase {
|
public class CycleDetectionByFastAndSlowIteratorsTest extends CycleDetectionTestBase {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<Integer> head;
|
||||||
|
|
||||||
@Test
|
public CycleDetectionByFastAndSlowIteratorsTest(Node<Integer> head, boolean cycleExists) {
|
||||||
public void givenNormalList_dontDetectLoop() {
|
super();
|
||||||
Node<Integer> root = createList();
|
this.cycleExists = cycleExists;
|
||||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
this.head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCyclicList_detectLoop() {
|
public void givenList_detectLoop() {
|
||||||
Node<Integer> root = createList();
|
Assert.assertEquals(cycleExists, CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists);
|
||||||
createLoop(root);
|
|
||||||
Assert.assertTrue(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,22 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
public class CycleDetectionByHashingTest extends CycleDetectionTestBase {
|
public class CycleDetectionByHashingTest extends CycleDetectionTestBase {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<Integer> head;
|
||||||
|
|
||||||
@Test
|
public CycleDetectionByHashingTest(Node<Integer> head, boolean cycleExists) {
|
||||||
public void givenNormalList_dontDetectLoop() {
|
super();
|
||||||
Node<Integer> root = createList();
|
this.cycleExists = cycleExists;
|
||||||
Assert.assertFalse(CycleDetectionByHashing.detectCycle(root));
|
this.head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCyclicList_detectLoop() {
|
public void givenList_detectLoop() {
|
||||||
Node<Integer> root = createList();
|
Assert.assertEquals(cycleExists, CycleDetectionByHashing.detectCycle(head).cycleExists);
|
||||||
createLoop(root);
|
|
||||||
Assert.assertTrue(CycleDetectionByHashing.detectCycle(root));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,22 @@
|
|||||||
package com.baeldung.algorithms.linkedlist;
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
public class CycleDetectionTestBase {
|
public class CycleDetectionTestBase {
|
||||||
|
|
||||||
|
@Parameters
|
||||||
|
public static Collection<Object[]> getLists() {
|
||||||
|
return Arrays.asList(new Object[][] {
|
||||||
|
{ createList(), false },
|
||||||
|
{ createListWithLoop(), true },
|
||||||
|
{ createListWithFullCycle(), true },
|
||||||
|
{ createListWithSingleNodeInCycle(), true }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static Node<Integer> createList() {
|
public static Node<Integer> createList() {
|
||||||
Node<Integer> root = Node.createNewNode(10, null);
|
Node<Integer> root = Node.createNewNode(10, null);
|
||||||
|
|
||||||
@ -13,6 +28,26 @@ public class CycleDetectionTestBase {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Node<Integer> createListWithLoop() {
|
||||||
|
Node<Integer> node = createList();
|
||||||
|
createLoop(node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Node<Integer> createListWithFullCycle() {
|
||||||
|
Node<Integer> head = createList();
|
||||||
|
Node<Integer> tail = Node.getTail(head);
|
||||||
|
tail.next = head;
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Node<Integer> createListWithSingleNodeInCycle() {
|
||||||
|
Node<Integer> head = createList();
|
||||||
|
Node<Integer> tail = Node.getTail(head);
|
||||||
|
tail.next = tail;
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
public static void createLoop(Node<Integer> root) {
|
public static void createLoop(Node<Integer> root) {
|
||||||
Node<Integer> tail = Node.getTail(root);
|
Node<Integer> tail = Node.getTail(root);
|
||||||
|
|
||||||
|
@ -2,20 +2,23 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
public class CycleRemovalBruteForceTest extends CycleDetectionTestBase {
|
public class CycleRemovalBruteForceTest extends CycleDetectionTestBase {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<Integer> head;
|
||||||
|
|
||||||
@Test
|
public CycleRemovalBruteForceTest(Node<Integer> head, boolean cycleExists) {
|
||||||
public void givenNormalList_dontDetectLoop() {
|
super();
|
||||||
Node<Integer> root = createList();
|
this.cycleExists = cycleExists;
|
||||||
Assert.assertFalse(CycleRemovalBruteForce.detectAndRemoveCycle(root));
|
this.head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCyclicList_detectAndRemoveLoop() {
|
public void givenList_ifLoopExists_thenDetectAndRemoveLoop() {
|
||||||
Node<Integer> root = createList();
|
Assert.assertEquals(cycleExists, CycleRemovalBruteForce.detectAndRemoveCycle(head));
|
||||||
createLoop(root);
|
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists);
|
||||||
Assert.assertTrue(CycleRemovalBruteForce.detectAndRemoveCycle(root));
|
|
||||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,23 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
public class CycleRemovalByCountingLoopNodesTest extends CycleDetectionTestBase {
|
public class CycleRemovalByCountingLoopNodesTest extends CycleDetectionTestBase {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<Integer> head;
|
||||||
|
|
||||||
@Test
|
public CycleRemovalByCountingLoopNodesTest(Node<Integer> head, boolean cycleExists) {
|
||||||
public void givenNormalList_dontDetectLoop() {
|
super();
|
||||||
Node<Integer> root = createList();
|
this.cycleExists = cycleExists;
|
||||||
Assert.assertFalse(CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(root));
|
this.head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCyclicList_detectAndRemoveLoop() {
|
public void givenList_ifLoopExists_thenDetectAndRemoveLoop() {
|
||||||
Node<Integer> root = createList();
|
Assert.assertEquals(cycleExists, CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(head));
|
||||||
createLoop(root);
|
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists);
|
||||||
Assert.assertTrue(CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(root));
|
|
||||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,23 @@ package com.baeldung.algorithms.linkedlist;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
public class CycleRemovalWithoutCountingLoopNodesTest extends CycleDetectionTestBase {
|
public class CycleRemovalWithoutCountingLoopNodesTest extends CycleDetectionTestBase {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<Integer> head;
|
||||||
|
|
||||||
@Test
|
public CycleRemovalWithoutCountingLoopNodesTest(Node<Integer> head, boolean cycleExists) {
|
||||||
public void givenNormalList_dontDetectLoop() {
|
super();
|
||||||
Node<Integer> root = createList();
|
this.cycleExists = cycleExists;
|
||||||
Assert.assertFalse(CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(root));
|
this.head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCyclicList_detectAndRemoveLoop() {
|
public void givenList_ifLoopExists_thenDetectAndRemoveLoop() {
|
||||||
Node<Integer> root = createList();
|
Assert.assertEquals(cycleExists, CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(head));
|
||||||
createLoop(root);
|
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists);
|
||||||
Assert.assertTrue(CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(root));
|
|
||||||
Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user