Skip to content

Commit 9b656ef

Browse files
authored
Merge branch 'master' into add-jacobsthal-numbers
2 parents ad5f42b + 4e817a8 commit 9b656ef

7 files changed

Lines changed: 903 additions & 54 deletions

File tree

‎DIRECTORY.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@
264264
* Arrays
265265
* [Equilibrium Index In Array](data_structures/arrays/equilibrium_index_in_array.py)
266266
* [Find Triplets With 0 Sum](data_structures/arrays/find_triplets_with_0_sum.py)
267+
* [Gas Station](data_structures/arrays/gas_station.py)
267268
* [Index 2D Array In 1D](data_structures/arrays/index_2d_array_in_1d.py)
268269
* [Kth Largest Element](data_structures/arrays/kth_largest_element.py)
269270
* [Median Two Array](data_structures/arrays/median_two_array.py)
@@ -639,7 +640,9 @@
639640
* [Gale Shapley Bigraph](graphs/gale_shapley_bigraph.py)
640641
* [Graph Adjacency List](graphs/graph_adjacency_list.py)
641642
* [Graph Adjacency Matrix](graphs/graph_adjacency_matrix.py)
643+
* [Graph Centrality](graphs/graph_centrality.py)
642644
* [Graph List](graphs/graph_list.py)
645+
* [Graph Peripherality](graphs/graph_peripherality.py)
643646
* [Graphs Floyd Warshall](graphs/graphs_floyd_warshall.py)
644647
* [Greedy Best First](graphs/greedy_best_first.py)
645648
* [Greedy Min Vertex Cover](graphs/greedy_min_vertex_cover.py)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing import NamedTuple
2+
3+
4+
class GasStation(NamedTuple):
5+
gas: int # Amount of gas available at this gas station
6+
cost: int # Cost of gas required to drive to the next station
7+
8+
9+
def can_complete_circuit(gas_stations: list[GasStation]) -> int:
10+
"""
11+
Finds the starting station index to complete the circuit,
12+
or returns -1 if not possible.
13+
Args:
14+
gas_stations (List[GasStation]): List of gas stations with gas and cost.
15+
Returns:
16+
The index of the starting station, or -1 if no solution exists.
17+
Examples:
18+
>>> GS = GasStation
19+
>>> test_stations = (
20+
... [GS(1, 3), GS(2, 4), GS(3, 5), GS(4, 1), GS(5, 2)],
21+
... [GS(2, 3), GS(3, 4), GS(4, 3)],
22+
... [GS(5, 4), GS(1, 4), GS(2, 1), GS(3, 5), GS(4, 1)]
23+
... )
24+
>>> can_complete_circuit(test_stations[0])
25+
3
26+
>>> can_complete_circuit(test_stations[1])
27+
-1
28+
>>> can_complete_circuit(test_stations[2])
29+
4
30+
"""
31+
total_gas = sum(station.gas - station.cost for station in gas_stations)
32+
current_gas: int = 0
33+
start_station = 0
34+
for i, gas_station in enumerate(gas_stations):
35+
needed_gas = gas_station.gas - gas_station.cost
36+
total_gas += needed_gas
37+
current_gas += needed_gas
38+
if current_gas < 0:
39+
start_station = i + 1
40+
current_gas = 0
41+
if total_gas < 0:
42+
return -1
43+
return start_station
44+
45+
46+
# Example usage with doctests
47+
if __name__ == "__main__":
48+
import doctest
49+
50+
doctest.testmod()

