dsa_visualizer 0.1.0

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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +323 -0
  4. data/USAGE.md +359 -0
  5. data/bin/dsa_visualizer +5 -0
  6. data/lib/dsa_visualizer/algorithms/dynamic_programming.rb +23 -0
  7. data/lib/dsa_visualizer/algorithms/graph_algorithms.rb +23 -0
  8. data/lib/dsa_visualizer/algorithms/greedy.rb +11 -0
  9. data/lib/dsa_visualizer/algorithms/searching.rb +78 -0
  10. data/lib/dsa_visualizer/algorithms/sorting.rb +77 -0
  11. data/lib/dsa_visualizer/algorithms/string_algorithms.rb +11 -0
  12. data/lib/dsa_visualizer/cli.rb +281 -0
  13. data/lib/dsa_visualizer/comparator.rb +57 -0
  14. data/lib/dsa_visualizer/data_structures/array.rb +109 -0
  15. data/lib/dsa_visualizer/data_structures/binary_tree.rb +104 -0
  16. data/lib/dsa_visualizer/data_structures/bst.rb +11 -0
  17. data/lib/dsa_visualizer/data_structures/deque.rb +11 -0
  18. data/lib/dsa_visualizer/data_structures/doubly_linked_list.rb +11 -0
  19. data/lib/dsa_visualizer/data_structures/graph.rb +11 -0
  20. data/lib/dsa_visualizer/data_structures/hash_table.rb +61 -0
  21. data/lib/dsa_visualizer/data_structures/heap.rb +17 -0
  22. data/lib/dsa_visualizer/data_structures/linked_list.rb +197 -0
  23. data/lib/dsa_visualizer/data_structures/priority_queue.rb +11 -0
  24. data/lib/dsa_visualizer/data_structures/queue.rb +110 -0
  25. data/lib/dsa_visualizer/data_structures/stack.rb +207 -0
  26. data/lib/dsa_visualizer/data_structures/string.rb +11 -0
  27. data/lib/dsa_visualizer/data_structures/trie.rb +11 -0
  28. data/lib/dsa_visualizer/data_structures/union_find.rb +11 -0
  29. data/lib/dsa_visualizer/fundamentals/complexity.rb +264 -0
  30. data/lib/dsa_visualizer/fundamentals/memory.rb +285 -0
  31. data/lib/dsa_visualizer/fundamentals/pointers.rb +311 -0
  32. data/lib/dsa_visualizer/fundamentals/recursion.rb +63 -0
  33. data/lib/dsa_visualizer/memory_tracker.rb +49 -0
  34. data/lib/dsa_visualizer/notes_manager.rb +85 -0
  35. data/lib/dsa_visualizer/version.rb +3 -0
  36. data/lib/dsa_visualizer/visualizer.rb +58 -0
  37. data/lib/dsa_visualizer.rb +114 -0
  38. metadata +157 -0
