From 770180ac40428bbeae30ac850cd346630497c169 Mon Sep 17 00:00:00 2001 From: Nikita Rusetskii Date: Sun, 21 Jul 2024 19:11:19 +0200 Subject: [PATCH] 141. Linked List Cycle --- .../com/xtenzq/linkedlist/FastPointers.java | 20 +++++++++++++ .../com/xtenzq/linkedlist/utils/ListNode.java | 28 +++++++++++++++++++ .../xtenzq/linkedlist/FastPointersTest.java | 25 +++++++++++++++-- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/FastPointers.java b/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/FastPointers.java index 919d4fa..0b42a4d 100644 --- a/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/FastPointers.java +++ b/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/FastPointers.java @@ -20,4 +20,24 @@ public static int getMiddle(ListNode head) { } return slow.val; } + + /** + * Checks if given linked list with {@code head} has cycle + * + * @param head head of a linked list + * @return {@code true} if linked list has cycle, {@code false} otherwise + * @implNote This method runs in {@code O(n)} time complexity and {@code O(1)} space complexity + */ + public static boolean hasCycle(ListNode head) { + ListNode slow = head; + ListNode fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (fast == slow) { + return true; + } + } + return false; + } } diff --git a/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/utils/ListNode.java b/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/utils/ListNode.java index 15602c4..34e4024 100644 --- a/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/utils/ListNode.java +++ b/algorithms-and-data-structures/src/main/java/com/xtenzq/linkedlist/utils/ListNode.java @@ -31,6 +31,34 @@ public static ListNode buildLinkedList(int...nums) { return head; } + public static ListNode buildLinkedListWithCycle(int[] nums, int cyclePos) { + if (nums == null || nums.length == 0) { + return null; + } + if (cyclePos > nums.length - 1 || cyclePos < -1) { + throw new IllegalArgumentException("cycle position is out of bound"); + } + + ListNode head = new ListNode(nums[0]); + ListNode current = head; + ListNode cycleNode = (cyclePos == 0) ? head : null; + + for (int i = 1; i < nums.length; i++) { + current.next = new ListNode(nums[i]); + current = current.next; + if (i == cyclePos) { + cycleNode = current; + } + } + + if (cyclePos != -1) { + current.next = cycleNode; + } + + return head; + } + + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/algorithms-and-data-structures/src/test/java/com/xtenzq/linkedlist/FastPointersTest.java b/algorithms-and-data-structures/src/test/java/com/xtenzq/linkedlist/FastPointersTest.java index aae248e..2769067 100644 --- a/algorithms-and-data-structures/src/test/java/com/xtenzq/linkedlist/FastPointersTest.java +++ b/algorithms-and-data-structures/src/test/java/com/xtenzq/linkedlist/FastPointersTest.java @@ -3,20 +3,39 @@ import org.junit.jupiter.api.Test; import static com.xtenzq.linkedlist.FastPointers.getMiddle; +import static com.xtenzq.linkedlist.FastPointers.hasCycle; import static com.xtenzq.linkedlist.utils.ListNode.buildLinkedList; -import static org.junit.jupiter.api.Assertions.*; +import static com.xtenzq.linkedlist.utils.ListNode.buildLinkedListWithCycle; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class FastPointersTest { @Test - void middleNode_case1() { + void getMiddle_case1() { int actual = getMiddle(buildLinkedList(1, 2, 3, 4, 5)); assertEquals(3, actual); } @Test - void middleNode_case2() { + void getMiddle_case2() { int actual = getMiddle(buildLinkedList(1)); assertEquals(1, actual); } + + @Test + void hasCycle_case1() { + assertTrue(hasCycle(buildLinkedListWithCycle(new int[]{3, 2, 0, -4}, 1))); + } + + @Test + void hasCycle_case2() { + assertTrue(hasCycle(buildLinkedListWithCycle(new int[]{1, 2}, 0))); + } + + @Test + void hasCycle_case3() { + assertFalse(hasCycle(buildLinkedListWithCycle(new int[]{1}, -1))); + } } \ No newline at end of file