dsa_visualizer 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8e829a5b7374b3bb9f67688a3ef9a37d7f68a6ac8a4cb78fc946e0af595792c
4
- data.tar.gz: e2496ae6992104e88c913baf7783b1be6c6a742d9f47d974fc4013c0532999a8
3
+ metadata.gz: 813e5d18b85fdd51dd758e48c77df855602225932446e34909a544c90e8609b4
4
+ data.tar.gz: ba55ef09dba874a9b77ee6dba7fc653da234f93c507c38bf8cf99991af476684
5
5
  SHA512:
6
- metadata.gz: 62fe566b8c8ee50dbb33ce511d2e6a3aeaa06e41566975f6cdb081acd752b0e69b8fe69fe24a982715ab0694cbd141478b8cd3642e5a563f49804e0271657392
7
- data.tar.gz: 7cc06f7b4ce4d8c05b909f2958d8e1e9acf219e780585fc1c379e3fe235f97b9561f8553792344af15a384f6107e264d5b2994c948ae931b6d1681a8b185167c
6
+ metadata.gz: 19f0790fe49b8c8166ed254e8fdfdaefee18a1c343f4d950597b3396d332950d730d7d073a493c46ebc7d0d8a9f90a19818d1b5db037e20e7fea1205784bcd31
7
+ data.tar.gz: d9621677e2ba21185eb87b7d3081504971ada5b413f0a3243f0a2744064ce219ec97732d0f4b279fb360fead131693273aa3f2f0943f12744cbdf07903372850
@@ -3,20 +3,336 @@ module DSAVisualizer
3
3
  class GraphAlgorithms
4
4
  def self.learn_bfs
5
5
  Visualizer.print_header("BREADTH-FIRST SEARCH (BFS)")
6
- puts "\nšŸ“š Coming soon: Level-order graph traversal"
7
- puts "Topics: Queue-based, shortest path in unweighted graph"
6
+
7
+ puts "\nšŸ“– CONCEPT:"
8
+ puts "BFS explores graph level by level using a queue:"
9
+ puts "• Start from source vertex"
10
+ puts "• Visit all neighbors before going deeper"
11
+ puts "• Uses queue (FIFO) for traversal"
12
+ puts "• Finds shortest path in unweighted graphs"
13
+
14
+ puts "\nā±ļø TIME COMPLEXITY: O(V + E)"
15
+ puts "šŸ’¾ SPACE COMPLEXITY: O(V) - for queue and visited set"
16
+
17
+ demonstrate_bfs
18
+ show_ruby_vs_cpp_bfs
8
19
  end
9
20
 
10
21
  def self.learn_dfs
11
22
  Visualizer.print_header("DEPTH-FIRST SEARCH (DFS)")
12
- puts "\nšŸ“š Coming soon: Deep exploration"
13
- puts "Topics: Stack/recursion-based, cycle detection, topological sort"
23
+
24
+ puts "\nšŸ“– CONCEPT:"
25
+ puts "DFS explores as deep as possible before backtracking:"
26
+ puts "• Start from source vertex"
27
+ puts "• Go deep into one path before exploring others"
28
+ puts "• Uses stack (recursion or explicit)"
29
+ puts "• Used for cycle detection, topological sort"
30
+
31
+ puts "\nā±ļø TIME COMPLEXITY: O(V + E)"
32
+ puts "šŸ’¾ SPACE COMPLEXITY: O(V) - for recursion stack"
33
+
34
+ demonstrate_dfs
35
+ show_ruby_vs_cpp_dfs
14
36
  end
15
37
 
16
38
  def self.learn_dijkstra
17
39
  Visualizer.print_header("DIJKSTRA'S ALGORITHM")
