-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathheap.rb
158 lines (124 loc) · 2.58 KB
/
heap.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
module HeapType
MIN = 0
MAX = 1
end
class Heap
attr_reader :type
def initialize(nums = [], type = HeapType::MAX, &compatrator)
@items = nums
@type = type
set_comparator(&compatrator)
build_heap(@items) if nums.any?
end
def clear
@items = []
end
def add(element)
@items << element
rebalance_up(size - 1)
end
alias << add
def peek
@items[0]
end
def empty?
size == 0
end
def any?
!empty?
end
def is_max_heap?
@type == HeapType::MAX
end
def is_min_heap?
@type == HeapType::MIN
end
def print
puts @items.to_s
end
# remove root node
def poll
return raise 'Heap Empty' if @items.length == 0
item = @items[0]
@items[0] = @items[-1]
@items.pop
heapify_down
item
end
def size
@items.count
end
private
def heapify_down
heapify(@items, 0)
end
def is_leaf?(i)
!has_left?(i) && !has_right?(i)
end
def has_left?(i)
left(i) < size
end
def has_right?(i)
right(i) < size
end
def swap(i, j)
@items[i], @items[j] = @items[j], @items[i]
end
def first_non_leaf_index
size / 2 - 1
end
def left(i)
2 * i + 1
end
def right(i)
2 * i + 2
end
def parent(i)
((i - 1) / 2).floor
end
def has_parent?(i)
i >= 1
end
def set_comparator(&comparator)
@compare = if block_given?
comparator
elsif is_min_heap?
->(a, b) { a < b }
else
->(a, b) { a > b }
end
end
def compare(a, b)
@compare.call(a, b)
end
def heapify(nums = @items, index)
length = nums.length
left = left(index)
right = right(index)
new_index = index
# if max heap, left or right will be greater than new_index
compatrator = is_max_heap? ? :> : :<
# equivalent to has_left?(index) && nums[left] > nums[new_index] in case of max heap
new_index = left if has_left?(index) && compare(nums[left], nums[new_index])
new_index = right if has_right?(index) && compare(nums[right], nums[new_index])
return if new_index == index
swap(new_index, index)
heapify(nums, new_index)
end
def rebalance_up(index)
parent_index = parent(index)
# puts [index, parent_index].to_s
if has_parent?(index) && compare(@items[index], @items[parent_index])
swap(index, parent_index)
# puts parent_index
rebalance_up(parent_index)
end
end
def build_heap(nums)
length = nums.length
start = first_non_leaf_index
start.downto(0).each do |i|
heapify(nums, i)
end
end
end