Skip to content

Commit 9793aac

Browse files
Merge branch 'master' into usage
2 parents de47704 + 2c15b8c commit 9793aac

File tree

9 files changed

+88
-11
lines changed

9 files changed

+88
-11
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
steps:
1212
- run: sudo apt-get update && sudo apt-get install -y libhdf5-dev
13-
- uses: actions/checkout@v5
13+
- uses: actions/checkout@v6
1414
- uses: astral-sh/setup-uv@v7
1515
with:
1616
enable-cache: true
@@ -22,7 +22,8 @@ jobs:
2222
- run: uv sync --group=test
2323
- name: Run tests
2424
# TODO: #8818 Re-enable quantum tests
25-
run: uv run pytest
25+
run: uv run --with=pytest-run-parallel pytest
26+
--iterations=8 --parallel-threads=auto
2627
--ignore=computer_vision/cnn_classification.py
2728
--ignore=docs/conf.py
2829
--ignore=dynamic_programming/k_means_clustering_tensorflow.py

.github/workflows/devcontainer_ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
build:
1313
runs-on: ubuntu-latest
1414
steps:
15-
- uses: actions/checkout@v5
15+
- uses: actions/checkout@v6
1616
- uses: devcontainers/[email protected]
1717
with:
1818
push: never

.github/workflows/directory_writer.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
directory_writer:
77
runs-on: ubuntu-latest
88
steps:
9-
- uses: actions/checkout@v5
9+
- uses: actions/checkout@v6
1010
with:
1111
fetch-depth: 0
1212
- uses: actions/setup-python@v6

.github/workflows/project_euler.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
libxml2-dev libxslt-dev
2222
libhdf5-dev
2323
libopenblas-dev
24-
- uses: actions/checkout@v5
24+
- uses: actions/checkout@v6
2525
- uses: astral-sh/setup-uv@v7
2626
- uses: actions/setup-python@v6
2727
with:
@@ -39,7 +39,7 @@ jobs:
3939
libxml2-dev libxslt-dev
4040
libhdf5-dev
4141
libopenblas-dev
42-
- uses: actions/checkout@v5
42+
- uses: actions/checkout@v6
4343
- uses: astral-sh/setup-uv@v7
4444
- uses: actions/setup-python@v6
4545
with:

.github/workflows/ruff.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ jobs:
1111
ruff:
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@v5
14+
- uses: actions/checkout@v6
1515
- uses: astral-sh/setup-uv@v7
1616
- run: uvx ruff check --output-format=github .

.github/workflows/sphinx.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
libxml2-dev libxslt-dev
3333
libhdf5-dev
3434
libopenblas-dev
35-
- uses: actions/checkout@v5
35+
- uses: actions/checkout@v6
3636
- uses: astral-sh/setup-uv@v7
3737
- uses: actions/setup-python@v6
3838
with:

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ repos:
1919
- id: auto-walrus
2020

2121
- repo: https://github.com/astral-sh/ruff-pre-commit
22-
rev: v0.13.3
22+
rev: v0.14.7
2323
hooks:
2424
- id: ruff-check
2525
- id: ruff-format
@@ -32,7 +32,7 @@ repos:
3232
- tomli
3333

3434
- repo: https://github.com/tox-dev/pyproject-fmt
35-
rev: v2.7.0
35+
rev: v2.11.1
3636
hooks:
3737
- id: pyproject-fmt
3838

@@ -50,7 +50,7 @@ repos:
5050
- id: validate-pyproject
5151

5252
- repo: https://github.com/pre-commit/mirrors-mypy
53-
rev: v1.18.2
53+
rev: v1.19.0
5454
hooks:
5555
- id: mypy
5656
args:

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ authors = [ { name = "TheAlgorithms Contributors" } ]
66
requires-python = ">=3.14"
77
classifiers = [
88
"Programming Language :: Python :: 3 :: Only",
9+
"Programming Language :: Python :: 3.14",
910
]
1011
dependencies = [
1112
"beautifulsoup4>=4.12.3",

searches/binary_search.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,81 @@ def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
243243
return -1
244244

245245

246+
def binary_search_with_duplicates(sorted_collection: list[int], item: int) -> list[int]:
247+
"""Pure implementation of a binary search algorithm in Python that supports
248+
duplicates.
249+
250+
Resources used:
251+
https://stackoverflow.com/questions/13197552/using-binary-search-with-sorted-array-with-duplicates
252+
253+
The collection must be sorted in ascending order; otherwise the result will be
254+
unpredictable. If the target appears multiple times, this function returns a
255+
list of all indexes where the target occurs. If the target is not found,
256+
this function returns an empty list.
257+
258+
:param sorted_collection: some ascending sorted collection with comparable items
259+
:param item: item value to search for
260+
:return: a list of indexes where the item is found (empty list if not found)
261+
262+
Examples:
263+
>>> binary_search_with_duplicates([0, 5, 7, 10, 15], 0)
264+
[0]
265+
>>> binary_search_with_duplicates([0, 5, 7, 10, 15], 15)
266+
[4]
267+
>>> binary_search_with_duplicates([1, 2, 2, 2, 3], 2)
268+
[1, 2, 3]
269+
>>> binary_search_with_duplicates([1, 2, 2, 2, 3], 4)
270+
[]
271+
"""
272+
if list(sorted_collection) != sorted(sorted_collection):
273+
raise ValueError("sorted_collection must be sorted in ascending order")
274+
275+
def lower_bound(sorted_collection: list[int], item: int) -> int:
276+
"""
277+
Returns the index of the first element greater than or equal to the item.
278+
279+
:param sorted_collection: The sorted list to search.
280+
:param item: The item to find the lower bound for.
281+
:return: The index where the item can be inserted while maintaining order.
282+
"""
283+
left = 0
284+
right = len(sorted_collection)
285+
while left < right:
286+
midpoint = left + (right - left) // 2
287+
current_item = sorted_collection[midpoint]
288+
if current_item < item:
289+
left = midpoint + 1
290+
else:
291+
right = midpoint
292+
return left
293+
294+
def upper_bound(sorted_collection: list[int], item: int) -> int:
295+
"""
296+
Returns the index of the first element strictly greater than the item.
297+
298+
:param sorted_collection: The sorted list to search.
299+
:param item: The item to find the upper bound for.
300+
:return: The index where the item can be inserted after all existing instances.
301+
"""
302+
left = 0
303+
right = len(sorted_collection)
304+
while left < right:
305+
midpoint = left + (right - left) // 2
306+
current_item = sorted_collection[midpoint]
307+
if current_item <= item:
308+
left = midpoint + 1
309+
else:
310+
right = midpoint
311+
return left
312+
313+
left = lower_bound(sorted_collection, item)
314+
right = upper_bound(sorted_collection, item)
315+
316+
if left == len(sorted_collection) or sorted_collection[left] != item:
317+
return []
318+
return list(range(left, right))
319+
320+
246321
def binary_search_by_recursion(
247322
sorted_collection: list[int], item: int, left: int = 0, right: int = -1
248323
) -> int:

0 commit comments

Comments
 (0)