DSA 0.0.2 → 0.0.3

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
  SHA1:
3
- metadata.gz: 8b389b9d9e679bc3e426a95abfc2f103c93596a8
4
- data.tar.gz: d44481bab4cc8cf982623829d822f91193880f3e
3
+ metadata.gz: 1cf45aaf41acd85971600055fd5dfac1825e539b
4
+ data.tar.gz: 8ab7c55725ae11725a9e9ef7bff86c6d9416a1a0
5
5
  SHA512:
6
- metadata.gz: 284aba1b513d954e3a87033ae58b3c4bb9b3123f9f1c49e028e310e0c2f36a6f79d9c5468f17564808e75fa83cc9c0a7736a0623128b3e863d1c49c671614312
7
- data.tar.gz: ff67a7432ac7656b1a7697f11448c1148fcc1034c4d5dcc4107a4f560abf33529354149c19aafa6289d8115d8c802146b5f5f47b85ab4cbecaadc22746d1092c
6
+ metadata.gz: 1085cb96b51a4b9dd4f168454cb9218bfa8cc473c2050a8cf662cfecc6feabd002876da7a6005bc28db323bd6a8f9f9a338dff5e6930cd20b4044b11a51051d1
7
+ data.tar.gz: bd64fc623b6c397346f355cc8b6e7cf289e7bc3e4e69138313c985cfb17830f7b2d466eec48df623f5cd217ac07ad771b611412803e6509523c952e954465845
data/README.md CHANGED
@@ -87,6 +87,34 @@ A help method tried to print a tree, not quite pretty, but may helps test
87
87
  Enumerable is included, all those method such as 'each' are all available, since other methods are based on each,
88
88
  the performance might not be the best, use only when a full traversal is inevitable.
89
89
 
90
+ ### SkipList
91
+ An ordered map, storing key/value pairs, duplicate keys are allowed, value can be omitted, preserves an order and provides range search, implemented as a skip list.
92
+
93
+ ```ruby
94
+ sl = DSA::SkipList.new
95
+ sl.add key, value
96
+ sl.add key # value will be nil
97
+ sl.delete key # all those pairs with the key will be deleted
98
+ sl.delete key, value # the pair has same key/value pair will be deleted
99
+ ```
100
+
101
+ And special methods related to orders, those methods yield key/value pairs to block, if no block, enumerator is returned.
102
+ ```ruby
103
+ sl.find key
104
+ sl.each # in-order traversal
105
+ sl.gt(key) # key/value pairs for keys greater than key
106
+ sl.ge(key)
107
+ sl.lt(key)
108
+ sl.le(key)
109
+ ```
110
+ A help method prints the skip list
111
+ ```ruby
112
+ sl.print_me
113
+ sl.print_me width # width, the length of evert key/value pair, default to 10
114
+ ```
115
+ Enumerable is included, all those method such as 'each' are all available, since other methods are based on each,
116
+ the performance might not be the best, use only when a full traversal is inevitable.
117
+
90
118
 
91
119
  ### PriorityQueue
92
120
  An array based heap, priority is a number, the smaller it is, higher priority it has
data/lib/DSA.rb CHANGED
@@ -4,6 +4,7 @@ require_relative 'DSA/stack_and_queue'
4
4
  require_relative 'DSA/list'
5
5
  require_relative 'DSA/priority_queue'
6
6
  require_relative 'DSA/binary_search_tree'
7
+ require_relative 'DSA/skip_list'
7
8
 
8
9
  module DSA
9
10
  # Your code goes here...
@@ -3,6 +3,7 @@ module DSA
3
3
  class BasicBinarySearchTreeNode
4
4
  attr_accessor :key, :value, :parent, :left, :right
5
5
  def initialize(key, value)
6
+ raise KeyError, 'Key cannot be nil' if key.nil?
6
7
  @key = key
7
8
  @value = value
8
9
  @parent = nil
@@ -1,6 +1,6 @@
1
1
  module DSA
2
2
  # Exception