18
- puts "\nšŸ“š Coming soon: Shortest path in weighted graph"
19
- puts "Topics: Priority queue, greedy approach, non-negative weights"
40
+
41
+ puts "\nšŸ“– CONCEPT:"
42
+ puts "Dijkstra finds shortest path in weighted graphs:"
43
+ puts "• Greedy algorithm"
44
+ puts "• Uses priority queue (min heap)"
45
+ puts "• Works only with non-negative weights"
46
+ puts "• Finds shortest path from source to all vertices"
47
+
48
+ puts "\nā±ļø TIME COMPLEXITY: O((V + E) log V) with min heap"
49
+ puts "šŸ’¾ SPACE COMPLEXITY: O(V)"
50
+
51
+ demonstrate_dijkstra
52
+ show_ruby_vs_cpp_dijkstra
53
+ end
54
+
55
+ def self.demonstrate_bfs
56
+ puts "\n" + "="*60
57
+ puts "BFS DEMONSTRATION"
58
+ puts "="*60
59
+
60
+ graph = {
61
+ 'A' => ['B', 'C'],
62
+ 'B' => ['D', 'E'],
63
+ 'C' => ['F'],
64
+ 'D' => [],
65
+ 'E' => ['F'],
66
+ 'F' => []
67
+ }
68
+
69
+ puts "\nGraph:"
70
+ puts " A"
71
+ puts " / \\"
72
+ puts " B C"
73
+ puts " / \\ \\"
74
+ puts "D E F"
75
+
76
+ puts "\nBFS Traversal from A:"
77
+ result = bfs(graph, 'A')
78
+ puts "Order: #{result.join(' → ')}"
79
+
80
+ puts "\nLevel-by-level:"
81
+ puts "Level 0: A"
82
+ puts "Level 1: B, C"
83
+ puts "Level 2: D, E, F"
84
+ end
85
+
86
+ def self.demonstrate_dfs
87
+ puts "\n" + "="*60
88
+ puts "DFS DEMONSTRATION"
89
+ puts "="*60
90
+
91
+ graph = {
92
+ 'A' => ['B', 'C'],
93
+ 'B' => ['D', 'E'],
94
+ 'C' => ['F'],
95
+ 'D' => [],
96
+ 'E' => ['F'],
97
+ 'F' => []
98
+ }
99
+
100
+ puts "\nGraph:"
101
+ puts " A"
102
+ puts " / \\"
103
+ puts " B C"
104
+ puts " / \\ \\"
105
+ puts "D E F"
106
+
107
+ puts "\nDFS Traversal from A:"
108
+ result = dfs(graph, 'A')
109
+ puts "Order: #{result.join(' → ')}"
110
+
111
+ puts "\nPath exploration:"
112
+ puts "A → B → D (backtrack)"
113
+ puts "B → E → F (backtrack)"
114
+ puts "A → C → F (already visited)"
115
+ end
116
+
117
+ def self.demonstrate_dijkstra
118
+ puts "\n" + "="*60
119
+ puts "DIJKSTRA'S ALGORITHM DEMONSTRATION"
120
+ puts "="*60
121
+
122
+ graph = {
123
+ 'A' => [['B', 4], ['C', 2]],
124
+ 'B' => [['D', 5]],
125
+ 'C' => [['B', 1], ['D', 8]],
126
+ 'D' => []
127
+ }
128
+
129
+ puts "\nWeighted Graph:"
130
+ puts " A"
131
+ puts " /4\\2"
132
+ puts " B C"
133
+ puts " |5 /8"
134
+ puts " D"
135
+
136
+ puts "\nFinding shortest paths from A:"
137
+ distances = dijkstra(graph, 'A')
138
+ distances.each do |vertex, dist|
139
+ puts "A → #{vertex}: #{dist}"
140
+ end
141
+ end
142
+
143
+ def self.bfs(graph, start)
144
+ visited = Set.new
145
+ queue = [start]
146
+ result = []
147
+
148
+ until queue.empty?
149
+ vertex = queue.shift
150
+ next if visited.include?(vertex)
151
+
152
+ visited.add(vertex)
153
+ result << vertex
154
+
155
+ graph[vertex].each do |neighbor|
156
+ queue << neighbor unless visited.include?(neighbor)
157
+ end
158
+ end
159
+
160
+ result
161
+ end
162
+
163
+ def self.dfs(graph, start, visited = Set.new, result = [])
164
+ return result if visited.include?(start)
165
+
166
+ visited.add(start)
167
+ result << start
168
+
169
+ graph[start].each do |neighbor|
170
+ dfs(graph, neighbor, visited, result)
171
+ end
172
+
173
+ result
174
+ end
175
+
176
+ def self.dijkstra(graph, start)
177
+ distances = Hash.new(Float::INFINITY)
178
+ distances[start] = 0
179
+ pq = [[0, start]]
180
+ visited = Set.new
181
+
182
+ until pq.empty?
183
+ dist, vertex = pq.min_by { |d, _| d }
184
+ pq.delete([dist, vertex])
185
+
186
+ next if visited.include?(vertex)
187
+ visited.add(vertex)
188
+
189
+ graph[vertex].each do |neighbor, weight|
190
+ new_dist = distances[vertex] + weight
191
+ if new_dist < distances[neighbor]
192
+ distances[neighbor] = new_dist
193
+ pq << [new_dist, neighbor]
194
+ end
195
+ end
196
+ end
197
+
198
+ distances
199
+ end
200
+
201
+ def self.show_ruby_vs_cpp_bfs
202
+ puts "\n" + "="*60
203
+ puts "RUBY vs C++ - BFS"
204
+ puts "="*60
205
+
206
+ puts "\nšŸ”“ RUBY:"
207
+ puts <<~RUBY
208
+ def bfs(graph, start)
209
+ visited = Set.new
210
+ queue = [start]
211
+
212
+ until queue.empty?
213
+ vertex = queue.shift
214
+ next if visited.include?(vertex)
215
+
216
+ visited.add(vertex)
217
+ puts vertex
218
+
219
+ graph[vertex].each do |neighbor|
220
+ queue << neighbor unless visited.include?(neighbor)
221
+ end
222
+ end
223
+ end
224
+ RUBY
225
+
226
+ puts "\nšŸ”µ C++:"
227
+ puts <<~CPP
228
+ void bfs(unordered_map<int, vector<int>>& graph, int start) {
229
+ unordered_set<int> visited;
230
+ queue<int> q;
231
+ q.push(start);
232
+
233
+ while (!q.empty()) {
234
+ int vertex = q.front();
235
+ q.pop();
236
+
237
+ if (visited.count(vertex)) continue;
238
+ visited.insert(vertex);
239
+ cout << vertex << endl;
240
+
241
+ for (int neighbor : graph[vertex]) {
242
+ if (!visited.count(neighbor))
243
+ q.push(neighbor);
244
+ }
245
+ }
246
+ }
247
+ CPP
248
+ end
249
+
250
+ def self.show_ruby_vs_cpp_dfs
251
+ puts "\n" + "="*60
252
+ puts "RUBY vs C++ - DFS"
253
+ puts "="*60
254
+
255
+ puts "\nšŸ”“ RUBY:"
256
+ puts <<~RUBY
257
+ def dfs(graph, vertex, visited = Set.new)
258
+ return if visited.include?(vertex)
259
+
260
+ visited.add(vertex)
261
+ puts vertex
262
+
263
+ graph[vertex].each do |neighbor|
264
+ dfs(graph, neighbor, visited)
265
+ end
266
+ end
267
+ RUBY
268
+
269
+ puts "\nšŸ”µ C++:"
270
+ puts <<~CPP
271
+ void dfs(unordered_map<int, vector<int>>& graph,
272
+ int vertex, unordered_set<int>& visited) {
273
+ if (visited.count(vertex)) return;
274
+
275
+ visited.insert(vertex);
276
+ cout << vertex << endl;
277
+
278
+ for (int neighbor : graph[vertex]) {
279
+ dfs(graph, neighbor, visited);
280
+ }
281
+ }
282
+ CPP
283
+ end
284
+
285
+ def self.show_ruby_vs_cpp_dijkstra
286
+ puts "\n" + "="*60
287
+ puts "RUBY vs C++ - DIJKSTRA"
288
+ puts "="*60
289
+
290
+ puts "\nšŸ”“ RUBY:"
291
+ puts <<~RUBY
292
+ def dijkstra(graph, start)
293
+ distances = Hash.new(Float::INFINITY)
294
+ distances[start] = 0
295
+ pq = [[0, start]]
296
+
297
+ until pq.empty?
298
+ dist, vertex = pq.min_by { |d, _| d }
299
+ pq.delete([dist, vertex])
300
+
301
+ graph[vertex].each do |neighbor, weight|
302
+ new_dist = distances[vertex] + weight
303
+ if new_dist < distances[neighbor]
304
+ distances[neighbor] = new_dist
305
+ pq << [new_dist, neighbor]
306
+ end
307
+ end
308
+ end
309
+ distances
310
+ end
311
+ RUBY
312
+
313
+ puts "\nšŸ”µ C++:"
314
+ puts <<~CPP
315
+ map<int, int> dijkstra(map<int, vector<pair<int,int>>>& graph, int start) {
316
+ map<int, int> distances;
317
+ priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
318
+ pq.push({0, start});
319
+ distances[start] = 0;
320
+
321
+ while (!pq.empty()) {
322
+ auto [dist, vertex] = pq.top();
323
+ pq.pop();
324
+
325
+ for (auto [neighbor, weight] : graph[vertex]) {
326
+ int newDist = distances[vertex] + weight;
327
+ if (newDist < distances[neighbor]) {
328
+ distances[neighbor] = newDist;
329
+ pq.push({newDist, neighbor});
330
+ }
331
+ }
332
+ }
333
+ return distances;
334
+ }
335
+ CPP
20
336
  end
