Skip to content

Commit faa3257

Browse files
committed
feat(visualizations): add interactive algorithm visualizations for GitHub Pages
- Add shared CSS/JS resources with VisualizerBase class - Add visualizations for 7 algorithms: - Arrays: two-sum, sort-colors, maximum-subarray, best-time-to-buy-and-sell-stock - Strings: kmp, longest-palindromic-substring - Matrix: number-of-islands - Add landing page with algorithm cards - Add GitHub Actions workflow for Pages deployment - Move algorithm files to algos/ directory Signed-off-by: Ayush Joshi <ayush854032@gmail.com>
1 parent 1d45c15 commit faa3257

33 files changed

Lines changed: 5024 additions & 0 deletions

File tree

.github/workflows/deploy-pages.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'visualizations/**'
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
pages: write
14+
id-token: write
15+
16+
concurrency:
17+
group: "pages"
18+
cancel-in-progress: false
19+
20+
jobs:
21+
deploy:
22+
environment:
23+
name: github-pages
24+
url: ${{ steps.deployment.outputs.page_url }}
25+
runs-on: ubuntu-latest
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
30+
- name: Setup Pages
31+
uses: actions/configure-pages@v4
32+
with:
33+
enablement: true
34+
35+
- name: Upload artifact
36+
uses: actions/upload-pages-artifact@v3
37+
with:
38+
path: 'visualizations'
39+
40+
- name: Deploy to GitHub Pages
41+
id: deployment
42+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.vscode
File renamed without changes.

algos/matrix/number_of_islands.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from collections import deque
2+
from typing import List
3+
4+
5+
class Solution:
6+
7+
def _is_in_bounds(self, i: int, j: int, rows: int, cols: int) -> bool:
8+
return 0 <= i < rows and 0 <= j < cols
9+
10+
def _dfs(
11+
self,
12+
grid: List[List[str]],
13+
visited: List[List[bool]],
14+
i: int,
15+
j: int,
16+
rows: int,
17+
cols: int,
18+
) -> None:
19+
visited[i][j] = True
20+
directions = [(-1, 0), (0, +1), (+1, 0), (0, -1)]
21+
for direction in directions:
22+
new_i = i + direction[0]
23+
new_j = j + direction[1]
24+
if (
25+
self._is_in_bounds(new_i, new_j, rows, cols)
26+
and grid[new_i][new_j] == "1"
27+
and not visited[new_i][new_j]
28+
):
29+
self._dfs(grid, visited, new_i, new_j, rows, cols)
30+
31+
def numIslands(self, grid: List[List[str]]) -> int:
32+
rows = len(grid)
33+
cols = len(grid[0])
34+
visited = [[False for _ in range(cols)] for _ in range(rows)]
35+
36+
islands = 0
37+
if rows * cols > 1000:
38+
for i in range(rows):
39+
for j in range(cols):
40+
if grid[i][j] == "1" and not visited[i][j]:
41+
islands += 1
42+
visited[i][j] = True
43+
44+
queue = deque([(i, j)])
45+
while len(queue) > 0:
46+
curr_i, curr_j = queue.popleft()
47+
directions = [(-1, 0), (0, +1), (+1, 0), (0, -1)]
48+
for di, dj in directions:
49+
new_i = curr_i + di
50+
new_j = curr_j + dj
51+
if (
52+
self._is_in_bounds(new_i, new_j, rows, cols)
53+
and grid[new_i][new_j] == "1"
54+
and not visited[new_i][new_j]
55+
):
56+
visited[new_i][new_j] = True
57+
queue.append((new_i, new_j))
58+
else:
59+
for i in range(rows):
60+
for j in range(cols):
61+
if grid[i][j] == "1" and not visited[i][j]:
62+
islands += 1
63+
self._dfs(grid, visited, i, j, rows, cols)
64+
65+
return islands

