Skip to content

Commit eddb548

Browse files
Merge branch 'TheAlgorithms:master' into master
2 parents b4ec916 + e9e7c96 commit eddb548

27 files changed

+694
-58
lines changed

Diff for: .devcontainer/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md
2-
ARG VARIANT=3.12-bookworm
2+
ARG VARIANT=3.13-bookworm
33
FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT}
44
COPY requirements.txt /tmp/pip-tmp/
55
RUN python3 -m pip install --upgrade pip \

Diff for: .devcontainer/devcontainer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// Update 'VARIANT' to pick a Python version: 3, 3.11, 3.10, 3.9, 3.8
88
// Append -bullseye or -buster to pin to an OS version.
99
// Use -bullseye variants on local on arm64/Apple Silicon.
10-
"VARIANT": "3.12-bookworm",
10+
"VARIANT": "3.13-bookworm",
1111
}
1212
},
1313

Diff for: .github/CODEOWNERS

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
/.* @cclauss
1111

12-
# /arithmetic_analysis/
13-
1412
# /backtracking/
1513

1614
# /bit_manipulation/

Diff for: .github/workflows/build.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ jobs:
2525
- name: Run tests
2626
# TODO: #8818 Re-enable quantum tests
2727
run: pytest
28-
--ignore=quantum/q_fourier_transform.py
2928
--ignore=computer_vision/cnn_classification.py
29+
--ignore=docs/conf.py
3030
--ignore=dynamic_programming/k_means_clustering_tensorflow.py
3131
--ignore=machine_learning/lstm/lstm_prediction.py
3232
--ignore=neural_network/input_data.py
3333
--ignore=project_euler/
34+
--ignore=quantum/q_fourier_transform.py
3435
--ignore=scripts/validate_solutions.py
3536
--cov-report=term-missing:skip-covered
3637
--cov=. .

Diff for: .github/workflows/sphinx.yml

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: sphinx
2+
3+
on:
4+
# Triggers the workflow on push or pull request events but only for the "master" branch
5+
push:
6+
branches: ["master"]
7+
pull_request:
8+
branches: ["master"]
9+
# Or manually from the Actions tab
10+
workflow_dispatch:
11+
12+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
13+
permissions:
14+
contents: read
15+
pages: write
16+
id-token: write
17+
18+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
19+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
20+
concurrency:
21+
group: "pages"
22+
cancel-in-progress: false
23+
24+
jobs:
25+
build_docs:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
- uses: actions/setup-python@v5
30+
with:
31+
python-version: 3.13
32+
allow-prereleases: true
33+
- run: pip install --upgrade pip
34+
- run: pip install myst-parser sphinx-autoapi sphinx-pyproject
35+
- uses: actions/configure-pages@v5
36+
- run: sphinx-build -c docs . docs/_build/html
37+
- uses: actions/upload-pages-artifact@v3
38+
with:
39+
path: docs/_build/html
40+
41+
deploy_docs:
42+
environment:
43+
name: github-pages
44+
url: ${{ steps.deployment.outputs.page_url }}
45+
if: github.event_name != 'pull_request'
46+
needs: build_docs
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/deploy-pages@v4
50+
id: deployment

Diff for: .pre-commit-config.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.6.0
3+
rev: v5.0.0
44
hooks:
55
- id: check-executables-have-shebangs
66
- id: check-toml
@@ -16,7 +16,7 @@ repos:
1616
- id: auto-walrus
1717

1818
- repo: https://github.com/astral-sh/ruff-pre-commit
19-
rev: v0.6.8
19+
rev: v0.6.9
2020
hooks:
2121
- id: ruff
2222
- id: ruff-format

Diff for: CONTRIBUTING.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pre-commit run --all-files --show-diff-on-failure
7777

7878
We want your work to be readable by others; therefore, we encourage you to note the following:
7979

