Merge sort

From Rosetta Code

Jump to: navigation, search

Programming Task
This is a programming task. It lays out a problem which Rosetta Code users are encouraged to solve, using languages they know.

Code examples should be formatted along the lines of one of the existing prototypes.

Sorting Algorithm
This is a sorting algorithm. It may be applied to a set of data in order to sort it.

For other sorting algorithms, see Category:Sorting Algorithms, or :

The merge sort is a recursive sort of order n*log(n) (technically n*lg(n)--lg is log base 2) sort. It is notable for having no worst case. It is always O(n*log(n)). The basic idea is to split the collection into smaller groups by halving it until the groups only have one element or no elements (which are both entirely sorted groups). Then merge the groups back together so that their elements are in order. This is how the algorithm gets its "divide and conquer" description.

Write a function to sort a collection of integers using the merge sort. The merge sort algorithm comes in two parts: a sort function and a merge function. The functions in pseudocode look like this:

function mergesort(m)
   var list left, right, result
   if length(m) ≤ 1
       return m
   else
       var middle = length(m) / 2
       for each x in m up to middle - 1
           add x to left
       for each x in m at and after middle
           add x to right
       left = mergesort(left)
       right = mergesort(right)
       result = merge(left, right)
       return result
function merge(left,right)
   var list result
   while length(left) > 0 and length(right) > 0
       if first(left) ≤ first(right)
           append first(left) to result
           left = rest(left)
       else
           append first(right) to result
           right = rest(right)
   if length(left) > 0 
       append rest(left) to result
   if length(right) > 0 
       append rest(right) to result
   return result

Contents

[edit] Ada

This example creates a generic package for sorting arrays of any type. Ada allows array indices to be any discrete type, including enumerated types which are non-numeric. Furthermore, numeric array indices can start at any value, positive, negative, or zero. The following code handles all the possible variations in index types.

 
generic
   type Element_Type is private;
   type Index_Type is (<>);
   type Collection_Type is array(Index_Type range <>) of Element_Type;
   with function "<"(Left, Right : Element_Type) return Boolean is <>;
 
package Mergesort is
   function Sort(Item : Collection_Type) return Collection_Type;
