Skip to content

Commit df5544b

Browse files
committed
slightly reimplement heap using new CompleteTree
1 parent a389ea8 commit df5544b

File tree

2 files changed

+113
-111
lines changed

2 files changed

+113
-111
lines changed

Diff for: lib/compsci/heap.rb

+96-94
Original file line numberDiff line numberDiff line change
@@ -18,110 +18,112 @@
1818
# swap nodes at each layer of the tree, and there are log(n, base b) layers
1919
# to the tree.
2020
#
21-
class CompSci::Heap < CompSci::CompleteTree
22-
# * defaults to a MaxHeap, with the largest node at the root
23-
# * specify a minheap with minheap: true or cmp_val: -1
24-
#
25-
def initialize(cmp_val: 1, minheap: false, child_slots: 2)
26-
super(child_slots: child_slots)
27-
cmp_val = -1 if minheap
28-
case cmp_val
29-
when -1, 1
30-
@cmp_val = cmp_val
31-
else
32-
raise(ArgumentError, "unknown comparison value: #{cmp_val}")
21+
module CompSci
22+
class Heap
23+
attr_reader :tree, :cmp_val
24+
25+
# * defaults to a MaxHeap, with the largest node at the root
26+
# * specify a minheap with minheap: true or cmp_val: -1
27+
#
28+
def initialize(cmp_val: 1, minheap: false, child_slots: 2)
29+
@tree = CompleteTree.new(child_slots: child_slots)
30+
cmp_val = -1 if minheap
31+
case cmp_val
32+
when -1, 1
33+
@cmp_val = cmp_val
34+
else
35+
raise(ArgumentError, "unknown comparison value: #{cmp_val}")
36+
end
3337
end
34-
end
3538

36-
# * append to the array
37-
# * sift_up -- O(log n) on heap size
38-
#
39-
def push(node)
40-
@array.push(node)
41-
self.sift_up(@array.size - 1)
42-
end
39+
# * append to the array
40+
# * sift_up -- O(log n) on heap size
41+
#
42+
def push(node)
43+
@tree.push(node)
44+
self.sift_up(@tree.size - 1)
45+
end
4346

44-
# * remove from the front of the array
45-
# * move last node to root
46-
# * sift_down -- O(log n) on heap size
47-
#
48-
def pop
49-
node = @array.shift
50-
replacement = @array.pop
51-
@array.unshift replacement if replacement
52-
self.sift_down(0)
53-
node
54-
end
47+
# * remove from the front of the array
48+
# * move last node to root
49+
# * sift_down -- O(log n) on heap size
50+
#
51+
def pop
52+
node = @tree.shift
53+
replacement = @tree.pop
54+
@tree.unshift replacement if replacement
55+
self.sift_down(0)
56+
node
57+
end
5558

56-
# * return what pop would return (avoid sifting)
57-
#
58-
def peek
59-
@array.first
60-
end
59+
# * return what pop would return (avoid sifting)
60+
#
61+
def peek
62+
@tree.first
63+
end
6164

62-
# * called recursively
63-
# * idx represents the node suspected to violate the heap
64-
# * intended to be O(log n) on heap size (log base child_slots)
65-
#
66-
def sift_up(idx = nil)
67-
idx ||= @array.size - 1
68-
return self if idx <= 0
69-
pidx = self.class.parent_idx(idx, @child_slots)
70-
if !self.heapish?(pidx, idx)
71-
@array[idx], @array[pidx] = @array[pidx], @array[idx] # swap
72-
self.sift_up(pidx)
65+
# * called recursively
66+
# * idx represents the node suspected to violate the heap
67+
# * intended to be O(log n) on heap size (log base child_slots)
68+
#
69+
def sift_up(idx = @tree.size - 1)
70+
return self if idx <= 0
71+
pidx = @tree.class.parent_idx(idx, @tree.child_slots)
72+
if !heapish?(pidx, idx)
73+
@tree.swap(idx, pidx)
74+
sift_up(pidx)
75+
end
76+
self
7377
end
74-
self
75-
end
7678

77-
# * called recursively
78-
# * idx represents the node suspected to violate the heap
79-
# * intended to be O(log n) on heap size (log base child_slots)
80-
# * slower than sift_up because one parent vs multiple children
81-
#
82-
def sift_down(idx = nil)
83-
idx ||= 0
84-
return self if idx >= @array.size
85-
cidxs = self.class.children_idx(idx, @child_slots)
86-
# promote the heapiest child
87-
cidx = self.heapiest(cidxs)
88-
if !self.heapish?(idx, cidx)
89-
@array[idx], @array[cidx] = @array[cidx], @array[idx] # swap
90-
self.sift_down(cidx)
79+
# * called recursively
80+
# * idx represents the node suspected to violate the heap
81+
# * intended to be O(log n) on heap size (log base child_slots)
82+
# * slower than sift_up because one parent vs multiple children
83+
#
84+
def sift_down(idx = 0)
85+
return self if idx >= @tree.size
86+
cidxs = @tree.class.children_idx(idx, @tree.child_slots)
87+
# promote the heapiest child
88+
cidx = self.heapiest(cidxs)
89+
if !self.heapish?(idx, cidx)
90+
@tree.swap(idx, cidx)
91+
self.sift_down(cidx)
92+
end
93+
self
9194
end
92-
self
93-
end
9495

95-
# are values of parent and child (by index) in accordance with heap property?
96-
#
97-
def heapish?(pidx, cidx)
98-
(@array[pidx] <=> @array[cidx]) != (@cmp_val * -1)
99-
end
96+
# do values of parent and child (by index) meet the heap test?
97+
#
98+
def heapish?(pidx, cidx)
99+
(@tree[pidx] <=> @tree[cidx]) != (@cmp_val * -1)
100+
end
100101

