Skip to content

Commit ca71208

Browse files
committed
feat: update
1 parent 041590d commit ca71208

File tree

4 files changed

+96
-62
lines changed

4 files changed

+96
-62
lines changed

leetcode_py/data_structures/list_node.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
class ListNode:
2-
def __init__(self, val: int = 0, next: "ListNode | None" = None):
1+
# from typing import Generic, TypeVar
2+
3+
# # TODO: Remove TypeVar when minimum Python version is 3.12+ (use class ListNode[T]: syntax)
4+
# T = TypeVar("T")
5+
6+
7+
class ListNode[T]:
8+
def __init__(self, val: T, next: "ListNode[T] | None" = None):
39
self.val = val
410
self.next = next
511

612
@classmethod
7-
def from_list(cls, arr: list[int]) -> "ListNode | None":
13+
def from_list(cls, arr: list[T]) -> "ListNode[T] | None":
814
if not arr:
915
return None
1016
head = cls(arr[0])
@@ -14,9 +20,9 @@ def from_list(cls, arr: list[int]) -> "ListNode | None":
1420
current = current.next
1521
return head
1622

17-
def to_list(self) -> list[int]:
23+
def to_list(self) -> list[T]:
1824
result = []
19-
current: "ListNode | None" = self
25+
current: "ListNode[T] | None" = self
2026
while current:
2127
result.append(current.val)
2228
current = current.next

leetcode_py/data_structures/tree_node.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
from typing import Generic, TypeVar
2+
13
import graphviz
24
from anytree import Node, RenderTree
35

6+
# TODO: Remove TypeVar when minimum Python version is 3.12+ (use class TreeNode[T]: syntax)
7+
T = TypeVar("T")
8+
49

5-
def build_anytree(node: "TreeNode | None", parent: Node | None = None) -> Node | None:
10+
def build_anytree(node: "TreeNode[T] | None", parent: Node | None = None) -> Node | None:
611
if not node:
712
return Node("None", parent=parent) if parent else None
813
current = Node(str(node.val), parent=parent)
@@ -12,7 +17,7 @@ def build_anytree(node: "TreeNode | None", parent: Node | None = None) -> Node |
1217
return current
1318

1419

15-
def add_nodes(dot: graphviz.Digraph, node: "TreeNode | None", node_id: int = 0) -> int:
20+
def add_nodes(dot: graphviz.Digraph, node: "TreeNode[T] | None", node_id: int = 0) -> int:
1621
if not node:
1722
return node_id
1823

@@ -31,14 +36,14 @@ def add_nodes(dot: graphviz.Digraph, node: "TreeNode | None", node_id: int = 0)
3136
return next_id - 1
3237

3338

34-
class TreeNode:
35-
def __init__(self, val: int = 0, left: "TreeNode | None" = None, right: "TreeNode | None" = None):
39+
class TreeNode(Generic[T]):
40+
def __init__(self, val: T, left: "TreeNode[T] | None" = None, right: "TreeNode[T] | None" = None):
3641
self.val = val
3742
self.left = left
3843
self.right = right
3944

4045
@classmethod
41-
def from_list(cls, arr: list[int | None]) -> "TreeNode | None":
46+
def from_list(cls, arr: list[T | None]) -> "TreeNode[T] | None":
4247
"""Convert array representation to binary tree."""
4348
if not arr or arr[0] is None:
4449
return None
@@ -66,10 +71,10 @@ def from_list(cls, arr: list[int | None]) -> "TreeNode | None":
6671

6772
return root
6873

69-
def to_list(self) -> list[int | None]:
74+
def to_list(self) -> list[T | None]:
7075
"""Convert binary tree to array representation."""
71-
result: list[int | None] = []
72-
queue: list[TreeNode | None] = [self]
76+
result: list[T | None] = []
77+
queue: list[TreeNode[T] | None] = [self]
7378

7479
while queue:
7580
node = queue.pop(0)

tests/data_structures/test_list_node.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,39 @@
1+
from typing import Any
2+
13
import pytest
24

35
from leetcode_py import ListNode
46

57

68
class TestListNode:
79
@pytest.mark.parametrize(
8-
"val,expected_val,expected_next",
10+
"val, expected_val, expected_next",
911
[
10-
(None, 0, None), # default
1112
(5, 5, None), # with value
13+
("hello", "hello", None), # string value
1214
],
1315
)
14-
def test_init(self, val, expected_val, expected_next):
15-
node = ListNode() if val is None else ListNode(val)
16+
def test_init(self, val: Any, expected_val: Any, expected_next: Any) -> None:
17+
node = ListNode(val)
1618
assert node.val == expected_val
1719
assert node.next == expected_next
1820