end MergeSort;
 
 
package body Mergesort is
 
   -----------
   -- Merge --
   -----------
 
   function Merge(Left, Right : Collection_Type) return Collection_Type is
      Result : Collection_Type(Left'First..Right'Last);
      Left_Index : Index_Type := Left'First;
      Right_Index : Index_Type := Right'First;
      Result_Index : Index_Type := Result'First;
   begin
      while Left_Index <= Left'Last and Right_Index <= Right'Last loop
         if Left(Left_Index) < Right(Right_Index) then
            Result(Result_Index) := Left(Left_Index);
            Left_Index := Index_Type'Succ(Left_Index); -- increment Left_Index
         else
            Result(Result_Index) := Right(Right_Index);
            Right_Index := Index_Type'Succ(Right_Index); -- increment Right_Index
         end if;
         Result_Index := Index_Type'Succ(Result_Index); -- increment Result_Index
      end loop;
      if Left_Index <= Left'Last then
         Result(Result_Index..Result'Last) := Left(Left_Index..Left'Last);
      end if;
      if Right_Index <= Right'Last then
         Result(Result_Index..Result'Last) := Right(Right_Index..Right'Last);
      end if;
      return Result;
   end Merge;
 
   ----------
   -- Sort --
   ----------
 
   function Sort (Item : Collection_Type) return Collection_Type is
      Result : Collection_Type(Item'range);
      Middle : Index_Type;
   begin
      if Item'Length <= 1 then
         return Item;
      else
         Middle := Index_Type'Val((Item'Length / 2) + Index_Type'Pos(Item'First));
         declare
            Left : Collection_Type(Item'First..Index_Type'Pred(Middle));
            Right : Collection_Type(Middle..Item'Last);
         begin
            for I in Left'range loop
               Left(I) := Item(I);
            end loop;
            for I in Right'range loop
               Right(I) := Item(I);
            end loop;
            Left := Sort(Left);
            Right := Sort(Right);
            Result := Merge(Left, Right);
         end;
         return Result;
      end if;
   end Sort;
 
end Mergesort;
 
 

The following code provides an usage example for the generic package defined above.

 
with Ada.Text_Io; use Ada.Text_Io;
with Mergesort; 
 
procedure Mergesort_Test is
   type List_Type is array(Positive range <>) of Integer;
   package List_Sort is new Mergesort(Integer, Positive, List_Type);
   procedure Print(Item : List_Type) is
   begin
      for I in Item'range loop
         Put(Integer'Image(Item(I)));
      end loop;
      New_Line;
   end Print;
 
   List : List_Type := (1, 5, 2, 7, 3, 9, 4, 6);
begin
   Print(List);
   Print(List_Sort.Sort(List));
end Mergesort_Test;
 

The output of this example is:

 1 5 2 7 3 9 4 6
 1 2 3 4 5 6 7 9

[edit] Clojure

This solution is pilfered from the Haskell version.

 (defn merge* [left right]
   (cond (nil? left) right
         (nil? right) left
         true (let [[l & *left] left
                    [r & *right] right]
                (if (< l r) (cons l (merge* *left right))
                            (cons r (merge* left *right))))))
 
 (defn merge-sort [L]
   (let [[l & *L] L]
     (if (nil? *L) 
       L
       (let [[left right] (split-at (/ (count L) 2) L)]
         (merge* (merge-sort left) (merge-sort right))))))

[edit] Common Lisp

(defun merge-sort (result-type sequence predicate)
  (let ((split (floor (length sequence) 2)))
    (if (zerop split)
      (copy-seq sequence)
      (merge result-type (merge-sort result-type (subseq sequence 0 split) predicate)
                         (merge-sort result-type (subseq sequence split)   predicate)
                         predicate))))

merge is a standard Common Lisp function.

> (merge-sort 'list (list 1 3 5 7 9 8 6 4 2) #'<)
(1 2 3 4 5 6 7 8 9)

[edit] D

Works with: Tango

module mergesort ;

version(Tango) {
  import tango.io.Stdout ;
  import tango.util.collection.LinkSeq ;
  alias LinkSeq!(int) LNK ;
  // Tango LinkSeq version
  void mergesort1(T)(T m) {
    if (m.length <= 1)
      return m ;
    int mid = m.length  / 2 ;   
    T left  = m.subset(0, mid) ;
    T right = m.subset(mid, m.length - mid) ;
    mergesort1(left) ;
    mergesort1(right) ;
    merge1(m, left, right) ;
  }
  void merge1(T)(T m, T left, T right) {
    m.clear ;
    while(left.length > 0 && right.length > 0)
      if (left.head <  right.head)
        m.append(left.take()) ;
      else 
        m.append(right.take()) ;      
    while(left.length > 0)
        m.append(left.take()) ;
    while(right.length > 0)
        m.append(right.take()) ;
  }
  alias Stdout print ;
} else { // not Version Tango
  import std.stdio ;
  alias writef print ;
}
// D array version
T[] mergesort2(T)(inout T[] m) {
  if (m.length <= 1)
    return m ;
  int mid = m.length / 2 ;
  T[] left, right;
  left = m[0..mid] ;
  right = m[mid..$] ;
  left.mergesort2() ;
  right.mergesort2() ;
  m.merge2(left, right) ;
  return m ;        
}
void merge2(T)(inout T[] merged, inout T[] left, inout T[] right) {
  T[] m = new T[left.length + right.length];
  int headL = 0 ;
  int headR = 0 ;
  int tailM = 0 ;
  while (headL < left.length && headR < right.length)
    if(left[headL] < right[headR])
      m[tailM++] = left[headL++] ;
    else
      m[tailM++] = right[headR++] ;
  if (headL < left.length)
    m[tailM..$] = left[headL..$] ;
  else if (headR < right.length)
    m[tailM..$] = right[headR..$] ;
  merged = m ;
}
void dump(T)(T l) {
  foreach(e ; l)
    print(e," ") ;
  print("\n") ;
}
void main() {
  int[] arr = [8,6,4,2,1,3,5,7,9] ; 

  version(Tango) {  
    LNK lnk = new LNK ; 
    foreach(e;arr)
      lnk.append(e);  
    dump(lnk) ;
    mergesort1(lnk) ;
    dump(lnk) ;
  }
  dump(arr) ;
  mergesort2(arr) ;
  dump(arr) ; 
}

[edit] E

def merge(var xs :List, var ys :List) {
    var result := []
    while (xs =~ [x] + xr && ys =~ [y] + yr) {
        if (x < y) {
            result with= x
            xs := xr
        } else {
            result with= y
            ys := yr
        }
    }
    return result + xs + ys
}

def sort(list :List) {
    if (list.size() <= 1) { return list }
    def split := list.size() // 2
    return merge(sort(list.run(0, split)),
                 sort(list.run(split)))
}

[edit] Haskell

merge []         ys                     = ys
merge xs         []                     = xs
merge xs@(x:xs') ys@(y:ys') | x < y     = x : merge xs' ys
                            | otherwise = y : merge xs  ys'
mergeSort []  = []
mergeSort [x] = [x]
mergeSort xs  = merge (mergeSort $ take n xs)
                      (mergeSort $ drop n xs)
                  where n = length xs `div` 2

[edit] Java

Works with: Java version 1.5+

import java.util.LinkedList;
public class Merge<E extends Comparable<E>> {
	public LinkedList<E> mergeSort(LinkedList<E> m){
		if(m.size() <= 1) return m;
 
		int middle= m.size() / 2;
		LinkedList<E> left= new LinkedList<E>();
		for(int i= 0;i < middle;i++) left.add(m.get(i));
		LinkedList<E> right= new LinkedList<E>();
		for(int i= middle;i < m.size();i++) right.add(m.get(i));
 
		right= mergeSort(right);
		left= mergeSort(left);
		LinkedList<E> result= merge(left, right);
 
		return result;
	}
 
	public LinkedList<E> merge(LinkedList<E> left, LinkedList<E> right){
		LinkedList<E> result= new LinkedList<E>();
 
		while(left.size() > 0 && right.size() > 0){
			//change the direction of this comparison to change the direction of the sort
			if(left.peek().compareTo(right.peek()) <= 0) result.add(left.remove());
			else result.add(right.remove());
		}
 
		if(left.size() > 0) result.addAll(left);
		if(right.size() > 0) result.addAll(right);
		return result;
	}
}

[edit] JavaScript

 
function sort(a) {
  var mid = a.length>>1;
  if (mid==0) return a;
  var less = sort(a.slice(0,mid));
  var more = sort(a.slice(mid));
  var merged = [];
  do {
    if (more[0] < less[0]) { var t=less; less=more; more=t; }
    merged.push(less.shift());
  } while (less.length > 0);
  return merged.concat(more);
}

[edit] Logo

Works with: UCB Logo

to split :size :front :list
  if :size < 1 [output list :front :list]
  output split :size-1 (lput first :list :front) (butfirst :list)
end

to merge :small :large
  if empty? :small [output :large]
  ifelse less? first :small first :large ~
    [output fput first :small merge butfirst :small :large] ~
    [output fput first :large merge butfirst :large :small]
end

to mergesort :list
  localmake "half split (count :list) / 2 [] :list
  if empty? first :half [output :list]
  output merge mergesort first :half mergesort last :half
end

[edit] Lucid

[1]

msort(a) = if iseod(first next a) then a else merge(msort(b0),msort(b1)) fi
  where
   p = false fby not p;
   b0 = a whenever p;
   b1 = a whenever not p;
   just(a) = ja
      where
         ja = a fby if iseod ja then eod else next a fi;
      end;
   merge(x,y) = if takexx then xx else yy fi
     where
      xx = (x) upon takexx;
      yy = (y) upon not takexx;
      takexx = if iseod(yy) then true elseif
                 iseod(xx) then false else xx < yy fi;
     end;
  end;

[edit] OCaml

let rec split_at n xs =
  match n, xs with
      0, xs ->
        [], xs
    | _, [] ->
        failwith "index too large"
    | n, x::xs when x > 0 ->
        let xs', xs'' = split_at (pred n) xs in
          x::xs', xs''
    | _, _ ->
        invalid_arg "negative argument"
 
let rec merge_sort cmp = function
    [] -> []
  | [x] -> [x]
  | xs ->
      let xs, ys = split_at (List.length xs / 2) xs in
        List.merge cmp (merge_sort cmp xs) (merge_sort cmp ys)
 
let _ =
  merge_sort compare [8;6;4;2;1;3;5;7;9]

[edit] Python

def merge_sort(m):
    if len(m) <= 1:
        return m
 
    middle = len(m) / 2
    left = m[:middle]
    right = m[middle:]
 
    left = merge_sort(left)
    right = merge_sort(right)
    return merge(left, right)
 
def merge(left, right):
    result = []
 
    while left and right:
        # change the direction of this comparison to change the direction of the sort
        if left[0] <= right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
 
    if left:
        result.extend(left)
    if right:
        result.extend(right)
    return result

[edit] Scheme

(define (merge-sort l gt?)
     (letrec
         (
          (merge
           (lambda (left right)
             (cond
               ((null? left) right)
               ((null? right) left)
               ((gt? (car left) (car right))
                (cons (car right) (merge left (cdr right))))
               (else
                (cons (car left) (merge (cdr left) right))))))

          (take
           (lambda (l num)
             (if (zero? num)
                 (list)
                 (cons (car l) (take (cdr l) (- num 1))))))

          (half (quotient (length l) 2)))

       (if (zero? half)
           l
           (merge
            (merge-sort (take      l half) gt?)
            (merge-sort (list-tail l half) gt?)))))
(merge-sort (list 1 3 5 7 9 8 6 4 2) >)

[edit] UnixPipes

Works with: Zsh


split() {
   (while read a b ; do
       echo $a > $1 ; echo $b > $2
   done)
}
mergesort() {
 xargs -n 2 | (read a b; test -n "$b" && (
     lc="1.$1" ; gc="2.$1"
     (echo $a $b;cat)|split >(mergesort $lc >$lc) >( mergesort $gc >$gc)
     sort -m $lc $gc
     rm -f $lc $gc;
 ) || echo $a)
}


cat to.sort | mergesort

[edit] V

merge uses the helper mergei to merge two lists. The mergei takes a stack of the form [mergedlist] [list1] [list2] it then extracts one element from list2, splits the list1 with it, joins the older merged list, first part of list1 and the element that was used for splitting (taken from list2) into the new merged list. the new list1 is the second part of the split on older list1. new list2 is the list remaining after the element e2 was extracted from it.

[merge
   [mergei
       uncons [swap [>] split] dip
       [[*m] e2 [*a1] b1 a2 : [*m *a1 e2] b1 a2] view].
    
   [a b : [] a b] view
   [size zero?] [pop concat]
       [mergei]
   tailrec].

[msort
  [splitat [arr a : [arr a take arr a drop]] view i].
  [splitarr dup size 2 / >int splitat].

  [small?] []
    [splitarr]
    [merge]
  binrec].
[8 7 6 5 4 2 1 3 9] msort puts
Personal tools