diff --git a/DIRECTORY.md b/DIRECTORY.md index 6c16c150..97d5f667 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -80,6 +80,8 @@ * Graphs * Course Schedule * [Test Course Schedule](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/graphs/course_schedule/test_course_schedule.py) + * Frog Position After T Seconds + * [Test Frog Position After T Seconds](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/graphs/frog_position_after_t_seconds/test_frog_position_after_t_seconds.py) * Greedy * Min Arrows * [Test Find Min Arrows](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/min_arrows/test_find_min_arrows.py) @@ -192,6 +194,9 @@ * [Test Array Advance Game](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/arrays/array_advance_game/test_array_advance_game.py) * Array Diff * [Diff Elements In List](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/arrays/array_diff/diff_elements_in_list.py) + * Contains Duplicates + * [Test Contains Nearby Almost Duplicate](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/arrays/contains_duplicates/test_contains_nearby_almost_duplicate.py) + * [Test Contains Nearby Duplicate](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/arrays/contains_duplicates/test_contains_nearby_duplicate.py) * Distinct * [Distinct Values](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/arrays/distinct/distinct_values.py) * Matrix @@ -288,8 +293,10 @@ * [Test Binary Search Tree Search](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/search_tree/test_binary_search_tree_search.py) * [Test Utils](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/test_utils.py) * Tree + * [Binary Tree](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/binary_tree.py) * [Test Binary Tree](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree.py) * [Test Binary Tree Deserialize](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_deserialize.py) + * [Test Binary Tree Min Camera Cover](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_min_camera_cover.py) * [Test Binary Tree Serialize](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_serialize.py) * [Test Binary Tree Visible Nodes](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_visible_nodes.py) * [Utils](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/utils.py) @@ -857,8 +864,6 @@ * [Test Array Pair Sum](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_array_pair_sum.py) * [Test Build Tower](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_build_tower.py) * [Test Cascading Subsets](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_cascading_subsets.py) - * [Test Contains Nearby Almost Duplicate](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_contains_nearby_almost_duplicate.py) - * [Test Contains Nearby Duplicate](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_contains_nearby_duplicate.py) * [Test Dynamic Array](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_dynamic_array.py) * [Test Find Unique](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_find_unique.py) * [Test Highest Rank](https://github.com/BrianLusina/PythonSnips/blob/master/tests/datastructures/arrays/test_highest_rank.py) diff --git a/datastructures/arrays/contains_duplicates/README.md b/datastructures/arrays/contains_duplicates/README.md index 74792e7c..29c23661 100644 --- a/datastructures/arrays/contains_duplicates/README.md +++ b/datastructures/arrays/contains_duplicates/README.md @@ -17,9 +17,18 @@ Example 3: Input: nums = [1,1,1,3,3,4,3,2,4,2] Output: true +--- + ## Contains Duplicates II -Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such that nums[i] == nums[j] and abs(i - j) <= k. +Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such +that nums[i] == nums[j] and abs(i - j) <= k. + +Constraints: + +- 1 <= nums.length <= 10^3 +- -10^3 <= nums[i] <= 10^3 +- 0 <= k <= 10^4 Example 1: @@ -34,6 +43,53 @@ Example 3: Input: nums = [1,2,3,1,2,3], k = 2 Output: false +### Solution + +The core intuition of solving this problem is maintaining a sliding window of size k to track elements within a limited +range using a set. As we iterate through the array, we check if the current element already exists in the set, +indicating a duplicate within the range. If it exists, we return TRUE. Otherwise, the element is added to the set. +If the set size exceeds k, we remove the oldest element to ensure that the set only contains elements within the valid +range at any time. + +Using the above intuition, the solution can be implemented as follows: + +1. Create a set, `seen`, to track elements within the sliding window of size `k`. +2. Loop through each index `i` of the array `nums`. + - If the current element, `nums[i]`, already exists in the set, a duplicate exists within a range of `k` indices. + Therefore, we return TRUE. + - Add the current element to the set. + - If the set’s size exceeds `k`, remove the oldest element in the window (`nums[i - k]`) to maintain the window’s size. + This ensures only elements within the range k are tracked. + +3. If the loop completes without finding duplicates, we return FALSE. + +Let’s look at the following illustration to get a better understanding of the solution: + +![Solution 1](./images/solutions/contains_duplicates_ii_solution_1.png) +![Solution 2](./images/solutions/contains_duplicates_ii_solution_2.png) +![Solution 3](./images/solutions/contains_duplicates_ii_solution_3.png) +![Solution 4](./images/solutions/contains_duplicates_ii_solution_4.png) +![Solution 5](./images/solutions/contains_duplicates_ii_solution_5.png) +![Solution 6](./images/solutions/contains_duplicates_ii_solution_6.png) +![Solution 7](./images/solutions/contains_duplicates_ii_solution_7.png) +![Solution 8](./images/solutions/contains_duplicates_ii_solution_8.png) +![Solution 9](./images/solutions/contains_duplicates_ii_solution_9.png) +![Solution 10](./images/solutions/contains_duplicates_ii_solution_10.png) +![Solution 11](./images/solutions/contains_duplicates_ii_solution_11.png) + +#### Time Complexity + +The time complexity of the solution is O(n), where n is the length of the input array `nums`. +This is because we iterate through the array once, performing constant-time operations for each element. + +#### Space Complexity + +The space complexity of the solution is O(min(n, k)), where n is the length of the input array `nums` and k is the +maximum number of steps between duplicate elements. This is because we use a set to store the elements within the +sliding window of size `k`, and the maximum size of the set is limited by the minimum of `n` and `k`. + +--- + ## Contains Duplicate III Given an integer array nums and two integers k and t, return true if there are two distinct indices i and j in the array diff --git a/datastructures/arrays/contains_duplicates/__init__.py b/datastructures/arrays/contains_duplicates/__init__.py index f8804b84..508b13b5 100644 --- a/datastructures/arrays/contains_duplicates/__init__.py +++ b/datastructures/arrays/contains_duplicates/__init__.py @@ -1,13 +1,60 @@ import sys -from typing import List +from typing import List, Set def contains_nearby_duplicate(nums: List[int], k: int) -> bool: + """ + Checks if there are any duplicate elements within k steps of each other + in the given list of numbers. + + Args: + nums (List[int]): The list of numbers to check. + k (int): The maximum number of steps between duplicate elements. + + Returns: + bool: True if there are any duplicate elements within k steps of each other, False otherwise. + """ + # Dictionary to store the indices of the numbers we have seen so far d = dict() for i, n in enumerate(nums): + # If we have seen this number before and it is within k steps of the current position if n in d and i - d[n] <= k: return True + # Store the index of the current number d[n] = i + # If we have not found any duplicate elements within k steps of each other + return False + + +def contains_nearby_duplicates_2(nums: List[int], k: int) -> bool: + """ + Checks if there are any duplicate elements within k steps of each other + in the given list of numbers. + + Args: + nums (List[int]): The list of numbers to check. + k (int): The maximum number of steps between duplicate elements. + + Returns: + bool: True if there are any duplicate elements within k steps of each other, False otherwise. + """ + # Set to store the numbers we have seen so far + seen: Set[int] = set() + # Iterate over the list of numbers + for idx in range(len(nums)): + # If we have seen this number before + if nums[idx] in seen: + # Return True + return True + # Add the current number to the set of seen numbers + seen.add(nums[idx]) + + # If we have seen more than k numbers + if len(seen) > k: + # Remove the number that is k steps behind the current number + seen.remove(nums[idx - k]) + + # If we have not found any duplicate elements within k steps of each other return False diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_1.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_1.png new file mode 100644 index 00000000..759ae541 Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_1.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_10.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_10.png new file mode 100644 index 00000000..e1f9edf3 Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_10.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_11.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_11.png new file mode 100644 index 00000000..1cf3d499 Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_11.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_2.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_2.png new file mode 100644 index 00000000..8317d5b5 Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_2.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_3.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_3.png new file mode 100644 index 00000000..d0cd66c2 Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_3.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_4.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_4.png new file mode 100644 index 00000000..836625eb Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_4.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_5.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_5.png new file mode 100644 index 00000000..a24be74b Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_5.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_6.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_6.png new file mode 100644 index 00000000..045ea199 Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_6.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_7.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_7.png new file mode 100644 index 00000000..7024b1f7 Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_7.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_8.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_8.png new file mode 100644 index 00000000..d492995e Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_8.png differ diff --git a/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_9.png b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_9.png new file mode 100644 index 00000000..ac2b624b Binary files /dev/null and b/datastructures/arrays/contains_duplicates/images/solutions/contains_duplicates_ii_solution_9.png differ diff --git a/tests/datastructures/arrays/test_contains_nearby_almost_duplicate.py b/datastructures/arrays/contains_duplicates/test_contains_nearby_almost_duplicate.py similarity index 100% rename from tests/datastructures/arrays/test_contains_nearby_almost_duplicate.py rename to datastructures/arrays/contains_duplicates/test_contains_nearby_almost_duplicate.py diff --git a/datastructures/arrays/contains_duplicates/test_contains_nearby_duplicate.py b/datastructures/arrays/contains_duplicates/test_contains_nearby_duplicate.py new file mode 100644 index 00000000..83474ebe --- /dev/null +++ b/datastructures/arrays/contains_duplicates/test_contains_nearby_duplicate.py @@ -0,0 +1,42 @@ +import unittest +from typing import List +from parameterized import parameterized +from datastructures.arrays.contains_duplicates import ( + contains_nearby_duplicate, + contains_nearby_duplicates_2, +) + + +class ContainsNearbyDuplicateTestCases(unittest.TestCase): + @parameterized.expand( + [ + ([7, 8, 6, 7, 9], 3, True), + ([7, 8, 6, 7, 6, 9], 2, True), + ([900], 900, False), + ([9, -6, 3, 0, -3, -6, 9], 5, True), + ([1, 2, 3, 1, 2, 3], 2, False), + ([1, 0, 1, 1], 1, True), + ([1, 2, 3, 1], 3, True), + ] + ) + def test_contains_duplicates(self, nums: List[int], k: int, expected: bool): + actual = contains_nearby_duplicate(nums, k) + self.assertEqual(expected, actual) + + @parameterized.expand( + [ + ([7, 8, 6, 7, 9], 3, True), + ([7, 8, 6, 7, 6, 9], 2, True), + ([900], 900, False), + ([1, 2, 3, 1, 2, 3], 2, False), + ([1, 0, 1, 1], 1, True), + ([1, 2, 3, 1], 3, True), + ] + ) + def test_contains_duplicates_2(self, nums: List[int], k: int, expected: bool): + actual = contains_nearby_duplicates_2(nums, k) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/datastructures/arrays/test_contains_nearby_duplicate.py b/tests/datastructures/arrays/test_contains_nearby_duplicate.py deleted file mode 100644 index 6c57bd3a..00000000 --- a/tests/datastructures/arrays/test_contains_nearby_duplicate.py +++ /dev/null @@ -1,33 +0,0 @@ -import unittest - -from datastructures.arrays.contains_duplicates import contains_nearby_duplicate - - -class ContainsNearbyDuplicateTestCases(unittest.TestCase): - def test_nums_is_1_2_3_1_and_k_is_3(self): - """Should return true for nums=[1,2,3,1] and k = 3""" - nums = [1, 2, 3, 1] - k = 3 - expected = True - actual = contains_nearby_duplicate(nums, k) - self.assertEqual(expected, actual) - - def test_nums_is_1_0_1_1_and_k_is_1(self): - """Should return true for nums=[1,0,1,1] and k = 1""" - nums = [1, 0, 1, 1] - k = 1 - expected = True - actual = contains_nearby_duplicate(nums, k) - self.assertEqual(expected, actual) - - def test_nums_is_1_2_3_1_2_3_and_k_is_2(self): - """Should return false for nums=[1,2,3,1,2,3] and k = 2""" - nums = [1, 2, 3, 1, 2, 3] - k = 2 - expected = False - actual = contains_nearby_duplicate(nums, k) - self.assertEqual(expected, actual) - - -if __name__ == "__main__": - unittest.main()