|
18 | 18 | # swap nodes at each layer of the tree, and there are log(n, base b) layers
|
19 | 19 | # to the tree.
|
20 | 20 | #
|
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 |
33 | 37 | end
|
34 |
| - end |
35 | 38 |
|
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 |
43 | 46 |
|
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 |
55 | 58 |
|
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 |
61 | 64 |
|
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 |
73 | 77 | end
|
74 |
| - self |
75 |
| - end |
76 | 78 |
|
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 |
91 | 94 | end
|
92 |
| - self |
93 |
| - end |
94 | 95 |
|
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 |
100 | 101 |
|
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 |
110 | 111 |
|
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 |
126 | 128 | end
|
127 | 129 | end
|
0 commit comments