Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,13 @@
* [Test Binary Search Tree Delete Node](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/search_tree/test_binary_search_tree_delete_node.py)
* [Test Binary Search Tree Insert](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/search_tree/test_binary_search_tree_insert.py)
* [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
* [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 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)
* Btree
* [Node](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/btree/node.py)
* Heaps
Expand Down
8 changes: 4 additions & 4 deletions algorithms/arrays/two_sum_less_k/test_two_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class TwoSumLessKTestCase(unittest.TestCase):
def test_1(self):
"""numbers = [4,2,11,2,5,3,5,8], target = 7"""
numbers = [4,2,11,2,5,3,5,8]
numbers = [4, 2, 11, 2, 5, 3, 5, 8]
target = 7
expected = 6
actual = two_sum_less_than_k(numbers, target)
Expand All @@ -21,23 +21,23 @@ def test_2(self):

def test_3(self):
"""numbers = [34,23,1,24,75,33,54,8], k = 60"""
numbers = [34,23,1,24,75,33,54,8]
numbers = [34, 23, 1, 24, 75, 33, 54, 8]
k = 60
expected = 58
actual = two_sum_less_than_k(numbers, k)
self.assertEqual(expected, actual)

def test_4(self):
"""numbers = [5,5,5,5,5,5], k = 15"""
numbers = [5,5,5,5,5,5]
numbers = [5, 5, 5, 5, 5, 5]
k = 15
expected = 10
actual = two_sum_less_than_k(numbers, k)
self.assertEqual(expected, actual)

def test_5(self):
"""numbers = [1,2,3,4,5], k = 3"""
numbers = [1,2,3,4,5]
numbers = [1, 2, 3, 4, 5]
k = 3
expected = -1
actual = two_sum_less_than_k(numbers, k)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ def can_run_for(batteries: List[int], n: int, target_time: int) -> bool:


def max_run_time_2(batteries: List[int], n: int) -> int:

"""
Finds the maximum runtime that can power the computers for the given amount of time.

Expand All @@ -67,7 +66,7 @@ def max_run_time_2(batteries: List[int], n: int) -> int:
usable = sum(min(b, mid) for b in batteries)

if usable >= mid * n:
left = mid
left = mid
else:
right = mid - 1
right = mid - 1
return left
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,38 @@


class MaxRunTimeTestCase(unittest.TestCase):

@parameterized.expand([
([2,3,3,4], 3, 4),
([1,1,4,5], 2, 5),
([2,2,2,2], 1, 8),
([7,2,5,10,8], 2, 16),
([1,2,3,4,5], 2, 7),
([3,4,3,4,5,5,8,2], 4, 8),
([5,2,4], 2, 5),
([1,6,2,6,8], 5, 1)
])
@parameterized.expand(
[
([2, 3, 3, 4], 3, 4),
([1, 1, 4, 5], 2, 5),
([2, 2, 2, 2], 1, 8),
([7, 2, 5, 10, 8], 2, 16),
([1, 2, 3, 4, 5], 2, 7),
([3, 4, 3, 4, 5, 5, 8, 2], 4, 8),
([5, 2, 4], 2, 5),
([1, 6, 2, 6, 8], 5, 1),
]
)
def test_max_runtime_1(self, batteries, n, expected):
actual = max_runtime(batteries, n)
self.assertEqual(expected, actual)

@parameterized.expand([
([2,3,3,4], 3, 4),
([1,1,4,5], 2, 5),
([2,2,2,2], 1, 8),
([7,2,5,10,8], 2, 16),
([1,2,3,4,5], 2, 7),
([3,4,3,4,5,5,8,2], 4, 8),
([5,2,4], 2, 5),
([1,6,2,6,8], 5, 1)
])
@parameterized.expand(
[
([2, 3, 3, 4], 3, 4),
([1, 1, 4, 5], 2, 5),
([2, 2, 2, 2], 1, 8),
([7, 2, 5, 10, 8], 2, 16),
([1, 2, 3, 4, 5], 2, 7),
([3, 4, 3, 4, 5, 5, 8, 2], 4, 8),
([5, 2, 4], 2, 5),
([1, 6, 2, 6, 8], 5, 1),
]
)
def test_max_runtime_2(self, batteries, n, expected):
actual = max_run_time_2(batteries, n)
self.assertEqual(expected, actual)


if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()
5 changes: 1 addition & 4 deletions datastructures/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from datastructures.sets import DisjointSetUnion, UnionFind

__all__ = [
"DisjointSetUnion",
"UnionFind"
]
__all__ = ["DisjointSetUnion", "UnionFind"]
4 changes: 2 additions & 2 deletions datastructures/sets/union_find/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_count(self) -> int:

class UnionFind:
"""A minimal Union-Find data structure with path compression."""

def __init__(self, size: int):
"""Initializes the data structure with 'size' elements."""
if size <= 0:
Expand All @@ -71,4 +71,4 @@ def union(self, x: int, y: int) -> bool:
if root_x != root_y:
self.parent[root_y] = root_x
return True
return False
return False
1 change: 0 additions & 1 deletion datastructures/streams/stream_checker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class StreamChecker(object):

def __init__(self, words: List[str]):
"""
Initializes a StreamChecker instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ def test_3(self):
self.assertFalse(stream.query("b"))


if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()
67 changes: 67 additions & 0 deletions datastructures/trees/binary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Binary Trees

## Lowest Common Ancestor of a Binary Tree

You are given two nodes, p and q. The task is to return their lowest common ancestor (LCA). Both nodes have a reference
to their parent node. The tree’s root is not provided; you must use the parent pointers to find the nodes’ common
ancestor.

> Note: The lowest common ancestor of two nodes, p and q, is the lowest node in the binary tree, with both p and q as
> descendants. In a tree, a descendant of a node is any node reachable by following edges downward from that node,
> including the node itself.

Constraints

- -10^4 ≤ `node.data` ≤ 10^4
- The number of nodes in the tree is in the range [2, 500]
- All `node.data` are unique
- `p` != `q`
- Both `p` and `q` are present in the tree

### Examples

![Example 1](./images/examples/lowest_common_ancestor_example_1.png)
![Example 2](./images/examples/lowest_common_ancestor_example_2.png)
![Example 3](./images/examples/lowest_common_ancestor_example_3.png)
![Example 4](./images/examples/lowest_common_ancestor_example_4.png)

### Solution

This solution finds the lowest common ancestor (LCA) of two nodes in a binary tree using a smart two-pointer approach.
We start by placing one pointer at node p and the other at node q. Both pointers move up the tree at each step by
following their parent pointers. If a pointer reaches the root (i.e., its parent is None), it jumps to the other
starting node. This process continues until the two pointers meet. The key idea is that by switching starting points
after reaching the top, both pointers end up traveling the same total distance, even if p and q are at different depths.
When they meet, that meeting point is their lowest common ancestor.

The steps of the algorithm are as follows:

1. Initialize two pointers: ptr1 starting at p and ptr2 starting at q.
2. While ptr1 and ptr2 are not pointing to the same node:
- If ptr1 has a parent, move ptr1 to ptr1.parent; otherwise, set ptr1 = q.
- If ptr2 has a parent, move ptr2 to ptr2.parent; otherwise, set ptr2 = p.

3. When ptr1 == ptr2, return ptr1. This node is the lowest common ancestor (LCA) of p and q.

Let’s look at the following illustration to get a better understanding of the solution:

![Solution 1](./images/solutions/lowest_common_ancestor_solution_1.png)
![Solution 2](./images/solutions/lowest_common_ancestor_solution_2.png)
![Solution 3](./images/solutions/lowest_common_ancestor_solution_3.png)
![Solution 4](./images/solutions/lowest_common_ancestor_solution_4.png)
![Solution 5](./images/solutions/lowest_common_ancestor_solution_5.png)
![Solution 6](./images/solutions/lowest_common_ancestor_solution_6.png)
![Solution 7](./images/solutions/lowest_common_ancestor_solution_7.png)

### Complexity

#### Time Complexity

The time complexity is `O(h)` where `h` is the height of the tree, as in the worst case, each pointer might traverse the
entire height of the tree, including `h` steps.

#### Space complexity

The space complexity of this solution is `O(1)` as there is no additional space being used. Only two pointers are being
maintained requiring constant space.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 22 additions & 4 deletions datastructures/trees/binary/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

class BinaryTreeNode(TreeNode):
"""
Binary tree node class which will implement Binary tree
Binary tree node class which will represents a Binary tree node in a binary tree. A binary tree node only has 2
children, named left and right, both are optional and if none exists, this is assumed to be a leaf node in a binary
tree. This allows to add a pointer to the parent node to allow for easy traversal of the tree upwards from this node
"""

def __init__(
Expand All @@ -14,8 +16,21 @@ def __init__(
left: Optional["BinaryTreeNode"] = None,
right: Optional["BinaryTreeNode"] = None,
key: Optional[Any] = None,
):
super().__init__(data, key)
parent: Optional["BinaryTreeNode"] = None,
) -> None:
"""
Constructor for BinaryTreeNode class. This will create a new node with the provided data and optional
left and right children. The key parameter is used to set a key for the node, if not provided then a hash
of the data is used.

Args:
data (T): Value to be stored in the node
left (Optional[BinaryTreeNode]): Left child of the node
right (Optional[BinaryTreeNode]): Right child of the node
key (Optional[Any]): Key for the node, if not provided a hash of the data is used
parent (Optional[BinaryTreeNode]): Parent of the node
"""
super().__init__(data, key, parent)
self.left: Optional[BinaryTreeNode] = left
self.right: Optional[BinaryTreeNode] = right

Expand Down Expand Up @@ -146,7 +161,7 @@ def insert_right(self, data: T) -> "BinaryTreeNode":
return self.right

@property
def children(self) -> List["BinaryTreeNode"]:
def children(self) -> List["BinaryTreeNode"] | None:
"""Returns children of this node.
Returns:
List: children of this node in a list
Expand Down Expand Up @@ -182,3 +197,6 @@ def __eq__(self, other: "BinaryTreeNode") -> bool:
return True

return False

def __hash__(self):
return hash(self.data)
2 changes: 1 addition & 1 deletion datastructures/trees/binary/search_tree/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, root: Optional[BinaryTreeNode] = None):
self.stack = DynamicSizeStack()

@staticmethod
def construct_bst(items: List[T]) -> Optional['BinarySearchTree']:
def construct_bst(items: List[T]) -> Optional["BinarySearchTree"]:
"""
Constructs a binary search tree from a sorted list of items.

Expand Down
Loading
Loading