binaryheap 1.0.1

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 (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/binaryheap.rb +178 -0
  3. metadata +46 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af0658be36292995ead9ab8ce6e1e4c273b6d67c
4
+ data.tar.gz: 9cfc88a2bbee5c757c5e3265fb58726f41fd58dd
5
+ SHA512:
6
+ metadata.gz: e0536e2fada1619d979f2c3219cc354be86417288bd6569dced4a165a7d314203a8f9e084bb1237c544724c86eaabdeae2b281dc3f469993ea65ba43d09785d2
7
+ data.tar.gz: 4d3493a8c5979764500405719ed51bc255cfc1027a99deebca2d673bd29e1bca04545e698a35d22d71580823f3c4cfdecbbbfdb18ea912182cc3e0b8de08540c
@@ -0,0 +1,178 @@
1
+ require 'thread'
2
+
3
+ # An implementation of standard binary heap data structure.
4
+ # Internally, it uses an array as data store and a mutex for thread synchronization (insert and eject operations)
5
+ class BinaryHeap
6
+ class Error < RuntimeError
7
+ end
8
+
9
+ class HeapError < Error
10
+ def mesage
11
+ "Heap Error: #{self.to_s}"
12
+ end
13
+ end
14
+
15
+ class ParameterError < Error
16
+ def message
17
+ "Parameter Error: #{self.to_s}"
18
+ end
19
+ end
20
+
21
+ # The constructor
22
+ # @param data [Array] the existent Array data on which the binary heap will be built
23
+ # @param block [Block] the comparator block, see examples.
24
+ # default comparator is lambda {|parent, child| parent <=> child} which makes it a max binary heap.
25
+ # @return [BinaryHeap] the Binary Heap instance
26
+ # @example A default max heap
27
+ # bh = BinaryHeap.new
28
+ # @example A min heap
29
+ # bh = BinaryHeap.new{|parent, child| child <=> parent}
30
+ # @example A max heap using self-defined comparator
31
+ # bh = BinaryHeap.new{|parent, child| parent.bigger_or_equal_than(child)}
32
+ def initialize(data=nil, &block)
33
+ @cmp = block || lambda{|parent, child| parent <=> child }
34
+ @mutex = Mutex.new
35
+
36
+ raise ParameterError.new("type of data must be Array or its subclass") unless data.nil? || data.is_a?(Array)
37
+ @ary = data.nil? ? [] : build_from(data)
38
+ end
39
+
40
+ # Insert an element into the heap (heap will be adjusted automatically)
41
+ # @param element [Object] the element to be inserted into the heap
42
+ # @return [BinaryHeap] the binary heap instance itself
43
+ def insert(element)
44
+ @mutex.synchronize do
45
+ @ary.push(element)
46
+ adjust(:bottom_up)
47
+ end
48
+ self
49
+ end
50
+
51
+ alias :push :insert
52
+
53
+ # Eject the top(max or min) element of the heap (heap will be adjusted automatically)
54
+ # @return [Object] the ejected element
55
+ def eject
56
+ @mutex.synchronize do
57
+ return nil if @ary.empty?
58
+ e = @ary.first
59
+ @ary[0] = @ary.last
60
+ @ary.pop
61
+ adjust(:top_down)
62
+ e
63
+ end
64
+ end
65
+
66
+ # Return the the top(max or min) element of the heap
67
+ def top
68
+ @ary.first
69
+ end
70
+
71
+ # Set the top element of the heap
72
+ # @param element [Object]
73
+ # @note in most cases, this method should be used with the BinaryHeap#adjust method
74
+ # @see BinaryHeap#adjust
75
+ def top=(element)
76
+ @ary[0] = element
77
+ end
78
+
79
+ # Return the underlying Aarray data
80
+ def data
81
+ @ary
82
+ end
83
+
84
+ # Adjust the heap to make it in good shape
85
+ # @param direction [Symbol] direction of adjustment, one of [:top_down, bottom_up]
86
+ # @return [BinaryHeap] the binay heap instance itself
87
+ # @note this method should be used carefully since it is not thread-safe, please check the implementation of BinaryHeap#eject and BinaryHeap#insert
88
+ def adjust(direction = :top_down)
89
+ return if @ary.size < 2
90
+ case direction
91
+ when :top_down
92
+ parent_idx = 0
93
+ until is_good?(parent_idx)
94
+ child_idx = target_child_idx(parent_idx)
95
+ swap(parent_idx, child_idx)
96
+ parent_idx = child_idx
97
+ end
98
+ when :bottom_up
99
+ child_idx = @ary.size - 1
100
+ parent_idx = p_idx(child_idx)
101
+ until child_idx == 0 || @cmp.call(@ary[parent_idx], @ary[child_idx]) >= 0
102
+ swap(parent_idx, child_idx)
103
+ child_idx = parent_idx
104
+ parent_idx = p_idx(child_idx)
105
+ end
106
+ else
107
+ raise ParameterError.new("invalid direction type")
108
+ end
109
+ self
110
+ end
111
+
112
+ # Forward methods to underlying Array instance
113
+ def method_missing(name, *args, &blcok)
114
+ @ary.send(name, *args, &blcok)
115
+ end
116
+
117
+ private
118
+ def build_from(data)
119
+ heap = BinaryHeap.new(&(@cmp))
120
+ data.each{|e| heap.insert e}
121
+ heap.data
122
+ end
123
+
124
+ def swap(i, j)
125
+ temp = @ary[i]
126
+ @ary[i] = @ary[j]
127
+ @ary[j] = temp
128
+ end
129
+
130
+ def p_idx(child_idx)
131
+ return nil if child_idx == 0
132
+ child_idx%2 == 0 ? (child_idx-2)/2 : child_idx/2
133
+ end
134
+
135
+ def l_idx(parent_idx)
136
+ 2*parent_idx + 1
137
+ end
138
+
139
+ def r_idx(parent_idx)
140
+ 2*parent_idx + 2
141
+ end
142
+
143
+ def lchid(parent_idx)
144
+ @ary[l_idx(parent_idx)]
145
+ end
146
+
147
+ def rchild(parent_idx)
148
+ @ary[r_idx(parent_idx)]
149
+ end
150
+
151
+ def is_good?(idx)
152
+ if !lchid(idx).nil?
153
+ return false unless @cmp.call(@ary[idx], lchid(idx)) >= 0
154
+ end
155
+
156
+ if !rchild(idx).nil?
157
+ return false unless @cmp.call(@ary[idx], rchild(idx)) >= 0
158
+ end
159
+
160
+ true
161
+ end
162
+
163
+ # return the max/min child index
164
+ def target_child_idx(parent_idx)
165
+ return nil if lchid(parent_idx).nil? && rchild(parent_idx).nil?
166
+
167
+ l = lchid(parent_idx)
168
+ r = rchild(parent_idx)
169
+ l_idx = l_idx(parent_idx)
170
+ r_idx = r_idx(parent_idx)
171
+
172
+ return r_idx if l.nil?
173
+ return l_idx if r.nil?
174
+
175
+ return @cmp.call(l, r) >= 0 ? l_idx : r_idx
176
+ end
177
+
178
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binaryheap
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hailong Cao
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |2
14
+ A standard binary heap implementation in ruby.
15
+ Internally, it uses an array as data store and a mutex to keep insert and eject opeations thread-safe (both on CRuby and JRuby).
16
+ email: hailengc@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/binaryheap.rb
22
+ homepage: https://github.com/hailengc/binaryheap
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.5.2
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: binary heap implementation
46
+ test_files: []