Skip to content

Commit 360f20a

Browse files
CaedenPHgithub-actionscclauss
authored andcommitted
Create number container system algorithm (TheAlgorithms#8808)
* feat: Create number container system algorithm * updating DIRECTORY.md * chore: Fix failing tests * Update other/number_container_system.py Co-authored-by: Christian Clauss <[email protected]> * Update other/number_container_system.py Co-authored-by: Christian Clauss <[email protected]> * Update other/number_container_system.py Co-authored-by: Christian Clauss <[email protected]> * chore: Add more tests * chore: Create binary_search_insert failing test * type: Update typehints to accept str, list and range --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss <[email protected]>
1 parent 79b1217 commit 360f20a

File tree

2 files changed

+185
-1
lines changed

2 files changed

+185
-1
lines changed

Diff for: DIRECTORY.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,9 @@
419419
* [Frequent Pattern Graph Miner](graphs/frequent_pattern_graph_miner.py)
420420
* [G Topological Sort](graphs/g_topological_sort.py)
421421
* [Gale Shapley Bigraph](graphs/gale_shapley_bigraph.py)
422+
* [Graph Adjacency List](graphs/graph_adjacency_list.py)
423+
* [Graph Adjacency Matrix](graphs/graph_adjacency_matrix.py)
422424
* [Graph List](graphs/graph_list.py)
423-
* [Graph Matrix](graphs/graph_matrix.py)
424425
* [Graphs Floyd Warshall](graphs/graphs_floyd_warshall.py)
425426
* [Greedy Best First](graphs/greedy_best_first.py)
426427
* [Greedy Min Vertex Cover](graphs/greedy_min_vertex_cover.py)
@@ -479,6 +480,7 @@
479480
* [Lib](linear_algebra/src/lib.py)
480481
* [Polynom For Points](linear_algebra/src/polynom_for_points.py)
481482
* [Power Iteration](linear_algebra/src/power_iteration.py)
483+
* [Rank Of Matrix](linear_algebra/src/rank_of_matrix.py)
482484
* [Rayleigh Quotient](linear_algebra/src/rayleigh_quotient.py)
483485
* [Schur Complement](linear_algebra/src/schur_complement.py)
484486
* [Test Linear Algebra](linear_algebra/src/test_linear_algebra.py)
@@ -651,6 +653,7 @@
651653
* [Sigmoid Linear Unit](maths/sigmoid_linear_unit.py)
652654
* [Signum](maths/signum.py)
653655
* [Simpson Rule](maths/simpson_rule.py)
656+
* [Simultaneous Linear Equation Solver](maths/simultaneous_linear_equation_solver.py)
654657
* [Sin](maths/sin.py)
655658
* [Sock Merchant](maths/sock_merchant.py)
656659
* [Softmax](maths/softmax.py)
@@ -726,6 +729,7 @@
726729
* [Maximum Subarray](other/maximum_subarray.py)
727730
* [Maximum Subsequence](other/maximum_subsequence.py)
728731
* [Nested Brackets](other/nested_brackets.py)
732+
* [Number Container System](other/number_container_system.py)
729733
* [Password](other/password.py)
730734
* [Quine](other/quine.py)
731735
* [Scoring Algorithm](other/scoring_algorithm.py)

Diff for: other/number_container_system.py

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
"""
2+
A number container system that uses binary search to delete and insert values into
3+
arrays with O(n logn) write times and O(1) read times.
4+
5+
This container system holds integers at indexes.
6+
7+
Further explained in this leetcode problem
8+
> https://leetcode.com/problems/minimum-cost-tree-from-leaf-values
9+
"""
10+
11+
12+
class NumberContainer:
13+
def __init__(self) -> None:
14+
# numbermap keys are the number and its values are lists of indexes sorted
15+
# in ascending order
16+
self.numbermap: dict[int, list[int]] = {}
17+
# indexmap keys are an index and it's values are the number at that index
18+
self.indexmap: dict[int, int] = {}
19+
20+
def binary_search_delete(self, array: list | str | range, item: int) -> list[int]:
21+
"""
22+
Removes the item from the sorted array and returns
23+
the new array.
24+
25+
>>> NumberContainer().binary_search_delete([1,2,3], 2)
26+
[1, 3]
27+
>>> NumberContainer().binary_search_delete([0, 0, 0], 0)
28+
[0, 0]
29+
>>> NumberContainer().binary_search_delete([-1, -1, -1], -1)
30+
[-1, -1]
31+
>>> NumberContainer().binary_search_delete([-1, 0], 0)
32+
[-1]
33+
>>> NumberContainer().binary_search_delete([-1, 0], -1)
34+
[0]
35+
>>> NumberContainer().binary_search_delete(range(7), 3)
36+
[0, 1, 2, 4, 5, 6]
37+
>>> NumberContainer().binary_search_delete([1.1, 2.2, 3.3], 2.2)
38+
[1.1, 3.3]
39+
>>> NumberContainer().binary_search_delete("abcde", "c")
40+
['a', 'b', 'd', 'e']
41+
>>> NumberContainer().binary_search_delete([0, -1, 2, 4], 0)
42+
Traceback (most recent call last):
43+
...
44+
ValueError: Either the item is not in the array or the array was unsorted
45+
>>> NumberContainer().binary_search_delete([2, 0, 4, -1, 11], -1)
46+
Traceback (most recent call last):
47+
...
48+
ValueError: Either the item is not in the array or the array was unsorted
49+
>>> NumberContainer().binary_search_delete(125, 1)
50+
Traceback (most recent call last):
51+
...
52+
TypeError: binary_search_delete() only accepts either a list, range or str
53+
"""
54+
if isinstance(array, (range, str)):
55+
array = list(array)
56+
elif not isinstance(array, list):
57+
raise TypeError(
58+
"binary_search_delete() only accepts either a list, range or str"
59+
)
60+
61+
low = 0
62+
high = len(array) - 1
63+
64+
while low <= high:
65+
mid = (low + high) // 2
66+
if array[mid] == item:
67+
array.pop(mid)
68+
return array
69+
elif array[mid] < item:
70+
low = mid + 1
71+
else:
72+
high = mid - 1
73+
raise ValueError(
74+
"Either the item is not in the array or the array was unsorted"
75+
)
76+
77+
def binary_search_insert(self, array: list | str | range, index: int) -> list[int]:
78+
"""
79+
Inserts the index into the sorted array
80+
at the correct position.
81+
82+
>>> NumberContainer().binary_search_insert([1,2,3], 2)
83+
[1, 2, 2, 3]
84+
>>> NumberContainer().binary_search_insert([0,1,3], 2)
85+
[0, 1, 2, 3]
86+
>>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 103], 51)
87+
[-5, -3, 0, 0, 11, 51, 103]
88+
>>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 100, 103], 101)
89+
[-5, -3, 0, 0, 11, 100, 101, 103]
90+
>>> NumberContainer().binary_search_insert(range(10), 4)
91+
[0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9]
92+
>>> NumberContainer().binary_search_insert("abd", "c")
93+
['a', 'b', 'c', 'd']
94+
>>> NumberContainer().binary_search_insert(131, 23)
95+
Traceback (most recent call last):
96+
...
97+
TypeError: binary_search_insert() only accepts either a list, range or str
98+
"""
99+
if isinstance(array, (range, str)):
100+
array = list(array)
101+
elif not isinstance(array, list):
102+
raise TypeError(
103+
"binary_search_insert() only accepts either a list, range or str"
104+
)
105+
106+
low = 0
107+
high = len(array) - 1
108+
109+
while low <= high:
110+
mid = (low + high) // 2
111+
if array[mid] == index:
112+
# If the item already exists in the array,
113+
# insert it after the existing item
114+
array.insert(mid + 1, index)
115+
return array
116+
elif array[mid] < index:
117+
low = mid + 1
118+
else:
119+
high = mid - 1
120+
121+
# If the item doesn't exist in the array, insert it at the appropriate position
122+
array.insert(low, index)
123+
return array
124+
125+
def change(self, index: int, number: int) -> None:
126+
"""
127+
Changes (sets) the index as number
128+
129+
>>> cont = NumberContainer()
130+
>>> cont.change(0, 10)
131+
>>> cont.change(0, 20)
132+
>>> cont.change(-13, 20)
133+
>>> cont.change(-100030, 20032903290)
134+
"""
135+
# Remove previous index
136+
if index in self.indexmap:
137+
n = self.indexmap[index]
138+
if len(self.numbermap[n]) == 1:
139+
del self.numbermap[n]
140+
else:
141+
self.numbermap[n] = self.binary_search_delete(self.numbermap[n], index)
142+
143+
# Set new index
144+
self.indexmap[index] = number
145+
146+
# Number not seen before or empty so insert number value
147+
if number not in self.numbermap:
148+
self.numbermap[number] = [index]
149+
150+
# Here we need to perform a binary search insertion in order to insert
151+
# The item in the correct place
152+
else:
153+
self.numbermap[number] = self.binary_search_insert(
154+
self.numbermap[number], index
155+
)
156+
157+
def find(self, number: int) -> int:
158+
"""
159+
Returns the smallest index where the number is.
160+
161+
>>> cont = NumberContainer()
162+
>>> cont.find(10)
163+
-1
164+
>>> cont.change(0, 10)
165+
>>> cont.find(10)
166+
0
167+
>>> cont.change(0, 20)
168+
>>> cont.find(10)
169+
-1
170+
>>> cont.find(20)
171+
0
172+
"""
173+
# Simply return the 0th index (smallest) of the indexes found (or -1)
174+
return self.numbermap.get(number, [-1])[0]
175+
176+
177+
if __name__ == "__main__":
178+
import doctest
179+
180+
doctest.testmod()

0 commit comments

Comments
 (0)