Skip to content

Commit 8333ebf

Browse files
jiayang laiclaude
authored andcommitted
Code review: fix 5 oldest code files with type hints and improvements
- searches/sentinel_linear_search.py: - Added type hints for function parameters and return type - Added Collection import - Fixed docstring formatting and improved examples - Fixed return check (index >= len(sequence) instead of index == len(sequence)) - Fixed grammar in print statement - sorts/pancake_sort.py: - Added type hints and return type annotation - Fixed list copying to avoid mutating input - Improved docstring with proper Args/Returns documentation - Fixed slicing syntax for clarity - graphs/g_topological_sort.py: - Refactored from global variables to proper class-based design - Added TopologicalSort class with type hints - Added proper docstrings and examples - Kept functional interface for backwards compatibility - Improved main block with both class and functional examples - dynamic_programming/abbreviation.py: - Added type hints and early exit checks - Fixed broken/malformed docstring - Added more comprehensive doctest examples - Added early exit for impossible cases - digital_image_processing/change_brightness.py: - Fixed typo: brigt_img -> bright_img - Added type hints - Fixed nested function docstring (wrong description about "bits") - Fixed pixel adjustment to use int() for proper clamping - Added CLI interface with proper argument handling - Made image paths configurable via command line Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 791deb4 commit 8333ebf

File tree

5 files changed

+262
-91
lines changed

5 files changed

+262
-91
lines changed
Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,53 @@
1+
"""
2+
Module for adjusting the brightness of images using PIL.
3+
"""
14
from PIL import Image
25

36

4-
def change_brightness(img: Image, level: float) -> Image:
5-
"""
6-
Change the brightness of a PIL Image to a given level.
7-
"""
7+
def change_brightness(img: Image.Image, level: float) -> Image.Image:
8+
"""Change the brightness of a PIL Image by the given level.
9+
10+
Args:
11+
img: The input PIL Image to modify.
12+
level: Brightness adjustment level between -255.0 (darker) and
13+
255.0 (brighter). 0 means no change.
814
9-
def brightness(c: int) -> float:
10-
"""
11-
Fundamental Transformation/Operation that'll be performed on
12-
every bit.
13-
"""
14-
return 128 + level + (c - 128)
15+
Returns:
16+
A new Image with adjusted brightness.
1517
18+
Raises:
19+
ValueError: If level is outside the valid range [-255.0, 255.0].
20+
21+
Examples:
22+
>>> from PIL import Image
23+
>>> img = Image.new("RGB", (10, 10), (128, 128, 128))
24+
>>> bright_img = change_brightness(img, 50)
25+
>>> bright_img.getpixel((5, 5)) # doctest: +ELLIPSIS
26+
(178, 178, 178)
27+
"""
1628
if not -255.0 <= level <= 255.0:
1729
raise ValueError("level must be between -255.0 (black) and 255.0 (white)")
18-
return img.point(brightness)
30+
31+
def adjust_pixel(c: int) -> int:
32+
"""Adjust a single pixel channel value by the brightness level."""
33+
return max(0, min(255, c + int(level)))
34+
35+
return img.point(adjust_pixel)
1936

2037

2138
if __name__ == "__main__":
22-
# Load image
23-
with Image.open("image_data/lena.jpg") as img:
24-
# Change brightness to 100
25-
brigt_img = change_brightness(img, 100)
26-
brigt_img.save("image_data/lena_brightness.png", format="png")
39+
import sys
40+
41+
if len(sys.argv) != 4:
42+
print("Usage: python change_brightness.py <input_image> <output_image> <level>")
43+
print(" level: brightness adjustment from -255 to 255")
44+
sys.exit(1)
45+
46+
input_path = sys.argv[1]
47+
output_path = sys.argv[2]
48+
level = float(sys.argv[3])
49+
50+
with Image.open(input_path) as img:
51+
bright_img = change_brightness(img, level)
52+
bright_img.save(output_path, format="png")
53+
print(f"Brightness changed by {level} and saved to {output_path}")

dynamic_programming/abbreviation.py

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,69 @@
11
"""
2-
https://www.hackerrank.com/challenges/abbr/problem
3-
You can perform the following operation on some string, :
2+
String abbreviation problem solved using dynamic programming.
43
5-
1. Capitalize zero or more of 's lowercase letters at some index i
6-
(i.e., make them uppercase).
7-
2. Delete all of the remaining lowercase letters in .
4+
Given two strings 'a' and 'b', determine if 'a' can be transformed to 'b' by:
5+
1. Capitalizing zero or more of 'a's lowercase letters at any index.
6+
2. Deleting all remaining lowercase letters.
7+
8+
Reference: https://www.hackerrank.com/challenges/Abbr/problem
89
910
Example:
10-
a=daBcd and b="ABC"
11-
daBcd -> capitalize a and c(dABCd) -> remove d (ABC)
11+
a = "daBcd" and b = "ABC"
12+
"daBcd" -> capitalize 'a' and 'c' to get "dABCd" -> delete 'd' to get "ABC"
1213
"""
1314