19-
def test_init_with_next(self):
20-
next_node = ListNode(2)
21-
node = ListNode(1, next_node)
21+
def test_init_with_next(self) -> None:
22+
next_node = ListNode[int](2)
23+
node = ListNode[int](1, next_node)
2224
assert node.val == 1
2325
assert node.next == next_node
2426

2527
@pytest.mark.parametrize(
26-
"input_list,expected_result",
28+
"input_list, expected_result",
2729
[
2830
([], None),
2931
([1], "single_node"),
3032
([1, 2, 3], "multiple_nodes"),
33+
(["a", "b"], "string_nodes"),
3134
],
3235
)
33-
def test_from_list(self, input_list, expected_result):
36+
def test_from_list(self, input_list: list[Any], expected_result: str | None) -> None:
3437
result = ListNode.from_list(input_list)
3538

3639
if expected_result is None:
@@ -47,47 +50,57 @@ def test_from_list(self, input_list, expected_result):
4750
assert result.next.next is not None
4851
assert result.next.next.val == 3
4952
assert result.next.next.next is None
53+
elif expected_result == "string_nodes":
54+
assert result is not None
55+
assert result.val == "a"
56+
assert result.next is not None
57+
assert result.next.val == "b"
58+
assert result.next.next is None
5059

5160
@pytest.mark.parametrize(
52-
"input_list,expected_output",
61+
"input_list, expected_output",
5362
[
5463
([1], [1]),
5564
([1, 2, 3], [1, 2, 3]),
65+
(["x", "y"], ["x", "y"]),
5666
],
5767
)
58-
def test_to_list(self, input_list, expected_output):
68+
def test_to_list(self, input_list: list[Any], expected_output: list[Any]) -> None:
5969
node = ListNode.from_list(input_list)
6070
assert node is not None
6171
assert node.to_list() == expected_output
6272

6373
@pytest.mark.parametrize(
64-
"input_list,expected_str,expected_repr",
74+
"input_list, expected_str, expected_repr",
6575
[
6676
([1, 2, 3], "1 -> 2 -> 3", "ListNode([1, 2, 3])"),
77+
(["a", "b"], "a -> b", "ListNode(['a', 'b'])"),
6778
],
6879
)
69-
def test_string_representations(self, input_list, expected_str, expected_repr):
80+
def test_string_representations(
81+
self, input_list: list[Any], expected_str: str, expected_repr: str
82+
) -> None:
7083
node = ListNode.from_list(input_list)
7184
assert node is not None
7285
assert str(node) == expected_str
7386
assert repr(node) == expected_repr
7487
assert node._repr_html_() == expected_str
7588

7689
@pytest.mark.parametrize(
77-
"list1,list2,should_equal",
90+
"list1,list2, should_equal",
7891
[
7992
([1, 2, 3], [1, 2, 3], True),
8093
([1, 2, 3], [1, 2, 4], False),
8194
],
8295
)
83-
def test_equality(self, list1, list2, should_equal):
96+
def test_equality(self, list1: list[int], list2: list[int], should_equal: bool) -> None:
8497
node1 = ListNode.from_list(list1)
8598
node2 = ListNode.from_list(list2)
8699
assert (node1 == node2) == should_equal
87100

88101
@pytest.mark.parametrize("other_value", [[1], "1"])
89-
def test_equality_different_types(self, other_value):
90-
node = ListNode(1)
102+
def test_equality_different_types(self, other_value: Any) -> None:
103+
node = ListNode[int](1)
91104
assert node != other_value
92105

93106
@pytest.mark.parametrize(
@@ -96,9 +109,11 @@ def test_equality_different_types(self, other_value):
96109
[1, 2, 3, 4, 5],
97110
[1],
98111
[10, 20, 30],
112+
["hello", "world"],
113+
[True, False, True],
99114
],
100115
)
101-
def test_roundtrip_conversion(self, test_list):
116+
def test_roundtrip_conversion(self, test_list: list[Any]) -> None:
102117
node = ListNode.from_list(test_list)
103118
assert node is not None
104119
result = node.to_list()

0 commit comments

Comments
 (0)