@@ -129,23 +129,38 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically com
129129"""
130130struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder end
131131
132- struct DegreeBuckets{B}
132+ struct DegreeBuckets
133133 degrees:: Vector{Int}
134- buckets:: B
134+ bucket_storage:: Vector{Int}
135+ bucket_low:: Vector{Int}
136+ bucket_high:: Vector{Int}
135137 positions:: Vector{Int}
136138end
137139
138140function DegreeBuckets (degrees:: Vector{Int} , dmax)
139- buckets = Dict (d => Int[] for d in 0 : dmax)
141+ # number of vertices per degree class
142+ deg_count = zeros (Int, dmax + 1 )
143+ for d in degrees
144+ deg_count[d + 1 ] += 1
145+ end
146+ # bucket limits
147+ bucket_high = cumsum (deg_count)
148+ bucket_low = vcat (0 , @view (bucket_high[1 : (end - 1 )]))
149+ bucket_low .+ = 1
150+ # assign each vertex to the correct position inside its degree class
151+ bucket_storage = similar (degrees, Int)
140152 positions = similar (degrees, Int)
141- for v in eachindex (degrees, positions )
153+ for v in eachindex (positions, degrees )
142154 d = degrees[v]
143- push! (buckets[d], v) # TODO : optimize
144- positions[v] = length (buckets[d])
155+ positions[v] = bucket_high[d + 1 ] - deg_count[d + 1 ] + 1
156+ bucket_storage[positions[v]] = v
157+ deg_count[d + 1 ] -= 1
145158 end
146- return DegreeBuckets (degrees, buckets , positions)
159+ return DegreeBuckets (degrees, bucket_storage, bucket_low, bucket_high , positions)
147160end
148161
162+ maxdeg (db:: DegreeBuckets ) = length (db. bucket_low) - 1
163+
149164function degree_increasing (; degtype, direction)
150165 increasing =
151166 (degtype == :back && direction == :low2high ) ||
@@ -155,43 +170,80 @@ end
155170
156171function mark_ordered! (db:: DegreeBuckets , v:: Integer )
157172 db. degrees[v] = - 1
158- db. positions[v] = - 1
173+ db. positions[v] = typemin (Int)
159174 return nothing
160175end
161176
162177already_ordered (db:: DegreeBuckets , v:: Integer ) = db. degrees[v] == - 1
163178
164179function pop_next_candidate! (db:: DegreeBuckets ; direction:: Symbol )
165- (; buckets) = db
180+ (; bucket_storage, bucket_low, bucket_high) = db
181+ dmax = maxdeg (db)
166182 if direction == :low2high
167- candidate_degree = maximum (d for (d, bucket) in pairs (buckets) if ! isempty (bucket))
183+ candidate_degree = dmax + 1
184+ for d in dmax: - 1 : 0
185+ if bucket_high[d + 1 ] >= bucket_low[d + 1 ] # not empty
186+ candidate_degree = d
187+ break
188+ end
189+ end
168190 else
169- candidate_degree = minimum (d for (d, bucket) in pairs (buckets) if ! isempty (bucket))
191+ candidate_degree = - 1
192+ for d in 0 : dmax
193+ if bucket_high[d + 1 ] >= bucket_low[d + 1 ] # not empty
194+ candidate_degree = d
195+ break
196+ end
197+ end
170198 end
171- candidate_bucket = buckets[candidate_degree]
172- candidate = pop! (candidate_bucket)
199+ high = bucket_high[candidate_degree + 1 ]
200+ candidate = bucket_storage[high]
201+ bucket_storage[high] = - 1
202+ bucket_high[candidate_degree + 1 ] -= 1
173203 mark_ordered! (db, candidate)
174204 return candidate
175205end
176206
177207function update_bucket! (db:: DegreeBuckets , v:: Integer ; degtype, direction)
178- (; degrees, buckets , positions) = db
208+ (; degrees, bucket_storage, bucket_low, bucket_high , positions) = db
179209 d, p = degrees[v], positions[v]
180- bucket = buckets[d ]
210+ low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
181211 # select previous or next bucket for the move
182- d_new = degree_increasing (; degtype, direction) ? d + 1 : d - 1
183- bucket_new = buckets[d_new]
184- # put v at the end of its bucket by swapping
185- w = bucket[end ]
186- bucket[p] = w
187- positions[w] = p
188- bucket[end ] = v
189- positions[v] = length (bucket)
190- # move v from the old bucket to the new one
191- @assert pop! (bucket) == v
192- push! (bucket_new, v)
193- degrees[v] = d_new
194- positions[v] = length (bucket_new)
212+ if degree_increasing (; degtype, direction)
213+ # put v at the end of its bucket by swapping
214+ w = bucket_storage[high]
215+ bucket_storage[p] = w
216+ bucket_storage[high] = v
217+ positions[w] = p
218+ positions[v] = high
219+ # move v to the beginning of the next bucket (mind the gap)
220+ d_new = d + 1
221+ low_new, high_new = bucket_low[d_new + 1 ], bucket_high[d_new + 1 ]
222+ bucket_storage[low_new - 1 ] = v
223+ # update v stats
224+ degrees[v] = d_new
225+ positions[v] = low_new - 1
226+ # grow next bucket to the left, shrink current one from the right
227+ bucket_low[d_new + 1 ] -= 1
228+ bucket_high[d + 1 ] -= 1
229+ else
230+ # put v at the beginning of its bucket by swapping
231+ w = bucket_storage[low]
232+ bucket_storage[p] = w
233+ bucket_storage[low] = v
234+ positions[w] = p
235+ positions[v] = low
236+ # move v to the end of the previous bucket (mind the gap)
237+ d_new = d - 1
238+ low_new, high_new = bucket_low[d_new + 1 ], bucket_high[d_new + 1 ]
239+ bucket_storage[high_new + 1 ] = v
240+ # update v stats
241+ degrees[v] = d_new
242+ positions[v] = high_new + 1
243+ # grow previous bucket to the right, shrink current one from the left
244+ bucket_high[d_new + 1 ] += 1
245+ bucket_low[d + 1 ] += 1
246+ end
195247 return nothing
196248end
197249
@@ -205,6 +257,7 @@ function vertices(
205257 end
206258 db = DegreeBuckets (degrees, maximum_degree (g))
207259 π = Int[]
260+ sizehint! (π, nb_vertices (g))
208261 for _ in 1 : nb_vertices (g)
209262 u = pop_next_candidate! (db; direction)
210263 direction == :low2high ? push! (π, u) : pushfirst! (π, u)
@@ -241,6 +294,7 @@ function vertices(
241294 maxd2 = maximum (degrees_dist2)
242295 db = DegreeBuckets (degrees, maxd2)
243296 π = Int[]
297+ sizehint! (π, n)
244298 visited = falses (n)
245299 for _ in 1 : nb_vertices (g, Val (side))
246300 u = pop_next_candidate! (db; direction)
0 commit comments