80-
- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will.
80+
- Please write in Python 3.13+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will.
8181
- Please focus hard on the naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments.
8282
- Single letter variable names are *old school* so please avoid them unless their life only spans a few lines.
8383
- Expand acronyms because `gcd()` is hard to understand but `greatest_common_divisor()` is not.
@@ -96,7 +96,7 @@ We want your work to be readable by others; therefore, we encourage you to note
9696

9797
```bash
9898
python3 -m pip install ruff # only required the first time
99-
ruff .
99+
ruff check
100100
```
101101

102102
- Original code submission require docstrings or comments to describe your work.

Diff for: DIRECTORY.md

+8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* [Rat In Maze](backtracking/rat_in_maze.py)
2323
* [Sudoku](backtracking/sudoku.py)
2424
* [Sum Of Subsets](backtracking/sum_of_subsets.py)
25+
* [Word Break](backtracking/word_break.py)
2526
* [Word Ladder](backtracking/word_ladder.py)
2627
* [Word Search](backtracking/word_search.py)
2728

@@ -99,6 +100,7 @@
99100
* [Elgamal Key Generator](ciphers/elgamal_key_generator.py)
100101
* [Enigma Machine2](ciphers/enigma_machine2.py)
101102
* [Fractionated Morse Cipher](ciphers/fractionated_morse_cipher.py)
103+
* [Gronsfeld Cipher](ciphers/gronsfeld_cipher.py)
102104
* [Hill Cipher](ciphers/hill_cipher.py)
103105
* [Mixed Keyword Cypher](ciphers/mixed_keyword_cypher.py)
104106
* [Mono Alphabetic Ciphers](ciphers/mono_alphabetic_ciphers.py)
@@ -211,6 +213,7 @@
211213
* [Lazy Segment Tree](data_structures/binary_tree/lazy_segment_tree.py)
212214
* [Lowest Common Ancestor](data_structures/binary_tree/lowest_common_ancestor.py)
213215
* [Maximum Fenwick Tree](data_structures/binary_tree/maximum_fenwick_tree.py)
216+
* [Maximum Sum Bst](data_structures/binary_tree/maximum_sum_bst.py)
214217
* [Merge Two Binary Trees](data_structures/binary_tree/merge_two_binary_trees.py)
215218
* [Mirror Binary Tree](data_structures/binary_tree/mirror_binary_tree.py)
216219
* [Non Recursive Segment Tree](data_structures/binary_tree/non_recursive_segment_tree.py)
@@ -284,6 +287,7 @@
284287
* [Dijkstras Two Stack Algorithm](data_structures/stacks/dijkstras_two_stack_algorithm.py)
285288
* [Infix To Postfix Conversion](data_structures/stacks/infix_to_postfix_conversion.py)
286289
* [Infix To Prefix Conversion](data_structures/stacks/infix_to_prefix_conversion.py)
290+
* [Lexicographical Numbers](data_structures/stacks/lexicographical_numbers.py)
287291
* [Next Greater Element](data_structures/stacks/next_greater_element.py)
288292
* [Postfix Evaluation](data_structures/stacks/postfix_evaluation.py)
289293
* [Prefix Evaluation](data_structures/stacks/prefix_evaluation.py)
@@ -347,6 +351,9 @@
347351
* [Power](divide_and_conquer/power.py)
348352
* [Strassen Matrix Multiplication](divide_and_conquer/strassen_matrix_multiplication.py)
349353

354+
## Docs
355+
* [Conf](docs/conf.py)
356+
350357
## Dynamic Programming
351358
* [Abbreviation](dynamic_programming/abbreviation.py)
352359
* [All Construct](dynamic_programming/all_construct.py)
@@ -1201,6 +1208,7 @@
12011208
* [Binary Tree Traversal](searches/binary_tree_traversal.py)
12021209
* [Double Linear Search](searches/double_linear_search.py)
12031210
* [Double Linear Search Recursion](searches/double_linear_search_recursion.py)
1211+
* [Exponential Search](searches/exponential_search.py)
12041212
* [Fibonacci Search](searches/fibonacci_search.py)
12051213
* [Hill Climbing](searches/hill_climbing.py)
12061214
* [Interpolation Search](searches/interpolation_search.py)

