Implement an algorithm to merge an arbitrary number of sorted lists into one sorted list. The aim is to create the smallest working programme, in whatever language you like.
function merge(a) {
var r=[], p;
while(a.length>0) {
for (var i=0,j=0; i<a.length && p!=a[j][0]; i++)
if (a[i][0]<a[j][0])
j = i;
r.push(p = a[j].shift());
if (!a[j].length)
a.splice(j, 1);
}
return r;
}
Test:
var arr = [[1, 4, 7], [2, 5, 8], [3, 6, 9]];
alert(merge(arr));
VB is usually not the language of choice for code golf, but here goes anyway.
The setup -
Dim m1 As List(Of Integer) = New List(Of Integer)
Dim m2 As List(Of Integer) = New List(Of Integer)
Dim m3 As List(Of Integer) = New List(Of Integer)
Dim m4 As List(Of Integer) = New List(Of Integer)
m1.Add(1)
m1.Add(2)
m1.Add(3)
m2.Add(4)
m2.Add(5)
m2.Add(6)
m3.Add(7)
m3.Add(8)
m3.Add(9)
Dim m5 As List(Of List(Of Integer)) = New List(Of List(Of Integer))
m5.Add(m1)
m5.Add(m2)
m5.Add(m3)
An attempt in VB.NET (without sort)
While m5.Count > 0
Dim idx As Integer = 0
Dim min As Integer = Integer.MaxValue
For k As Integer = 0 To m5.Count - 1
If m5(k)(0) < min Then min = m5(k)(0) : idx = k
Next
m4.Add(min) : m5(idx).RemoveAt(0)
If m5(idx).Count = 0 Then m5.RemoveAt(idx)
End While
Another VB.NET attempt (with an allowed sort)
Private Function Comp(ByVal l1 As List(Of Integer), ByVal l2 As List(Of Integer)) As Integer
Return l1(0).CompareTo(l2(0))
End Function
.
.
.
While m5.Count > 0
m5.Sort(AddressOf Comp)
m4.Add(m5(0)(0)) : m5(0).RemoveAt(0)
If m5(0).Count = 0 Then m5.RemoveAt(0)
End While
The entire program -
Dim rand As New Random
Dim m1 As List(Of Integer) = New List(Of Integer)
Dim m2 As List(Of Integer) = New List(Of Integer)
Dim m3 As List(Of Integer) = New List(Of Integer)
Dim m4 As List(Of Integer) = New List(Of Integer)
Dim m5 As List(Of List(Of Integer)) = New List(Of List(Of Integer))
m5.Add(m1)
m5.Add(m2)
m5.Add(m3)
For Each d As List(Of Integer) In m5
For i As Integer = 0 To 100000
d.Add(rand.Next())
Next
d.Sort()
Next
Dim sw As New Stopwatch
sw.Start()
While m5.Count > 0
Dim idx As Integer = 0
Dim min As Integer = Integer.MaxValue
For k As Integer = 0 To m5.Count - 1
If m5(k)(0) < min Then min = m5(k)(0) : idx = k
Next
m4.Add(min) : m5(idx).RemoveAt(0)
If m5(idx).Count = 0 Then m5.RemoveAt(idx)
End While
sw.Stop()
'Dim sw As New Stopwatch
'sw.Start()
'While m5.Count > 0
' m5.Sort(AddressOf Comp)
' m4.Add(m5(0)(0)) : m5(0).RemoveAt(0)
' If m5(0).Count = 0 Then m5.RemoveAt(0)
'End While
'sw.Stop()
Console.WriteLine(sw.Elapsed)
Console.ReadLine()
Ruby: 100 characters (1 significant whitespace, 4 significant newlines)
def m(i)
a=[]
i.each{|s|s.each{|n|a.insert((a.index(a.select{|j|j>n}.last)||-1)+1,n)}}
a.reverse
end
Human version:
def sorted_join(numbers)
sorted_numbers=[]
numbers.each do |sub_numbers|
sub_numbers.each do |number|
bigger_than_me = sorted_numbers.select { |i| i > number }
if bigger_than_me.last
pos = sorted_numbers.index(bigger_than_me.last) + 1
else
pos = 0
end
sorted_numbers.insert(pos, number)
end
end
sorted_numbers.reverse
end
This can all just be replaced by numbers.flatten.sort
Benchmarks:
a = [[1, 4, 7], [2, 4, 8], [3, 6, 9]]
n = 50000
Benchmark.bm do |b|
b.report { n.times { m(a) } }
b.report { n.times { a.flatten.sort } }
end
Produces:
user system total real
2.940000 0.380000 3.320000 ( 7.573263)
0.380000 0.000000 0.380000 ( 0.892291)
So my algorithm performs horribly, yey!
F#: 116 chars:
let p l=
let f a b=List.filter(a b) in
let rec s=function[]->[]|x::y->s(f(>)x y)@[x]@s(f(<=)x y) in
[for a in l->>a]|>s
Note: this code causes F# to throw a lot of warnings, but it works :)
Here's the annotated version with whitespace and meaningful identifiers (note: the code above doesn't use #light syntax, the code below does):
let golf l=
// filters my list with a specified filter operator
// uses built-in F# function
// ('a -> 'b -> bool) -> 'a -> ('b list -> 'b list)
let filter a b = List.filter(a b)
// quicksort algorithm
// ('a list -> 'a list)
let rec qsort =function
| []->[]
| x :: y -> qsort ( filter (>) x y) @ [x] @ qsort ( filter (<=) x y)
// flattens list
[for a in l ->> a ] |> qsort
BASH is not really good at list manipulation, anyway this is does the job.
# This merges two lists together
m(){
[[ -z $1 ]] && echo $2 && return;
[[ -z $2 ]] && echo $1 && return;
A=($1); B=($2);
if (( ${A[0]} > ${B[0]} ));then
echo -n ${B[0]}\ ;
unset B[0];
else
echo -n ${A[0]}\ ;
unset A[0];
fi;
m "${A[*]}" "${B[*]}";
}
# This merges multiple lists
M(){
A=$1;
shift;
for x in $@; do
A=`m "$A" "$x"`
done
echo $A
}
$ M '1 4 7' '2 5 8' '3 6 9'
1 2 3 4 5 6 7 8 9
VB
The setup:
Sub Main()
f(New Int32() {1, 4, 7}, _
New Int32() {2, 5, 8}, _
New Int32() {3, 6, 9})
End Sub
The output:
Sub f(ByVal ParamArray b As Int32()())
Dim l = New List(Of Int32)
For Each a In b
l.AddRange(a)
Next
For Each a In l.OrderBy(Function(i) i)
Console.WriteLine(a)
Next
End Sub