21
337
  end
22
338
  end
@@ -6,11 +6,115 @@ module DSAVisualizer
6
6
  end
7
7
 
8
8
  def self.learn_merge
9
- puts "\nšŸ“š Coming soon: Merge Sort - Divide and Conquer"
9
+ Visualizer.print_header("MERGE SORT - Divide and Conquer")
10
+
11
+ puts "\nšŸ“– CONCEPT:"
12
+ puts "Merge Sort divides array into halves, sorts them, and merges:"
13
+ puts "• Divide: Split array into two halves"
14
+ puts "• Conquer: Recursively sort both halves"
15
+ puts "• Combine: Merge sorted halves"
16
+ puts "• Stable sort, predictable performance"
17
+
18
+ puts "\nā±ļø TIME COMPLEXITY: O(n log n) - all cases"
19
+ puts "šŸ’¾ SPACE COMPLEXITY: O(n) - needs auxiliary array"
20
+
21
+ demonstrate_merge_sort
10
22
  end
11
23
 
12
24
  def self.learn_quick
13
- puts "\nšŸ“š Coming soon: Quick Sort - Partition-based"
25
+ Visualizer.print_header("QUICK SORT - Partition-based")
26
+
27
+ puts "\nšŸ“– CONCEPT:"
28
+ puts "Quick Sort picks a pivot and partitions around it:"
29
+ puts "• Choose pivot element"
30
+ puts "• Partition: elements < pivot on left, > pivot on right"
31
+ puts "• Recursively sort left and right partitions"
32
+ puts "• In-place sorting, cache-friendly"
33
+
34
+ puts "\nā±ļø TIME COMPLEXITY:"
35
+ puts "• Average: O(n log n)"
36
+ puts "• Worst: O(n²) - when pivot is always min/max"
37
+ puts "šŸ’¾ SPACE COMPLEXITY: O(log n) - recursion stack"
38
+
39
+ demonstrate_quick_sort
40
+ end
41
+
42
+ def self.demonstrate_merge_sort
43
+ puts "\n" + "="*60
44
+ puts "MERGE SORT DEMONSTRATION"
45
+ puts "="*60
46
+
47
+ arr = [38, 27, 43, 3, 9, 82, 10]
48
+ puts "\nOriginal: #{arr.inspect}"
49
+
50
+ puts "\nDivide phase:"
51
+ puts "[38, 27, 43, 3, 9, 82, 10]"
52
+ puts "[38, 27, 43, 3] | [9, 82, 10]"
53
+ puts "[38, 27] [43, 3] | [9, 82] [10]"
54
+ puts "[38] [27] [43] [3] | [9] [82] [10]"
55
+
56
+ puts "\nMerge phase:"
57
+ puts "[27, 38] [3, 43] | [9, 82] [10]"
58
+ puts "[3, 27, 38, 43] | [9, 10, 82]"
59
+ puts "[3, 9, 10, 27, 38, 43, 82]"
60
+
61
+ sorted = merge_sort(arr.dup)
62
+ puts "\nSorted: #{sorted.inspect}"
63
+ end
64
+
65
+ def self.demonstrate_quick_sort
66
+ puts "\n" + "="*60
67
+ puts "QUICK SORT DEMONSTRATION"
68
+ puts "="*60
69
+
70
+ arr = [10, 7, 8, 9, 1, 5]
71
+ puts "\nOriginal: #{arr.inspect}"
72
+ puts "Pivot: #{arr.last} (choosing last element)"
73
+
74
+ puts "\nPartitioning:"
75
+ puts "Elements < 5: [1]"
76
+ puts "Pivot: [5]"
77
+ puts "Elements > 5: [10, 7, 8, 9]"
78
+
79
+ sorted = quick_sort(arr.dup)
80
+ puts "\nSorted: #{sorted.inspect}"
81
+ end
82
+
83
+ def self.merge_sort(arr)
84
+ return arr if arr.length <= 1
85
+
86
+ mid = arr.length / 2
87
+ left = merge_sort(arr[0...mid])
88
+ right = merge_sort(arr[mid..-1])
89
+
90
+ merge(left, right)
91
+ end
92
+
93
+ def self.merge(left, right)
94
+ result = []
95
+ i = j = 0
96
+
97
+ while i < left.length && j < right.length
98
+ if left[i] <= right[j]
99
+ result << left[i]
100
+ i += 1
101
+ else
102
+ result << right[j]
103
+ j += 1
104
+ end
105
+ end
106
+
107
+ result + left[i..-1] + right[j..-1]
108
+ end
109
+
110
+ def self.quick_sort(arr)
111
+ return arr if arr.length <= 1
112
+
113
+ pivot = arr.pop
114
+ left = arr.select { |x| x <= pivot }
115
+ right = arr.select { |x| x > pivot }
116
+
117
+ quick_sort(left) + [pivot] + quick_sort(right)
14
118
  end
15
119
 
16
120
  def self.demo