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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +323 -0
- data/USAGE.md +359 -0
- data/bin/dsa_visualizer +5 -0
- data/lib/dsa_visualizer/algorithms/dynamic_programming.rb +23 -0
- data/lib/dsa_visualizer/algorithms/graph_algorithms.rb +23 -0
- data/lib/dsa_visualizer/algorithms/greedy.rb +11 -0
- data/lib/dsa_visualizer/algorithms/searching.rb +78 -0
- data/lib/dsa_visualizer/algorithms/sorting.rb +77 -0
- data/lib/dsa_visualizer/algorithms/string_algorithms.rb +11 -0
- data/lib/dsa_visualizer/cli.rb +281 -0
- data/lib/dsa_visualizer/comparator.rb +57 -0
- data/lib/dsa_visualizer/data_structures/array.rb +109 -0
- data/lib/dsa_visualizer/data_structures/binary_tree.rb +104 -0
- data/lib/dsa_visualizer/data_structures/bst.rb +11 -0
- data/lib/dsa_visualizer/data_structures/deque.rb +11 -0
- data/lib/dsa_visualizer/data_structures/doubly_linked_list.rb +11 -0
- data/lib/dsa_visualizer/data_structures/graph.rb +11 -0
- data/lib/dsa_visualizer/data_structures/hash_table.rb +61 -0
- data/lib/dsa_visualizer/data_structures/heap.rb +17 -0
- data/lib/dsa_visualizer/data_structures/linked_list.rb +197 -0
- data/lib/dsa_visualizer/data_structures/priority_queue.rb +11 -0
- data/lib/dsa_visualizer/data_structures/queue.rb +110 -0
- data/lib/dsa_visualizer/data_structures/stack.rb +207 -0
- data/lib/dsa_visualizer/data_structures/string.rb +11 -0
- data/lib/dsa_visualizer/data_structures/trie.rb +11 -0
- data/lib/dsa_visualizer/data_structures/union_find.rb +11 -0
- data/lib/dsa_visualizer/fundamentals/complexity.rb +264 -0
- data/lib/dsa_visualizer/fundamentals/memory.rb +285 -0
- data/lib/dsa_visualizer/fundamentals/pointers.rb +311 -0
- data/lib/dsa_visualizer/fundamentals/recursion.rb +63 -0
- data/lib/dsa_visualizer/memory_tracker.rb +49 -0
- data/lib/dsa_visualizer/notes_manager.rb +85 -0
- data/lib/dsa_visualizer/version.rb +3 -0
- data/lib/dsa_visualizer/visualizer.rb +58 -0
- data/lib/dsa_visualizer.rb +114 -0
- metadata +157 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
module DSAVisualizer
|
|
2
|
+
module Fundamentals
|
|
3
|
+
class Memory
|
|
4
|
+
def self.learn
|
|
5
|
+
Visualizer.print_header("MEMORY MANAGEMENT - How Data Lives in Computer Memory")
|
|
6
|
+
|
|
7
|
+
# Introduction
|
|
8
|
+
puts "\nš UNDERSTANDING COMPUTER MEMORY".colorize(:cyan).bold
|
|
9
|
+
puts "ā" * 80
|
|
10
|
+
puts "Memory is organized in a hierarchy:"
|
|
11
|
+
puts " Registers (fastest, smallest) ā Cache ā RAM ā Disk (slowest, largest)"
|
|
12
|
+
puts "\nWe primarily work with RAM (Random Access Memory)"
|
|
13
|
+
|
|
14
|
+
# Memory Layout
|
|
15
|
+
Visualizer.print_section("1. Memory Layout of a Program")
|
|
16
|
+
puts "\nāāāāāāāāāāāāāāāāāāāāāāā High Address"
|
|
17
|
+
puts "ā Command Line Args ā"
|
|
18
|
+
puts "āāāāāāāāāāāāāāāāāāāāāāā¤"
|
|
19
|
+
puts "ā Stack ā ā Grows downward"
|
|
20
|
+
puts "ā (Local variables) ā Function calls"
|
|
21
|
+
puts "ā (Function calls) ā Automatic cleanup"
|
|
22
|
+
puts "āāāāāāāāāāāāāāāāāāāāāāā¤"
|
|
23
|
+
puts "ā ā ā"
|
|
24
|
+
puts "ā ā"
|
|
25
|
+
puts "ā ā ā"
|
|
26
|
+
puts "āāāāāāāāāāāāāāāāāāāāāāā¤"
|
|
27
|
+
puts "ā Heap ā ā Grows upward"
|
|
28
|
+
puts "ā (Dynamic memory) ā Manual/GC managed"
|
|
29
|
+
puts "ā (Objects) ā Flexible size"
|
|
30
|
+
puts "āāāāāāāāāāāāāāāāāāāāāāā¤"
|
|
31
|
+
puts "ā BSS (Uninitialized)ā"
|
|
32
|
+
puts "āāāāāāāāāāāāāāāāāāāāāāā¤"
|
|
33
|
+
puts "ā Data (Initialized)ā"
|
|
34
|
+
puts "āāāāāāāāāāāāāāāāāāāāāāā¤"
|
|
35
|
+
puts "ā Text (Code) ā"
|
|
36
|
+
puts "āāāāāāāāāāāāāāāāāāāāāāā Low Address"
|
|
37
|
+
|
|
38
|
+
# Stack vs Heap
|
|
39
|
+
Visualizer.print_section("2. Stack vs Heap Memory")
|
|
40
|
+
|
|
41
|
+
puts "\nšµ STACK MEMORY:".colorize(:blue).bold
|
|
42
|
+
puts " ⢠Fast allocation/deallocation (just move stack pointer)"
|
|
43
|
+
puts " ⢠Limited size (typically 1-8 MB)"
|
|
44
|
+
puts " ⢠Automatic cleanup (LIFO - Last In First Out)"
|
|
45
|
+
puts " ⢠Stores: local variables, function parameters, return addresses"
|
|
46
|
+
puts " ⢠Contiguous memory"
|
|
47
|
+
|
|
48
|
+
puts "\nš¢ HEAP MEMORY:".colorize(:green).bold
|
|
49
|
+
puts " ⢠Slower allocation (must find free block)"
|
|
50
|
+
puts " ⢠Large size (limited by system RAM)"
|
|
51
|
+
puts " ⢠Manual management (C++) or Garbage Collection (Ruby)"
|
|
52
|
+
puts " ⢠Stores: dynamically allocated objects, large data structures"
|
|
53
|
+
puts " ⢠Fragmented memory possible"
|
|
54
|
+
|
|
55
|
+
# Ruby vs C++ Memory Management
|
|
56
|
+
Visualizer.print_section("3. Ruby vs C++ Memory Management")
|
|
57
|
+
|
|
58
|
+
ruby_code = <<~RUBY
|
|
59
|
+
# Ruby - Automatic Memory Management
|
|
60
|
+
def create_array
|
|
61
|
+
arr = [1, 2, 3, 4, 5] # Heap allocated
|
|
62
|
+
# Ruby's GC will clean up automatically
|
|
63
|
+
arr
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
result = create_array
|
|
67
|
+
# 'arr' in function is gone (stack)
|
|
68
|
+
# but array object still exists (heap)
|
|
69
|
+
# GC tracks references
|
|
70
|
+
|
|
71
|
+
# When no references exist:
|
|
72
|
+
result = nil # Array becomes eligible for GC
|
|
73
|
+
RUBY
|
|
74
|
+
|
|
75
|
+
cpp_code = <<~CPP
|
|
76
|
+
// C++ - Manual Memory Management
|
|
77
|
+
int* createArray() {
|
|
78
|
+
int* arr = new int[5]{1,2,3,4,5}; // Heap
|
|
79
|
+
// Must manually delete!
|
|
80
|
+
return arr;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
int* result = createArray();
|
|
84
|
+
// Use the array...
|
|
85
|
+
|
|
86
|
+
delete[] result; // MUST free memory!
|
|
87
|
+
result = nullptr; // Good practice
|
|
88
|
+
|
|
89
|
+
// Forgetting delete[] = memory leak!
|
|
90
|
+
CPP
|
|
91
|
+
|
|
92
|
+
Visualizer.print_comparison(ruby_code, cpp_code,
|
|
93
|
+
"Ruby uses Garbage Collection (automatic). C++ requires manual memory management with new/delete.")
|
|
94
|
+
|
|
95
|
+
# Stack Example
|
|
96
|
+
Visualizer.print_section("4. Stack Memory Example")
|
|
97
|
+
|
|
98
|
+
ruby_code = <<~RUBY
|
|
99
|
+
def factorial(n)
|
|
100
|
+
return 1 if n <= 1
|
|
101
|
+
n * factorial(n - 1)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
factorial(4)
|
|
105
|
+
|
|
106
|
+
# Call Stack:
|
|
107
|
+
# factorial(4) ā calls factorial(3)
|
|
108
|
+
# factorial(3) ā calls factorial(2)
|
|
109
|
+
# factorial(2) ā calls factorial(1)
|
|
110
|
+
# factorial(1) ā returns 1
|
|
111
|
+
# Then unwinds: 2*1, 3*2, 4*6 = 24
|
|
112
|
+
RUBY
|
|
113
|
+
|
|
114
|
+
cpp_code = <<~CPP
|
|
115
|
+
int factorial(int n) {
|
|
116
|
+
if(n <= 1) return 1;
|
|
117
|
+
return n * factorial(n - 1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
factorial(4);
|
|
121
|
+
|
|
122
|
+
// Stack frames:
|
|
123
|
+
// [factorial(4)] ā top
|
|
124
|
+
// [factorial(3)]
|
|
125
|
+
// [factorial(2)]
|
|
126
|
+
// [factorial(1)] ā base
|
|
127
|
+
// Each frame stores: n, return address
|
|
128
|
+
CPP
|
|
129
|
+
|
|
130
|
+
puts "\nStack Frame Visualization:"
|
|
131
|
+
puts "āāāāāāāāāāāāāāāāāāāā"
|
|
132
|
+
puts "ā factorial(4) ā ā Current"
|
|
133
|
+
puts "ā n=4, ret_addr ā"
|
|
134
|
+
puts "āāāāāāāāāāāāāāāāāāāā¤"
|
|
135
|
+
puts "ā factorial(3) ā"
|
|
136
|
+
puts "ā n=3, ret_addr ā"
|
|
137
|
+
puts "āāāāāāāāāāāāāāāāāāāā¤"
|
|
138
|
+
puts "ā factorial(2) ā"
|
|
139
|
+
puts "ā n=2, ret_addr ā"
|
|
140
|
+
puts "āāāāāāāāāāāāāāāāāāāā¤"
|
|
141
|
+
puts "ā factorial(1) ā"
|
|
142
|
+
puts "ā n=1, ret_addr ā"
|
|
143
|
+
puts "āāāāāāāāāāāāāāāāāāāā"
|
|
144
|
+
|
|
145
|
+
Visualizer.print_comparison(ruby_code, cpp_code,
|
|
146
|
+
"Both use call stack for recursion. Deep recursion can cause stack overflow!")
|
|
147
|
+
|
|
148
|
+
# Heap Example
|
|
149
|
+
Visualizer.print_section("5. Heap Memory Example")
|
|
150
|
+
|
|
151
|
+
ruby_code = <<~RUBY
|
|
152
|
+
class Node
|
|
153
|
+
attr_accessor :data, :next
|
|
154
|
+
def initialize(data)
|
|
155
|
+
@data = data
|
|
156
|
+
@next = nil
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Creating linked list
|
|
161
|
+
head = Node.new(10) # Heap allocated
|
|
162
|
+
head.next = Node.new(20) # Heap allocated
|
|
163
|
+
head.next.next = Node.new(30)
|
|
164
|
+
|
|
165
|
+
# All Node objects on heap
|
|
166
|
+
# GC tracks references
|
|
167
|
+
# When head = nil, all become eligible for GC
|
|
168
|
+
RUBY
|
|
169
|
+
|
|
170
|
+
cpp_code = <<~CPP
|
|
171
|
+
struct Node {
|
|
172
|
+
int data;
|
|
173
|
+
Node* next;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Creating linked list
|
|
177
|
+
Node* head = new Node{10, nullptr};
|
|
178
|
+
head->next = new Node{20, nullptr};
|
|
179
|
+
head->next->next = new Node{30, nullptr};
|
|
180
|
+
|
|
181
|
+
// Must manually free each node!
|
|
182
|
+
Node* temp;
|
|
183
|
+
while(head) {
|
|
184
|
+
temp = head;
|
|
185
|
+
head = head->next;
|
|
186
|
+
delete temp; // Free each node
|
|
187
|
+
}
|
|
188
|
+
CPP
|
|
189
|
+
|
|
190
|
+
Visualizer.print_comparison(ruby_code, cpp_code,
|
|
191
|
+
"Heap allows dynamic sizing. Ruby's GC vs C++'s manual delete. Memory leaks happen when you forget to free!")
|
|
192
|
+
|
|
193
|
+
# Memory Leaks
|
|
194
|
+
Visualizer.print_section("6. Memory Leaks")
|
|
195
|
+
|
|
196
|
+
puts "\nā Common Memory Leak in C++:"
|
|
197
|
+
puts "```cpp"
|
|
198
|
+
puts "void leak() {"
|
|
199
|
+
puts " int* arr = new int[1000];"
|
|
200
|
+
puts " // Forgot to delete[]!"
|
|
201
|
+
puts " // Memory is lost forever (until program ends)"
|
|
202
|
+
puts "}"
|
|
203
|
+
puts "```"
|
|
204
|
+
|
|
205
|
+
puts "\nā
Proper Memory Management:"
|
|
206
|
+
puts "```cpp"
|
|
207
|
+
puts "void noLeak() {"
|
|
208
|
+
puts " int* arr = new int[1000];"
|
|
209
|
+
puts " // Use the array..."
|
|
210
|
+
puts " delete[] arr; // Clean up!"
|
|
211
|
+
puts "}"
|
|
212
|
+
puts "```"
|
|
213
|
+
|
|
214
|
+
puts "\nā
Modern C++ (RAII - Resource Acquisition Is Initialization):"
|
|
215
|
+
puts "```cpp"
|
|
216
|
+
puts "void modern() {"
|
|
217
|
+
puts " std::vector<int> arr(1000); // Automatic cleanup!"
|
|
218
|
+
puts " std::unique_ptr<int[]> ptr(new int[1000]);"
|
|
219
|
+
puts " // Automatically deleted when out of scope"
|
|
220
|
+
puts "}"
|
|
221
|
+
puts "```"
|
|
222
|
+
|
|
223
|
+
# Cache Locality
|
|
224
|
+
Visualizer.print_section("7. Cache Locality & Performance")
|
|
225
|
+
|
|
226
|
+
puts "\nCPU Cache is much faster than RAM:"
|
|
227
|
+
puts " L1 Cache: ~1 ns"
|
|
228
|
+
puts " L2 Cache: ~4 ns"
|
|
229
|
+
puts " L3 Cache: ~10 ns"
|
|
230
|
+
puts " RAM: ~100 ns"
|
|
231
|
+
|
|
232
|
+
puts "\nš Good Cache Locality (Array):"
|
|
233
|
+
puts " [1][2][3][4][5] ā Contiguous memory"
|
|
234
|
+
puts " CPU loads chunk into cache"
|
|
235
|
+
puts " Accessing next element is fast (already in cache)"
|
|
236
|
+
|
|
237
|
+
puts "\nš Poor Cache Locality (Linked List):"
|
|
238
|
+
puts " [1]ā...ā[2]ā...ā[3]ā...ā[4] ā Scattered memory"
|
|
239
|
+
puts " Each access might miss cache"
|
|
240
|
+
puts " Must fetch from RAM (100Ć slower!)"
|
|
241
|
+
|
|
242
|
+
# Important Notes
|
|
243
|
+
notes = [
|
|
244
|
+
"Stack: Fast, automatic, limited size, LIFO structure",
|
|
245
|
+
"Heap: Flexible, larger, requires management (manual or GC)",
|
|
246
|
+
"Ruby uses Garbage Collection - automatic but has overhead",
|
|
247
|
+
"C++ requires manual memory management - fast but error-prone",
|
|
248
|
+
"Modern C++ uses RAII and smart pointers for safety",
|
|
249
|
+
"Stack overflow happens with deep recursion or large local arrays",
|
|
250
|
+
"Memory leaks occur when heap memory isn't freed",
|
|
251
|
+
"Cache locality matters - contiguous memory is faster",
|
|
252
|
+
"Pointer size: 8 bytes on 64-bit systems, 4 bytes on 32-bit"
|
|
253
|
+
]
|
|
254
|
+
NotesManager.print_notes("Memory Management", notes)
|
|
255
|
+
|
|
256
|
+
# Key Points
|
|
257
|
+
key_points = [
|
|
258
|
+
"Stack is for local variables, Heap is for dynamic allocation",
|
|
259
|
+
"Ruby's GC trades performance for safety and convenience",
|
|
260
|
+
"C++ gives control but requires discipline (use smart pointers!)",
|
|
261
|
+
"Arrays have better cache locality than linked lists",
|
|
262
|
+
"Deep recursion uses stack memory - can cause overflow",
|
|
263
|
+
"Always free what you allocate in C++ (or use RAII)",
|
|
264
|
+
"Memory alignment affects performance (CPU reads in chunks)"
|
|
265
|
+
]
|
|
266
|
+
NotesManager.print_key_points(key_points)
|
|
267
|
+
|
|
268
|
+
# Common Mistakes
|
|
269
|
+
mistakes = [
|
|
270
|
+
"Forgetting to delete[] in C++ (memory leak)",
|
|
271
|
+
"Deleting same memory twice (double free - crash!)",
|
|
272
|
+
"Using memory after freeing it (dangling pointer)",
|
|
273
|
+
"Creating too deep recursion (stack overflow)",
|
|
274
|
+
"Allocating huge arrays on stack (use heap instead)",
|
|
275
|
+
"Not considering cache locality in performance-critical code"
|
|
276
|
+
]
|
|
277
|
+
NotesManager.print_common_mistakes(mistakes)
|
|
278
|
+
|
|
279
|
+
puts "\nš” PRO TIP:".colorize(:yellow).bold
|
|
280
|
+
puts "In C++, prefer std::vector over raw arrays, and std::unique_ptr/shared_ptr"
|
|
281
|
+
puts "over raw pointers. They provide automatic memory management!"
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
module DSAVisualizer
|
|
2
|
+
module Fundamentals
|
|
3
|
+
class Pointers
|
|
4
|
+
def self.learn
|
|
5
|
+
Visualizer.print_header("POINTERS & REFERENCES - The Foundation of Data Structures")
|
|
6
|
+
|
|
7
|
+
puts "\nš WHAT ARE POINTERS?".colorize(:cyan).bold
|
|
8
|
+
puts "ā" * 80
|
|
9
|
+
puts "A pointer is a variable that stores a memory address."
|
|
10
|
+
puts "It 'points to' another variable's location in memory."
|
|
11
|
+
|
|
12
|
+
# Basic Concept
|
|
13
|
+
Visualizer.print_section("1. Pointer Basics")
|
|
14
|
+
|
|
15
|
+
puts "\nMemory Visualization:"
|
|
16
|
+
puts "āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāā"
|
|
17
|
+
puts "ā Address ā Name ā Value ā"
|
|
18
|
+
puts "āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāā¤"
|
|
19
|
+
puts "ā 0x1000 ā x ā 42 ā ā Regular variable"
|
|
20
|
+
puts "ā 0x1004 ā ptr ā 0x1000 ā ā Pointer (stores address of x)"
|
|
21
|
+
puts "āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāā"
|
|
22
|
+
|
|
23
|
+
# Ruby vs C++
|
|
24
|
+
Visualizer.print_section("2. Ruby References vs C++ Pointers")
|
|
25
|
+
|
|
26
|
+
ruby_code = <<~RUBY
|
|
27
|
+
# Ruby - No explicit pointers
|
|
28
|
+
# Everything is an object reference
|
|
29
|
+
|
|
30
|
+
x = 42
|
|
31
|
+
y = x # y references same object
|
|
32
|
+
|
|
33
|
+
puts x.object_id # Memory address
|
|
34
|
+
puts y.object_id # Same address!
|
|
35
|
+
|
|
36
|
+
# For immutable objects (numbers, symbols):
|
|
37
|
+
x = 43 # Creates NEW object
|
|
38
|
+
# y still references old object (42)
|
|
39
|
+
|
|
40
|
+
# For mutable objects (arrays, hashes):
|
|
41
|
+
arr1 = [1, 2, 3]
|
|
42
|
+
arr2 = arr1 # Both reference same array
|
|
43
|
+
arr2 << 4 # Modifies shared array
|
|
44
|
+
puts arr1 # [1, 2, 3, 4]
|
|
45
|
+
RUBY
|
|
46
|
+
|
|
47
|
+
cpp_code = <<~CPP
|
|
48
|
+
// C++ - Explicit pointers
|
|
49
|
+
|
|
50
|
+
int x = 42;
|
|
51
|
+
int* ptr = &x; // ptr stores address of x
|
|
52
|
+
|
|
53
|
+
cout << x; // 42 (value)
|
|
54
|
+
cout << &x; // 0x1000 (address)
|
|
55
|
+
cout << ptr; // 0x1000 (address stored in ptr)
|
|
56
|
+
cout << *ptr; // 42 (dereference - get value)
|
|
57
|
+
|
|
58
|
+
*ptr = 43; // Modify x through pointer
|
|
59
|
+
cout << x; // 43 (x changed!)
|
|
60
|
+
|
|
61
|
+
// Pointer arithmetic
|
|
62
|
+
int arr[] = {1, 2, 3};
|
|
63
|
+
int* p = arr; // Points to first element
|
|
64
|
+
p++; // Now points to second element
|
|
65
|
+
cout << *p; // 2
|
|
66
|
+
CPP
|
|
67
|
+
|
|
68
|
+
Visualizer.print_comparison(ruby_code, cpp_code,
|
|
69
|
+
"Ruby uses implicit references (managed by GC). C++ uses explicit pointers (manual control).")
|
|
70
|
+
|
|
71
|
+
# Pointer Operations
|
|
72
|
+
Visualizer.print_section("3. C++ Pointer Operations")
|
|
73
|
+
|
|
74
|
+
puts "\nš¹ Declaration:"
|
|
75
|
+
puts " int* ptr; // Pointer to int"
|
|
76
|
+
puts " int *ptr; // Same thing (style choice)"
|
|
77
|
+
puts " int* p1, p2; // p1 is pointer, p2 is int! (confusing)"
|
|
78
|
+
puts " int *p1, *p2; // Both are pointers"
|
|
79
|
+
|
|
80
|
+
puts "\nš¹ Address-of operator (&):"
|
|
81
|
+
puts " int x = 10;"
|
|
82
|
+
puts " int* ptr = &x; // Get address of x"
|
|
83
|
+
|
|
84
|
+
puts "\nš¹ Dereference operator (*):"
|
|
85
|
+
puts " int value = *ptr; // Get value at address"
|
|
86
|
+
puts " *ptr = 20; // Set value at address"
|
|
87
|
+
|
|
88
|
+
puts "\nš¹ Null pointer:"
|
|
89
|
+
puts " int* ptr = nullptr; // C++11 (preferred)"
|
|
90
|
+
puts " int* ptr = NULL; // Old style"
|
|
91
|
+
puts " int* ptr = 0; // Also works"
|
|
92
|
+
|
|
93
|
+
# Pointer Arithmetic
|
|
94
|
+
Visualizer.print_section("4. Pointer Arithmetic")
|
|
95
|
+
|
|
96
|
+
puts "\nArray in memory:"
|
|
97
|
+
puts "āāāāāā¬āāāāā¬āāāāā¬āāāāā¬āāāāā"
|
|
98
|
+
puts "ā 10 ā 20 ā 30 ā 40 ā 50 ā"
|
|
99
|
+
puts "āāāāāā“āāāāā“āāāāā“āāāāā“āāāāā"
|
|
100
|
+
puts "0x100 0x104 0x108 0x10C 0x110 (each int = 4 bytes)"
|
|
101
|
+
|
|
102
|
+
cpp_code = <<~CPP
|
|
103
|
+
int arr[] = {10, 20, 30, 40, 50};
|
|
104
|
+
int* ptr = arr; // Points to arr[0]
|
|
105
|
+
|
|
106
|
+
cout << *ptr; // 10
|
|
107
|
+
cout << *(ptr+1); // 20 (moves 4 bytes, not 1!)
|
|
108
|
+
cout << *(ptr+2); // 30
|
|
109
|
+
|
|
110
|
+
ptr++; // Now points to arr[1]
|
|
111
|
+
cout << *ptr; // 20
|
|
112
|
+
|
|
113
|
+
// Array indexing is pointer arithmetic!
|
|
114
|
+
arr[2] == *(arr + 2) // TRUE!
|
|
115
|
+
CPP
|
|
116
|
+
|
|
117
|
+
puts "\n#{cpp_code}"
|
|
118
|
+
puts "\nš” Key Insight: ptr+1 doesn't add 1 byte, it adds sizeof(int) bytes!"
|
|
119
|
+
puts " The compiler knows the type and adjusts automatically."
|
|
120
|
+
|
|
121
|
+
# Pointers and Arrays
|
|
122
|
+
Visualizer.print_section("5. Pointers and Arrays")
|
|
123
|
+
|
|
124
|
+
cpp_code = <<~CPP
|
|
125
|
+
int arr[5] = {1, 2, 3, 4, 5};
|
|
126
|
+
int* ptr = arr; // Array name is pointer to first element
|
|
127
|
+
|
|
128
|
+
// These are equivalent:
|
|
129
|
+
arr[0] == *arr == *(arr + 0)
|
|
130
|
+
arr[1] == *(arr+1) == ptr[1]
|
|
131
|
+
arr[2] == *(ptr+2) == ptr[2]
|
|
132
|
+
|
|
133
|
+
// Passing array to function:
|
|
134
|
+
void process(int* arr, int size) {
|
|
135
|
+
// arr is just a pointer!
|
|
136
|
+
// Size information is lost
|
|
137
|
+
}
|
|
138
|
+
CPP
|
|
139
|
+
|
|
140
|
+
puts "\n#{cpp_code}"
|
|
141
|
+
|
|
142
|
+
# Pointers in Data Structures
|
|
143
|
+
Visualizer.print_section("6. Pointers in Data Structures")
|
|
144
|
+
|
|
145
|
+
puts "\nš Linked List Node:"
|
|
146
|
+
cpp_code = <<~CPP
|
|
147
|
+
struct Node {
|
|
148
|
+
int data;
|
|
149
|
+
Node* next; // Pointer to next node
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
Node* head = new Node{10, nullptr};
|
|
153
|
+
head->next = new Node{20, nullptr};
|
|
154
|
+
head->next->next = new Node{30, nullptr};
|
|
155
|
+
|
|
156
|
+
// Traversal:
|
|
157
|
+
Node* current = head;
|
|
158
|
+
while(current != nullptr) {
|
|
159
|
+
cout << current->data;
|
|
160
|
+
current = current->next; // Move to next node
|
|
161
|
+
}
|
|
162
|
+
CPP
|
|
163
|
+
puts cpp_code
|
|
164
|
+
|
|
165
|
+
puts "\nš³ Binary Tree Node:"
|
|
166
|
+
cpp_code = <<~CPP
|
|
167
|
+
struct TreeNode {
|
|
168
|
+
int data;
|
|
169
|
+
TreeNode* left; // Pointer to left child
|
|
170
|
+
TreeNode* right; // Pointer to right child
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
TreeNode* root = new TreeNode{50, nullptr, nullptr};
|
|
174
|
+
root->left = new TreeNode{30, nullptr, nullptr};
|
|
175
|
+
root->right = new TreeNode{70, nullptr, nullptr};
|
|
176
|
+
CPP
|
|
177
|
+
puts cpp_code
|
|
178
|
+
|
|
179
|
+
# Ruby Equivalent
|
|
180
|
+
Visualizer.print_section("7. Ruby's Object References")
|
|
181
|
+
|
|
182
|
+
ruby_code = <<~RUBY
|
|
183
|
+
# Ruby doesn't have pointers, but references work similarly
|
|
184
|
+
|
|
185
|
+
class Node
|
|
186
|
+
attr_accessor :data, :next
|
|
187
|
+
def initialize(data)
|
|
188
|
+
@data = data
|
|
189
|
+
@next = nil # Reference to next node
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
head = Node.new(10)
|
|
194
|
+
head.next = Node.new(20)
|
|
195
|
+
head.next.next = Node.new(30)
|
|
196
|
+
|
|
197
|
+
# Traversal:
|
|
198
|
+
current = head
|
|
199
|
+
while current
|
|
200
|
+
puts current.data
|
|
201
|
+
current = current.next # Follow reference
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Key difference: No manual memory management!
|
|
205
|
+
# GC handles cleanup automatically
|
|
206
|
+
RUBY
|
|
207
|
+
|
|
208
|
+
puts ruby_code
|
|
209
|
+
|
|
210
|
+
# Common Pointer Errors
|
|
211
|
+
Visualizer.print_section("8. Common Pointer Errors (C++)")
|
|
212
|
+
|
|
213
|
+
puts "\nā 1. Dangling Pointer:"
|
|
214
|
+
puts "```cpp"
|
|
215
|
+
puts "int* ptr = new int(42);"
|
|
216
|
+
puts "delete ptr;"
|
|
217
|
+
puts "cout << *ptr; // ERROR! Memory freed, pointer still points there"
|
|
218
|
+
puts "ptr = nullptr; // Good practice after delete"
|
|
219
|
+
puts "```"
|
|
220
|
+
|
|
221
|
+
puts "\nā 2. Memory Leak:"
|
|
222
|
+
puts "```cpp"
|
|
223
|
+
puts "int* ptr = new int(42);"
|
|
224
|
+
puts "ptr = new int(100); // Lost reference to first allocation!"
|
|
225
|
+
puts "// First allocation leaked - can't free it now"
|
|
226
|
+
puts "```"
|
|
227
|
+
|
|
228
|
+
puts "\nā 3. Null Pointer Dereference:"
|
|
229
|
+
puts "```cpp"
|
|
230
|
+
puts "int* ptr = nullptr;"
|
|
231
|
+
puts "cout << *ptr; // CRASH! Can't dereference null"
|
|
232
|
+
puts "// Always check: if(ptr != nullptr) { *ptr = 10; }"
|
|
233
|
+
puts "```"
|
|
234
|
+
|
|
235
|
+
puts "\nā 4. Wild Pointer:"
|
|
236
|
+
puts "```cpp"
|
|
237
|
+
puts "int* ptr; // Uninitialized! Contains garbage"
|
|
238
|
+
puts "*ptr = 42; // CRASH! Writing to random memory"
|
|
239
|
+
puts "// Always initialize: int* ptr = nullptr;"
|
|
240
|
+
puts "```"
|
|
241
|
+
|
|
242
|
+
# Smart Pointers
|
|
243
|
+
Visualizer.print_section("9. Modern C++ Smart Pointers")
|
|
244
|
+
|
|
245
|
+
puts "\nā
Smart pointers provide automatic memory management:"
|
|
246
|
+
|
|
247
|
+
cpp_code = <<~CPP
|
|
248
|
+
#include <memory>
|
|
249
|
+
|
|
250
|
+
// unique_ptr - Single ownership
|
|
251
|
+
std::unique_ptr<int> ptr1(new int(42));
|
|
252
|
+
// or better:
|
|
253
|
+
auto ptr1 = std::make_unique<int>(42);
|
|
254
|
+
// Automatically deleted when out of scope!
|
|
255
|
+
|
|
256
|
+
// shared_ptr - Shared ownership (reference counting)
|
|
257
|
+
std::shared_ptr<int> ptr2 = std::make_shared<int>(42);
|
|
258
|
+
std::shared_ptr<int> ptr3 = ptr2; // Both own it
|
|
259
|
+
// Deleted when last shared_ptr goes out of scope
|
|
260
|
+
|
|
261
|
+
// weak_ptr - Non-owning reference (breaks cycles)
|
|
262
|
+
std::weak_ptr<int> ptr4 = ptr2;
|
|
263
|
+
// Doesn't prevent deletion
|
|
264
|
+
CPP
|
|
265
|
+
|
|
266
|
+
puts cpp_code
|
|
267
|
+
|
|
268
|
+
# Notes
|
|
269
|
+
notes = [
|
|
270
|
+
"Pointer stores memory address, not the value itself",
|
|
271
|
+
"& (address-of) gets address, * (dereference) gets value",
|
|
272
|
+
"Ruby uses implicit references, C++ uses explicit pointers",
|
|
273
|
+
"Array name is pointer to first element in C++",
|
|
274
|
+
"Pointer arithmetic: ptr+1 moves by sizeof(type) bytes",
|
|
275
|
+
"Always initialize pointers (nullptr in C++)",
|
|
276
|
+
"Check for nullptr before dereferencing",
|
|
277
|
+
"Use smart pointers in modern C++ (unique_ptr, shared_ptr)",
|
|
278
|
+
"Ruby's GC eliminates pointer management complexity"
|
|
279
|
+
]
|
|
280
|
+
NotesManager.print_notes("Pointers & References", notes)
|
|
281
|
+
|
|
282
|
+
# Key Points
|
|
283
|
+
key_points = [
|
|
284
|
+
"Pointers enable dynamic data structures (linked lists, trees, graphs)",
|
|
285
|
+
"Ruby abstracts pointers as object references with automatic GC",
|
|
286
|
+
"C++ pointers give control but require careful management",
|
|
287
|
+
"Null pointer dereference is a common crash cause",
|
|
288
|
+
"Memory leaks happen when you lose pointer to allocated memory",
|
|
289
|
+
"Smart pointers (C++11+) provide safety with RAII",
|
|
290
|
+
"Understanding pointers is crucial for DSA implementation"
|
|
291
|
+
]
|
|
292
|
+
NotesManager.print_key_points(key_points)
|
|
293
|
+
|
|
294
|
+
# Common Mistakes
|
|
295
|
+
mistakes = [
|
|
296
|
+
"Dereferencing null or uninitialized pointer (crash!)",
|
|
297
|
+
"Using pointer after freeing memory (dangling pointer)",
|
|
298
|
+
"Forgetting to free allocated memory (memory leak)",
|
|
299
|
+
"Confusing pointer and value (missing * or &)",
|
|
300
|
+
"Array out of bounds with pointer arithmetic",
|
|
301
|
+
"Double-freeing same memory (crash!)"
|
|
302
|
+
]
|
|
303
|
+
NotesManager.print_common_mistakes(mistakes)
|
|
304
|
+
|
|
305
|
+
puts "\nš” PRO TIP:".colorize(:yellow).bold
|
|
306
|
+
puts "In C++, prefer references (&) for function parameters when you don't need"
|
|
307
|
+
puts "pointer arithmetic or reassignment. Use smart pointers for ownership."
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module DSAVisualizer
|
|
2
|
+
module Fundamentals
|
|
3
|
+
class Recursion
|
|
4
|
+
def self.learn
|
|
5
|
+
Visualizer.print_header("RECURSION - Function Calling Itself")
|
|
6
|
+
|
|
7
|
+
puts "\nš WHAT IS RECURSION?".colorize(:cyan).bold
|
|
8
|
+
puts "ā" * 80
|
|
9
|
+
puts "Recursion is when a function calls itself to solve a problem"
|
|
10
|
+
puts "by breaking it down into smaller subproblems."
|
|
11
|
+
|
|
12
|
+
Visualizer.print_section("1. Basic Recursion Example")
|
|
13
|
+
|
|
14
|
+
ruby_code = <<~RUBY
|
|
15
|
+
def factorial(n)
|
|
16
|
+
return 1 if n <= 1 # Base case
|
|
17
|
+
n * factorial(n - 1) # Recursive case
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
factorial(5)
|
|
21
|
+
# 5 * factorial(4)
|
|
22
|
+
# 5 * 4 * factorial(3)
|
|
23
|
+
# 5 * 4 * 3 * factorial(2)
|
|
24
|
+
# 5 * 4 * 3 * 2 * factorial(1)
|
|
25
|
+
# 5 * 4 * 3 * 2 * 1 = 120
|
|
26
|
+
RUBY
|
|
27
|
+
|
|
28
|
+
cpp_code = <<~CPP
|
|
29
|
+
int factorial(int n) {
|
|
30
|
+
if(n <= 1) return 1; // Base case
|
|
31
|
+
return n * factorial(n - 1); // Recursive
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
factorial(5);
|
|
35
|
+
// Call stack grows then unwinds
|
|
36
|
+
// Space: O(n) due to call stack
|
|
37
|
+
CPP
|
|
38
|
+
|
|
39
|
+
Visualizer.print_comparison(ruby_code, cpp_code,
|
|
40
|
+
"Both use call stack. Each recursive call adds a stack frame. Must have base case to stop!")
|
|
41
|
+
|
|
42
|
+
notes = [
|
|
43
|
+
"Every recursion needs a base case (stopping condition)",
|
|
44
|
+
"Recursive case must progress toward base case",
|
|
45
|
+
"Space complexity: O(n) for call stack depth n",
|
|
46
|
+
"Can cause stack overflow if too deep",
|
|
47
|
+
"Some recursions can be optimized with tail call optimization",
|
|
48
|
+
"Iterative solutions often more efficient (no stack overhead)"
|
|
49
|
+
]
|
|
50
|
+
NotesManager.print_notes("Recursion", notes)
|
|
51
|
+
|
|
52
|
+
key_points = [
|
|
53
|
+
"Recursion = function calling itself",
|
|
54
|
+
"Base case prevents infinite recursion",
|
|
55
|
+
"Call stack stores each recursive call",
|
|
56
|
+
"Useful for tree/graph traversal, divide-and-conquer",
|
|
57
|
+
"Trade-off: elegant code vs stack space"
|
|
58
|
+
]
|
|
59
|
+
NotesManager.print_key_points(key_points)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|