3
- class ListInsertError < StandardError
3
+ class ListIndexError < StandardError
4
4
  end
5
5
  class ListRemovalError < StandardError
6
6
  end
@@ -156,7 +156,7 @@ module DSA
156
156
  private
157
157
  def get_node(index)
158
158
  index += @length if index < 0
159
- raise(ListInsertError, 'Index out of bound: ' + index.to_s) if index > @length - 1 || index < 0
159
+ raise(ListIndexError, 'Index out of bound: ' + index.to_s) if index > @length - 1 || index < 0
160
160
  if index > @length / 2
161
161
  node = @tail.prev
162
162
  current_index = @length - 1
@@ -0,0 +1,358 @@
1
+ module DSA
2
+ class SkipListNode
3
+ attr_accessor :key, :value, :prev, :next, :up, :down
4
+ def initialize(key, value = nil)
5
+ @key = key
6
+ @value = value
7
+ @prev = nil
8
+ @next = nil
9
+ @up = nil
10
+ @down = nil
11
+ end
12
+
13
+ def is_sentinel?
14
+ @key.equal? SkipListLevel::SENTINEL
15
+ end
16
+ end
17
+
18
+ class SkipListLevel
19
+ attr_accessor :head, :tail
20
+ SENTINEL = Object.new
21
+ def initialize
22
+ @head = SkipListNode.new(SENTINEL, 'Sentinel')
23
+ @tail = SkipListNode.new(SENTINEL, 'Sentinel')
24
+ @head.next = @tail
25
+ @tail.prev = @head
26
+ end
27
+ end
28
+
29
+ class SkipList
30
+ attr_reader :length, :height
31
+ include Enumerable
32
+ def initialize
33
+ @levels = [SkipListLevel.new]
34
+ @length = 0
35
+ @height = 0
36
+ end
37
+
38
+ def find(key)
39
+ nodes = find_nodes key
40
+
41
+ if block_given?
42
+ return unless nodes
43
+ nodes.each do |node|
44
+ yield [node.key, node.value]
45
+ end
46
+ else
47
+ Enumerator.new do |y|
48
+ find(key) do |key, value|
49
+ y << [key, value]
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ def each
57
+ walk = @levels.first.head
58
+ if block_given?
59
+ walk = walk.next
60
+ until walk.is_sentinel?
61
+ yield [ walk.key, walk.value ]
62
+ walk = walk.next
63
+ end
64
+ else
65
+ Enumerator.new do |y|
66
+ each do |key, value|
67
+ y << [key, value]
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def gt(key)
74
+ nodes = find_nodes(key, true, false)
75
+
76
+ if block_given?
77
+ return unless nodes
78
+ walk = nodes.last.next
79
+ until walk.is_sentinel?
80
+ yield [ walk.key, walk.value ]
81
+ walk = walk.next
82
+ end
83
+ else
84
+ Enumerator.new do |y|
85
+ gt(key) do |key, value|
86
+ y << [key, value]
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def ge(key)
93
+ nodes = find_nodes(key, true, false)
94
+
95
+ if block_given?
96
+ return unless nodes
97
+ if nodes.first.key == key
98
+ walk = nodes.first
99
+ else
100
+ walk = nodes.first.next
101
+ end
102
+ until walk.is_sentinel?
103
+ yield [ walk.key, walk.value ]
104
+ walk = walk.next
105
+ end
106
+ else
107
+ Enumerator.new do |y|
108
+ ge(key) do |key, value|
109
+ y << [key, value]
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ def lt(key)
116
+ nodes = find_nodes(key, false, true)
117
+
118
+ if block_given?
119
+ return unless nodes
120
+ walk = nodes.first.prev
121
+ until walk.is_sentinel?
122
+ yield [ walk.key, walk.value ]
123
+ walk = walk.prev
124
+ end
125
+ else
126
+ Enumerator.new do |y|
127
+ lt(key) do |key, value|
128
+ y << [key, value]
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ def le(key)
135
+ nodes = find_nodes(key, false, true)
136
+
137
+ if block_given?
138
+ return unless nodes
139
+ if nodes.first.key == key
140
+ walk = nodes.last
141
+ else
142
+ walk = nodes.last.prev
143
+ end
144
+ until walk.is_sentinel?
145
+ yield [ walk.key, walk.value ]
146
+ walk = walk.prev
147
+ end
148
+ else
149
+ Enumerator.new do |y|
150
+ le(key) do |key, value|
151
+ y << [key, value]
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+
158
+ # if value provided, the nodes have the same key/value pairs will be deleted,
159
+ # otherwise, all nodes have the same key are deleted
160
+ # return the value of last deleted node
161
+ def delete(key, value = nil)
162
+ nodes = find_nodes(key)
163
+ return false unless nodes
164
+
165
+ return_value = false
166
+ nodes.each do |node|
167
+ if !value || value == node.value
168
+ return_value = remove_tower node
169
+ @length -= 1
170
+ remove_empty_level
171
+ end
172
+ end
173
+
174
+ return_value
175
+ end
176
+
177
+ def add(key, value = nil)
178
+ potential_place = []
179
+ walk = start_node
180
+ while 1
181
+ if walk.next.is_sentinel? || key <= walk.next.key
182
+ if walk.down
183
+ potential_place.push walk
184
+ walk = walk.down
185
+ next
186
+ else
187
+ insert_node_between walk, walk.next, SkipListNode.new(key, value)
188
+ @length += 1
189
+ build_tower walk.next, potential_place, key, value
190
+ break
191
+ end
192
+ else
193
+ walk = walk.next
194
+ next
195
+ end
196
+ end
197
+ end
198
+
199
+ def print_me(width = 10)
200
+ rec = Hash.new
201
+ walk = @levels.first.head
202
+ i = 0
203
+ j = 0
204
+ walk = walk.next
205
+ while ! walk.is_sentinel?
206
+ rec[ [i, j] ] = "#{walk.key},#{walk.value}"
207
+ up_walk = walk
208
+ while up_walk.up
209
+ up_walk = up_walk.up
210
+ j += 1
211
+ rec[ [i, j] ] ="#{up_walk.key},#{up_walk.value}"
212
+ end
213
+ walk = walk.next
214
+ i += 1
215
+ j = 0
216
+ end
217
+
218
+ # print
219
+ puts '=' * 100
220
+ (height+1).times.reverse_each do |j|
221
+ printf 'Sentinel--'
222
+ (length).times do |i|
223
+ if rec[ [i, j] ]
224
+ printf ">%#{width+2}s", "#{rec[ [i, j] ]}--"
225
+ else
226
+ printf '-' + '-' * width + '--'
227
+ end
228
+ end
229
+ puts '>Sentinel'
230
+ end
231
+ puts '=' * 100
232
+ end
233
+
234
+ private
235
+ def remove_empty_level
236
+ return if @length < 2
237
+ one_to_last = @levels[-2]
238
+ if one_to_last.head.next.is_sentinel?
239
+ @levels.pop
240
+ @height -= 1
241
+ end
242
+ end
243
+
244
+ # if not found, two more options are provided
245
+ def find_nodes(key, return_previous = false, return_next = false )
246
+ walk = start_node
247
+ while 1
248
+ if !walk.is_sentinel? && key == walk.key
249
+ break
250
+ elsif walk.next.is_sentinel? || key < walk.next.key
251
+ if walk.down
252
+ walk = walk.down
253
+ next
254
+ else
255
+ if return_previous
256
+ return [walk]
257
+ elsif return_next
258
+ return [walk.next]
259
+ else
260
+ return nil
261
+ end
262
+ end
263
+ else
264
+ walk = walk.next
265
+ next
266
+ end
267
+ end
268
+
269
+ nodes = []
270
+ while !walk.is_sentinel? && walk.down
271
+ walk = walk.down
272
+ end
273
+
274
+ nodes.push walk
275
+
276
+ # to find the duplicates
277
+ other_node = walk.next
278
+ while !other_node.is_sentinel? && other_node.key == key
279
+ nodes.push other_node
280
+ other_node = other_node.next
281
+ end
282
+
283
+ other_node = walk.prev
284
+ while !other_node.is_sentinel? && other_node.key == key
285
+ nodes.unshift other_node
286
+ other_node = other_node.prev
287
+ end
288
+
289
+ nodes
290
+ end
291
+
292
+ def remove_tower(base)
293
+ remove_node base
294
+ remove_tower base.up if base.up
295
+ base.value
296
+ end
297
+
298
+ def build_tower(base, tower, key, value)
299
+ up_stair_previous = nil
300
+ tower.reverse_each do |node|
301
+ return unless go_up?
302
+ up_stair_previous = node
303
+ new_node = SkipListNode.new(key, value)
304
+ insert_node_between up_stair_previous, up_stair_previous.next, new_node
305
+ vertical_link_node new_node, base
306
+ base = new_node
307
+ end
308
+ # add new level
309
+ if go_up?
310
+ add_level
311
+ @height += 1
312
+ end
313
+
314
+ end
315
+
316
+ def add_level
317
+ new_level = SkipListLevel.new
318
+ vertical_link_node new_level.head, @levels.last.head
319
+ vertical_link_node new_level.tail, @levels.last.tail
320
+ @levels.push new_level
321
+ end
322
+
323
+ def vertical_link_node(above, below)
324
+ above.down = below
325
+ below.up = above
326
+ end
327
+
328
+ def go_up?
329
+ [true, false].sample
330
+ end
331
+
332
+ def insert_node_between(a, b, new_one)
333
+ a.next = new_one
334
+ new_one.prev = a
335
+ new_one.next = b
336
+ b.prev = new_one
337
+ end
338
+
339
+ # remove a node
340
+ def remove_node(node)
341
+ before = node.prev
342
+ after = node.next
343
+ before.next = after
344
+ after.prev = before
345
+ node
346
+ end
347
+
348
+ def start_node
349
+ @levels.last.head
350
+ end
351
+
352
+ def end_node
353
+ @levels.first.tail
354
+ end
355
+
356
+
357
+ end
358
+ end
@@ -1,3 +1,3 @@
1
1
  module DSA
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -270,6 +270,7 @@ class MyTest < Test::Unit::TestCase
270
270
  def test_performance
