mirror of
https://github.com/TheAlgorithms/Python.git
synced 2025-01-18 16:27:02 +00:00
Create number container system algorithm (#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 <cclauss@me.com> * Update other/number_container_system.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update other/number_container_system.py Co-authored-by: Christian Clauss <cclauss@me.com> * 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 <cclauss@me.com>
This commit is contained in:
parent
fa12b9a286
commit
7775de0ef7
|
@ -419,8 +419,9 @@
|
|||
* [Frequent Pattern Graph Miner](graphs/frequent_pattern_graph_miner.py)
|
||||
* [G Topological Sort](graphs/g_topological_sort.py)
|
||||
* [Gale Shapley Bigraph](graphs/gale_shapley_bigraph.py)
|
||||
* [Graph Adjacency List](graphs/graph_adjacency_list.py)
|
||||
* [Graph Adjacency Matrix](graphs/graph_adjacency_matrix.py)
|
||||
* [Graph List](graphs/graph_list.py)
|
||||
* [Graph Matrix](graphs/graph_matrix.py)
|
||||
* [Graphs Floyd Warshall](graphs/graphs_floyd_warshall.py)
|
||||
* [Greedy Best First](graphs/greedy_best_first.py)
|
||||
* [Greedy Min Vertex Cover](graphs/greedy_min_vertex_cover.py)
|
||||
|
@ -479,6 +480,7 @@
|
|||
* [Lib](linear_algebra/src/lib.py)
|
||||
* [Polynom For Points](linear_algebra/src/polynom_for_points.py)
|
||||
* [Power Iteration](linear_algebra/src/power_iteration.py)
|
||||
* [Rank Of Matrix](linear_algebra/src/rank_of_matrix.py)
|
||||
* [Rayleigh Quotient](linear_algebra/src/rayleigh_quotient.py)
|
||||
* [Schur Complement](linear_algebra/src/schur_complement.py)
|
||||
* [Test Linear Algebra](linear_algebra/src/test_linear_algebra.py)
|
||||
|
@ -651,6 +653,7 @@
|
|||
* [Sigmoid Linear Unit](maths/sigmoid_linear_unit.py)
|
||||
* [Signum](maths/signum.py)
|
||||
* [Simpson Rule](maths/simpson_rule.py)
|
||||
* [Simultaneous Linear Equation Solver](maths/simultaneous_linear_equation_solver.py)
|
||||
* [Sin](maths/sin.py)
|
||||
* [Sock Merchant](maths/sock_merchant.py)
|
||||
* [Softmax](maths/softmax.py)
|
||||
|
@ -726,6 +729,7 @@
|
|||
* [Maximum Subarray](other/maximum_subarray.py)
|
||||
* [Maximum Subsequence](other/maximum_subsequence.py)
|
||||
* [Nested Brackets](other/nested_brackets.py)
|
||||
* [Number Container System](other/number_container_system.py)
|
||||
* [Password](other/password.py)
|
||||
* [Quine](other/quine.py)
|
||||
* [Scoring Algorithm](other/scoring_algorithm.py)
|
||||
|
|
180
other/number_container_system.py
Normal file
180
other/number_container_system.py
Normal file
|
@ -0,0 +1,180 @@
|
|||
"""
|
||||
A number container system that uses binary search to delete and insert values into
|
||||
arrays with O(n logn) write times and O(1) read times.
|
||||
|
||||
This container system holds integers at indexes.
|
||||
|
||||
Further explained in this leetcode problem
|
||||
> https://leetcode.com/problems/minimum-cost-tree-from-leaf-values
|
||||
"""
|
||||
|
||||
|
||||
class NumberContainer:
|
||||
def __init__(self) -> None:
|
||||
# numbermap keys are the number and its values are lists of indexes sorted
|
||||
# in ascending order
|
||||
self.numbermap: dict[int, list[int]] = {}
|
||||
# indexmap keys are an index and it's values are the number at that index
|
||||
self.indexmap: dict[int, int] = {}
|
||||
|
||||
def binary_search_delete(self, array: list | str | range, item: int) -> list[int]:
|
||||
"""
|
||||
Removes the item from the sorted array and returns
|
||||
the new array.
|
||||
|
||||
>>> NumberContainer().binary_search_delete([1,2,3], 2)
|
||||
[1, 3]
|
||||
>>> NumberContainer().binary_search_delete([0, 0, 0], 0)
|
||||
[0, 0]
|
||||
>>> NumberContainer().binary_search_delete([-1, -1, -1], -1)
|
||||
[-1, -1]
|
||||
>>> NumberContainer().binary_search_delete([-1, 0], 0)
|
||||
[-1]
|
||||
>>> NumberContainer().binary_search_delete([-1, 0], -1)
|
||||
[0]
|
||||
>>> NumberContainer().binary_search_delete(range(7), 3)
|
||||
[0, 1, 2, 4, 5, 6]
|
||||
>>> NumberContainer().binary_search_delete([1.1, 2.2, 3.3], 2.2)
|
||||
[1.1, 3.3]
|
||||
>>> NumberContainer().binary_search_delete("abcde", "c")
|
||||
['a', 'b', 'd', 'e']
|
||||
>>> NumberContainer().binary_search_delete([0, -1, 2, 4], 0)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Either the item is not in the array or the array was unsorted
|
||||
>>> NumberContainer().binary_search_delete([2, 0, 4, -1, 11], -1)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Either the item is not in the array or the array was unsorted
|
||||
>>> NumberContainer().binary_search_delete(125, 1)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: binary_search_delete() only accepts either a list, range or str
|
||||
"""
|
||||
if isinstance(array, (range, str)):
|
||||
array = list(array)
|
||||
elif not isinstance(array, list):
|
||||
raise TypeError(
|
||||
"binary_search_delete() only accepts either a list, range or str"
|
||||
)
|
||||
|
||||
low = 0
|
||||
high = len(array) - 1
|
||||
|
||||
while low <= high:
|
||||
mid = (low + high) // 2
|
||||
if array[mid] == item:
|
||||
array.pop(mid)
|
||||
return array
|
||||
elif array[mid] < item:
|
||||
low = mid + 1
|
||||
else:
|
||||
high = mid - 1
|
||||
raise ValueError(
|
||||
"Either the item is not in the array or the array was unsorted"
|
||||
)
|
||||
|
||||
def binary_search_insert(self, array: list | str | range, index: int) -> list[int]:
|
||||
"""
|
||||
Inserts the index into the sorted array
|
||||
at the correct position.
|
||||
|
||||
>>> NumberContainer().binary_search_insert([1,2,3], 2)
|
||||
[1, 2, 2, 3]
|
||||
>>> NumberContainer().binary_search_insert([0,1,3], 2)
|
||||
[0, 1, 2, 3]
|
||||
>>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 103], 51)
|
||||
[-5, -3, 0, 0, 11, 51, 103]
|
||||
>>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 100, 103], 101)
|
||||
[-5, -3, 0, 0, 11, 100, 101, 103]
|
||||
>>> NumberContainer().binary_search_insert(range(10), 4)
|
||||
[0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9]
|
||||
>>> NumberContainer().binary_search_insert("abd", "c")
|
||||
['a', 'b', 'c', 'd']
|
||||
>>> NumberContainer().binary_search_insert(131, 23)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: binary_search_insert() only accepts either a list, range or str
|
||||
"""
|
||||
if isinstance(array, (range, str)):
|
||||
array = list(array)
|
||||
elif not isinstance(array, list):
|
||||
raise TypeError(
|
||||
"binary_search_insert() only accepts either a list, range or str"
|
||||
)
|
||||
|
||||
low = 0
|
||||
high = len(array) - 1
|
||||
|
||||
while low <= high:
|
||||
mid = (low + high) // 2
|
||||
if array[mid] == index:
|
||||
# If the item already exists in the array,
|
||||
# insert it after the existing item
|
||||
array.insert(mid + 1, index)
|
||||
return array
|
||||
elif array[mid] < index:
|
||||
low = mid + 1
|
||||
else:
|
||||
high = mid - 1
|
||||
|
||||
# If the item doesn't exist in the array, insert it at the appropriate position
|
||||
array.insert(low, index)
|
||||
return array
|
||||
|
||||
def change(self, index: int, number: int) -> None:
|
||||
"""
|
||||
Changes (sets) the index as number
|
||||
|
||||
>>> cont = NumberContainer()
|
||||
>>> cont.change(0, 10)
|
||||
>>> cont.change(0, 20)
|
||||
>>> cont.change(-13, 20)
|
||||
>>> cont.change(-100030, 20032903290)
|
||||
"""
|
||||
# Remove previous index
|
||||
if index in self.indexmap:
|
||||
n = self.indexmap[index]
|
||||
if len(self.numbermap[n]) == 1:
|
||||
del self.numbermap[n]
|
||||
else:
|
||||
self.numbermap[n] = self.binary_search_delete(self.numbermap[n], index)
|
||||
|
||||
# Set new index
|
||||
self.indexmap[index] = number
|
||||
|
||||
# Number not seen before or empty so insert number value
|
||||
if number not in self.numbermap:
|
||||
self.numbermap[number] = [index]
|
||||
|
||||
# Here we need to perform a binary search insertion in order to insert
|
||||
# The item in the correct place
|
||||
else:
|
||||
self.numbermap[number] = self.binary_search_insert(
|
||||
self.numbermap[number], index
|
||||
)
|
||||
|
||||
def find(self, number: int) -> int:
|
||||
"""
|
||||
Returns the smallest index where the number is.
|
||||
|
||||
>>> cont = NumberContainer()
|
||||
>>> cont.find(10)
|
||||
-1
|
||||
>>> cont.change(0, 10)
|
||||
>>> cont.find(10)
|
||||
0
|
||||
>>> cont.change(0, 20)
|
||||
>>> cont.find(10)
|
||||
-1
|
||||
>>> cont.find(20)
|
||||
0
|
||||
"""
|
||||
# Simply return the 0th index (smallest) of the indexes found (or -1)
|
||||
return self.numbermap.get(number, [-1])[0]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
|
||||
doctest.testmod()
|
Loading…
Reference in New Issue
Block a user