diff --git a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForce.java b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForce.java index 532be70480..1df425ad2e 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForce.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForce.java @@ -2,9 +2,9 @@ package com.baeldung.algorithms.linkedlist; public class CycleDetectionBruteForce { - public static boolean detectCycle(Node head) { + public static CycleDetectionResult detectCycle(Node head) { if (head == null) { - return false; + return new CycleDetectionResult<>(false, null); } Node it1 = head; @@ -25,14 +25,14 @@ public class CycleDetectionBruteForce { } if (noOfTimesCurrentNodeVisited == 2) { - return true; + return new CycleDetectionResult<>(true, it1); } x--; } } - return false; + return new CycleDetectionResult<>(false, null); } } diff --git a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIterators.java b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIterators.java index 7c8e038cb2..ab088de44a 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIterators.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIterators.java @@ -2,9 +2,9 @@ package com.baeldung.algorithms.linkedlist; public class CycleDetectionByFastAndSlowIterators { - public static boolean detectCycle(Node head) { + public static CycleDetectionResult detectCycle(Node head) { if (head == null) { - return false; + return new CycleDetectionResult<>(false, null); } Node slow = head; @@ -15,11 +15,11 @@ public class CycleDetectionByFastAndSlowIterators { fast = fast.next.next; if (slow == fast) { - return true; + return new CycleDetectionResult<>(true, fast); } } - return false; + return new CycleDetectionResult<>(false, null); } } diff --git a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashing.java b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashing.java index 0b3f91b97c..90d5ecd711 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashing.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashing.java @@ -5,9 +5,9 @@ import java.util.Set; public class CycleDetectionByHashing { - public static boolean detectCycle(Node head) { + public static CycleDetectionResult detectCycle(Node head) { if (head == null) { - return false; + return new CycleDetectionResult<>(false, null); } Set> set = new HashSet<>(); @@ -15,13 +15,13 @@ public class CycleDetectionByHashing { while (node != null) { if (set.contains(node)) { - return true; + return new CycleDetectionResult<>(true, node); } set.add(node); node = node.next; } - return false; + return new CycleDetectionResult<>(false, null); } } diff --git a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionResult.java b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionResult.java new file mode 100644 index 0000000000..e7556311b3 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleDetectionResult.java @@ -0,0 +1,12 @@ +package com.baeldung.algorithms.linkedlist; + +public class CycleDetectionResult { + boolean cycleExists; + Node node; + + public CycleDetectionResult(boolean cycleExists, Node node) { + super(); + this.cycleExists = cycleExists; + this.node = node; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForce.java b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForce.java index cacfd44121..a2bfaee9a1 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForce.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForce.java @@ -3,26 +3,21 @@ package com.baeldung.algorithms.linkedlist; public class CycleRemovalBruteForce { public static boolean detectAndRemoveCycle(Node head) { - if (head == null) { - return false; + CycleDetectionResult result = CycleDetectionByFastAndSlowIterators.detectCycle(head); + + if (result.cycleExists) { + removeCycle(result.node, head); } - Node slow = head; - Node 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; + return result.cycleExists; } + /** + * @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 void removeCycle(Node loopNodeParam, Node head) { Node it = head; @@ -39,12 +34,12 @@ public class CycleRemovalBruteForce { private static boolean isNodeReachableFromLoopNode(Node it, Node loopNodeParam) { Node loopNode = loopNodeParam; - while (loopNode.next != loopNodeParam) { + do { if (it == loopNode) { return true; } loopNode = loopNode.next; - } + } while (loopNode.next != loopNodeParam); return false; } diff --git a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodes.java b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodes.java index afc525a8d3..d8db37fc4c 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodes.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodes.java @@ -3,28 +3,17 @@ package com.baeldung.algorithms.linkedlist; public class CycleRemovalByCountingLoopNodes { public static boolean detectAndRemoveCycle(Node head) { - if (head == null) { - return false; + CycleDetectionResult result = CycleDetectionByFastAndSlowIterators.detectCycle(head); + + if (result.cycleExists) { + removeCycle(result.node, head); } - Node slow = head; - Node 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; + return result.cycleExists; } - private static void removeCycle(Node head, int cycleLength) { + private static void removeCycle(Node loopNodeParam, Node head) { + int cycleLength = calculateCycleLength(loopNodeParam); Node cycleLengthAdvancedIterator = head; Node it = head; diff --git a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodes.java b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodes.java index d5b8b1cc8f..b979f7f677 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodes.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodes.java @@ -3,24 +3,13 @@ package com.baeldung.algorithms.linkedlist; public class CycleRemovalWithoutCountingLoopNodes { public static boolean detectAndRemoveCycle(Node head) { - if (head == null) { - return false; + CycleDetectionResult result = CycleDetectionByFastAndSlowIterators.detectCycle(head); + + if (result.cycleExists) { + removeCycle(result.node, head); } - Node slow = head; - Node 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; + return result.cycleExists; } private static void removeCycle(Node meetingPointParam, Node head) { diff --git a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForceTest.java b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForceTest.java index 09250a8a66..7f9b8acdbd 100644 --- a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForceTest.java +++ b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionBruteForceTest.java @@ -2,19 +2,22 @@ package com.baeldung.algorithms.linkedlist; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(value = Parameterized.class) public class CycleDetectionBruteForceTest extends CycleDetectionTestBase { + boolean cycleExists; + Node head; - @Test - public void givenNormalList_dontDetectLoop() { - Node root = createList(); - Assert.assertFalse(CycleDetectionBruteForce.detectCycle(root)); + public CycleDetectionBruteForceTest(Node head, boolean cycleExists) { + super(); + this.cycleExists = cycleExists; + this.head = head; } @Test - public void givenCyclicList_detectLoop() { - Node root = createList(); - createLoop(root); - Assert.assertTrue(CycleDetectionBruteForce.detectCycle(root)); + public void givenList_detectLoop() { + Assert.assertEquals(cycleExists, CycleDetectionBruteForce.detectCycle(head).cycleExists); } } diff --git a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIteratorsTest.java b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIteratorsTest.java index cfe00aaefc..17d339bc33 100644 --- a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIteratorsTest.java +++ b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByFastAndSlowIteratorsTest.java @@ -2,19 +2,22 @@ package com.baeldung.algorithms.linkedlist; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(value = Parameterized.class) public class CycleDetectionByFastAndSlowIteratorsTest extends CycleDetectionTestBase { + boolean cycleExists; + Node head; - @Test - public void givenNormalList_dontDetectLoop() { - Node root = createList(); - Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root)); + public CycleDetectionByFastAndSlowIteratorsTest(Node head, boolean cycleExists) { + super(); + this.cycleExists = cycleExists; + this.head = head; } @Test - public void givenCyclicList_detectLoop() { - Node root = createList(); - createLoop(root); - Assert.assertTrue(CycleDetectionByFastAndSlowIterators.detectCycle(root)); + public void givenList_detectLoop() { + Assert.assertEquals(cycleExists, CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists); } } diff --git a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashingTest.java b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashingTest.java index af9a8239ef..73a2cc7861 100644 --- a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashingTest.java +++ b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionByHashingTest.java @@ -2,19 +2,22 @@ package com.baeldung.algorithms.linkedlist; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(value = Parameterized.class) public class CycleDetectionByHashingTest extends CycleDetectionTestBase { + boolean cycleExists; + Node head; - @Test - public void givenNormalList_dontDetectLoop() { - Node root = createList(); - Assert.assertFalse(CycleDetectionByHashing.detectCycle(root)); + public CycleDetectionByHashingTest(Node head, boolean cycleExists) { + super(); + this.cycleExists = cycleExists; + this.head = head; } @Test - public void givenCyclicList_detectLoop() { - Node root = createList(); - createLoop(root); - Assert.assertTrue(CycleDetectionByHashing.detectCycle(root)); + public void givenList_detectLoop() { + Assert.assertEquals(cycleExists, CycleDetectionByHashing.detectCycle(head).cycleExists); } } diff --git a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionTestBase.java b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionTestBase.java index 2abf5de1d4..51906de8e5 100644 --- a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionTestBase.java +++ b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleDetectionTestBase.java @@ -1,7 +1,22 @@ package com.baeldung.algorithms.linkedlist; +import java.util.Arrays; +import java.util.Collection; + +import org.junit.runners.Parameterized.Parameters; + public class CycleDetectionTestBase { + @Parameters + public static Collection getLists() { + return Arrays.asList(new Object[][] { + { createList(), false }, + { createListWithLoop(), true }, + { createListWithFullCycle(), true }, + { createListWithSingleNodeInCycle(), true } + }); + } + public static Node createList() { Node root = Node.createNewNode(10, null); @@ -13,6 +28,26 @@ public class CycleDetectionTestBase { return root; } + public static Node createListWithLoop() { + Node node = createList(); + createLoop(node); + return node; + } + + public static Node createListWithFullCycle() { + Node head = createList(); + Node tail = Node.getTail(head); + tail.next = head; + return head; + } + + public static Node createListWithSingleNodeInCycle() { + Node head = createList(); + Node tail = Node.getTail(head); + tail.next = tail; + return head; + } + public static void createLoop(Node root) { Node tail = Node.getTail(root); diff --git a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForceTest.java b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForceTest.java index b32f20fb75..6484c9988e 100644 --- a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForceTest.java +++ b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalBruteForceTest.java @@ -2,20 +2,23 @@ package com.baeldung.algorithms.linkedlist; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(value = Parameterized.class) public class CycleRemovalBruteForceTest extends CycleDetectionTestBase { + boolean cycleExists; + Node head; - @Test - public void givenNormalList_dontDetectLoop() { - Node root = createList(); - Assert.assertFalse(CycleRemovalBruteForce.detectAndRemoveCycle(root)); + public CycleRemovalBruteForceTest(Node head, boolean cycleExists) { + super(); + this.cycleExists = cycleExists; + this.head = head; } @Test - public void givenCyclicList_detectAndRemoveLoop() { - Node root = createList(); - createLoop(root); - Assert.assertTrue(CycleRemovalBruteForce.detectAndRemoveCycle(root)); - Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root)); + public void givenList_ifLoopExists_thenDetectAndRemoveLoop() { + Assert.assertEquals(cycleExists, CycleRemovalBruteForce.detectAndRemoveCycle(head)); + Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists); } } diff --git a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodesTest.java b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodesTest.java index dd938afae1..7bfd89c502 100644 --- a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodesTest.java +++ b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalByCountingLoopNodesTest.java @@ -2,20 +2,23 @@ package com.baeldung.algorithms.linkedlist; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(value = Parameterized.class) public class CycleRemovalByCountingLoopNodesTest extends CycleDetectionTestBase { + boolean cycleExists; + Node head; - @Test - public void givenNormalList_dontDetectLoop() { - Node root = createList(); - Assert.assertFalse(CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(root)); + public CycleRemovalByCountingLoopNodesTest(Node head, boolean cycleExists) { + super(); + this.cycleExists = cycleExists; + this.head = head; } @Test - public void givenCyclicList_detectAndRemoveLoop() { - Node root = createList(); - createLoop(root); - Assert.assertTrue(CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(root)); - Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root)); + public void givenList_ifLoopExists_thenDetectAndRemoveLoop() { + Assert.assertEquals(cycleExists, CycleRemovalByCountingLoopNodes.detectAndRemoveCycle(head)); + Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists); } } diff --git a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodesTest.java b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodesTest.java index 56a08a5ee4..c77efb3e3e 100644 --- a/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodesTest.java +++ b/algorithms/src/test/java/com/baeldung/algorithms/linkedlist/CycleRemovalWithoutCountingLoopNodesTest.java @@ -2,20 +2,23 @@ package com.baeldung.algorithms.linkedlist; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(value = Parameterized.class) public class CycleRemovalWithoutCountingLoopNodesTest extends CycleDetectionTestBase { + boolean cycleExists; + Node head; - @Test - public void givenNormalList_dontDetectLoop() { - Node root = createList(); - Assert.assertFalse(CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(root)); + public CycleRemovalWithoutCountingLoopNodesTest(Node head, boolean cycleExists) { + super(); + this.cycleExists = cycleExists; + this.head = head; } @Test - public void givenCyclicList_detectAndRemoveLoop() { - Node root = createList(); - createLoop(root); - Assert.assertTrue(CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(root)); - Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(root)); + public void givenList_ifLoopExists_thenDetectAndRemoveLoop() { + Assert.assertEquals(cycleExists, CycleRemovalWithoutCountingLoopNodes.detectAndRemoveCycle(head)); + Assert.assertFalse(CycleDetectionByFastAndSlowIterators.detectCycle(head).cycleExists); } } \ No newline at end of file