diff --git a/algorithms/arrays/two_sum/README.md b/algorithms/arrays/two_sum/README.md index da055900..b5b6a601 100644 --- a/algorithms/arrays/two_sum/README.md +++ b/algorithms/arrays/two_sum/README.md @@ -35,6 +35,74 @@ Output: [1,2] Explanation: The sum of -1 and 0 is -1. Therefore index1 = 1, index2 = 2. We return [1, 2]. ``` +--- + +# Two Sum IV - Input Is a BST + +Given the root of a binary search tree and an integer k, determine whether there are two elements in the BST whose sum +equals k. Return TRUE if such elements exist or FALSE otherwise. + +## Constraints + +- The number of nodes in the tree is in the range [1, 10^3]. +- 10^3 <= Node.data <= 10^3 +- root is guaranteed to be a valid binary search tree. +- 10^4 <= k <= 10^4 + +## Examples + +![Example 1](./images/examples/two_sum_4_input_is_bst_example_1.png) +![Example 2](./images/examples/two_sum_4_input_is_bst_example_2.png) +![Example 3](./images/examples/two_sum_4_input_is_bst_example_3.png) + +## Solution + +The core intuition behind solving this problem is to use a set to track values encountered during a breadth-first search +of the binary search tree (BST). We calculate each node’s complement (i.e., the difference between k and the node’s value) +and check if this complement already exists in the set. If it does, it means there is another node in the BST whose value, +when added to the current node’s value, equals k, and we return TRUE. Otherwise, we add the current node’s value to the set. +If no two nodes with the required sum are found by the end of the traversal, we return FALSE. + +Using the intuition above, the solution can be implemented as follows: + +1. If the BST is empty, return FALSE. +2. Define a set, seen, to store the visited node values. +3. Define a queue, q, to perform the level-order traversal of the BST. + - Enqueue root to q to start the traversal from the root node. +4. While the queue is not empty, repeat the following steps: + - Dequeue a node from the front of the queue. + - Check if the complement of the current node’s value exists in the seen set. If yes, return TRUE. + - Add the current node’s value to the seen set. + - Enqueue its left and right children. +5. If no two nodes with the required sum are found, return FALSE. + +Let’s look at the following illustration to get a better understanding of the solution: + +![Solution 1](./images/solutions/two_sum_4_input_is_bst_solution_1.png) +![Solution 2](./images/solutions/two_sum_4_input_is_bst_solution_2.png) +![Solution 3](./images/solutions/two_sum_4_input_is_bst_solution_3.png) +![Solution 4](./images/solutions/two_sum_4_input_is_bst_solution_4.png) +![Solution 5](./images/solutions/two_sum_4_input_is_bst_solution_5.png) +![Solution 6](./images/solutions/two_sum_4_input_is_bst_solution_6.png) +![Solution 7](./images/solutions/two_sum_4_input_is_bst_solution_7.png) +![Solution 8](./images/solutions/two_sum_4_input_is_bst_solution_8.png) +![Solution 9](./images/solutions/two_sum_4_input_is_bst_solution_9.png) +![Solution 10](./images/solutions/two_sum_4_input_is_bst_solution_10.png) +![Solution 11](./images/solutions/two_sum_4_input_is_bst_solution_11.png) +![Solution 12](./images/solutions/two_sum_4_input_is_bst_solution_12.png) +![Solution 13](./images/solutions/two_sum_4_input_is_bst_solution_13.png) +![Solution 14](./images/solutions/two_sum_4_input_is_bst_solution_14.png) + +### Time Complexity + +The algorithm’s time complexity is O(n), where n is the number of nodes in the binary search tree. + +### Space Complexity + +The algorithm’s space complexity is O(n), as the size of the set can grow up to at most n. + +--- + ## Related Topics - Array diff --git a/algorithms/arrays/two_sum/__init__.py b/algorithms/arrays/two_sum/__init__.py index 6b79adc2..cec31c78 100644 --- a/algorithms/arrays/two_sum/__init__.py +++ b/algorithms/arrays/two_sum/__init__.py @@ -1,4 +1,6 @@ -from typing import List +from typing import List, Deque, Set +from collections import deque +from datastructures.trees.binary.node import BinaryTreeNode def two_sum(numbers: List[int], target: int) -> List[int]: @@ -59,3 +61,46 @@ def two_sum_with_pointers(numbers: List[int], target: int) -> List[int]: first_pointer += 1 else: last_pointer -= 1 + return [] + + +def two_sum_find_target(root: BinaryTreeNode, k: int) -> bool: + """ + Checks if two nodes in the binary search tree add up to the given target number + + Args: + root (BinaryTreeNode): root of the binary search tree + k (int): target number + Returns: + bool: True if there are two nodes in the binary search tree that add up to the target, False otherwise + """ + if not root: + return False + + # store the seen values so far during the traversal + seen: Set[int] = set() + + # Keep track of visited nodes, we start with the root node. We use a FIFO queue here + queue: Deque[BinaryTreeNode] = deque() + queue.append(root) + + while queue: + # dequeue the first node, the front of the queue. + curr = queue.popleft() + + if curr: + # check if the complement of this node's value exists in the values we have seen so far + if (k - curr.data) in seen: + return True + + # if not, add the value + seen.add(curr.data) + + # enqueue the left and right children of the current node + if curr.left: + queue.append(curr.left) + if curr.right: + queue.append(curr.right) + + # if we reach here, we have not found two nodes that add up to the target + return False diff --git a/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_1.png b/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_1.png new file mode 100644 index 00000000..e2e5248f Binary files /dev/null and b/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_1.png differ diff --git a/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_2.png b/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_2.png new file mode 100644 index 00000000..0b6a5b28 Binary files /dev/null and b/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_2.png differ diff --git a/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_3.png b/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_3.png new file mode 100644 index 00000000..b4668f36 Binary files /dev/null and b/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_3.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_1.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_1.png new file mode 100644 index 00000000..dbb88bf1 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_1.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_10.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_10.png new file mode 100644 index 00000000..50e9237f Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_10.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_11.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_11.png new file mode 100644 index 00000000..e02b4678 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_11.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_12.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_12.png new file mode 100644 index 00000000..cbba157c Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_12.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_13.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_13.png new file mode 100644 index 00000000..99d30a7b Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_13.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_14.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_14.png new file mode 100644 index 00000000..9583b070 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_14.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_2.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_2.png new file mode 100644 index 00000000..a110c89b Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_2.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_3.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_3.png new file mode 100644 index 00000000..344a4e41 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_3.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_4.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_4.png new file mode 100644 index 00000000..2c920349 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_4.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_5.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_5.png new file mode 100644 index 00000000..28092840 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_5.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_6.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_6.png new file mode 100644 index 00000000..998c0284 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_6.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_7.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_7.png new file mode 100644 index 00000000..93356319 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_7.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_8.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_8.png new file mode 100644 index 00000000..d1b48b27 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_8.png differ diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_9.png b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_9.png new file mode 100644 index 00000000..03710c50 Binary files /dev/null and b/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_9.png differ diff --git a/algorithms/arrays/two_sum/test_two_sum.py b/algorithms/arrays/two_sum/test_two_sum.py index 0584f72f..09b08070 100644 --- a/algorithms/arrays/two_sum/test_two_sum.py +++ b/algorithms/arrays/two_sum/test_two_sum.py @@ -1,56 +1,55 @@ import unittest -from . import two_sum, two_sum_with_pointers +from typing import List +from parameterized import parameterized +from datastructures.trees.binary.node import BinaryTreeNode +from algorithms.arrays.two_sum import ( + two_sum, + two_sum_with_pointers, + two_sum_find_target, +) + +TWO_SUM_TEST_CASES = [ + ([2, 7, 11, 15], 9, [1, 2]), + ([2, 3, 4], 6, [1, 3]), + ([-1, 0], -1, [1, 2]), +] + +TWO_SUM_TWO_POINTERS_TEST_CASES = [ + ([2, 7, 11, 15], 9, [0, 1]), + ([2, 3, 4], 6, [0, 2]), + ([-1, 0], -1, [0, 1]), +] + +TWO_SUM_INPUT_BST_TEST_CASES = [ + ([7, 3, 13, 2, 5, None, 19], 18, True), + ([8, 4, None, 0, None, -11], 4, True), + ([1, None, 2, None, 3, None, 4], 5, True), + ([900], 900, False), + ([0, -200, 500, -300, -100, 400, 600], 500, True), +] class TwoSumTestCase(unittest.TestCase): - def test_1(self): - """numbers = [2,7,11,15], target = 9""" - numbers = [2, 7, 11, 15] - target = 9 - expected = [1, 2] + @parameterized.expand(TWO_SUM_TEST_CASES) + def test_two_sum(self, numbers: List[int], target: int, expected: List[int]): actual = two_sum(numbers, target) self.assertEqual(expected, actual) - def test_2(self): - """numbers = [2,3,4], target = 6""" - numbers = [2, 3, 4] - target = 6 - expected = [1, 3] - actual = two_sum(numbers, target) - self.assertEqual(expected, actual) - - def test_3(self): - """numbers = [-1,0], target = -1""" - numbers = [-1, 0] - target = -1 - expected = [1, 2] - actual = two_sum(numbers, target) - self.assertEqual(expected, actual) - - -class TwoSumWithPointersTestCase(unittest.TestCase): - def test_1(self): - """numbers = [2,7,11,15], target = 9""" - numbers = [2, 7, 11, 15] - target = 9 - expected = [0, 1] + @parameterized.expand(TWO_SUM_TWO_POINTERS_TEST_CASES) + def test_two_sum_with_two_pointers( + self, numbers: List[int], target: int, expected: List[int] + ): actual = two_sum_with_pointers(numbers, target) self.assertEqual(expected, actual) - def test_2(self): - """numbers = [2,3,4], target = 6""" - numbers = [2, 3, 4] - target = 6 - expected = [0, 2] - actual = two_sum_with_pointers(numbers, target) - self.assertEqual(expected, actual) + @parameterized.expand(TWO_SUM_INPUT_BST_TEST_CASES) + def test_two_sum_input_bst(self, numbers: List[int], target: int, expected: bool): + root = BinaryTreeNode(numbers[0]) + for data in numbers[1:]: + if data is not None: + root.insert_node(data) - def test_3(self): - """numbers = [-1,0], target = -1""" - numbers = [-1, 0] - target = -1 - expected = [0, 1] - actual = two_sum_with_pointers(numbers, target) + actual = two_sum_find_target(root, target) self.assertEqual(expected, actual)