lite-containers 0.0.5
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/.gitignore +5 -0
- data/.rubocop.yml +72 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +127 -0
- data/README.md +145 -0
- data/bench/containers_bench.rb +122 -0
- data/lib/lite/containers/abstract/collection.rb +25 -0
- data/lib/lite/containers/abstract/deque.rb +23 -0
- data/lib/lite/containers/abstract/implicit_key.rb +23 -0
- data/lib/lite/containers/abstract/queue.rb +24 -0
- data/lib/lite/containers/abstract/sorted_map.rb +29 -0
- data/lib/lite/containers/avl_tree/balance.rb +74 -0
- data/lib/lite/containers/avl_tree/delete.rb +32 -0
- data/lib/lite/containers/avl_tree/find.rb +99 -0
- data/lib/lite/containers/avl_tree/implementation.rb +88 -0
- data/lib/lite/containers/avl_tree/insert.rb +27 -0
- data/lib/lite/containers/avl_tree/interfaces/bracket_assign.rb +33 -0
- data/lib/lite/containers/avl_tree/interfaces/key_extraction_strategy/abstract.rb +48 -0
- data/lib/lite/containers/avl_tree/interfaces/key_extraction_strategy/explicit.rb +34 -0
- data/lib/lite/containers/avl_tree/interfaces/key_extraction_strategy/implicit.rb +60 -0
- data/lib/lite/containers/avl_tree/node.rb +35 -0
- data/lib/lite/containers/avl_tree/traversal.rb +43 -0
- data/lib/lite/containers/avl_tree.rb +159 -0
- data/lib/lite/containers/error.rb +7 -0
- data/lib/lite/containers/heap.rb +169 -0
- data/lib/lite/containers/helpers/comparison.rb +106 -0
- data/lib/lite/containers/helpers/key_extractor.rb +29 -0
- data/lib/lite/containers/helpers/merge.rb +49 -0
- data/lib/lite/containers/sorted_array/binary_search.rb +42 -0
- data/lib/lite/containers/sorted_array.rb +188 -0
- data/lib/lite/containers/top_n/abstract.rb +56 -0
- data/lib/lite/containers/top_n/avl_tree.rb +25 -0
- data/lib/lite/containers/top_n/deque.rb +33 -0
- data/lib/lite/containers/top_n/error.rb +11 -0
- data/lib/lite/containers/top_n/heap.rb +32 -0
- data/lib/lite/containers/top_n.rb +31 -0
- data/lib/lite/containers/version.rb +7 -0
- data/lib/lite/containers.rb +3 -0
- data/lite_containers.gemspec +25 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ab4551d5c45aa8900a6f1efc501a564eab1a4a010a1c7ecaa309965a0541c272
|
4
|
+
data.tar.gz: d6fd375ee6d2eb8b0a127cd14190e8bf3f2cf596f2ca1247107a6b951c0d98da
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 35b991f983faff3278c9b86d02d77c1cb86cbd7b5b0ff9b2129aa3046d19c40322dc9134ac528b85676efafc4d490bf2b07364112cfa9f8a7d9aa366f3c08749
|
7
|
+
data.tar.gz: 600d9406293be9e0dae2680b8e7d25faa39ff4f54be3ef77c4f6fdfb1f3d45fb87ef583c2f08ac75c0800db1bc6e276a95f8d50448597cb19f5a63e96e544258
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
NewCops: enable
|
6
|
+
|
7
|
+
Gemspec/RequireMFA:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/ClassLength:
|
11
|
+
Exclude:
|
12
|
+
- 'spec/**/*.rb'
|
13
|
+
|
14
|
+
Metrics/AbcSize:
|
15
|
+
Exclude:
|
16
|
+
- 'spec/**/*.rb'
|
17
|
+
|
18
|
+
Metrics/BlockLength:
|
19
|
+
Exclude:
|
20
|
+
- 'spec/**/*.rb'
|
21
|
+
|
22
|
+
Metrics/CyclomaticComplexity:
|
23
|
+
Exclude:
|
24
|
+
- 'spec/**/*.rb'
|
25
|
+
|
26
|
+
Metrics/MethodLength:
|
27
|
+
Max: 20
|
28
|
+
|
29
|
+
Metrics/ModuleLength:
|
30
|
+
Exclude:
|
31
|
+
- 'spec/**/*.rb'
|
32
|
+
|
33
|
+
Metrics/PerceivedComplexity:
|
34
|
+
Exclude:
|
35
|
+
- 'spec/**/*.rb'
|
36
|
+
|
37
|
+
Naming/MethodParameterName:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Naming/VariableNumber:
|
41
|
+
Exclude:
|
42
|
+
- 'spec/**/*.rb'
|
43
|
+
|
44
|
+
Lint/ShadowingOuterLocalVariable:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
Layout/EndAlignment:
|
48
|
+
EnforcedStyleAlignWith: start_of_line
|
49
|
+
|
50
|
+
RSpec/ContextWording:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
RSpec/ExampleLength:
|
54
|
+
Enabled: false
|
55
|
+
|
56
|
+
RSpec/IndexedLet:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
RSpec/MultipleExpectations:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
RSpec/MultipleMemoizedHelpers:
|
63
|
+
Enabled: false
|
64
|
+
|
65
|
+
Style/ArgumentsForwarding:
|
66
|
+
Enabled: false
|
67
|
+
|
68
|
+
Style/Documentation:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
Style/HashSyntax:
|
72
|
+
Enabled: false
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.8.7)
|
5
|
+
public_suffix (>= 2.0.2, < 7.0)
|
6
|
+
ast (2.4.2)
|
7
|
+
axiom-types (0.1.1)
|
8
|
+
descendants_tracker (~> 0.0.4)
|
9
|
+
ice_nine (~> 0.11.0)
|
10
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
11
|
+
byebug (11.1.3)
|
12
|
+
childprocess (5.1.0)
|
13
|
+
logger (~> 1.5)
|
14
|
+
coercible (1.0.0)
|
15
|
+
descendants_tracker (~> 0.0.1)
|
16
|
+
descendants_tracker (0.0.4)
|
17
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
18
|
+
diff-lcs (1.5.1)
|
19
|
+
docile (1.4.1)
|
20
|
+
erubi (1.13.1)
|
21
|
+
flay (2.13.3)
|
22
|
+
erubi (~> 1.10)
|
23
|
+
path_expander (~> 1.0)
|
24
|
+
ruby_parser (~> 3.0)
|
25
|
+
sexp_processor (~> 4.0)
|
26
|
+
flog (4.8.0)
|
27
|
+
path_expander (~> 1.0)
|
28
|
+
ruby_parser (~> 3.1, > 3.1.0)
|
29
|
+
sexp_processor (~> 4.8)
|
30
|
+
ice_nine (0.11.2)
|
31
|
+
json (2.9.1)
|
32
|
+
kwalify (0.7.2)
|
33
|
+
language_server-protocol (3.17.0.3)
|
34
|
+
launchy (3.0.1)
|
35
|
+
addressable (~> 2.8)
|
36
|
+
childprocess (~> 5.0)
|
37
|
+
logger (1.6.4)
|
38
|
+
markly (0.12.1)
|
39
|
+
parallel (1.26.3)
|
40
|
+
parser (3.2.2.4)
|
41
|
+
ast (~> 2.4.1)
|
42
|
+
racc
|
43
|
+
path_expander (1.1.3)
|
44
|
+
public_suffix (5.1.1)
|
45
|
+
racc (1.8.1)
|
46
|
+
rainbow (3.1.1)
|
47
|
+
reek (6.1.4)
|
48
|
+
kwalify (~> 0.7.0)
|
49
|
+
parser (~> 3.2.0)
|
50
|
+
rainbow (>= 2.0, < 4.0)
|
51
|
+
regexp_parser (2.10.0)
|
52
|
+
rexml (3.4.0)
|
53
|
+
rspec (3.13.0)
|
54
|
+
rspec-core (~> 3.13.0)
|
55
|
+
rspec-expectations (~> 3.13.0)
|
56
|
+
rspec-mocks (~> 3.13.0)
|
57
|
+
rspec-core (3.13.2)
|
58
|
+
rspec-support (~> 3.13.0)
|
59
|
+
rspec-expectations (3.13.3)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.13.0)
|
62
|
+
rspec-mocks (3.13.2)
|
63
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
64
|
+
rspec-support (~> 3.13.0)
|
65
|
+
rspec-support (3.13.2)
|
66
|
+
rubocop (1.59.0)
|
67
|
+
json (~> 2.3)
|
68
|
+
language_server-protocol (>= 3.17.0)
|
69
|
+
parallel (~> 1.10)
|
70
|
+
parser (>= 3.2.2.4)
|
71
|
+
rainbow (>= 2.2.2, < 4.0)
|
72
|
+
regexp_parser (>= 1.8, < 3.0)
|
73
|
+
rexml (>= 3.2.5, < 4.0)
|
74
|
+
rubocop-ast (>= 1.30.0, < 2.0)
|
75
|
+
ruby-progressbar (~> 1.7)
|
76
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
77
|
+
rubocop-ast (1.30.0)
|
78
|
+
parser (>= 3.2.1.0)
|
79
|
+
rubocop-rspec (3.0.0)
|
80
|
+
rubocop (~> 1.40)
|
81
|
+
ruby-prof (1.6.3)
|
82
|
+
ruby-progressbar (1.13.0)
|
83
|
+
ruby_parser (3.21.1)
|
84
|
+
racc (~> 1.5)
|
85
|
+
sexp_processor (~> 4.16)
|
86
|
+
rubycritic (4.9.1)
|
87
|
+
flay (~> 2.13)
|
88
|
+
flog (~> 4.7)
|
89
|
+
launchy (>= 2.5.2)
|
90
|
+
parser (>= 3.2.2.4)
|
91
|
+
rainbow (~> 3.1.1)
|
92
|
+
reek (~> 6.0, < 6.2)
|
93
|
+
rexml
|
94
|
+
ruby_parser (~> 3.21)
|
95
|
+
simplecov (>= 0.22.0)
|
96
|
+
tty-which (~> 0.5.0)
|
97
|
+
virtus (~> 2.0)
|
98
|
+
sexp_processor (4.17.3)
|
99
|
+
simplecov (0.22.0)
|
100
|
+
docile (~> 1.1)
|
101
|
+
simplecov-html (~> 0.11)
|
102
|
+
simplecov_json_formatter (~> 0.1)
|
103
|
+
simplecov-html (0.13.1)
|
104
|
+
simplecov_json_formatter (0.1.4)
|
105
|
+
thread_safe (0.3.6)
|
106
|
+
tty-which (0.5.0)
|
107
|
+
unicode-display_width (2.6.0)
|
108
|
+
virtus (2.0.0)
|
109
|
+
axiom-types (~> 0.1)
|
110
|
+
coercible (~> 1.0)
|
111
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
112
|
+
|
113
|
+
PLATFORMS
|
114
|
+
x86_64-linux
|
115
|
+
|
116
|
+
DEPENDENCIES
|
117
|
+
byebug (~> 11)
|
118
|
+
markly
|
119
|
+
rspec (~> 3)
|
120
|
+
rubocop
|
121
|
+
rubocop-rspec
|
122
|
+
ruby-prof
|
123
|
+
rubycritic
|
124
|
+
simplecov
|
125
|
+
|
126
|
+
BUNDLED WITH
|
127
|
+
2.4.1
|
data/README.md
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# lite-containers
|
2
|
+
|
3
|
+
A collection of containers implemented in pure Ruby.
|
4
|
+
|
5
|
+
- `Heap` uses the well-known heap algorithm. The heap contract
|
6
|
+
guarantees that the highest ranking element is always on top
|
7
|
+
- `SortedArray` uses built-in quick sort and binary search to
|
8
|
+
keep elements ordered
|
9
|
+
- `AvlTree` keeps elements in a self-balancing binary search tree
|
10
|
+
|
11
|
+
## Common features
|
12
|
+
All containers have the following in common:
|
13
|
+
- `empty?` and `size` methods to query the size
|
14
|
+
(along with the synonyms: `length`, `count`)
|
15
|
+
- the `drain!` method that empties the container and
|
16
|
+
returns all elements in descending order (top priority first).
|
17
|
+
|
18
|
+
## Constructor parameters
|
19
|
+
To keep elements sorted the containers need certain parameters
|
20
|
+
and helpers provided by the client code.
|
21
|
+
- First parameter to the constructor is always `type`, taking values
|
22
|
+
of `min` and `max`, telling the container how to interpret priority.
|
23
|
+
- `key_extractor` is a proc that receives an element and produces
|
24
|
+
the key used for sorting. When key extractor is `nil`, the
|
25
|
+
value itself is considered the sort key.
|
26
|
+
- Sorted array and AVL tree can only operate on unique
|
27
|
+
entries. Their constructors expect `merge` parameter
|
28
|
+
to supply a merge strategy. Allowed values are `:replace`, `:keep`
|
29
|
+
or a proc that performs the merge. The proc should take two elements
|
30
|
+
as parameters and return a single one. The default strategy
|
31
|
+
for both containers is `:replace` meaning that in presence of duplicates
|
32
|
+
the new element replaces the old one.
|
33
|
+
|
34
|
+
Helpers must honor the following rules for the containers
|
35
|
+
to maintain consistent sort order:
|
36
|
+
- The key extractor must always return the same key for the same value
|
37
|
+
- The new merged element must resolve to the same key
|
38
|
+
as the original two.
|
39
|
+
|
40
|
+
Containers do not enforce these rules; it is the responsibility
|
41
|
+
of the client code to provide correctly implemented helpers.
|
42
|
+
|
43
|
+
## Heap
|
44
|
+
Heap is the simplest of the three containers in this library.
|
45
|
+
It supports `push`/`<<` method that inserts an element, `top` to
|
46
|
+
return the top priority element, and `pop` to remove the top priority
|
47
|
+
element.
|
48
|
+
|
49
|
+
```ruby rspec heap
|
50
|
+
require 'lite/containers/heap'
|
51
|
+
|
52
|
+
heap = Lite::Containers::Heap.instance(:max)
|
53
|
+
heap << 5 << 1 << 3
|
54
|
+
|
55
|
+
expect(heap.top).to eq(5)
|
56
|
+
expect(heap.pop).to eq(5)
|
57
|
+
expect(heap.drain!).to eq([3, 1])
|
58
|
+
```
|
59
|
+
|
60
|
+
## Sorted array
|
61
|
+
Useful for maintaining relatively stable lists that either never change
|
62
|
+
or change infrequently. Each insert or delete will reallocate
|
63
|
+
the underlying array making it really inefficient
|
64
|
+
when the churn is high.
|
65
|
+
|
66
|
+
Depending on the nature of the data, different constructors
|
67
|
+
can be used to build the sorted array. If the
|
68
|
+
data come in random order it is necessary to call `from_unsorted`
|
69
|
+
to have the container sort them.
|
70
|
+
If the data are already sorted, `from_sorted` could be used,
|
71
|
+
or even `from_sorted_unsafe`. The former checks whether the data
|
72
|
+
are properly sorted and raises an error when it discovers
|
73
|
+
unexpected ordering. The second one performs no check on
|
74
|
+
the data leaving that responsibility to the client code.
|
75
|
+
|
76
|
+
Sorted array includes the `Enumerable` module, giving
|
77
|
+
access to all its methods like `map`, `reduce` etc.
|
78
|
+
|
79
|
+
|
80
|
+
```ruby rspec sorted_array
|
81
|
+
require 'lite/containers/sorted_array'
|
82
|
+
|
83
|
+
sorted_array = Lite::Containers::SortedArray.from_unsorted(
|
84
|
+
:max,
|
85
|
+
[{ income: 0, tax: 15 }, { income: 120_000, tax: 25 }, { income: 60_000, tax: 20 }],
|
86
|
+
key_extractor: proc { |elem| elem[:income] }
|
87
|
+
)
|
88
|
+
|
89
|
+
expect(sorted_array.front).to eq({ income: 120_000, tax: 25 })
|
90
|
+
expect(sorted_array.back).to eq({ income: 0, tax: 15 })
|
91
|
+
expect(sorted_array.find(60_000)).to eq({ income: 60_000, tax: 20 })
|
92
|
+
expect(sorted_array.find_or_nearest_forwards(80_000)).to eq({ income: 120_000, tax: 25 })
|
93
|
+
expect(sorted_array.find_or_nearest_backwards(80_000)).to eq({ income: 60_000, tax: 20 })
|
94
|
+
```
|
95
|
+
|
96
|
+
## AVL tree
|
97
|
+
Comes in two flavors – `ImplicitKey` and `ExplicitKey`.
|
98
|
+
|
99
|
+
- `ImplicitKey` implements `push`/`<<` with single parameter – the element.
|
100
|
+
It may need a *key extractor* to be supplied.
|
101
|
+
|
102
|
+
```ruby rspec avl_tree_implicit
|
103
|
+
require 'lite/containers/avl_tree'
|
104
|
+
|
105
|
+
tree = Lite::Containers::AvlTree::ImplicitKey.instance(
|
106
|
+
:max,
|
107
|
+
key_extractor: proc { |elem| elem[:time] }
|
108
|
+
)
|
109
|
+
|
110
|
+
tree << { time: 0, t: 30 } << { time: 45, t: 600 } << { time: 60, t: 800 } << { time: 120, t: 750 }
|
111
|
+
|
112
|
+
expect(tree.front).to eq({ time: 120, t: 750 })
|
113
|
+
expect(tree.back).to eq({ time: 0, t: 30 })
|
114
|
+
expect(tree.find(45)).to eq({ time: 45, t: 600 })
|
115
|
+
expect(tree.find_or_nearest_forwards(55)).to eq({ time: 60, t: 800 })
|
116
|
+
expect(tree.find_or_nearest_backwards(55)).to eq({ time: 45, t: 600 })
|
117
|
+
```
|
118
|
+
|
119
|
+
- `ExplicitKey` implements `insert` that takes two parameters – the key and the value.
|
120
|
+
Methods like `front`, `back`, `find` etc. return a key/value tuple instead
|
121
|
+
of a single element.
|
122
|
+
|
123
|
+
```ruby rspec avl_tree_explicit
|
124
|
+
require 'lite/containers/avl_tree'
|
125
|
+
|
126
|
+
tree = Lite::Containers::AvlTree::ExplicitKey.instance(
|
127
|
+
:min,
|
128
|
+
merge: proc { |old, fresh| { count: old[:count] + fresh[:count] } }
|
129
|
+
)
|
130
|
+
|
131
|
+
tree.insert(0, { count: 1 })
|
132
|
+
.insert(10, { count: 1 })
|
133
|
+
.insert(10, { count: 1 })
|
134
|
+
.insert(20, { count: 1 })
|
135
|
+
.insert(30, { count: 1 })
|
136
|
+
|
137
|
+
expect(tree.pop_front).to eq([0, { count: 1 }])
|
138
|
+
expect(tree.pop_back).to eq([30, { count: 1 }])
|
139
|
+
expect(tree.drain!).to eq([[10, { count: 2 }], [20, { count: 1 }]])
|
140
|
+
```
|
141
|
+
|
142
|
+
AVL tree also includes the `Enumerable` module.
|
143
|
+
|
144
|
+
## License
|
145
|
+
This library is published under MIT license
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require_relative '../lib/lite/containers/sorted_array'
|
5
|
+
require_relative '../lib/lite/containers/heap'
|
6
|
+
require_relative '../lib/lite/containers/avl_tree'
|
7
|
+
|
8
|
+
module Lite
|
9
|
+
module Containers
|
10
|
+
module AbstractBench
|
11
|
+
def measure(n)
|
12
|
+
warm_up
|
13
|
+
subjects = populate(n)
|
14
|
+
output_to_sorted_array(subjects)
|
15
|
+
end
|
16
|
+
|
17
|
+
def warm_up
|
18
|
+
puts ' warm up ...'
|
19
|
+
|
20
|
+
100.times do
|
21
|
+
subject = initial_subject
|
22
|
+
1000.times do
|
23
|
+
insert_single(subject, rand(1000))
|
24
|
+
output(subject)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def populate(n)
|
30
|
+
puts "\n populate ..."
|
31
|
+
|
32
|
+
subjects = []
|
33
|
+
times = 100_000 / n
|
34
|
+
|
35
|
+
result = Benchmark.measure do
|
36
|
+
times.times do
|
37
|
+
subject = initial_subject
|
38
|
+
n.times do
|
39
|
+
insert_single(subject, rand(n))
|
40
|
+
end
|
41
|
+
subjects << subject
|
42
|
+
end
|
43
|
+
end
|
44
|
+
puts result
|
45
|
+
subjects
|
46
|
+
end
|
47
|
+
|
48
|
+
def output_to_sorted_array(subjects)
|
49
|
+
puts "\n output from #{subjects.length} subjects to sorted array of #{subjects.first&.length || 0}"
|
50
|
+
|
51
|
+
result = Benchmark.measure do
|
52
|
+
subjects.each do |subject|
|
53
|
+
output(subject)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
puts result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module ArrayBench
|
61
|
+
extend AbstractBench
|
62
|
+
|
63
|
+
def self.initial_subject
|
64
|
+
SortedArray.from_sorted_unsafe(:max, [])
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.insert_single(array, value)
|
68
|
+
array << value
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.output(array)
|
72
|
+
array.to_array
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module HeapBench
|
77
|
+
extend AbstractBench
|
78
|
+
|
79
|
+
def self.initial_subject
|
80
|
+
Heap.instance(:max)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.insert_single(heap, value)
|
84
|
+
heap << value
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.output(heap)
|
88
|
+
heap.drain!
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
module AvlTreeBench
|
93
|
+
extend AbstractBench
|
94
|
+
|
95
|
+
def self.initial_subject
|
96
|
+
AvlTree::ExplicitKey.instance :max
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.insert_single(tree, value)
|
100
|
+
tree.insert(value, nil)
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.output(tree)
|
104
|
+
tree.traverse.to_a
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
N = 10_000
|
111
|
+
|
112
|
+
puts "------------- ARRAY #{N} -------------"
|
113
|
+
Lite::Containers::ArrayBench.measure(N)
|
114
|
+
puts '--------------------------------------'
|
115
|
+
puts "------------- HEAP #{N} --------------"
|
116
|
+
Lite::Containers::HeapBench.measure(N)
|
117
|
+
puts '--------------------------------------'
|
118
|
+
puts "------------- AVL #{N} ---------------"
|
119
|
+
Lite::Containers::AvlTreeBench.measure(N)
|
120
|
+
puts '--------------------------------------'
|
121
|
+
|
122
|
+
# https://stackoverflow.com/questions/6531543/efficient-implementation-of-binary-heaps
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Containers
|
5
|
+
module Abstract
|
6
|
+
module Collection
|
7
|
+
def size
|
8
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
9
|
+
end
|
10
|
+
|
11
|
+
def length
|
12
|
+
size
|
13
|
+
end
|
14
|
+
|
15
|
+
def count
|
16
|
+
size
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty?
|
20
|
+
size.zero?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'queue'
|
4
|
+
|
5
|
+
module Lite
|
6
|
+
module Containers
|
7
|
+
module Abstract
|
8
|
+
module Deque
|
9
|
+
include Queue
|
10
|
+
|
11
|
+
# Removes and returns lowest priority element. Does nothing if empty
|
12
|
+
def back
|
13
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
14
|
+
end
|
15
|
+
|
16
|
+
# Removes and returns lowest priority element. Does nothing if empty
|
17
|
+
def pop_back
|
18
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'queue'
|
4
|
+
|
5
|
+
module Lite
|
6
|
+
module Containers
|
7
|
+
module Abstract
|
8
|
+
module ImplicitKey
|
9
|
+
# Inserts element at its corresponding place.
|
10
|
+
# In case element is already there applies merge strategy
|
11
|
+
def push(_element)
|
12
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Inserts element the way push does. Returns self
|
16
|
+
def <<(element)
|
17
|
+
push(element)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Containers
|
5
|
+
module Abstract
|
6
|
+
module Queue
|
7
|
+
# Returns highest priority element or nil if empty
|
8
|
+
def front
|
9
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Removes and returns highest priority element or nil if empty
|
13
|
+
def pop_front
|
14
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns all elements in descending order by priority
|
18
|
+
def drain!
|
19
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Containers
|
5
|
+
module Abstract
|
6
|
+
module SortedMap
|
7
|
+
# Returns if key exists, false otherwise
|
8
|
+
def key?(key)
|
9
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns element under given key if exists
|
13
|
+
def find(_key)
|
14
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns element under given key or nearest element under lower priority key if exists
|
18
|
+
def find_or_nearest_backwards(_key)
|
19
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns value under given key or nearest element under higher priority key if exists
|
23
|
+
def find_or_nearest_forwards(_key)
|
24
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Containers
|
5
|
+
class AvlTree
|
6
|
+
module Balance
|
7
|
+
def rebalance(node) # rubocop:disable Metrics/AbcSize
|
8
|
+
case balance_factor(node)
|
9
|
+
when -2
|
10
|
+
if height(node.left.right) > height(node.left.left)
|
11
|
+
rotate_left_right(node)
|
12
|
+
else
|
13
|
+
rotate_right(node)
|
14
|
+
end
|
15
|
+
when 2
|
16
|
+
if height(node.right.left) > height(node.right.right)
|
17
|
+
rotate_right_left(node)
|
18
|
+
else
|
19
|
+
rotate_left(node)
|
20
|
+
end
|
21
|
+
else
|
22
|
+
update_height(node)
|
23
|
+
node
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rotate_left(node)
|
28
|
+
right = node.right
|
29
|
+
node.right = right.left
|
30
|
+
right.left = node
|
31
|
+
update_height node
|
32
|
+
update_height right
|
33
|
+
right
|
34
|
+
end
|
35
|
+
|
36
|
+
def rotate_right(node)
|
37
|
+
left = node.left
|
38
|
+
node.left = left.right
|
39
|
+
left.right = node
|
40
|
+
update_height node
|
41
|
+
update_height left
|
42
|
+
left
|
43
|
+
end
|
44
|
+
|
45
|
+
def rotate_right_left(node)
|
46
|
+
node.right = rotate_right(node.right)
|
47
|
+
rotate_left(node)
|
48
|
+
end
|
49
|
+
|
50
|
+
def rotate_left_right(node)
|
51
|
+
node.left = rotate_left(node.left)
|
52
|
+
rotate_right(node)
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_height(node)
|
56
|
+
left_height = height(node.left)
|
57
|
+
right_height = height(node.right)
|
58
|
+
# rubocop:disable Style/MinMaxComparison
|
59
|
+
max = left_height > right_height ? left_height : right_height
|
60
|
+
# rubocop:enable Style/MinMaxComparison
|
61
|
+
node.height = max + 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def balance_factor(node)
|
65
|
+
height(node.right) - height(node.left)
|
66
|
+
end
|
67
|
+
|
68
|
+
def height(node)
|
69
|
+
node&.height || 0
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|