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.
@@ -1,10 +1,269 @@
1
1
  module DSAVisualizer
2
2
  module DataStructures
3
3
  class Trie
4
+ class TrieNode
5
+ attr_accessor :children, :is_end_of_word
6
+
7
+ def initialize
8
+ @children = {}
9
+ @is_end_of_word = false
10
+ end
11
+ end
12
+
4
13
  def self.learn
5
14
  Visualizer.print_header("TRIE - Prefix Tree")
6
- puts "\nšŸ“š Coming soon: String storage and search"
7
- puts "Topics: Insert, search, prefix matching, autocomplete"
15
+
16
+ puts "\nšŸ“– CONCEPT:"
17
+ puts "A Trie (Prefix Tree) is a tree-like data structure for storing strings:"
18
+ puts "• Each node represents a character"
19
+ puts "• Root represents empty string"
20
+ puts "• Path from root to node = prefix"
21
+ puts "• Efficient for prefix-based operations"
22
+
23
+ puts "\nā±ļø TIME COMPLEXITY:"
24
+ puts "• Insert: O(m) where m = word length"
25
+ puts "• Search: O(m)"
26
+ puts "• Prefix search: O(m)"
27
+ puts "• Delete: O(m)"
28
+
29
+ puts "\nšŸ’¾ SPACE COMPLEXITY: O(ALPHABET_SIZE * N * M)"
30
+
31
+ demonstrate_trie
32
+ demonstrate_operations
33
+ show_ruby_vs_cpp
34
+ show_practice_problems
35
+ end
36
+
37
+ def self.demonstrate_trie
38
+ puts "\n" + "="*60
39
+ puts "DEMONSTRATION: Building a Trie"
40
+ puts "="*60
41
+
42
+ trie = TrieImplementation.new
43
+ words = ["cat", "car", "card", "care", "dog", "dodge"]
44
+
45
+ puts "\nInserting words: #{words.join(', ')}"
46
+ words.each { |word| trie.insert(word) }
47
+
48
+ puts "\nšŸ“Š Trie Structure:"
49
+ puts " root"
50
+ puts " / \\"
51
+ puts " c d"
52
+ puts " | |"
53
+ puts " a o"
54
+ puts " / \\ |"
55
+ puts " t r g"
56
+ puts " |\\ |"
57
+ puts " d e e"
58
+ puts " | |"
59
+ puts " e (dodge)"
60
+
61
+ puts "\nšŸ” Searching:"
62
+ ["cat", "can", "card"].each do |word|
63
+ result = trie.search(word)
64
+ puts "Search '#{word}': #{result ? 'āœ“ Found' : 'āœ— Not found'}"
65
+ end
66
+ end
67
+
68
+ def self.demonstrate_operations
69
+ puts "\n" + "="*60
70
+ puts "TRIE OPERATIONS"
71
+ puts "="*60
72
+
73
+ trie = TrieImplementation.new
74
+ ["apple", "app", "application", "apply"].each { |w| trie.insert(w) }
75
+
76
+ puts "\n1ļøāƒ£ PREFIX SEARCH:"
77
+ prefix = "app"
78
+ words = trie.words_with_prefix(prefix)
79
+ puts "Words starting with '#{prefix}': #{words.join(', ')}"
80
+
81
+ puts "\n2ļøāƒ£ AUTOCOMPLETE:"
82
+ puts "Type 'appl'..."
83
+ suggestions = trie.words_with_prefix("appl")
84
+ puts "Suggestions: #{suggestions.join(', ')}"
85
+
86
+ puts "\n3ļøāƒ£ COUNT WORDS:"
87
+ puts "Total words: #{trie.count_words}"
88
+
89
+ puts "\n4ļøāƒ£ LONGEST PREFIX:"
90
+ puts "Longest common prefix: '#{trie.longest_common_prefix}'"
91
+ end
92
+
93
+ def self.show_ruby_vs_cpp
94
+ puts "\n" + "="*60
95
+ puts "RUBY vs C++ IMPLEMENTATION"
96
+ puts "="*60
97
+
98
+ puts "\nšŸ”“ RUBY:"
99
+ puts <<~RUBY
100
+ class TrieNode
101
+ attr_accessor :children, :is_end_of_word
102
+ def initialize
103
+ @children = {}
104
+ @is_end_of_word = false
105
+ end
106
+ end
107
+
108
+ class Trie
109
+ def initialize
110
+ @root = TrieNode.new
111
+ end
112
+
113
+ def insert(word)
114
+ node = @root
115
+ word.each_char do |char|
116
+ node.children[char] ||= TrieNode.new
117
+ node = node.children[char]
118
+ end
119
+ node.is_end_of_word = true
120
+ end
121
+
122
+ def search(word)
123
+ node = @root
124
+ word.each_char do |char|
125
+ return false unless node.children[char]
126
+ node = node.children[char]
127
+ end
128
+ node.is_end_of_word
129
+ end
130
+ end
131
+ RUBY
132
+
133
+ puts "\nšŸ”µ C++:"
134
+ puts <<~CPP
135
+ struct TrieNode {
136
+ unordered_map<char, TrieNode*> children;
137
+ bool isEndOfWord;
138
+ TrieNode() : isEndOfWord(false) {}
139
+ };
140
+
141
+ class Trie {
142
+ TrieNode* root;
143
+ public:
144
+ Trie() { root = new TrieNode(); }
145
+
146
+ void insert(string word) {
147
+ TrieNode* node = root;
148
+ for (char c : word) {
149
+ if (!node->children[c])
150
+ node->children[c] = new TrieNode();
151
+ node = node->children[c];
152
+ }
153
+ node->isEndOfWord = true;
154
+ }
155
+
156
+ bool search(string word) {
157
+ TrieNode* node = root;
158
+ for (char c : word) {
159
+ if (!node->children[c]) return false;
160
+ node = node->children[c];
161
+ }
162
+ return node->isEndOfWord;
163
+ }
164
+ };
165
+ CPP
166
+ end
167
+
168
+ def self.show_practice_problems
169
+ puts "\n" + "="*60
170
+ puts "PRACTICE PROBLEMS"
171
+ puts "="*60
172
+
173
+ problems = [
174
+ "1. Implement autocomplete system",
175
+ "2. Word search in 2D grid",
176
+ "3. Design search autocomplete system",
177
+ "4. Replace words (dictionary)",
178
+ "5. Longest word in dictionary",
179
+ "6. Add and search word (with wildcards)",
180
+ "7. Word break problem",
181
+ "8. Maximum XOR of two numbers"
182
+ ]
183
+
184
+ problems.each { |p| puts p }
185
+
186
+ puts "\nšŸ’” TIP: Trie is perfect for prefix-based searches!"
187
+ puts "šŸ’” TIP: Space-time tradeoff: uses more space for faster search"
188
+ end
189
+
190
+ class TrieImplementation
191
+ def initialize
192
+ @root = TrieNode.new
193
+ end
194
+
195
+ def insert(word)
196
+ node = @root
197
+ word.each_char do |char|
198
+ node.children[char] ||= TrieNode.new
199
+ node = node.children[char]
200
+ end
201
+ node.is_end_of_word = true
202
+ end
203
+
204
+ def search(word)
205
+ node = find_node(word)
206
+ node && node.is_end_of_word
207
+ end
208
+
209
+ def starts_with(prefix)
210
+ !find_node(prefix).nil?
211
+ end
212
+
213
+ def words_with_prefix(prefix)
214
+ node = find_node(prefix)
215
+ return [] unless node
216
+
217
+ words = []
218
+ collect_words(node, prefix, words)
219
+ words
220
+ end
221
+
222
+ def count_words
223
+ count_words_recursive(@root)
224
+ end
225
+
226
+ def longest_common_prefix
227
+ return "" if @root.children.empty?
228
+
229
+ prefix = ""
230
+ node = @root
231
+
232
+ while node.children.size == 1 && !node.is_end_of_word
233
+ char = node.children.keys.first
234
+ prefix += char
235
+ node = node.children[char]
236
+ end
237
+
238
+ prefix
239
+ end
240
+
241
+ private
242
+
243
+ def find_node(word)
244
+ node = @root
245
+ word.each_char do |char|
246
+ return nil unless node.children[char]
247
+ node = node.children[char]
248
+ end
249
+ node
250
+ end
251
+
252
+ def collect_words(node, prefix, words)
253
+ words << prefix if node.is_end_of_word
254
+
255
+ node.children.each do |char, child_node|
256
+ collect_words(child_node, prefix + char, words)
257
+ end
258
+ end
259
+
260
+ def count_words_recursive(node)
261
+ count = node.is_end_of_word ? 1 : 0
262
+ node.children.each_value do |child|
263
+ count += count_words_recursive(child)
264
+ end
265
+ count
266
+ end
8
267
  end
