dsa-ruby 1.0.0 → 1.0.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 +4 -4
- data/README.md +9 -3
- data/lib/dsa-ruby/binary_search_tree.rb +258 -123
- data/lib/dsa-ruby/deque.rb +136 -55
- data/lib/dsa-ruby/heap.rb +235 -107
- data/lib/dsa-ruby/linked_list.rb +218 -0
- data/lib/dsa-ruby/priority_queue.rb +107 -41
- data/lib/dsa-ruby/queue.rb +72 -26
- data/lib/dsa-ruby/stack.rb +72 -26
- data/lib/dsa-ruby/trie.rb +109 -51
- data/lib/dsa-ruby/union_find.rb +93 -38
- data/lib/dsa-ruby/version.rb +8 -1
- metadata +15 -1
data/lib/dsa-ruby/heap.rb
CHANGED
|
@@ -1,145 +1,273 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
# DSA::MinHeap - A min-heap (priority queue) data structure.
|
|
2
|
+
#
|
|
3
|
+
# The smallest element is always at the root. Provides O(log n) insertion
|
|
4
|
+
# and extraction of the minimum element.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# heap = DSA::MinHeap.new([5, 3, 7, 1])
|
|
8
|
+
# heap.pop # => 1
|
|
9
|
+
# heap.peek # => 3
|
|
10
|
+
#
|
|
11
|
+
# # Or build incrementally with chained pushes:
|
|
12
|
+
# heap = DSA::MinHeap.new
|
|
13
|
+
# heap.push(5).push(3).push(7).push(1)
|
|
14
|
+
class DSA::MinHeap
|
|
15
|
+
# Initialize a new min-heap with optional initial elements.
|
|
16
|
+
#
|
|
17
|
+
# @param elements [Array<Comparable>] optional array of elements to heapify
|
|
18
|
+
# @return [DSA::MinHeap] a new min-heap
|
|
19
|
+
# @example
|
|
20
|
+
# DSA::MinHeap.new([3, 1, 2])
|
|
21
|
+
def initialize(elements = [])
|
|
22
|
+
@elements = []
|
|
23
|
+
elements.each { |e| push(e) }
|
|
24
|
+
end
|
|
7
25
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
26
|
+
# Inserts an element into the heap.
|
|
27
|
+
#
|
|
28
|
+
# @param val [Comparable] the value to insert
|
|
29
|
+
# @return [DSA::MinHeap] self for method chaining
|
|
30
|
+
# @example
|
|
31
|
+
# heap.push(5).push(3).push(1)
|
|
32
|
+
def push(val)
|
|
33
|
+
@elements << val
|
|
34
|
+
sift_up(@elements.size - 1)
|
|
35
|
+
self
|
|
36
|
+
end
|
|
13
37
|
|
|
14
|
-
|
|
15
|
-
|
|
38
|
+
# Removes and returns the minimum element from the heap.
|
|
39
|
+
#
|
|
40
|
+
# @return [Comparable] the minimum element
|
|
41
|
+
# @raise [IndexError] if the heap is empty
|
|
42
|
+
# @example
|
|
43
|
+
# heap.push(5).push(1).push(3)
|
|
44
|
+
# heap.pop # => 1
|
|
45
|
+
def pop
|
|
46
|
+
raise IndexError, "heap is empty" if empty?
|
|
16
47
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
48
|
+
swap(0, @elements.size - 1)
|
|
49
|
+
min = @elements.pop
|
|
50
|
+
sift_down(0)
|
|
51
|
+
min
|
|
52
|
+
end
|
|
22
53
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
54
|
+
# Returns the minimum element without removing it.
|
|
55
|
+
#
|
|
56
|
+
# @return [Comparable] the minimum element
|
|
57
|
+
# @raise [IndexError] if the heap is empty
|
|
58
|
+
# @example
|
|
59
|
+
# heap.push(5).push(1).push(3)
|
|
60
|
+
# heap.peek # => 1
|
|
61
|
+
def peek
|
|
62
|
+
raise IndexError, "heap is empty" if empty?
|
|
63
|
+
@elements[0]
|
|
64
|
+
end
|
|
27
65
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
66
|
+
# Returns the number of elements in the heap.
|
|
67
|
+
#
|
|
68
|
+
# @return [Integer] the number of elements
|
|
69
|
+
# @example
|
|
70
|
+
# heap.push(5).push(3)
|
|
71
|
+
# heap.size # => 2
|
|
72
|
+
def size
|
|
73
|
+
@elements.size
|
|
74
|
+
end
|
|
31
75
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
76
|
+
# Checks if the heap is empty.
|
|
77
|
+
#
|
|
78
|
+
# @return [Boolean] true if the heap contains no elements
|
|
79
|
+
# @example
|
|
80
|
+
# heap = DSA::MinHeap.new
|
|
81
|
+
# heap.empty? # => true
|
|
82
|
+
def empty?
|
|
83
|
+
@elements.empty?
|
|
84
|
+
end
|
|
35
85
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
86
|
+
# Returns a defensive copy of the heap as an array.
|
|
87
|
+
#
|
|
88
|
+
# @return [Array<Comparable>] an array containing all elements (not in sorted order)
|
|
89
|
+
# @example
|
|
90
|
+
# heap.push(5).push(3).push(1)
|
|
91
|
+
# heap.to_a # => [1, 3, 5] (may vary based on internal structure)
|
|
92
|
+
def to_a
|
|
93
|
+
@elements.dup
|
|
94
|
+
end
|
|
39
95
|
|
|
40
|
-
|
|
96
|
+
private
|
|
41
97
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
98
|
+
# Moves an element up the heap to restore the heap property.
|
|
99
|
+
#
|
|
100
|
+
# @param i [Integer] the index of the element to sift up
|
|
101
|
+
def sift_up(i)
|
|
102
|
+
while i > 0
|
|
103
|
+
parent = (i - 1) / 2
|
|
104
|
+
break if (@elements[parent] <=> @elements[i]) <= 0
|
|
46
105
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end
|
|
106
|
+
swap(parent, i)
|
|
107
|
+
i = parent
|
|
50
108
|
end
|
|
109
|
+
end
|
|
51
110
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
111
|
+
# Moves an element down the heap to restore the heap property.
|
|
112
|
+
#
|
|
113
|
+
# @param i [Integer] the index of the element to sift down
|
|
114
|
+
def sift_down(i)
|
|
115
|
+
n = @elements.size
|
|
116
|
+
loop do
|
|
117
|
+
smallest = i
|
|
118
|
+
left = 2 * i + 1
|
|
119
|
+
right = 2 * i + 2
|
|
58
120
|
|
|
59
|
-
|
|
60
|
-
|
|
121
|
+
smallest = left if left < n && (@elements[left] <=> @elements[smallest]) < 0
|
|
122
|
+
smallest = right if right < n && (@elements[right] <=> @elements[smallest]) < 0
|
|
61
123
|
|
|
62
|
-
|
|
124
|
+
break if smallest == i
|
|
63
125
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
end
|
|
126
|
+
swap(i, smallest)
|
|
127
|
+
i = smallest
|
|
67
128
|
end
|
|
129
|
+
end
|
|
68
130
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
131
|
+
# Swaps two elements in the heap array.
|
|
132
|
+
#
|
|
133
|
+
# @param i [Integer] index of first element
|
|
134
|
+
# @param j [Integer] index of second element
|
|
135
|
+
def swap(i, j)
|
|
136
|
+
@elements[i], @elements[j] = @elements[j], @elements[i]
|
|
72
137
|
end
|
|
138
|
+
end
|
|
73
139
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
140
|
+
# DSA::MaxHeap - A max-heap (priority queue) data structure.
|
|
141
|
+
#
|
|
142
|
+
# The largest element is always at the root. Provides O(log n) insertion
|
|
143
|
+
# and extraction of the maximum element.
|
|
144
|
+
#
|
|
145
|
+
# @example
|
|
146
|
+
# heap = DSA::MaxHeap.new([5, 3, 7, 1])
|
|
147
|
+
# heap.pop # => 7
|
|
148
|
+
# heap.peek # => 5
|
|
149
|
+
class DSA::MaxHeap
|
|
150
|
+
# Initialize a new max-heap with optional initial elements.
|
|
151
|
+
#
|
|
152
|
+
# @param elements [Array<Comparable>] optional array of elements to heapify
|
|
153
|
+
# @return [DSA::MaxHeap] a new max-heap
|
|
154
|
+
# @example
|
|
155
|
+
# DSA::MaxHeap.new([3, 1, 2])
|
|
156
|
+
def initialize(elements = [])
|
|
157
|
+
@elements = []
|
|
158
|
+
elements.each { |e| push(e) }
|
|
159
|
+
end
|
|
79
160
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
161
|
+
# Inserts an element into the heap.
|
|
162
|
+
#
|
|
163
|
+
# @param val [Comparable] the value to insert
|
|
164
|
+
# @return [DSA::MaxHeap] self for method chaining
|
|
165
|
+
# @example
|
|
166
|
+
# heap.push(5).push(3).push(1)
|
|
167
|
+
def push(val)
|
|
168
|
+
@elements << val
|
|
169
|
+
sift_up(@elements.size - 1)
|
|
170
|
+
self
|
|
171
|
+
end
|
|
85
172
|
|
|
86
|
-
|
|
87
|
-
|
|
173
|
+
# Removes and returns the maximum element from the heap.
|
|
174
|
+
#
|
|
175
|
+
# @return [Comparable] the maximum element
|
|
176
|
+
# @raise [IndexError] if the heap is empty
|
|
177
|
+
# @example
|
|
178
|
+
# heap.push(5).push(1).push(3)
|
|
179
|
+
# heap.pop # => 5
|
|
180
|
+
def pop
|
|
181
|
+
raise IndexError, "heap is empty" if empty?
|
|
88
182
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
183
|
+
swap(0, @elements.size - 1)
|
|
184
|
+
max = @elements.pop
|
|
185
|
+
sift_down(0)
|
|
186
|
+
max
|
|
187
|
+
end
|
|
94
188
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
189
|
+
# Returns the maximum element without removing it.
|
|
190
|
+
#
|
|
191
|
+
# @return [Comparable] the maximum element
|
|
192
|
+
# @raise [IndexError] if the heap is empty
|
|
193
|
+
# @example
|
|
194
|
+
# heap.push(5).push(1).push(3)
|
|
195
|
+
# heap.peek # => 5
|
|
196
|
+
def peek
|
|
197
|
+
raise IndexError, "heap is empty" if empty?
|
|
198
|
+
@elements[0]
|
|
199
|
+
end
|
|
99
200
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
201
|
+
# Returns the number of elements in the heap.
|
|
202
|
+
#
|
|
203
|
+
# @return [Integer] the number of elements
|
|
204
|
+
# @example
|
|
205
|
+
# heap.push(5).push(3)
|
|
206
|
+
# heap.size # => 2
|
|
207
|
+
def size
|
|
208
|
+
@elements.size
|
|
209
|
+
end
|
|
103
210
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
211
|
+
# Checks if the heap is empty.
|
|
212
|
+
#
|
|
213
|
+
# @return [Boolean] true if the heap contains no elements
|
|
214
|
+
# @example
|
|
215
|
+
# heap = DSA::MaxHeap.new
|
|
216
|
+
# heap.empty? # => true
|
|
217
|
+
def empty?
|
|
218
|
+
@elements.empty?
|
|
219
|
+
end
|
|
107
220
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
221
|
+
# Returns a defensive copy of the heap as an array.
|
|
222
|
+
#
|
|
223
|
+
# @return [Array<Comparable>] an array containing all elements (not in sorted order)
|
|
224
|
+
# @example
|
|
225
|
+
# heap.push(5).push(3).push(1)
|
|
226
|
+
# heap.to_a # => [5, 3, 1] (may vary based on internal structure)
|
|
227
|
+
def to_a
|
|
228
|
+
@elements.dup
|
|
229
|
+
end
|
|
111
230
|
|
|
112
|
-
|
|
231
|
+
private
|
|
113
232
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
233
|
+
# Moves an element up the heap to restore the heap property.
|
|
234
|
+
#
|
|
235
|
+
# @param i [Integer] the index of the element to sift up
|
|
236
|
+
def sift_up(i)
|
|
237
|
+
while i > 0
|
|
238
|
+
parent = (i - 1) / 2
|
|
239
|
+
break if (@elements[parent] <=> @elements[i]) >= 0
|
|
118
240
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
end
|
|
241
|
+
swap(parent, i)
|
|
242
|
+
i = parent
|
|
122
243
|
end
|
|
244
|
+
end
|
|
123
245
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
246
|
+
# Moves an element down the heap to restore the heap property.
|
|
247
|
+
#
|
|
248
|
+
# @param i [Integer] the index of the element to sift down
|
|
249
|
+
def sift_down(i)
|
|
250
|
+
n = @elements.size
|
|
251
|
+
loop do
|
|
252
|
+
largest = i
|
|
253
|
+
left = 2 * i + 1
|
|
254
|
+
right = 2 * i + 2
|
|
130
255
|
|
|
131
|
-
|
|
132
|
-
|
|
256
|
+
largest = left if left < n && (@elements[left] <=> @elements[largest]) > 0
|
|
257
|
+
largest = right if right < n && (@elements[right] <=> @elements[largest]) > 0
|
|
133
258
|
|
|
134
|
-
|
|
259
|
+
break if largest == i
|
|
135
260
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
end
|
|
261
|
+
swap(i, largest)
|
|
262
|
+
i = largest
|
|
139
263
|
end
|
|
264
|
+
end
|
|
140
265
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
266
|
+
# Swaps two elements in the heap array.
|
|
267
|
+
#
|
|
268
|
+
# @param i [Integer] index of first element
|
|
269
|
+
# @param j [Integer] index of second element
|
|
270
|
+
def swap(i, j)
|
|
271
|
+
@elements[i], @elements[j] = @elements[j], @elements[i]
|
|
144
272
|
end
|
|
145
273
|
end
|