Mergesort: Difference between revisions
Line 22: | Line 22: | ||
== Induction Basis == | == Induction Basis == | ||
'''Abstract view:''' Nothing to do on | '''Abstract view:''' Nothing to do on a singleton. | ||
'''Implementation:''' Ditto. | '''Implementation:''' Ditto. | ||
'''Proof:''' | '''Proof:''' A singleton is trivially sorted. | ||
== Induction Step == | == Induction Step == |
Revision as of 16:19, 16 May 2015
General Information
Algorithmic problem: Sorting based on pairwise comparison
Type of algorithm: recursion
Abstract View
Invariant: After a recursive call, the input sequence of this recursive call is sorted.
Variant: For a recursive call on a subsequence [math]\displaystyle{ S' }[/math] of [math]\displaystyle{ S }[/math], let [math]\displaystyle{ S_1' }[/math] and [math]\displaystyle{ S_2' }[/math] denote the subsequences of [math]\displaystyle{ S' }[/math] with which Mergesort is called recursively from that call. Then it is [math]\displaystyle{ |S_1'| \leq \lceil|S'| /2\rceil }[/math] and [math]\displaystyle{ |S_2'| \leq \lceil|S'| /2\rceil }[/math].
Break condition: The current subsequence of the recursive call is a singleton.
Induction Basis
Abstract view: Nothing to do on a singleton.
Implementation: Ditto.
Proof: A singleton is trivially sorted.
Induction Step
Abstract view: The sequence is divided into two subsequences of approximately half size, it does not matter at all in which way this is done. Both subsequences are sorted recursively using Mergesort. The sorted subsequences are "merged" to one using algorithm Merge.
Implementation: Obvious.
Correctness: By induction hypothesis, the recursive calls sort correctly. Algorithm Merge unites two sorted sequences into one sorted sequence.
Complexity
Statement: The complexity is in [math]\displaystyle{ O(n \log n) }[/math] in the best and worst case.
Proof: Obviously, the variant is correct. So, the lengths of [math]\displaystyle{ S_1' }[/math] and [math]\displaystyle{ S_2' }[/math] are at most [math]\displaystyle{ \lceil1/2\rceil }[/math] of the length of [math]\displaystyle{ S' }[/math]. Consequently, the lengths of [math]\displaystyle{ S_1' }[/math] and [math]\displaystyle{ S_2' }[/math] are at least [math]\displaystyle{ \lfloor1/2\rfloor }[/math] of the length of [math]\displaystyle{ S' }[/math]. In summary, the overall recursion depth is in [math]\displaystyle{ \Theta(\log n) }[/math] in the best and worst case. Next consider the run time of a single recursive call, which receives some [math]\displaystyle{ S' }[/math] as input and calls Mergesort recursively with two subsequences [math]\displaystyle{ S_1' }[/math] and [math]\displaystyle{ S_2' }[/math]. The run time of this recursive call (disregarding the run times of the recursive calls with [math]\displaystyle{ S_1' }[/math] and [math]\displaystyle{ S_2' }[/math]) is linear in the length of [math]\displaystyle{ S' }[/math]. Since all recursive calls on the same recursion level operate on pairwise disjoint subsequences, the total run time of all calls on the same recursive level is linear in the length of the original sequence.
Exaple implementations
Java
public static <T> void mergesort(List<T> liste, Comparator<T> cmp) {
if (liste.size() <= 1)
return;
LinkedList<T> teilliste1 = new LinkedList<T>(); // leer
LinkedList<T> teilliste2 = new LinkedList<T>();
zerlegeInTeillisten(liste, teilliste1, teilliste2);
mergesort(teilliste1, cmp);
mergesort(teilliste2, cmp);
liste.clear();
merge(teilliste1, teilliste2, liste, cmp);
}
// -----------------
public static <T> void zerlegeInTeillisten(List<T> liste,
List<T> teilliste1, List<T> teilliste2) {
ListIterator<T> it = liste.listIterator();
for (int i = 0; i < liste.size(); i++) {
T elem = it.next();
if (i <= liste.size() / 2)
teilliste1.add(elem); // Haengt elem hinten an teilliste1 an
else
teilliste2.add(elem); // Dito teilliste2
}
}
// ---------------
public static <T> void merge(List<T> teilliste1, List<T> teilliste2,
List<T> liste, Comparator<T> cmp) {
ListIterator<T> it1 = teilliste1.listIterator();
ListIterator<T> it2 = teilliste2.listIterator();
T elem1 = it1.next();
T elem2 = it2.next();
while (true) {
if (cmp.compare(elem1, elem2) <= 0) {
liste.add(elem1);
if (!it1.hasNext())
break; // Bricht die Schleife ab, aber im Gegensatz zu
// return nicht die Methode.
elem1 = it1.next();
} else {
liste.add(elem2);
if (!it2.hasNext())
break;
elem2 = it2.next();
}
}
// Hier wissen wir: ! it1.hasNext() || ! it2.hasNext()
while (it1.hasNext())
liste.add(it1.next());
while (it2.hasNext())
liste.add(it2.next());
}