271
271
  puts
272
272
  rb = DSA::RedBlackTree.new
273
+ sl = DSA::SkipList.new
273
274
  hash = Hash.new
274
275
  value = 10**5
275
276
  Benchmark.bm(20) do |x|
@@ -277,6 +278,10 @@ class MyTest < Test::Unit::TestCase
277
278
  x.report('RedBlack Find') { value.times { rb[Random.rand(value)] } }
278
279
  x.report('RedBlack Find gt') { value.times { rb.gt(Random.rand(value)) } }
279
280
  x.report('RedBlack Deletion') { (value/2).times { rb.delete Random.rand(value) } }
281
+ x.report('SkipList') { value.times { sl.add Random.rand(value), 'whatever' } }
282
+ x.report('SkipList Find') { value.times { sl.find Random.rand(value) } }
283
+ x.report('SkipList Find gt') { value.times { sl.gt Random.rand(value) } }
284
+ x.report('SkipList Deletion') { (value/2).times { sl.delete Random.rand(value) } }
280
285
  x.report('Built-In Hash') { value.times { hash[Random.rand(value)] = 'whatever' } }
281
286
  x.report('Built-In Hash Find') { value.times { hash[Random.rand(value)] } }
282
287
  x.report('Built-In Hash Deletion') { (value/2).times { hash.delete Random.rand(value) } }