1415

15-
def abbr(a: str, b: str) -> bool:
16-
"""
17-
>>> abbr("daBcd", "ABC")
16+
def abbreviation(a: str, b: str) -> bool:
17+
"""Check if string 'a' can be transformed to 'b'.
18+
19+
Uses dynamic programming where dp[i][j] indicates whether the first i
20+
characters of 'a' can be transformed to the first j characters of 'b'.
21+
22+
Args:
23+
a: The source string containing lowercase and uppercase letters.
24+
b: The target string containing only uppercase letters.
25+
26+
Returns:
27+
True if 'a' can be transformed to 'b', False otherwise.
28+
29+
Examples:
30+
>>> abbreviation("daBcd", "ABC")
31+
True
32+
>>> abbreviation("dBcd", "ABC")
33+
False
34+
>>> abbreviation("ABC", "ABC")
1835
True
19-
>>> abbr("dBcd", "ABC")
36+
>>> abbreviation("abc", "ABC")
2037
False
38+
>>> abbreviation("abCD", "ABCD")
39+
True
2140
"""
2241
n = len(a)
2342
m = len(b)
24-
dp = [[False for _ in range(m + 1)] for _ in range(n + 1)]
43+
44+
# Early exit: if 'b' has lowercase letters, it can never match
45+
if b != b.upper():
46+
return False
47+
48+
# Early exit: if 'a' has more uppercase letters than the length of 'b'
49+
a_upper_count = sum(1 for c in a if c.isupper())
50+
if a_upper_count > m:
51+
return False
52+
53+
dp = [[False] * (m + 1) for _ in range(n + 1)]
2554
dp[0][0] = True
55+
2656
for i in range(n):
2757
for j in range(m + 1):
28-
if dp[i][j]:
29-
if j < m and a[i].upper() == b[j]:
30-
dp[i + 1][j + 1] = True
31-
if a[i].islower():
32-
dp[i + 1][j] = True
58+
if not dp[i][j]:
59+
continue
60+
61+
if j < m and a[i].upper() == b[j]:
62+
dp[i + 1][j + 1] = True
63+
64+
if a[i].islower():
65+
dp[i + 1][j] = True
66+
3367
return dp[n][m]
3468

3569

graphs/g_topological_sort.py

Lines changed: 114 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,129 @@
1-
# Author: Phyllipe Bezerra (https://github.com/pmba)
1+
"""
2+
Graph topological sort implementation using Depth-First Search (DFS).
23
3-
clothes = {
4-
0: "underwear",
5-
1: "pants",
6-
2: "belt",
7-
3: "suit",
8-
4: "shoe",
9-
5: "socks",
10-
6: "shirt",
11-
7: "tie",
12-
8: "watch",
13-
}
4+
A topological sort or topological ordering of a directed graph is a linear
5+
ordering of its vertices in which each vertex comes before all vertices
6+
to which it has outgoing edges.
147
15-
graph = [[1, 4], [2, 4], [3], [], [], [4], [2, 7], [3], []]
8+
For example, the graph representing clothing dependencies:
9+
- underwear -> pants -> belt -> suit
10+
- shirt -> tie -> suit
11+
- socks -> shoes
1612
17-
visited = [0 for x in range(len(graph))]
18-
stack = []
13+
Author: Phyllipe Bezerra (https://github.com/pmba)
14+
"""
1915

16+
from collections.abc import Sequence
2017

21-
def print_stack(stack, clothes):
22-
order = 1
23-
while stack:
24-
current_clothing = stack.pop()
25-
print(order, clothes[current_clothing])
26-
order += 1
2718

19+
class TopologicalSort:
20+
"""Topological sort implementation using DFS."""
2821

29-
def depth_first_search(u, visited, graph):
30-
visited[u] = 1
31-
for v in graph[u]:
32-
if not visited[v]:
33-
depth_first_search(v, visited, graph)
22+
def __init__(self, graph: list[list[int]], labels: dict[int, str] | None = None) -> None:
23+
"""Initialize the topological sorter.
24+
25+
Args:
26+
graph: Adjacency list representation where graph[u] contains
27+
all vertices v such that there is an edge from u to v.
28+
labels: Optional mapping from vertex indices to label strings
29+
for pretty printing results.
30+
"""
31+
self.graph = graph
32+
self.labels = labels or {}
33+
self.n = len(graph)
34+
self.visited: list[int] = [0] * self.n
35+
self.stack: list[int] = []
36+
37+
def _depth_first_search(self, u: int) -> None:
38+
"""Perform DFS from vertex u, adding to stack after exploring all neighbors."""
39+
self.visited[u] = 1
40+
for v in self.graph[u]:
41+
if not self.visited[v]:
42+
self._depth_first_search(v)
43+
self.stack.append(u)
44+
45+
def sort(self) -> list[int]:
46+
"""Compute and return the topological ordering of vertices.
47+
48+
Returns:
49+
A list of vertices in topological order (each vertex appears before
50+
all vertices reachable from it).
51+
"""
52+
self.stack = []
53+
self.visited = [0] * self.n
54+
55+
for v in range(self.n):
56+
if not self.visited[v]:
57+
self._depth_first_search(v)
58+
59+
return self.stack[::-1]
60+
61+
def print_sorted(self, order: list[int]) -> None:
62+
"""Print the vertices in topological order with their labels.
63+
64+
Args:
65+
order: A list of vertices in topological order.
66+
"""
67+
for i, vertex in enumerate(order, 1):
68+
label = self.labels.get(vertex, str(vertex))
69+
print(f"{i}. {label}")
3470

35-
stack.append(u)
3671

72+
def topological_sort(graph: list[list[int]], visited: list[int]) -> list[int]:
73+
"""Topological sort of a directed acyclic graph using DFS.
74+
75+
Args:
76+
graph: Adjacency list representation of the graph.
77+
visited: List to track visited vertices (modified in place).
78+
79+
Returns:
80+
Vertices in topological order.
81+
82+
Examples:
83+
>>> graph = [[1, 2], [3], [3], []]
84+
>>> visited = [0] * len(graph)
85+
>>> topological_sort(graph, visited)
86+
[0, 2, 1, 3]
87+
"""
88+
stack = []
89+
90+
def dfs(u: int) -> None:
91+
visited[u] = 1
92+
for v in graph[u]:
93+
if not visited[v]:
94+
dfs(v)
95+
stack.append(u)
3796

38-
def topological_sort(graph, visited):
3997
for v in range(len(graph)):
4098
if not visited[v]:
41-
depth_first_search(v, visited, graph)
99+
dfs(v)
100+
101+
return stack[::-1]
42102

43103

44104
if __name__ == "__main__":
45-
topological_sort(graph, visited)
46-
print(stack)
47-
print_stack(stack, clothes)
105+
# Example: Clothing dependencies
106+
clothes = {
107+
0: "underwear",
108+
1: "pants",
109+
2: "belt",
110+
3: "suit",
111+
4: "shoe",
112+
5: "socks",
113+
6: "shirt",
114+
7: "tie",
115+
8: "watch",
116+
}
117+
118+
graph = [[1, 4], [2, 4], [3], [], [], [4], [2, 7], [3], []]
119+
120+
# Using the class-based interface
121+
sorter = TopologicalSort(graph, clothes)
122+
order = sorter.sort()
123+
sorter.print_sorted(order)
124+
125+
# Also demonstrate the functional interface
126+
print("\n--- Using functional interface ---")
127+
visited = [0] * len(graph)
128+
result = topological_sort(graph, visited)
129+
print(result)

searches/sentinel_linear_search.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""
2-
This is pure Python implementation of sentinel linear search algorithm
2+
This is pure Python implementation of sentinel linear search algorithm.
3+
4+
The sentinel linear search improves on linear search by adding a sentinel
5+
(target value) at the end of the list to avoid bounds checking on each iteration.
36
47
For doctests run following command:
58
python -m doctest -v sentinel_linear_search.py
@@ -9,27 +12,30 @@
912
For manual testing run:
1013
python sentinel_linear_search.py
1114
"""
15+
from collections.abc import Sequence
16+
from typing import Any
17+
1218

19+
def sentinel_linear_search(sequence: list, target: Any) -> int | None:
20+
"""Pure implementation of sentinel linear search algorithm in Python.
1321
14-
def sentinel_linear_search(sequence, target):
15-
"""Pure implementation of sentinel linear search algorithm in Python
22+
Args:
23+
sequence: A list of comparable items. Note: this list will be modified
24+
temporarily by appending the target value (restored before return).
25+
target: The item value to search for.
1626
17-
:param sequence: some sequence with comparable items
18-
:param target: item value to search
19-
:return: index of found item or None if item is not found
27+
Returns:
28+
The index of the found item, or None if the item is not found.
2029
2130
Examples:
2231
>>> sentinel_linear_search([0, 5, 7, 10, 15], 0)
2332
0
24-
2533
>>> sentinel_linear_search([0, 5, 7, 10, 15], 15)
2634
4
27-
2835
>>> sentinel_linear_search([0, 5, 7, 10, 15], 5)
2936
1
30-
31-
>>> sentinel_linear_search([0, 5, 7, 10, 15], 6)
32-
37+
>>> sentinel_linear_search([0, 5, 7, 10, 15], 6) is None
38+
True
3339
"""
3440
sequence.append(target)
3541

@@ -39,7 +45,7 @@ def sentinel_linear_search(sequence, target):
3945

4046
sequence.pop()
4147

42-
if index == len(sequence):
48+
if index >= len(sequence):
4349
return None
4450

4551
return index
@@ -53,6 +59,6 @@ def sentinel_linear_search(sequence, target):
5359
target = int(target_input)
5460
result = sentinel_linear_search(sequence, target)
5561
if result is not None:
56-
print(f"{target} found at positions: {result}")
62+
print(f"{target} found at position: {result}")
5763
else:
5864
print("Not found")

0 commit comments

Comments
 (0)