9
268
  end
10
269
  end
@@ -3,8 +3,213 @@ module DSAVisualizer
3
3
  class UnionFind
4
4
  def self.learn
5
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"
6
+
7
+ puts "\nšŸ“– CONCEPT:"
8
+ puts "Union-Find (Disjoint Set Union) tracks connected components:"
9
+ puts "• Find: Determine which set an element belongs to"
10
+ puts "• Union: Merge two sets together"
11
+ puts "• Path compression: Flatten tree during find"
12
+ puts "• Union by rank: Attach smaller tree to larger"
13
+
14
+ puts "\nā±ļø TIME COMPLEXITY:"
15
+ puts "• Find: O(α(n)) ā‰ˆ O(1) amortized"
16
+ puts "• Union: O(α(n)) ā‰ˆ O(1) amortized"
17
+ puts "• α(n) is inverse Ackermann function (very slow growing)"
18
+
19
+ puts "\nšŸ’¾ SPACE COMPLEXITY: O(n)"
20
+
21
+ demonstrate_union_find
22
+ demonstrate_operations
23
+ show_ruby_vs_cpp
24
+ show_practice_problems
25
+ end
26
+
27
+ def self.demonstrate_union_find
28
+ puts "\n" + "="*60
29
+ puts "DEMONSTRATION: Union-Find Operations"
30
+ puts "="*60
31
+
32
+ uf = UnionFindImplementation.new(10)
33
+
34
+ puts "\nInitial state: Each element is its own set"
35
+ puts "Sets: {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}"
36
+
37
+ puts "\nPerforming unions:"
38
+ unions = [[0, 1], [2, 3], [4, 5], [6, 7], [0, 2], [4, 6]]
39
+ unions.each do |a, b|
40
+ uf.union(a, b)
41
+ puts "Union(#{a}, #{b})"
42
+ end
43
+
44
+ puts "\nšŸ“Š Current sets:"
45
+ puts "Set 1: {0, 1, 2, 3}"
46
+ puts "Set 2: {4, 5, 6, 7}"
47
+ puts "Set 3: {8}"
48
+ puts "Set 4: {9}"
49
+
50
+ puts "\nNumber of components: #{uf.count}"
51
+ end
52
+
53
+ def self.demonstrate_operations
54
+ puts "\n" + "="*60
55
+ puts "UNION-FIND OPERATIONS"
56
+ puts "="*60
57
+
58
+ uf = UnionFindImplementation.new(6)
59
+
60
+ puts "\n1ļøāƒ£ FIND OPERATION:"
61
+ puts "Find(0): #{uf.find(0)}"
62
+ puts "Find(3): #{uf.find(3)}"
63
+
64
+ puts "\n2ļøāƒ£ UNION OPERATION:"
65
+ uf.union(0, 1)
66
+ uf.union(2, 3)
67
+ puts "After Union(0,1) and Union(2,3)"
68
+ puts "Connected(0, 1): #{uf.connected?(0, 1)}"
69
+ puts "Connected(0, 2): #{uf.connected?(0, 2)}"
70
+
71
+ puts "\n3ļøāƒ£ CHECK CONNECTIVITY:"
72
+ uf.union(1, 2)
73
+ puts "After Union(1,2)"
74
+ puts "Connected(0, 3): #{uf.connected?(0, 3)}"
75
+
76
+ puts "\n4ļøāƒ£ COUNT COMPONENTS:"
77
+ puts "Number of disjoint sets: #{uf.count}"
78
+ end
79
+
80
+ def self.show_ruby_vs_cpp
81
+ puts "\n" + "="*60
82
+ puts "RUBY vs C++ IMPLEMENTATION"
83
+ puts "="*60
84
+
85
+ puts "\nšŸ”“ RUBY:"
86
+ puts <<~RUBY
87
+ class UnionFind
88
+ def initialize(n)
89
+ @parent = (0...n).to_a
90
+ @rank = Array.new(n, 0)
91
+ end
92
+
93
+ def find(x)
94
+ if @parent[x] != x
95
+ @parent[x] = find(@parent[x]) # Path compression
96
+ end
97
+ @parent[x]
98
+ end
99
+
100
+ def union(x, y)
101
+ root_x = find(x)
102
+ root_y = find(y)
103
+ return if root_x == root_y
104
+
105
+ # Union by rank
106
+ if @rank[root_x] < @rank[root_y]
107
+ @parent[root_x] = root_y
108
+ elsif @rank[root_x] > @rank[root_y]
109
+ @parent[root_y] = root_x
110
+ else
111
+ @parent[root_y] = root_x
112
+ @rank[root_x] += 1
113
+ end
114
+ end
115
+ end
116
+ RUBY
117
+
118
+ puts "\nšŸ”µ C++:"
119
+ puts <<~CPP
120
+ class UnionFind {
121
+ vector<int> parent, rank;
122
+ public:
123
+ UnionFind(int n) : parent(n), rank(n, 0) {
124
+ iota(parent.begin(), parent.end(), 0);
125
+ }
126
+
127
+ int find(int x) {
128
+ if (parent[x] != x)
129
+ parent[x] = find(parent[x]); // Path compression
130
+ return parent[x];
131
+ }
132
+
133
+ void unite(int x, int y) {
134
+ int rootX = find(x);
135
+ int rootY = find(y);
136
+ if (rootX == rootY) return;
137
+
138
+ // Union by rank
139
+ if (rank[rootX] < rank[rootY]) {
140
+ parent[rootX] = rootY;
141
+ } else if (rank[rootX] > rank[rootY]) {
142
+ parent[rootY] = rootX;
143
+ } else {
144
+ parent[rootY] = rootX;
145
+ rank[rootX]++;
146
+ }
147
+ }
148
+ };
149
+ CPP
150
+ end
151
+
152
+ def self.show_practice_problems
153
+ puts "\n" + "="*60
154
+ puts "PRACTICE PROBLEMS"
155
+ puts "="*60
156
+
157
+ problems = [
158
+ "1. Number of connected components in graph",
159
+ "2. Detect cycle in undirected graph",
160
+ "3. Friend circles / Number of provinces",
161
+ "4. Redundant connection",
162
+ "5. Accounts merge",
163
+ "6. Most stones removed",
164
+ "7. Satisfiability of equality equations",
165
+ "8. Smallest string with swaps"
166
+ ]
167
+
168
+ problems.each { |p| puts p }
169
+
170
+ puts "\nšŸ’” TIP: Union-Find is perfect for dynamic connectivity problems!"
171
+ puts "šŸ’” TIP: Use for Kruskal's MST algorithm"
172
+ end
173
+
174
+ class UnionFindImplementation
175
+ def initialize(n)
176
+ @parent = (0...n).to_a
177
+ @rank = Array.new(n, 0)
178
+ @count = n
179
+ end
180
+
181
+ def find(x)
182
+ if @parent[x] != x
183
+ @parent[x] = find(@parent[x]) # Path compression
184
+ end
185
+ @parent[x]
186
+ end
187
+
188
+ def union(x, y)
189
+ root_x = find(x)
190
+ root_y = find(y)
191
+ return if root_x == root_y
192
+
193
+ # Union by rank
194
+ if @rank[root_x] < @rank[root_y]
195
+ @parent[root_x] = root_y
196
+ elsif @rank[root_x] > @rank[root_y]
197
+ @parent[root_y] = root_x
198
+ else
199
+ @parent[root_y] = root_x
200
+ @rank[root_x] += 1
201
+ end
202
+
203
+ @count -= 1
204
+ end
205
+
206
+ def connected?(x, y)
207
+ find(x) == find(y)
208
+ end
209
+
210
+ def count
211
+ @count
212
+ end
8
213
  end
9
214
  end
10
215
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dsa_visualizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - DSA Learning Team
@@ -131,10 +131,10 @@ homepage: https://github.com/Avinashkrmehta/dsa_visualizer
131
131
  licenses:
132
132
  - MIT
133
133
  metadata:
134
- bug_tracker_uri: https://github.com/yourusername/dsa_visualizer/issues
135
- changelog_uri: https://github.com/yourusername/dsa_visualizer/blob/main/CHANGELOG.md
136
- documentation_uri: https://github.com/yourusername/dsa_visualizer/blob/main/README.md
137
- source_code_uri: https://github.com/yourusername/dsa_visualizer
134
+ bug_tracker_uri: https://github.com/Avinashkrmehta/dsa_visualizer/issues
135
+ changelog_uri: https://github.com/Avinashkrmehta/dsa_visualizer/blob/main/CHANGELOG.md
136
+ documentation_uri: https://github.com/Avinashkrmehta/dsa_visualizer/blob/main/README.md
137
+ source_code_uri: https://github.com/Avinashkrmehta/dsa_visualizer
138
138
  post_install_message:
139
139
  rdoc_options: []
140
140
  require_paths: