Skip to content

Commit 1aff4bb

Browse files
authored
Merge pull request #96 from BrianLusina/feat/find-closest-value-bst
feat(datastructures, puzzles): find the closest value in a binary search tree
2 parents 3c5ac87 + fb749bd commit 1aff4bb

File tree

6 files changed

+153
-3
lines changed

6 files changed

+153
-3
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@
545545
* [Test Cyclically Shifted Array](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/cyclically_shifted_array/test_cyclically_shifted_array.py)
546546
* Find Closest Number
547547
* [Test Find Closest Number](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_closest_number/test_find_closest_number.py)
548+
* Find Closest Value
549+
* [Test Find Closest Value](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_closest_value/test_find_closest_value.py)
548550
* Find First In Duplicate List
549551
* [Test Find First In Duplicates](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_first_in_duplicate_list/test_find_first_in_duplicates.py)
550552
* Find Fixed Number

datastructures/trees/binary/search_tree/__init__.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,46 @@ def find_second_largest(self) -> BinaryTreeNode:
219219

220220
return current
221221

222+
def find_closest_value_in_bst(self, target: T) -> Optional[BinaryTreeNode]:
223+
"""
224+
Finds the closest value in the binary search tree to the given target value.
225+
226+
Args:
227+
target T: Value to search for
228+
Returns:
229+
Node with the closest value to the target
230+
"""
231+
# edge case for empty nodes, if none is provided, we can't find a value that is close to the target
232+
if not self.root:
233+
return None
234+
235+
# if the node's data is the target, exit early by returning it
236+
if self.root.data == target:
237+
return self.root
238+
239+
# this keeps track of the minimum on both the left and the right
240+
closest_node = self.root
241+
min_diff = abs(target - self.root.data)
242+
current = self.root
243+
244+
# while the queue is not empty, we pop off nodes from the queue and check for their values
245+
while current:
246+
current_diff = abs(target - self.root.data)
247+
248+
if current_diff < min_diff:
249+
min_diff = current_diff
250+
closest_node = current
251+
252+
if current.data == target:
253+
return current
254+
255+
if target < current.data:
256+
current = current.left
257+
else:
258+
current = current.right
259+
260+
return closest_node
261+
222262
def range_sum(self, low: int, high: int):
223263
"""
224264
returns the sum of datas of all nodes with a data in the range [low, high].

puzzles/search/binary_search/find_closest_number/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
# Find the Closest Number
22

3-
we will be given a sorted array and a target number. Our goal is to find a number in the array that is closest to the
4-
target number. We will be making use of a binary search to solve this problem, so make sure that you have gone through
5-
the previous lesson.
3+
We will be given a sorted array and a target number. Our goal is to find a number in the array that is closest to the
4+
target number. We will be making use of a binary search to solve this problem.
65

76
The array may contain duplicate values and negative numbers.
87

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Find Closest Value in BST
2+
3+
Write a function that takes in a Binary Search Tree (BST) and a target integer
4+
value and returns the closest value to that target value contained in the BST.
5+
6+
You can assume that there will only be one closest value.
7+
8+
Each <span>BST</span> node has an integer <span>value</span>, a
9+
<span>left</span> child node, and a <span>right</span> child node. A node is
10+
said to be a valid <span>BST</span> node if and only if it satisfies the BST
11+
property: its <span>value</span> is strictly greater than the values of every
12+
node to its left; its <span>value</span> is less than or equal to the values
13+
of every node to its right; and its children nodes are either valid
14+
<span>BST</span> nodes themselves or <span>None</span> / <span>null</span>.
15+
16+
Sample Input:
17+
18+
```text
19+
tree = 10
20+
/ \
21+
5 15
22+
/ \ / \
23+
2 5 13 22
24+
/ \
25+
1 14
26+
target = 12
27+
```
28+
29+
Sample output: 13
30+
31+
## Hints
32+
33+
- Try traversing the BST node by node, all the while keeping track of the node with the value closest to the target value.
34+
Calculating the absolute value of the difference between a node's value and the target value should allow you to
35+
check if that node is closer than the current closest one.
36+
- Make use of the BST property to determine what side of any given node has values close to the target value and is
37+
therefore worth exploring.
38+
- What are the advantages and disadvantages of solving this problem iteratively as opposed to recursively?
39+
40+
## Optimal Space & Time Complexity
41+
42+
Average: O(log(n)) time | O(1) space where n is the number of nodes in the tree
43+
BST Worst: O(n) time | O(1) space where n is the number of nodes in the tree
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import Optional
2+
from datastructures.trees.binary.search_tree import BinaryTreeNode
3+
4+
5+
def find_closest_value_in_bst(node: BinaryTreeNode, target: int) -> Optional[int]:
6+
# edge case for empty nodes, if none is provided, we can't find a value that is close to the target
7+
if not node:
8+
return None
9+
10+
# if the node's data is the target, exit early by returning it
11+
if node.data == target:
12+
return node.data
13+
14+
# this keeps track of the minimum on both the left and the right
15+
closest_value = node.data
16+
min_diff = abs(target - node.data)
17+
current = node
18+
19+
# while the queue is not empty, we pop off nodes from the queue and check for their values
20+
while current:
21+
current_diff = abs(target - current.data)
22+
23+
if current_diff < min_diff:
24+
min_diff = current_diff
25+
closest_value = current.data
26+
27+
if current.data == target:
28+
return current.data
29+
30+
if target < current.data:
31+
current = current.left
32+
else:
33+
current = current.right
34+
35+
return closest_value
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import unittest
2+
from datastructures.trees.binary.search_tree import BinaryTreeNode
3+
from . import find_closest_value_in_bst
4+
5+
6+
class FindClosestValueTestCases(unittest.TestCase):
7+
def test_something(self):
8+
root = BinaryTreeNode(
9+
data=10,
10+
left=BinaryTreeNode(data=5,
11+
left=BinaryTreeNode(
12+
data=2,
13+
left=BinaryTreeNode(data=1),
14+
right=BinaryTreeNode(data=5))
15+
),
16+
right=BinaryTreeNode(data=15,
17+
left=BinaryTreeNode(
18+
data=13,
19+
right=BinaryTreeNode(
20+
data=14,
21+
right=BinaryTreeNode(data=22)
22+
)
23+
))
24+
)
25+
expected = 13
26+
actual = find_closest_value_in_bst(root, target=12)
27+
self.assertEqual(expected, actual)
28+
29+
30+
if __name__ == '__main__':
31+
unittest.main()

0 commit comments

Comments
 (0)