‎docs/hacktober_2026_prep.md‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ Notation: `<index>. [ ] #<pr_id> <labels>`
801801
787. [ ] #11924 no labels
802802
788. [x] #11919 closed
803803
789. [ ] #11918 awaiting reviews
804-
790. [ ] #11910 no labels
804+
790. [x] #11910 closed
805805
791. [ ] #11898 no labels
806806
792. [ ] #11897 awaiting reviews
807807
793. [x] #11886 closed
@@ -811,15 +811,15 @@ Notation: `<index>. [ ] #<pr_id> <labels>`
811811
797. [ ] #11875 no labels
812812
798. [x] #11872 closed
813813
799. [x] #11870 merged
814-
800. [ ] #11851 no labels
814+
800. [x] #11851 merged
815815
801. [x] #11848 merged
816816
802. [x] #11842 closed
817817
803. [x] #11834 merged
818818
804. [x] #11827 closed
819819
805. [ ] #11826 awaiting reviews
820820
806. [ ] #11825 enhancement, awaiting reviews
821821
807. [x] #11823 merged
822-
808. [ ] #11822 awaiting reviews
822+
808. [x] #11822 merged
823823
809. [x] #11820 closed
824824
810. [x] #11809 merged
825825
811. [x] #11805 closed
@@ -1097,7 +1097,7 @@ data_structures:
10971097
13. [x] #11566 closed
10981098
14. [x] #11607 closed
10991099
15. [x] #11695 merged
1100-
16. [ ] #11822
1100+
16. [x] #11822 merged
11011101
17. [x] #11870 merged
11021102
18. [x] #11886 closed
11031103
19. [ ] #11918
@@ -1186,7 +1186,7 @@ electronics:
11861186
financial:
11871187
1. [x] #11368 closed
11881188
2. [ ] #11826
1189-
3. [ ] #11851
1189+
3. [x] #11851 merged
11901190
4. [ ] #12357
11911191
5. [ ] #12358
11921192
6. [ ] #13888
@@ -1612,7 +1612,7 @@ sorts:
16121612
4. [x] #11725 closed
16131613
5. [x] #11752 closed
16141614
6. [x] #11809 merged
1615-
7. [ ] #11910
1615+
7. [x] #11910 closed
16161616
8. [x] #11993 merged
16171617
9. [x] #12012 closed
16181618
10. [ ] #12100
@@ -2064,15 +2064,15 @@ I can take a second pass through the remaining `awaiting triage` items (relabel
20642064
_Generated automatically by `scripts/hacktoberfest_prep_update.py` on 2026-09-23 (UTC)._
20652065

20662066
- **Open issues:** 9
2067-
- **Open pull requests:** 156
2068-
- **Open PRs labelled `awaiting reviews`:** 112
2067+
- **Open pull requests:** 157
2068+
- **Open PRs labelled `awaiting reviews`:** 109
20692069
- **Days until Hacktoberfest (2026-10-01):** 8
20702070
- **Issues to close per day to clear the backlog:** 2 per day (over 8 days)
20712071
- **Pull requests to merge or close per day to clear the backlog:** 20 per day (over 8 days)
20722072

20732073
**Top three directories to work on** (most open pull requests labelled `awaiting reviews`):
20742074

2075-
1. `data_structures/` — 20 awaiting-reviews PRs
2076-
2. `maths/` — 13 awaiting-reviews PRs
2075+
1. `data_structures/` — 19 awaiting-reviews PRs
2076+
2. `maths/` — 14 awaiting-reviews PRs
20772077
3. `dynamic_programming/` — 11 awaiting-reviews PRs
20782078

‎dynamic_programming/optimal_binary_search_tree.py‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def find_optimal_binary_search_tree(nodes) -> None:
102102
# This 2D array stores the overall tree cost (which's as minimized as possible);
103103
# for a single key, cost is equal to frequency of the key.
104104
dp = [[freqs[i] if i == j else 0 for j in range(n)] for i in range(n)]
105-
# sum[i][j] stores the sum of key frequencies between i and j inclusive in nodes
105+
# total[i][j] stores the sum of key frequencies between i and j inclusive in nodes
106106
# array
107107
total = [[freqs[i] if i == j else 0 for j in range(n)] for i in range(n)]
108108
# stores tree roots that will be used later for constructing binary search tree
@@ -115,11 +115,17 @@ def find_optimal_binary_search_tree(nodes) -> None:
115115
dp[i][j] = sys.maxsize # set the value to "infinity"
116116
total[i][j] = total[i][j - 1] + freqs[j]
117117

118-
# Apply Knuth's optimization
119-
# Loop without optimization: for r in range(i, j + 1):
120-
for r in range(root[i][j - 1], root[i + 1][j] + 1): # r is a temporal root
121-
left = dp[i][r - 1] if r != i else 0 # optimal cost for left subtree
122-
right = dp[r + 1][j] if r != j else 0 # optimal cost for right subtree
118+
# Apply Knuth's optimization with safe boundary handling
119+
r_start = root[i][j - 1] if j > i else i
120+
r_end = root[i + 1][j] if i < j else j
121+
122+
# Ensure r_start and r_end are within valid bounds
123+
r_start = max(i, min(r_start, j))
124+
r_end = min(j, max(r_end, i))
125+
126+
for r in range(r_start, r_end + 1):
127+
left = dp[i][r - 1] if r > i else 0 # optimal cost for left subtree
128+
right = dp[r + 1][j] if r < j else 0 # optimal cost for right subtree
123129
cost = left + total[i][j] + right
124130

125131
if dp[i][j] > cost:

‎graphs/dijkstra.py‎

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@
33
44
DIJKSTRA(graph G, start vertex s, destination vertex d):
55
6-
//all nodes initially unexplored
7-
8-
1 - let H = min heap data structure, initialized with 0 and s [here 0 indicates
9-
the distance from start vertex s]
10-
2 - while H is non-empty:
11-
3 - remove the first node and cost of H, call it U and cost
12-
4 - if U has been previously explored:
13-
5 - go to the while loop, line 2 //Once a node is explored there is no need
14-
to make it again
15-
6 - mark U as explored
16-
7 - if U is d:
17-
8 - return cost // total cost from start to destination vertex
18-
9 - for each edge(U, V): c=cost of edge(U,V) // for V in graph[U]
19-
10 - if V explored:
20-
11 - go to next V in line 9
21-
12 - total_cost = cost + c
22-
13 - add (total_cost,V) to H
6+
// all nodes initially unexplored
7+
8+
1 - let H = min heap data structure, initialized with (0, s)
9+
// 0 is the distance from start vertex s
10+
2 - let costs = dictionary to store minimum costs to reach each node,
11+
initialized with {s: 0}
12+
3 - while H is non-empty:
13+
4 - remove the first node and cost from H, call them U and cost
14+
5 - if U has been previously explored:
15+
6 - continue // skip further processing and go back to while loop, line 3
16+
7 - mark U as explored
17+
8 - if U is d:
18+
9 - return cost // total cost from start to destination vertex
19+
10 - for each neighbor V and edge cost c of U in G:
20+
11 - if V has been previously explored:
21+
12 - continue to next neighbor V in line 10
22+
13 - total_cost = cost + c
23+
14 - if total_cost is less than costs.get(V, ∞):
24+
15 - update costs[V] to total_cost
25+
16 - add (total_cost, V) to H
26+
27+
// At the end, if destination d is not reachable, return -1
2328
2429
You can think at cost as a distance where Dijkstra finds the shortest distance
2530
between vertices s and v in a graph G. The use of a min heap as H guarantees
@@ -34,7 +39,7 @@
3439
import heapq
3540

3641

37-
def dijkstra(graph, start, end):
42+
def dijkstra(graph: dict[str, list[tuple[str, int]]], start: str, end: str) -> int:
3843
"""Return the cost of the shortest path between vertices start and end.
3944
4045
>>> dijkstra(G, "E", "C")
@@ -44,31 +49,37 @@ def dijkstra(graph, start, end):
4449
>>> dijkstra(G3, "E", "F")
4550
3
4651
"""
52+
heap: list[tuple[int, str]] = [(0, start)] # (cost, node)
53+
visited: set[str] = set()
54+
costs: dict[str, int] = {start: 0} # Store minimum costs to reach each node
4755

48-
heap = [(0, start)] # cost from start node,end node
49-
visited = set()
5056
while heap:
51-
(cost, u) = heapq.heappop(heap)
57+
cost, u = heapq.heappop(heap)
5258
if u in visited:
5359
continue
5460
visited.add(u)
5561
if u == end:
5662
return cost
63+
5764
for v, c in graph[u]:
5865
if v in visited:
5966
continue
60-
next_item = cost + c
61-
heapq.heappush(heap, (next_item, v))
67+
next_cost = cost + c
68+
# Only push to heap if a cheaper path is found
69+
if next_cost < costs.get(v, float("inf")):
70+
costs[v] = next_cost
71+
heapq.heappush(heap, (next_cost, v))
72+
6273
return -1
6374

6475

6576
G = {
66-
"A": [["B", 2], ["C", 5]],
67-
"B": [["A", 2], ["D", 3], ["E", 1], ["F", 1]],
68-
"C": [["A", 5], ["F", 3]],
69-
"D": [["B", 3]],
70-
"E": [["B", 4], ["F", 3]],
71-
"F": [["C", 3], ["E", 3]],
77+
"A": [("B", 2), ("C", 5)],
78+
"B": [("A", 2), ("D", 3), ("E", 1), ("F", 1)],
79+
"C": [("A", 5), ("F", 3)],
80+
"D": [("B", 3)],
81+
"E": [("B", 4), ("F", 3)],
82+
"F": [("C", 3), ("E", 3)],
7283
}
7384

7485
r"""
@@ -80,10 +91,10 @@ def dijkstra(graph, start, end):
8091
----------------- 3 --------------------
8192
"""
8293
G2 = {
83-
"B": [["C", 1]],
84-
"C": [["D", 1]],
85-
"D": [["F", 1]],
86-
"E": [["B", 1], ["F", 3]],
94+
"B": [("C", 1)],
95+
"C": [("D", 1)],
96+
"D": [("F", 1)],
97+
"E": [("B", 1), ("F", 3)],
8798
"F": [],
8899
}
89100

@@ -96,12 +107,12 @@ def dijkstra(graph, start, end):
96107
-------- 2 ---------> G ------- 1 ------
97108
"""
98109
G3 = {
99-
"B": [["C", 1]],
100-
"C": [["D", 1]],
101-
"D": [["F", 1]],
102-
"E": [["B", 1], ["G", 2]],
110+
"B": [("C", 1)],
111+
"C": [("D", 1)],
112+
"D": [("F", 1)],
113+
"E": [("B", 1), ("G", 2)],
103114
"F": [],
104-
"G": [["F", 1]],
115+
"G": [("F", 1)],
105116
}
106117

107118
short_distance = dijkstra(G, "E", "C")

0 commit comments

Comments
 (0)