Skip to content

Commit 1f99120

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents d25a926 + b3ae392 commit 1f99120

File tree

6 files changed

+205
-2
lines changed

6 files changed

+205
-2
lines changed

DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@
267267
* [Karger](https://github.com/TheAlgorithms/Python/blob/master/graphs/karger.py)
268268
* [Minimum Spanning Tree Boruvka](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_boruvka.py)
269269
* [Minimum Spanning Tree Kruskal](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_kruskal.py)
270+
* [Minimum Spanning Tree Kruskal2](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_kruskal2.py)
270271
* [Minimum Spanning Tree Prims](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_prims.py)
271272
* [Multi Heuristic Astar](https://github.com/TheAlgorithms/Python/blob/master/graphs/multi_heuristic_astar.py)
272273
* [Page Rank](https://github.com/TheAlgorithms/Python/blob/master/graphs/page_rank.py)

data_structures/binary_tree/basic_binary_tree.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class Node:
55
"""
66
A Node has data variable and pointers to Nodes to its left and right.
77
"""
8+
89
def __init__(self, data: int) -> None:
910
self.data = data
1011
self.left: Optional[Node] = None
+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from __future__ import annotations
2+
3+
4+
class DisjointSetTreeNode:
5+
# Disjoint Set Node to store the parent and rank
6+
def __init__(self, key: int) -> None:
7+
self.key = key
8+
self.parent = self
9+
self.rank = 0
10+
11+
12+
class DisjointSetTree:
13+
# Disjoint Set DataStructure
14+
def __init__(self):
15+
# map from node name to the node object
16+
self.map = {}
17+
18+
def make_set(self, x: int) -> None:
19+
# create a new set with x as its member
20+
self.map[x] = DisjointSetTreeNode(x)
21+
22+
def find_set(self, x: int) -> DisjointSetTreeNode:
23+
# find the set x belongs to (with path-compression)
24+
elem_ref = self.map[x]
25+
if elem_ref != elem_ref.parent:
26+
elem_ref.parent = self.find_set(elem_ref.parent.key)
27+
return elem_ref.parent
28+
29+
def link(self, x: int, y: int) -> None:
30+
# helper function for union operation
31+
if x.rank > y.rank:
32+
y.parent = x
33+
else:
34+
x.parent = y
35+
if x.rank == y.rank:
36+
y.rank += 1
37+
38+
def union(self, x: int, y: int) -> None:
39+
# merge 2 disjoint sets
40+
self.link(self.find_set(x), self.find_set(y))
41+
42+
43+
class GraphUndirectedWeighted:
44+
def __init__(self):
45+
# connections: map from the node to the neighbouring nodes (with weights)
46+
self.connections = {}
47+
48+
def add_node(self, node: int) -> None:
49+
# add a node ONLY if its not present in the graph
50+
if node not in self.connections:
51+
self.connections[node] = {}
52+
53+
def add_edge(self, node1: int, node2: int, weight: int) -> None:
54+
# add an edge with the given weight
55+
self.add_node(node1)
56+
self.add_node(node2)
57+
self.connections[node1][node2] = weight
58+
self.connections[node2][node1] = weight
59+
60+
def kruskal(self) -> GraphUndirectedWeighted:
61+
# Kruskal's Algorithm to generate a Minimum Spanning Tree (MST) of a graph
62+
"""
63+
Details: https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
64+
65+
Example:
66+
67+
>>> graph = GraphUndirectedWeighted()
68+
>>> graph.add_edge(1, 2, 1)
69+
>>> graph.add_edge(2, 3, 2)
70+
>>> graph.add_edge(3, 4, 1)
71+
>>> graph.add_edge(3, 5, 100) # Removed in MST
72+
>>> graph.add_edge(4, 5, 5)
73+
>>> assert 5 in graph.connections[3]
74+
>>> mst = graph.kruskal()
75+
>>> assert 5 not in mst.connections[3]
76+
"""
77+
78+
# getting the edges in ascending order of weights
79+
edges = []
80+
seen = set()
81+
for start in self.connections:
82+
for end in self.connections[start]:
83+
if (start, end) not in seen:
84+
seen.add((end, start))
85+
edges.append((start, end, self.connections[start][end]))
86+
edges.sort(key=lambda x: x[2])
87+
# creating the disjoint set
88+
disjoint_set = DisjointSetTree()
89+
[disjoint_set.make_set(node) for node in self.connections]
90+
# MST generation
91+
num_edges = 0
92+
index = 0
93+
graph = GraphUndirectedWeighted()
94+
while num_edges < len(self.connections) - 1:
95+
u, v, w = edges[index]
96+
index += 1
97+
parentu = disjoint_set.find_set(u)
98+
parentv = disjoint_set.find_set(v)
99+
if parentu != parentv:
100+
num_edges += 1
101+
graph.add_edge(u, v, w)
102+
disjoint_set.union(u, v)
103+
return graph
104+
105+
106+
if __name__ == "__main__":
107+
import doctest
108+
109+
doctest.testmod()

maths/number_of_digits.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,20 @@ def num_digits(n: int) -> int:
1010
5
1111
>>> num_digits(123)
1212
3
13+
>>> num_digits(0)
14+
1
15+
>>> num_digits(-1)
16+
1
17+
>>> num_digits(-123456)
18+
6
1319
"""
1420
digits = 0
15-
while n > 0:
21+
n = abs(n)
22+
while True:
1623
n = n // 10
1724
digits += 1
25+
if n == 0:
26+
break
1827
return digits
1928

2029

@@ -27,8 +36,14 @@ def num_digits_fast(n: int) -> int:
2736
5
2837
>>> num_digits_fast(123)
2938
3
39+
>>> num_digits_fast(0)
40+
1
41+
>>> num_digits_fast(-1)
42+
1
43+
>>> num_digits_fast(-123456)
44+
6
3045
"""
31-
return math.floor(math.log(abs(n), 10) + 1)
46+
return 1 if n == 0 else math.floor(math.log(abs(n), 10) + 1)
3247

3348

3449
def num_digits_faster(n: int) -> int:
@@ -40,6 +55,12 @@ def num_digits_faster(n: int) -> int:
4055
5
4156
>>> num_digits_faster(123)
4257
3
58+
>>> num_digits_faster(0)
59+
1
60+
>>> num_digits_faster(-1)
61+
1
62+
>>> num_digits_faster(-123456)
63+
6
4364
"""
4465
return len(str(abs(n)))
4566

@@ -133,3 +154,6 @@ def benchmark() -> None:
133154
medium_num = 1125899906842624
134155
large_num = 1267650600228229401496703205376
135156
benchmark()
157+
import doctest
158+
159+
doctest.testmod()

project_euler/problem_34/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#

project_euler/problem_34/sol1.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
145 is a curious number, as 1! + 4! + 5! = 1 + 24 + 120 = 145.
3+
Find the sum of all numbers which are equal to the sum of the factorial of their digits.
4+
Note: As 1! = 1 and 2! = 2 are not sums they are not included.
5+
"""
6+
7+
8+
def factorial(n: int) -> int:
9+
"""Return the factorial of n.
10+
>>> factorial(5)
11+
120
12+
>>> factorial(1)
13+
1
14+
>>> factorial(0)
15+
1
16+
>>> factorial(-1)
17+
Traceback (most recent call last):
18+
...
19+
ValueError: n must be >= 0
20+
>>> factorial(1.1)
21+
Traceback (most recent call last):
22+
...
23+
ValueError: n must be exact integer
24+
"""
25+
26+
if not n >= 0:
27+
raise ValueError("n must be >= 0")
28+
if int(n) != n:
29+
raise ValueError("n must be exact integer")
30+
if n + 1 == n: # catch a value like 1e300
31+
raise OverflowError("n too large")
32+
result = 1
33+
factor = 2
34+
while factor <= n:
35+
result *= factor
36+
factor += 1
37+
return result
38+
39+
40+
def sum_of_digit_factorial(n: int) -> int:
41+
"""
42+
Returns the sum of the digits in n
43+
>>> sum_of_digit_factorial(15)
44+
121
45+
>>> sum_of_digit_factorial(0)
46+
1
47+
"""
48+
return sum(factorial(int(digit)) for digit in str(n))
49+
50+
51+
def compute() -> int:
52+
"""
53+
Returns the sum of all numbers whose
54+
sum of the factorials of all digits
55+
add up to the number itself.
56+
>>> compute()
57+
40730
58+
"""
59+
return sum(
60+
num
61+
for num in range(3, 7 * factorial(9) + 1)
62+
if sum_of_digit_factorial(num) == num
63+
)
64+
65+
66+
if __name__ == "__main__":
67+
print(compute())

0 commit comments

Comments
 (0)