@@ -0,0 +1,115 @@
1
+ require 'test/unit'
2
+ require 'DSA'
3
+
4
+ class MyTest < Test::Unit::TestCase
5
+
6
+ # Called before every test method runs. Can be used
7
+ # to set up fixture information.
8
+ def setup
9
+ # Do nothing
10
+ end
11
+
12
+ # Called after every test method runs. Can be used to tear
13
+ # down fixture information.
14
+
15
+ def teardown
16
+ # Do nothing
17
+ end
18
+
19
+ def test_skip_list
20
+ sl = DSA::SkipList.new
21
+ value = 20
22
+ value.times { sl.add Random.rand(value/3), Random.rand(value*2) }
23
+
24
+ sl.print_me
25
+
26
+ sl.find 5 do |key, value|
27
+ puts "(#{key}, #{value})"
28
+ end
29
+
30
+ e = sl.find 5
31
+ loop do
32
+ key, value = e.next
33
+ puts "(#{key}, #{value})"
34
+ end
35
+
36
+ sl.add 5, 100
37
+ sl.add 5, 200
38
+ sl.print_me
39
+ assert_equal 200, sl.delete(5, 200), 'delete failed'
40
+ sl.print_me
41
+ puts sl.delete 5
42
+ sl.print_me
43
+
44
+ value = 20
45
+ value.times { sl.add Random.rand(value), Random.rand(value*2) }
46
+ sl.print_me
47
+ value.times { sl.delete Random.rand(value) }
48
+ sl.print_me 5
49
+
50
+ sl.each do |key, value|
51
+ printf "(#{key}, #{value})"
52
+ end
53
+ puts
54
+
55
+ e = sl.each
56
+ loop do
57
+ key, value = e.next
58
+ printf "(#{key}, #{value})"
59
+ end
60
+ puts
61
+
62
+ sl.gt(10) do |key, value|
63
+ printf "(#{key}, #{value})"
64
+ end
65
+ puts
66
+
67
+ e = sl.gt(10)
68
+ loop do
69
+ key, value = e.next
70
+ printf "(#{key}, #{value})"
71
+ end
72
+ puts
73
+
74
+ sl.ge(10) do |key, value|
75
+ printf "(#{key}, #{value})"
76
+ end
77
+ puts
78
+
79
+ e = sl.ge(10)
80
+ loop do
81
+ key, value = e.next
82
+ printf "(#{key}, #{value})"
83
+ end
84
+ puts
85
+
86
+ sl.lt(10) do |key, value|
87
+ printf "(#{key}, #{value})"
88
+ end
89
+ puts
90
+
91
+ e = sl.lt(10)
92
+ loop do
93
+ key, value = e.next
94
+ printf "(#{key}, #{value})"
95
+ end
96
+ puts
97
+
98
+ sl.le(10) do |key, value|
99
+ printf "(#{key}, #{value})"
100
+ end
101
+ puts
102
+
103
+ e = sl.le(10)
104
+ loop do
105
+ key, value = e.next
106
+ printf "(#{key}, #{value})"
107
+ end
108
+ puts
109
+
110
+ value = 10 ** 3
111
+ value.times { sl.add Random.rand(value), Random.rand(value*2) }
112
+ sl.print_me
113
+
114
+ end
115
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: DSA
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - lusaisai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-21 00:00:00.000000000 Z
11
+ date: 2014-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -57,12 +57,14 @@ files:
57
57
  - lib/DSA/binary_search_tree.rb
58
58
  - lib/DSA/list.rb
59
59
  - lib/DSA/priority_queue.rb
60
+ - lib/DSA/skip_list.rb
60
61
  - lib/DSA/stack_and_queue.rb
61
62
  - lib/DSA/version.rb
62
63
  - test/algorithms_test.rb
63
64
  - test/binary_search_tree_test.rb
64
65
  - test/list_test.rb
65
66
  - test/priority_queue_test.rb
67
+ - test/skip_list_test.rb
66
68
  - test/stack_and_queue_test.rb
67
69
  homepage: https://github.com/lusaisai/DSA
68
70
  licenses:
@@ -93,4 +95,5 @@ test_files:
93
95
  - test/binary_search_tree_test.rb
94
96
  - test/list_test.rb
95
97
  - test/priority_queue_test.rb
98
+ - test/skip_list_test.rb
96
99
  - test/stack_and_queue_test.rb