Diff for: LICENSE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
MIT License
1+
## MIT License
22

33
Copyright (c) 2016-2022 TheAlgorithms and contributors
44

Diff for: ciphers/autokey.py

+16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ def encrypt(plaintext: str, key: str) -> str:
2424
Traceback (most recent call last):
2525
...
2626
ValueError: plaintext is empty
27+
>>> encrypt("coffee is good as python", "")
28+
Traceback (most recent call last):
29+
...
30+
ValueError: key is empty
31+
>>> encrypt(527.26, "TheAlgorithms")
32+
Traceback (most recent call last):
33+
...
34+
TypeError: plaintext must be a string
2735
"""
2836
if not isinstance(plaintext, str):
2937
raise TypeError("plaintext must be a string")
@@ -80,6 +88,14 @@ def decrypt(ciphertext: str, key: str) -> str:
8088
Traceback (most recent call last):
8189
...
8290
TypeError: ciphertext must be a string
91+
>>> decrypt("", "TheAlgorithms")
92+
Traceback (most recent call last):
93+
...
94+
ValueError: ciphertext is empty
95+
>>> decrypt("vvjfpk wj ohvp su ddylsv", 2)
96+
Traceback (most recent call last):
97+
...
98+
TypeError: key must be a string
8399
"""
84100
if not isinstance(ciphertext, str):
85101
raise TypeError("ciphertext must be a string")

Diff for: ciphers/gronsfeld_cipher.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from string import ascii_uppercase
2+
3+
4+
def gronsfeld(text: str, key: str) -> str:
5+
"""
6+
Encrypt plaintext with the Gronsfeld cipher
7+
8+
>>> gronsfeld('hello', '412')
9+
'LFNPP'
10+
>>> gronsfeld('hello', '123')
11+
'IGOMQ'
12+
>>> gronsfeld('', '123')
13+
''
14+
>>> gronsfeld('yes, ¥€$ - _!@#%?', '0')
15+
'YES, ¥€$ - _!@#%?'
16+
>>> gronsfeld('yes, ¥€$ - _!@#%?', '01')
17+
'YFS, ¥€$ - _!@#%?'
18+
>>> gronsfeld('yes, ¥€$ - _!@#%?', '012')
19+
'YFU, ¥€$ - _!@#%?'
20+
>>> gronsfeld('yes, ¥€$ - _!@#%?', '')
21+
Traceback (most recent call last):
22+
...
23+
ZeroDivisionError: integer modulo by zero
24+
"""
25+
ascii_len = len(ascii_uppercase)
26+
key_len = len(key)
27+
encrypted_text = ""
28+
keys = [int(char) for char in key]
29+
upper_case_text = text.upper()
30+
31+
for i, char in enumerate(upper_case_text):
32+
if char in ascii_uppercase:
33+
new_position = (ascii_uppercase.index(char) + keys[i % key_len]) % ascii_len
34+
shifted_letter = ascii_uppercase[new_position]
35+
encrypted_text += shifted_letter
36+
else:
37+
encrypted_text += char
38+
39+
return encrypted_text
40+
41+
42+
if __name__ == "__main__":
43+
from doctest import testmod
44+
45+
testmod()