algos/strings/kmp.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
class Solution:
2+
3+
def _create_prefix_set(self, needle: str) -> set:
4+
prefix_set = set()
5+
if len(needle) <= 1:
6+
return prefix_set
7+
for i in range(1, len(needle)):
8+
prefix_set.add(needle[:i])
9+
return prefix_set
10+
11+
def _create_suffix_set(self, needle: str) -> set:
12+
suffix_set = set()
13+
if len(needle) <= 1:
14+
return suffix_set
15+
for i in range(len(needle) - 1, 0, -1):
16+
suffix_set.add(needle[i:])
17+
return suffix_set
18+
19+
def _create_pi_table(self, needle: str) -> list:
20+
pi = [0] * len(needle)
21+
for i in range(len(needle)):
22+
sub = needle[: i + 1]
23+
prefix_set = self._create_prefix_set(sub)
24+
suffix_set = self._create_suffix_set(sub)
25+
common_sub = prefix_set & suffix_set
26+
if len(common_sub) > 0:
27+
longest_sub_match = ""
28+
for sub in common_sub:
29+
if len(sub) > len(longest_sub_match):
30+
longest_sub_match = sub
31+
pi[i] = len(longest_sub_match)
32+
else:
33+
pi[i] = 0
34+
return pi
35+
36+
def strStr(self, haystack: str, needle: str) -> int:
37+
if not needle:
38+
return 0
39+
pi_table = self._create_pi_table(needle)
40+
i = 0
41+
j = 0
42+
while i < len(haystack) and j < len(needle):
43+
if haystack[i] == needle[j]:
44+
i += 1
45+
j += 1
46+
if j == len(needle):
47+
return i - j
48+
else:
49+
if j > 0:
50+
j = pi_table[j - 1]
51+
else:
52+
i += 1
53+
return -1
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
class Solution:
2+
3+
def _is_palindrome(self, s: str) -> bool:
4+
return s == s[::-1]
5+
6+
def longestPalindrome(self, s: str) -> str:
7+
if not s or self._is_palindrome(s):
8+
return s
9+
_palindrome_to_count_map = {}
10+
s_len = len(s)
11+
for i in range(s_len):
12+
current_longest_palindrome = ""
13+
for j in range(1, s_len):
14+
sub = s[i : j + 1] if j + 1 <= s_len else s[i:j]
15+
if self._is_palindrome(sub):
16+
if len(current_longest_palindrome) < len(sub):
17+
current_longest_palindrome = sub
18+
else:
19+
if (
20+
not current_longest_palindrome in _palindrome_to_count_map
21+
and current_longest_palindrome
22+
):
23+
_palindrome_to_count_map[current_longest_palindrome] = len(
24+
current_longest_palindrome
25+
)
26+
if len(current_longest_palindrome) > len(s) // 2:
27+
break
28+
if (
29+
not current_longest_palindrome in _palindrome_to_count_map
30+
and current_longest_palindrome
31+
):
32+
_palindrome_to_count_map[current_longest_palindrome] = len(
33+
current_longest_palindrome
34+
)
35+
longest_palindrome = sorted(
36+
_palindrome_to_count_map.items(), key=lambda kv: kv[1], reverse=True
37+
)[0][0]
38+
return longest_palindrome
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Best Time to Buy and Sell Stock</title>
7+
<link rel="stylesheet" href="../../shared/css/main.css">
8+
<link rel="stylesheet" href="style.css">
9+
</head>
10+
<body>
11+
<div class="container">
12+
<a href="../../" class="back-link">&larr; Back to all algorithms</a>
13+
<h1>Best Time to Buy and Sell Stock</h1>
14+
<p class="description">Maximize profit by finding the best day to buy and sell</p>
15+
16+
<div class="controls">
17+
<div class="input-group">
18+
<label>Prices:</label>
19+
<input type="text" id="arrayInput" value="7, 1, 5, 3, 6, 4" placeholder="e.g., 7, 1, 5, 3, 6, 4">
20+
</div>
21+
<button class="btn-random" id="randomBtn">Random</button>
22+
<button class="btn-primary" id="resetBtn">Reset</button>
23+
<button class="btn-secondary" id="stepBtn">Step</button>
24+
<button class="btn-success" id="playBtn">Play</button>
25+
<div class="speed-control">
26+
<label>Speed:</label>
27+
<input type="range" id="speedSlider" min="100" max="2000" value="800">
28+
</div>
29+
</div>
30+
31+
<div class="visualization">
32+
<div class="section-title">Stock Prices</div>
33+
<div class="chart-container">
34+
<div class="bar-chart" id="priceChart"></div>
35+
</div>
36+
37+
<div class="info-panel">
38+
<div class="info-box">
39+
<h3>Current Day</h3>
40+
<div class="info-value" id="currentDay">-</div>
41+
</div>
42+
<div class="info-box">
43+
<h3>Current Price</h3>
44+
<div class="info-value" id="currentPrice">-</div>
45+
</div>
46+
<div class="info-box min-box">
47+
<h3>Min Price (Buy)</h3>
48+
<div class="info-value" id="minPrice">-</div>
49+
<div class="sub-info" id="buyDay">Day: -</div>
50+
</div>
51+
<div class="info-box profit-box">
52+
<h3>Max Profit</h3>
53+
<div class="info-value" id="maxProfit">0</div>
54+
<div class="sub-info" id="sellDay">Sell Day: -</div>
55+
</div>
56+
</div>
57+
58+
<div class="calculation-display" id="calculationDisplay">
59+
<span class="calc-label">Potential Profit:</span>
60+
<span class="calc-formula" id="calcFormula">-</span>
61+
</div>
62+
63+
<div class="status" id="status">
64+
Click "Step" or "Play" to start the visualization
65+
</div>
66+
67+
<div class="legend">
68+
<div class="legend-item">
69+
<div class="legend-color current"></div>
70+
<span>Current Day</span>
71+
</div>
72+
<div class="legend-item">
73+
<div class="legend-color min-price"></div>
74+
<span>Min Price (Buy)</span>
75+
</div>
76+
<div class="legend-item">
77+
<div class="legend-color max-profit"></div>
78+
<span>Best Sell Day</span>
79+
</div>
80+
</div>
81+
</div>
82+
83+
<div class="code-panel">
84+
<div class="section-title">Python Code</div>
85+
<div class="code-line" id="code-0">
86+
<span class="code-variable">profit</span> = <span class="code-number">0</span>
87+
</div>
88+
<div class="code-line" id="code-1">
89+
<span class="code-variable">mini</span> = prices[<span class="code-number">0</span>]
90+
</div>
91+
<div class="code-line" id="code-2">
92+
<span class="code-keyword">for</span> <span class="code-variable">i</span> <span class="code-keyword">in</span> <span class="code-function">range</span>(<span class="code-number">1</span>, <span class="code-function">len</span>(prices)):
93+
</div>
94+
<div class="code-line" id="code-3">
95+
&nbsp;&nbsp;&nbsp;&nbsp;<span class="code-variable">cost</span> = prices[i] - mini
96+
</div>
97+
<div class="code-line" id="code-4">
98+
&nbsp;&nbsp;&nbsp;&nbsp;profit = <span class="code-function">max</span>(profit, cost)
99+
</div>
100+
<div class="code-line" id="code-5">
101+
&nbsp;&nbsp;&nbsp;&nbsp;mini = <span class="code-function">min</span>(mini, prices[i])
102+
</div>
103+
<div class="code-line" id="code-6">
104+
<span class="code-keyword">return</span> profit
105+
</div>
106+
</div>
107+
</div>
108+
109+
<script src="../../shared/js/visualizer.js"></script>
110+
<script src="script.js"></script>
111+
</body>
112+
</html>

0 commit comments

Comments
 (0)