101-
# return the heapiest idx in cidxs
102-
#
103-
def heapiest(cidxs)
104-
idx = cidxs.first
105-
cidxs.each { |cidx|
106-
idx = cidx if cidx < @array.size and self.heapish?(cidx, idx)
107-
}
108-
idx
109-
end
102+
# return the heapiest idx in cidxs
103+
#
104+
def heapiest(cidxs)
105+
idx = cidxs.first
106+
cidxs.each { |cidx|
107+
idx = cidx if cidx < @tree.size and heapish?(cidx, idx)
108+
}
109+
idx
110+
end
110111

111-
# * not used internally
112-
# * checks that every node satisfies the heap property
113-
# * calls heapish? on idx's children and then heap? on them recursively
114-
#
115-
def heap?(idx: 0)
116-
check_children = []
117-
self.class.children_idx(idx, @child_slots).each { |cidx|
118-
# cidx is arithmetically produced; the corresponding child may not exist
119-
if cidx < @array.size
120-
return false unless self.heapish?(idx, cidx)
121-
check_children << cidx
122-
end
123-
}
124-
check_children.each { |cidx| return false unless self.heap?(idx: cidx) }
125-
true
112+
# * not used internally
113+
# * checks that every node satisfies the heap property
114+
# * calls heapish? on idx's children and then heap? on them recursively
115+
#
116+
def heap?(idx: 0)
117+
check_children = []
118+
@tree.class.children_idx(idx, @tree.child_slots).each { |cidx|
119+
# cidx may not be valid
120+
if cidx < @tree.size
121+
return false unless heapish?(idx, cidx)
122+
check_children << cidx
123+
end
124+
}
125+
check_children.each { |cidx| return false unless self.heap?(idx: cidx) }
126+
true
127+
end
126128
end
127129
end

Diff for: test/heap.rb

+17-17
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@
1414

1515
it "must satisfy the heap property" do
1616
expect(@maxheap.heap?).must_equal true
17-
expect(@maxheap.array).wont_equal @inserts
18-
expect(@maxheap.array).wont_equal @inserts.reverse
17+
expect(@maxheap.tree.array).wont_equal @inserts
18+
expect(@maxheap.tree.array).wont_equal @inserts.reverse
1919
end
2020

2121
it "must recognize heap violations" do
22-
@maxheap.array.unshift 0
22+
@maxheap.tree.unshift 0
2323
expect(@maxheap.heap?).must_equal false
24-
@maxheap.array.shift
24+
@maxheap.tree.shift
2525
expect(@maxheap.heap?).must_equal true
2626

27-
@maxheap.array.push 10
27+
@maxheap.tree.push 10
2828
expect(@maxheap.heap?).must_equal false
2929
@maxheap.sift_up
3030
expect(@maxheap.heap?).must_equal true
@@ -37,7 +37,7 @@
3737
end
3838

3939
it "must heapish?" do
40-
expect(@maxheap.array[0]).must_be :>, @maxheap.array[1]
40+
expect(@maxheap.tree[0]).must_be :>, @maxheap.tree[1]
4141
expect(@maxheap.heapish?(0, 1)).must_equal true
4242
end
4343

@@ -57,16 +57,16 @@
5757

5858
it "must satisfy the heap property" do
5959
expect(@minheap.heap?).must_equal true
60-
expect(@minheap.array).must_equal @inserts
60+
expect(@minheap.tree.array).must_equal @inserts
6161
end
6262

6363
it "must recognize heap violations" do
64-
@minheap.array.unshift 10
64+
@minheap.tree.unshift 10
6565
expect(@minheap.heap?).must_equal false
66-
@minheap.array.shift
66+
@minheap.tree.shift
6767
expect(@minheap.heap?).must_equal true
6868

69-
@minheap.array.push 0
69+
@minheap.tree.push 0
7070
expect(@minheap.heap?).must_equal false
7171
@minheap.sift_up
7272
expect(@minheap.heap?).must_equal true
@@ -79,7 +79,7 @@
7979
end
8080

8181
it "must heapish?" do
82-
expect(@minheap.array[0]).must_be :<, @minheap.array[1]
82+
expect(@minheap.tree[0]).must_be :<, @minheap.tree[1]
8383
expect(@minheap.heapish?(0, 1)).must_equal true
8484
end
8585

@@ -99,17 +99,17 @@
9999

100100
it "must satisfy the heap property" do
101101
expect(@heap3.heap?).must_equal true
102-
expect(@heap3.array).wont_equal @inserts
103-
expect(@heap3.array).wont_equal @inserts.reverse
102+
expect(@heap3.tree.array).wont_equal @inserts
103+
expect(@heap3.tree.array).wont_equal @inserts.reverse
104104
end
105105

106106
it "must recognize heap violations" do
107-
@heap3.array.unshift 0
107+
@heap3.tree.unshift 0
108108
expect(@heap3.heap?).must_equal false
109-
@heap3.array.shift
109+
@heap3.tree.shift
110110
expect(@heap3.heap?).must_equal true
111111

112-
@heap3.array.push 10
112+
@heap3.tree.push 10
113113
expect(@heap3.heap?).must_equal false
114114
@heap3.sift_up
115115
expect(@heap3.heap?).must_equal true
@@ -122,7 +122,7 @@
122122
end
123123

124124
it "must heapish?" do
125-
expect(@heap3.array[0]).must_be :>, @heap3.array[1]
125+
expect(@heap3.tree[0]).must_be :>, @heap3.tree[1]
126126
expect(@heap3.heapish?(0, 1)).must_equal true
127127
end
128128

0 commit comments

Comments
 (0)