Diff for: data_structures/binary_tree/maximum_sum_bst.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from __future__ import annotations
2+
3+
import sys
4+
from dataclasses import dataclass
5+
6+
INT_MIN = -sys.maxsize + 1
7+
INT_MAX = sys.maxsize - 1
8+
9+
10+
@dataclass
11+
class TreeNode:
12+
val: int = 0
13+
left: TreeNode | None = None
14+
right: TreeNode | None = None
15+
16+
17+
def max_sum_bst(root: TreeNode | None) -> int:
18+
"""
19+
The solution traverses a binary tree to find the maximum sum of
20+
keys in any subtree that is a Binary Search Tree (BST). It uses
21+
recursion to validate BST properties and calculates sums, returning
22+
the highest sum found among all valid BST subtrees.
23+
24+
>>> t1 = TreeNode(4)
25+
>>> t1.left = TreeNode(3)
26+
>>> t1.left.left = TreeNode(1)
27+
>>> t1.left.right = TreeNode(2)
28+
>>> print(max_sum_bst(t1))
29+
2
30+
>>> t2 = TreeNode(-4)
31+
>>> t2.left = TreeNode(-2)
32+
>>> t2.right = TreeNode(-5)
33+
>>> print(max_sum_bst(t2))
34+
0
35+
>>> t3 = TreeNode(1)
36+
>>> t3.left = TreeNode(4)
37+
>>> t3.left.left = TreeNode(2)
38+
>>> t3.left.right = TreeNode(4)
39+
>>> t3.right = TreeNode(3)
40+
>>> t3.right.left = TreeNode(2)
41+
>>> t3.right.right = TreeNode(5)
42+
>>> t3.right.right.left = TreeNode(4)
43+
>>> t3.right.right.right = TreeNode(6)
44+
>>> print(max_sum_bst(t3))
45+
20
46+
"""
47+
ans: int = 0
48+
49+
def solver(node: TreeNode | None) -> tuple[bool, int, int, int]:
50+
"""
51+
Returns the maximum sum by making recursive calls
52+
>>> t1 = TreeNode(1)
53+
>>> print(solver(t1))
54+
1
55+
"""
56+
nonlocal ans
57+
58+
if not node:
59+
return True, INT_MAX, INT_MIN, 0 # Valid BST, min, max, sum
60+
61+
is_left_valid, min_left, max_left, sum_left = solver(node.left)
62+
is_right_valid, min_right, max_right, sum_right = solver(node.right)
63+
64+
if is_left_valid and is_right_valid and max_left < node.val < min_right:
65+
total_sum = sum_left + sum_right + node.val
66+
ans = max(ans, total_sum)
67+
return True, min(min_left, node.val), max(max_right, node.val), total_sum
68+
69+
return False, -1, -1, -1 # Not a valid BST
70+
71+
solver(root)
72+
return ans
73+
74+
75+
if __name__ == "__main__":
76+
import doctest
77+
78+
doctest.testmod()

Diff for: data_structures/stacks/lexicographical_numbers.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from collections.abc import Iterator
2+
3+
4+
def lexical_order(max_number: int) -> Iterator[int]:
5+
"""
6+
Generate numbers in lexical order from 1 to max_number.
7+
8+
>>> " ".join(map(str, lexical_order(13)))
9+
'1 10 11 12 13 2 3 4 5 6 7 8 9'
10+
>>> list(lexical_order(1))
11+
[1]
12+
>>> " ".join(map(str, lexical_order(20)))
13+
'1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9'
14+
>>> " ".join(map(str, lexical_order(25)))
15+
'1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 3 4 5 6 7 8 9'
16+
>>> list(lexical_order(12))
17+
[1, 10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9]
18+
"""
19+
20+
stack = [1]
21+
22+
while stack:
23+
num = stack.pop()
24+
if num > max_number:
25+
continue
26+
27+
yield num
28+
if (num % 10) != 9:
29+
stack.append(num + 1)
30+
31+
stack.append(num * 10)
32+
33+
34+
if __name__ == "__main__":
35+
from doctest import testmod
36+
37+
testmod()
38+
print(f"Numbers from 1 to 25 in lexical order: {list(lexical_order(26))}")
File renamed without changes.

Diff for: docs/conf.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from sphinx_pyproject import SphinxConfig
2+
3+
project = SphinxConfig("../pyproject.toml", globalns=globals()).name

0 commit comments

Comments
 (0)