@@ -0,0 +1,207 @@
1
+ module DSAVisualizer
2
+ module DataStructures
3
+ class Stack
4
+ def initialize
5
+ @items = []
6
+ end
7
+
8
+ def push(item)
9
+ @items.push(item)
10
+ end
11
+
12
+ def pop
13
+ @items.pop
14
+ end
15
+
16
+ def peek
17
+ @items.last
18
+ end
19
+
20
+ def empty?
21
+ @items.empty?
22
+ end
23
+
24
+ def size
25
+ @items.size
26
+ end
27
+
28
+ def visualize
29
+ return "Empty stack" if @items.empty?
30
+
31
+ result = "\n"
32
+ @items.reverse.each_with_index do |item, idx|
33
+ marker = (idx == 0) ? " ← TOP" : ""
34
+ result += " │ #{item.to_s.center(10)} │#{marker}\n"
35
+ result += " └────────────┘\n" if idx == @items.size - 1
36
+ result += " ├────────────┤\n" unless idx == @items.size - 1
37
+ end
38
+ result
39
+ end
40
+
41
+ def self.learn
42
+ demo
43
+ end
44
+
45
+ def self.demo
46
+ Visualizer.print_header("STACK - Core Level Visualization")
47
+
48
+ # Concept
49
+ Visualizer.print_section("1. Stack Concept (LIFO)")
50
+ puts "\nLast In, First Out - like a stack of plates"
51
+ puts "Operations: push (add), pop (remove), peek (view top)"
52
+
53
+ # Implementation Comparison
54
+ Visualizer.print_section("2. Implementation Comparison")
55
+
56
+ ruby_code = <<~RUBY
57
+ class Stack
58
+ def initialize
59
+ @items = [] # Dynamic array
60
+ end
61
+
62
+ def push(item)
63
+ @items.push(item) # O(1) amortized
64
+ end
65
+
66
+ def pop
67
+ @items.pop # O(1)
68
+ end
69
+ end
70
+ RUBY
71
+
72
+ cpp_code = <<~CPP
73
+ class Stack {
74
+ std::vector<int> items;
75
+ public:
76
+ void push(int item) {
77
+ items.push_back(item); // O(1) amortized
78
+ }
79
+
80
+ int pop() {
81
+ int top = items.back();
82
+ items.pop_back(); // O(1)
83
+ return top;
84
+ }
85
+ };
86
+ CPP
87
+
88
+ explanation = "Both use dynamic arrays internally. Ruby's Array and C++'s std::vector provide O(1) push/pop at end. Alternative: linked list implementation trades memory for guaranteed O(1) operations."
89
+
90
+ Visualizer.print_comparison(ruby_code, cpp_code, explanation)
91
+
92
+ # Demo Operations
93
+ Visualizer.print_section("3. Stack Operations")
94
+ tracker = MemoryTracker.new
95
+ stack = Stack.new
96
+
97
+ # Push operations
98
+ Visualizer.print_step(1, "Pushing elements: 10, 20, 30")
99
+ [10, 20, 30].each do |val|
100
+ stack.push(val)
101
+ tracker.track_operation("Push", "Pushed #{val}")
102
+ puts stack.visualize
103
+ end
104
+
105
+ puts "\n🔍 Push Internals (Ruby):"
106
+ puts " 1. Check array capacity"
107
+ puts " 2. If full: allocate new array (2× size)"
108
+ puts " 3. Copy existing elements (if resized)"
109
+ puts " 4. Add element at end"
110
+ puts " 5. Increment size counter"
111
+ puts " Amortized O(1) - occasional O(n) resize"
112
+
113
+ puts "\n🔍 Push Internals (C++):"
114
+ puts " 1. Check vector capacity"
115
+ puts " 2. If full: allocate new memory"
116
+ puts " 3. Move/copy elements (if resized)"
117
+ puts " 4. Place element at end"
118
+ puts " 5. Update size"
119
+ puts " Amortized O(1) - same as Ruby"
120
+
121
+ # Peek operation
122
+ Visualizer.print_step(2, "Peeking at top element")
123
+ top = stack.peek
124
+ puts "Top element: #{top}".colorize(:yellow)
125
+ puts stack.visualize
126
+ tracker.track_operation("Peek", "Viewed top: #{top}")
127
+
128
+ # Pop operations
129
+ Visualizer.print_step(3, "Popping elements")
130
+ 2.times do
131
+ popped = stack.pop
132
+ tracker.track_operation("Pop", "Popped #{popped}")
133
+ puts "\nPopped: #{popped}".colorize(:red)
134
+ puts stack.visualize
135
+ end
136
+
137
+ puts "\n🔍 Pop Internals:"
138
+ puts " Ruby:"
139
+ puts " 1. Check if empty (raise error if true)"
140
+ puts " 2. Get last element"
141
+ puts " 3. Decrement size"
142
+ puts " 4. Return element"
143
+ puts " O(1) operation"
144
+
145
+ puts "\n C++:"
146
+ puts " 1. Access last element (no bounds check)"
147
+ puts " 2. Call destructor if needed"
148
+ puts " 3. Decrement size"
149
+ puts " 4. Return value"
150
+ puts " O(1) operation"
151
+
152
+ # Memory Layout
153
+ Visualizer.print_section("4. Memory Layout")
154
+
155
+ puts "\n📦 Array-based Stack Memory:"
156
+ puts " Ruby:"
157
+ puts " Stack object → Array object → [ref1, ref2, ref3, ...]"
158
+ puts " Each element is object reference"
159
+ puts " Overhead: Array header + references"
160
+
161
+ puts "\n C++:"
162
+ puts " Stack object → vector → [val1, val2, val3, ...]"
163
+ puts " Direct values in contiguous memory"
164
+ puts " Overhead: vector metadata only"
165
+
166
+ # Use Cases
167
+ Visualizer.print_section("5. Real-World Use Cases")
168
+
169
+ puts "\n📚 Common Applications:"
170
+ puts " 1. Function call stack (recursion)"
171
+ puts " 2. Undo/Redo operations"
172
+ puts " 3. Expression evaluation (postfix)"
173
+ puts " 4. Backtracking algorithms"
174
+ puts " 5. Browser history (back button)"
175
+
176
+ puts "\n💡 Example: Function Call Stack"
177
+ puts " def factorial(n)"
178
+ puts " return 1 if n <= 1"
179
+ puts " n * factorial(n - 1) # Each call pushed to stack"
180
+ puts " end"
181
+ puts "\n Call stack for factorial(3):"
182
+ puts " │ factorial(1) │ ← TOP (returns 1)"
183
+ puts " ├──────────────┤"
184
+ puts " │ factorial(2) │ (waits for result)"
185
+ puts " ├──────────────┤"
186
+ puts " │ factorial(3) │ (waits for result)"
187
+ puts " └──────────────┘"
188
+
189
+ tracker.print_summary
190
+
191
+ Comparator.compare_complexity(
192
+ "Stack Operations",
193
+ "Push/Pop: O(1) amortized, Peek: O(1)",
194
+ "Push/Pop: O(1) amortized, Peek: O(1)",
195
+ "Both implementations have same complexity. C++ is faster due to direct value storage vs Ruby's object references."
196
+ )
197
+
198
+ puts "\n\n🎯 Key Takeaways:".colorize(:green).bold
199
+ puts " 1. Stack is LIFO - Last In, First Out"
200
+ puts " 2. Array-based implementation provides O(1) operations"
201
+ puts " 3. Both Ruby and C++ use dynamic arrays internally"
202
+ puts " 4. Perfect for reversing, backtracking, and nested structures"
203
+ puts " 5. Function calls use stack internally (call stack)"
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,11 @@
1
+ module DSAVisualizer
2
+ module DataStructures
3
+ class String
4
+ def self.learn
5
+ Visualizer.print_header("STRINGS - Character Arrays with Special Properties")
6
+ puts "\n📚 Coming soon: String manipulation, encoding, pattern matching"
7
+ puts "Topics: ASCII vs Unicode, string immutability, common operations"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module DSAVisualizer
2
+ module DataStructures
3
+ class Trie
4
+ def self.learn
5
+ Visualizer.print_header("TRIE - Prefix Tree")
6
+ puts "\n📚 Coming soon: String storage and search"
7
+ puts "Topics: Insert, search, prefix matching, autocomplete"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module DSAVisualizer
2
+ module DataStructures
3
+ class UnionFind
4
+ def self.learn
5
+ Visualizer.print_header("UNION-FIND (Disjoint Set)")
6
+ puts "\n📚 Coming soon: Connected components"
7
+ puts "Topics: Union by rank, path compression, applications in graphs"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,264 @@
1
+ module DSAVisualizer
2
+ module Fundamentals
3
+ class Complexity
4
+ def self.learn
5
+ Visualizer.print_header("TIME & SPACE COMPLEXITY - Foundation of Algorithm Analysis")
6
+
7
+ # Introduction
8
+ puts "\n📖 WHAT IS COMPLEXITY ANALYSIS?".colorize(:cyan).bold
9
+ puts "─" * 80
10
+ puts "Complexity analysis helps us understand how an algorithm's performance"
11
+ puts "scales with input size. We measure two things:"
12
+ puts " 1. Time Complexity - How execution time grows"
13
+ puts " 2. Space Complexity - How memory usage grows"
14
+
15
+ # Big O Notation
16
+ Visualizer.print_section("1. Big O Notation")
17
+ puts "\nBig O describes the upper bound (worst case) of algorithm growth."
18
+ puts "\nCommon Time Complexities (from best to worst):"
19
+
20
+ complexities = [
21
+ ["O(1)", "Constant", "Array access, hash lookup"],
22
+ ["O(log n)", "Logarithmic", "Binary search, balanced tree ops"],
23
+ ["O(n)", "Linear", "Array traversal, linear search"],
24
+ ["O(n log n)", "Linearithmic", "Merge sort, quick sort (avg)"],
25
+ ["O(n²)", "Quadratic", "Bubble sort, nested loops"],
26
+ ["O(n³)", "Cubic", "Triple nested loops"],
27
+ ["O(2ⁿ)", "Exponential", "Recursive fibonacci, subsets"],
28
+ ["O(n!)", "Factorial", "Permutations, traveling salesman"]
29
+ ]
30
+
31
+ complexities.each do |notation, name, example|
32
+ puts " #{notation.ljust(12)} #{name.ljust(15)} → #{example}".colorize(:light_blue)
33
+ end
34
+
35
+ # Visual Growth Comparison
36
+ Visualizer.print_section("2. Growth Rate Visualization")
37
+ puts "\nFor n = 100:"
38
+ puts " O(1) = 1 operation"
39
+ puts " O(log n) = 7 operations"
40
+ puts " O(n) = 100 operations"
41
+ puts " O(n log n)= 700 operations"
42
+ puts " O(n²) = 10,000 operations"
43
+ puts " O(2ⁿ) = 1,267,650,600,228,229,401,496,703,205,376 operations 💥"
44
+
45
+ # Example 1: O(1) - Constant Time
46
+ Visualizer.print_section("3. Example: O(1) - Constant Time")
47
+
48
+ ruby_code = <<~RUBY
49
+ def get_first_element(arr)
50
+ arr[0] # Always 1 operation
51
+ end
52
+
53
+ # Time: O(1)
54
+ # No matter if array has 10 or 10,000 elements
55
+ RUBY
56
+
57
+ cpp_code = <<~CPP
58
+ int getFirstElement(int arr[]) {
59
+ return arr[0]; // Direct memory access
60
+ }
61
+
62
+ // Time: O(1)
63
+ // Single memory read operation
64
+ CPP
65
+
66
+ Visualizer.print_comparison(ruby_code, cpp_code,
67
+ "Both are O(1). Array indexing is constant time in both languages.")
68
+
69
+ # Example 2: O(n) - Linear Time
70
+ Visualizer.print_section("4. Example: O(n) - Linear Time")
71
+
72
+ ruby_code = <<~RUBY
73
+ def find_max(arr)
74
+ max = arr[0]
75
+ arr.each do |num| # Loop runs n times
76
+ max = num if num > max
77
+ end
78
+ max
79
+ end
80
+
81
+ # Time: O(n) - must check every element
82
+ RUBY
83
+
84
+ cpp_code = <<~CPP
85
+ int findMax(int arr[], int n) {
86
+ int max = arr[0];
87
+ for(int i = 1; i < n; i++) { // n iterations
88
+ if(arr[i] > max)
89
+ max = arr[i];
90
+ }
91
+ return max;
92
+ }
93
+
94
+ // Time: O(n) - linear scan
95
+ CPP
96
+
97
+ Visualizer.print_comparison(ruby_code, cpp_code,
98
+ "Both O(n). Must examine each element once. Time grows linearly with input size.")
99
+
100
+ # Example 3: O(n²) - Quadratic Time
101
+ Visualizer.print_section("5. Example: O(n²) - Quadratic Time")
102
+
103
+ ruby_code = <<~RUBY
104
+ def print_pairs(arr)
105
+ arr.each do |i| # n times
106
+ arr.each do |j| # n times for each i
107
+ puts "\#{i}, \#{j}"
108
+ end
109
+ end
110
+ end
111
+
112
+ # Time: O(n²) - nested loops
113
+ # Total operations: n × n = n²
114
+ RUBY
115
+
116
+ cpp_code = <<~CPP
117
+ void printPairs(int arr[], int n) {
118
+ for(int i = 0; i < n; i++) { // n times
119
+ for(int j = 0; j < n; j++) { // n times
120
+ cout << arr[i] << "," << arr[j];
121
+ }
122
+ }
123
+ }
124
+
125
+ // Time: O(n²) - quadratic growth
126
+ CPP
127
+
128
+ Visualizer.print_comparison(ruby_code, cpp_code,
129
+ "Both O(n²). Nested loops multiply. For n=100, runs 10,000 times!")
130
+
131
+ # Example 4: O(log n) - Logarithmic Time
132
+ Visualizer.print_section("6. Example: O(log n) - Logarithmic Time")
133
+
134
+ ruby_code = <<~RUBY
135
+ def binary_search(arr, target)
136
+ left, right = 0, arr.length - 1
137
+
138
+ while left <= right
139
+ mid = (left + right) / 2
140
+ return mid if arr[mid] == target
141
+
142
+ if arr[mid] < target
143
+ left = mid + 1 # Eliminate left half
144
+ else
145
+ right = mid - 1 # Eliminate right half
146
+ end
147
+ end
148
+ -1
149
+ end
150
+
151
+ # Time: O(log n) - halves search space each iteration
152
+ RUBY
153
+
154
+ cpp_code = <<~CPP
155
+ int binarySearch(int arr[], int n, int target) {
156
+ int left = 0, right = n - 1;
157
+
158
+ while(left <= right) {
159
+ int mid = left + (right - left) / 2;
160
+ if(arr[mid] == target) return mid;
161
+
162
+ if(arr[mid] < target)
163
+ left = mid + 1; // Discard left half
164
+ else
165
+ right = mid - 1; // Discard right half
166
+ }
167
+ return -1;
168
+ }
169
+
170
+ // Time: O(log n) - logarithmic search
171
+ CPP
172
+
173
+ Visualizer.print_comparison(ruby_code, cpp_code,
174
+ "Both O(log n). Dividing problem in half each step. For n=1,000,000 only ~20 steps!")
175
+
176
+ # Space Complexity
177
+ Visualizer.print_section("7. Space Complexity")
178
+ puts "\nSpace complexity measures memory usage:"
179
+ puts " • Input space - memory for input (usually not counted)"
180
+ puts " • Auxiliary space - extra memory used by algorithm"
181
+ puts " • Total space = Input space + Auxiliary space"
182
+
183
+ puts "\nExample - Iterative vs Recursive Sum:"
184
+
185
+ ruby_code = <<~RUBY
186
+ # Iterative - O(1) space
187
+ def sum_iterative(n)
188
+ total = 0
189
+ (1..n).each { |i| total += i }
190
+ total
191
+ end
192
+ # Only uses 'total' variable
193
+
194
+ # Recursive - O(n) space
195
+ def sum_recursive(n)
196
+ return 0 if n == 0
197
+ n + sum_recursive(n - 1)
198
+ end
199
+ # Each call adds to call stack
200
+ # n recursive calls = O(n) space
201
+ RUBY
202
+
203
+ cpp_code = <<~CPP
204
+ // Iterative - O(1) space
205
+ int sumIterative(int n) {
206
+ int total = 0;
207
+ for(int i = 1; i <= n; i++)
208
+ total += i;
209
+ return total;
210
+ }
211
+
212
+ // Recursive - O(n) space
213
+ int sumRecursive(int n) {
214
+ if(n == 0) return 0;
215
+ return n + sumRecursive(n - 1);
216
+ }
217
+ // Call stack grows to depth n
218
+ CPP
219
+
220
+ Visualizer.print_comparison(ruby_code, cpp_code,
221
+ "Recursion uses call stack memory. Each recursive call adds a stack frame.")
222
+
223
+ # Important Notes
224
+ notes = [
225
+ "Big O describes worst-case scenario (upper bound)",
226
+ "Drop constants: O(2n) → O(n), O(n/2) → O(n)",
227
+ "Drop non-dominant terms: O(n² + n) → O(n²)",
228
+ "Different inputs use different variables: O(a + b) not O(n)",
229
+ "Best case (Ω), Average case (Θ), Worst case (O)",
230
+ "Space complexity includes call stack for recursion",
231
+ "In-place algorithms use O(1) extra space",
232
+ "Amortized analysis considers average over sequence of operations"
233
+ ]
234
+ NotesManager.print_notes("Complexity Analysis", notes)
235
+
236
+ # Key Points
237
+ key_points = [
238
+ "O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ) < O(n!)",
239
+ "Nested loops usually mean multiplication: O(n × m)",
240
+ "Consecutive loops mean addition: O(n + m)",
241
+ "Recursive algorithms often have O(n) space due to call stack",
242
+ "Binary search is O(log n) because it halves the problem",
243
+ "Sorting comparison-based algorithms can't be better than O(n log n)"
244
+ ]
245
+ NotesManager.print_key_points(key_points)
246
+
247
+ # Practice Problems
248
+ problems = [
249
+ { title: "Analyze nested loop complexity", difficulty: "Easy",
250
+ description: "Given code with 3 nested loops, determine time complexity" },
251
+ { title: "Space complexity of merge sort", difficulty: "Medium",
252
+ description: "Calculate auxiliary space used by merge sort algorithm" },
253
+ { title: "Optimize O(n²) to O(n)", difficulty: "Medium",
254
+ description: "Use hash table to improve two-sum problem from O(n²) to O(n)" }
255
+ ]
256
+ NotesManager.print_practice_problems(problems)
257
+
258
+ puts "\n💡 PRO TIP:".colorize(:yellow).bold
259
+ puts "Always analyze both time AND space complexity."
260
+ puts "Sometimes you trade space for time (memoization) or vice versa."
261
+ end
262
+ end
263
+ end
264
+ end