1- # Design a data type that supports:
2-
3- # - insert in logarithmic time
4- # - find-the-median in constant time
5- # - remove-the-median in logarithmic time
6-
7- # If the number of keys in the data type is even, find/remove the lower median.
8-
9- class MedianStruct
1+ class MedianStructBaseline
102 attr_reader :array
113
12- def initialize ( array )
13- @array = array . sort
4+ def initialize
5+ @array = [ ]
146 end
157
168 def insert ( n )
@@ -19,10 +11,165 @@ def insert(n)
1911 end
2012
2113 def median
22- array [ array . size / 2 ]
14+ array [ median_i ]
2315 end
2416
2517 def remove_median
26- array . delete_at ( array . size / 2 )
18+ array . delete_at ( median_i )
19+ end
20+
21+ private def median_i
22+ ( array . size - 1 ) / 2
2723 end
2824end
25+
26+ class Heap
27+ attr_reader :arr , :type
28+
29+ def initialize ( type )
30+ @arr = [ ]
31+ @type = type
32+ end
33+
34+ def push ( value )
35+ i = arr . size
36+ arr << value
37+ parent_i = ( i - 1 ) / 2
38+
39+ while i > 0 && cmp ( arr [ i ] , arr [ parent_i ] )
40+ swap ( i , parent_i )
41+ i = parent_i
42+ parent_i = ( i - 1 ) / 2
43+ end
44+ end
45+
46+ def top
47+ arr . first
48+ end
49+
50+ def pop
51+ value = arr [ 0 ]
52+ last = arr . pop
53+ unless arr . empty?
54+ arr [ 0 ] = last
55+ heapify! ( 0 )
56+ end
57+ value
58+ end
59+
60+ def length
61+ arr . size
62+ end
63+
64+ private def heapify! ( i )
65+ loop do
66+ left_child = 2 * i + 1
67+ right_child = 2 * i + 2
68+ top_child = i
69+
70+ top_child = left_child if left_child < arr . size && cmp ( arr [ left_child ] , arr [ top_child ] )
71+ top_child = right_child if right_child < arr . size && cmp ( arr [ right_child ] , arr [ top_child ] )
72+
73+ break if top_child == i
74+
75+ swap ( i , top_child )
76+ i = top_child
77+ end
78+ end
79+
80+ private def cmp ( a , b )
81+ type == :max ? a > b : a < b
82+ end
83+
84+ private def swap ( i , j )
85+ arr [ i ] , arr [ j ] = arr [ j ] , arr [ i ]
86+ end
87+ end
88+
89+ class MedianStruct
90+ attr_reader :min_heap , :max_heap , :median
91+
92+ def initialize
93+ @min_heap = Heap . new ( :min ) # all elements are greater than median
94+ @max_heap = Heap . new ( :max ) # all elements are smaller than median
95+ @median = nil
96+ end
97+
98+ # insert in logarithmic time
99+ def insert ( n )
100+ if median . nil?
101+ @median = n
102+ elsif n < median
103+ if max_heap . length < min_heap . length
104+ max_heap . push ( n )
105+ elsif max_heap . top && n < max_heap . top
106+ min_heap . push ( median )
107+ @median = max_heap . pop
108+ max_heap . push ( n )
109+ else
110+ min_heap . push ( median )
111+ @median = n
112+ end
113+ elsif n > median
114+ if min_heap . length <= max_heap . length
115+ min_heap . push ( n )
116+ elsif n > min_heap . top
117+ max_heap . push ( median )
118+ @median = min_heap . pop
119+ min_heap . push ( n )
120+ else
121+ max_heap . push ( median )
122+ @median = n
123+ end
124+ else # n == median
125+ if max_heap . length <= min_heap . length
126+ max_heap . push ( n )
127+ else
128+ min_heap . push ( n )
129+ end
130+ end
131+ end
132+
133+ # find-the-median in constant time
134+ attr_reader :median
135+
136+ # remove-the-median in logarithmic time
137+ def remove_median
138+ if max_heap . length >= min_heap . length
139+ @median = max_heap . pop
140+ else
141+ @median = min_heap . pop
142+ end
143+ end
144+ end
145+
146+
147+ base = MedianStructBaseline . new
148+ struct = MedianStruct . new
149+
150+ 100 . times do |x |
151+ n = rand ( 100 )
152+
153+ base . insert ( n )
154+ struct . insert ( n )
155+
156+ unless base . median == struct . median
157+ puts "x#{ x } | ERROR: base.median = #{ base . median } , struct.median = #{ struct . median } "
158+
159+ p [ base . median , base . array ]
160+ p [ struct . median , struct . max_heap . arr , struct . min_heap . arr ]
161+ exit
162+ end
163+ end
164+
165+ 100 . times do |y |
166+ base . remove_median
167+ struct . remove_median
168+
169+ unless base . median == struct . median
170+ puts "y#{ y } | ERROR: base.median = #{ base . median } , struct.median = #{ struct . median } "
171+ exit
172+ end
173+ end
174+
175+ puts "OK"
0 commit comments