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.
- checksums.yaml +7 -0
- data/lib/binaryheap.rb +178 -0
- metadata +46 -0
checksums.yaml
ADDED
|
@@ -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
|
data/lib/binaryheap.rb
ADDED
|
@@ -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: []
|