Unverified Commit e89676a6 authored by Yukihiro "Matz" Matsumoto's avatar Yukihiro "Matz" Matsumoto Committed by GitHub

Merge pull request #4142 from iij/mergesort

replace quicksort with mergesort.
parents fdd5ce8f 91444c47
...@@ -201,9 +201,7 @@ module Enumerable ...@@ -201,9 +201,7 @@ module Enumerable
ary.push([block.call(e), i]) ary.push([block.call(e), i])
} }
if ary.size > 1 if ary.size > 1
ary.__sort_sub__(0, ary.size - 1) do |a,b| ary.sort!
a <=> b
end
end end
ary.collect{|e,i| orig[i]} ary.collect{|e,i| orig[i]}
end end
......
...@@ -193,43 +193,61 @@ class Array ...@@ -193,43 +193,61 @@ class Array
include Enumerable include Enumerable
## ##
# Quick sort # Sort all elements and replace +self+ with these
# left : the beginning of sort region # elements.
# right : the end of sort region def sort!(&block)
def __sort_sub__(left, right, &block) stack = [ [ 0, self.size - 1 ] ]
stack = [ [left, right] ]
until stack.empty? until stack.empty?
left, right = stack.pop left, mid, right = stack.pop
if right == nil
right = mid
# sort self[left..right]
if left < right if left < right
i = left if left + 1 == right
j = right lval = self[left]
pivot = self[i + (j - i) / 2] rval = self[right]
while true if (block&.call(lval, rval) || (lval <=> rval)) > 0
while ((block)? block.call(self[i], pivot): (self[i] <=> pivot)) < 0 self[left] = rval
i += 1 self[right] = lval
end end
while ((block)? block.call(pivot, self[j]): (pivot <=> self[j])) < 0 else
j -= 1 mid = ((left + right + 1) / 2).floor
stack.push [ left, mid, right ]
stack.push [ mid, right ]
stack.push [ left, (mid - 1) ] if left < mid - 1
end end
break if (i >= j)
tmp = self[i]; self[i] = self[j]; self[j] = tmp;
i += 1
j -= 1
end end
stack.push [left, i-1] else
stack.push [j+1, right] lary = self[left, mid - left]
lsize = lary.size
# The entity sharing between lary and self may cause a large memory
# copy operation in the merge loop below. This harmless operation
# cancels the sharing and provides a huge performance gain.
lary[0] = lary[0]
# merge
lidx = 0
ridx = mid
(left..right).each { |i|
if lidx >= lsize
break
elsif ridx > right
self[i, lsize - lidx] = lary[lidx, lsize - lidx]
break
else
lval = lary[lidx]
rval = self[ridx]
if (block&.call(lval, rval) || (lval <=> rval)) <= 0
self[i] = lval
lidx += 1
else
self[i] = rval
ridx += 1
end end
end end
}
end end
# private :__sort_sub__
##
# Sort all elements and replace +self+ with these
# elements.
def sort!(&block)
size = self.size
if size > 1
__sort_sub__(0, size - 1, &block)
end end
self self
end end
......
...@@ -386,6 +386,12 @@ assert("Array#rindex") do ...@@ -386,6 +386,12 @@ assert("Array#rindex") do
assert_equal 0, $a.rindex(1) assert_equal 0, $a.rindex(1)
end end
assert('Array#sort!') do
a = [3, 2, 1]
assert_equal a, a.sort! # sort! returns self.
assert_equal [1, 2, 3], a # it is sorted.
end
assert('Array#freeze') do assert('Array#freeze') do
a = [].freeze a = [].freeze
assert_raise(RuntimeError) do assert_raise(RuntimeError) do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment