Thanks for your question.
Merge sort is one of my favorite algorithms, actually; it's the first
sorting algorithm I learned.
It's almost miraculous how simple its expression is:
sort(list) =
if (list has 1 or 0 elements return list)
else
sort(first half of list)
sort(second half of list)
merge the results.
There are a number of merge-sort algorithms and demos on the net, one
animation you might enjoy is at:
http://www2.ics.hawaii.edu/~qizhang/ics665/mergesort.html
Anyway, it is remarkable that if you look at the code to this
algorithm, it is hard even to see where it compares elements! Most of
the code just handles trivialities, the comparison is hidden away.
By the way I assume you are familiar with the java "Comparable"
interface. This is interface that objects which can be compared
implement. So, when you sort something, you have to make sure all your
objects implement "Comparable".
By the way, for general coding practice, I recommend the site
http://www.topcoder.com .
It has various programming competitions and it's a good way to learn
Java (or other languages).
OK:
To compile the code, put the ChainWithNaturalMergeSort class source
below in the dataStructures directory, the same directory where Chain
is. The compile it just as you would compile
Chain.java.
I've included a main() method that lets you test the implementation. I
find it's always a good idea to have one of these.
To run the class, from one directory above dataStructures, just type
something like: java dataStructures.ChainWithNaturalMergeSort a b c...
Here are examples of how I ran it:
java dataStructures.ChainWithNaturalMergeSort
The initial value of the chain is:
[]
The sorted value of the chain is:
[]
java dataStructures.ChainWithNaturalMergeSort one-element
The initial value of the chain is:
[one-element]
The sorted value of the chain is:
[one-element]
java dataStructures.ChainWithNaturalMergeSort one-element two-element
The initial value of the chain is:
[one-element, two-element]
The sorted value of the chain is:
[one-element, two-element]
java dataStructures.ChainWithNaturalMergeSort two-element one-element
The initial value of the chain is:
[two-element, one-element]
The sorted value of the chain is:
[one-element, two-element]
java dataStructures.ChainWithNaturalMergeSort p q r s t u v f g h i j
k l m n o w x y z a b c d e
The initial value of the chain is:
[p, q, r, s, t, u, v, f, g, h, i, j, k, l, m, n, o, w, x, y, z, a, b,
c, d, e]
The sorted value of the chain is:
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w,
x, y, z]
Feel free to ask any questions and good luck.
package dataStructures;
import java.util.Iterator;
public class ChainWithNaturalMergeSort extends Chain
{
//constructors
public ChainWithNaturalMergeSort(int initialCapacity)
{ super(initialCapacity);}
public ChainWithNaturalMergeSort()
{ super();}
//methods
/** Sort the list according to the natural order of its elements.
Each element must implement the Comparator interface.
This uses a merges sort algorithm
*/
public void naturalMergeSort()
{
int nelements=size(); // number of elements
if (nelements==0||nelements==1)return; // Already sorted
Comparable[] array=new Comparable[nelements]; //first we get
everything into an array,
//since these are
a bit more efficient and easier to work with
Iterator iter= iterator(); //Iterate through
each element of the Chain
for (int i=0;i<nelements;++i) // Set the value
of the i'th array element to the i'th element in the chain.
array[i]=(Comparable)(iter.next()); //Note that this will give a
"class cast exception" if the elements are not Comparables.
sort(array); //merge sort the value
ChainNode current=firstNode; //and set each element
in the chain to the sorted value.
for (int i=0;i<nelements;++i){
current.element=array[i];
current=current.next;
}
}
//This is the routine that actually does the work. It takes an
array of Comparables and sorts it using merge sort.
void sort(Comparable[] array){
int nelements=array.length;
if (nelements<=1)return; // already sorted
int midpoint=nelements/2; // get the first half
Comparable[] array1=new Comparable[midpoint]; // we partition the
array
Comparable[] array2=new Comparable[nelements-midpoint];
System.arraycopy(array,0,array1,0,array1.length); // set up the
first array
System.arraycopy(array,array1.length,array2,0,array2.length); //set
up the second array
sort(array1); //recursively sort the first array
sort(array2); //recursively sort the second array
//The following line simply merges the sorted arrays we've made, and
then copies the result back to the original array.
System.arraycopy(merge(array1,array2),
0,
array,
0,
nelements);
}
//This function takes two sorted arrays as input and returns the
sorted array that has all the elements of each array
Comparable[] merge(Comparable[] array1, Comparable[] array2){
Comparable[] result=new Comparable[array1.length+array2.length];
//will hold the result
int nextinsert=0; // next element of result to set
int nextarray1element=0; // next element of array1 to check
int nextarray2element=0; // next element of array2 to check
while(nextinsert<result.length){ //while there are still more
elements
if (nextarray1element>=array1.length) // have we finished
array1? if so, add an array2 element
result[nextinsert++]=array2[nextarray2element++];
else if (nextarray2element>=array2.length) //similarly for array2
result[nextinsert++]=array1[nextarray1element++];
//The following line is the only place in the whole class we
actually compare anything!!
//we see if the next array1 element is smaller than or
equal to according to the natural order to the next array2 element,
// and if so we add it to the result.
//Because we use "<=0" not "<0" this is a "stable sort" which
means it doesn't change the order of equal elements in the input.
else if (array1[nextarray1element].compareTo(array2[nextarray2element])<=0)
result[nextinsert++]=array1[nextarray1element++];
else
result[nextinsert++]=array2[nextarray2element++];
}
return result;
}
//The next routine is simply used for testing. To test, just call
the function main with arguments the strings to be sorted.
public static void main(String[]argv){
ChainWithNaturalMergeSort chain=new ChainWithNaturalMergeSort();
for (int i=0;i<argv.length;++i)
chain.add(i,argv[i]);
System.out.println("The initial value of the chain is: \n"+chain);
chain.naturalMergeSort();
System.out.println("The sorted value of the chain is: \n"+chain);
}
} |
Request for Answer Clarification by
aznmattuf-ga
on
04 Aug 2002 18:59 PDT
Thanks for the answer, however, the natural merge sort is what i'm
looking for. The natural merge sort looks for elements through the
chain that are already in order, and uses those "segments" to merge
upon one another. I.E.
if the original chain is: 6,2,1,2,3,68,65,44,45,46,47,8,5,10,3,6
Then the first run will look like:
6 -- 2 -- 1,2,3,68 -- 65 -- 44,45,46,47 - 8 - 5,10 -- 3,6 with 8
segments
The program would then merge 6 and 2 to yield
2,6 -- 1,2,3,68 -- 65-- 44,45,46,47 - 8 - 5,10 -- 3,6 with 7 segments
then it merges (1,2,3,68) and 65 to yield
2,6, -- 1,2,3,65,68 -- 44,45,46,47 -- 8 -- 5,10 -- 3,6 with 6 segments
then merges (44,45,46,47) and 8 to yield
2,6 -- 1,2,3,65,68 -- 8,44,45,46,47 -- 5,10 -- 3,6 with 5 segments
then merges 5,10 with 3, 6 to yield
2,6 -- 1,2,3,65,68 -- 8,44,45,46,47 -- 3,5,6,10 with 4 segments
then merges 2,6 with 1,2,3,65,68 to yield
1,2,2,3,6,65,68 -- 8,44,45,46,47 -- 3,5,6,10 with 3 segments
then merges 8,44,45,46,47 with 3,5,6,10 to yield
1,2,2,3,6,65,68 -- 3,5,6,10,44,45,46,47 with 2 segments
and finally merge those two
to get 1,2,2,3,3,6,8,10,44,45,46,47,65,68 consisting of one segment
I have a similar code for this "Natural" merge sort but not for
chains, if you could help construct the code based on how the example
is run, that would be very appreciated. I already have the coding for
a regular merge sort based on size. Thank you
|
Request for Answer Clarification by
aznmattuf-ga
on
04 Aug 2002 19:02 PDT
Here's what i have for a natural merge sort, however, I'm trying to
code it using ChainNodes. here's the regular class:
public class NaturalMergeSort
{
// define a queue used for the start positions of sorted segments
static ArrayQueue queue = new ArrayQueue(10);
static int numberOfSegments;
/** sort the elements a[0 : a.length - 1] using
* the natural merge sort method */
public static void naturalMergeSort(Comparable [] a)
{
if (a.length <= 1)
// already sorted
return;
// define an auxiliary array to faciltate merging
Comparable [] b = new Comparable [a.length];
// find the natural segments and save their start positions in a
queue
queue.put(new Integer(0)); // first segment starts at 0
numberOfSegments = 1;
for (int i = 1; i < a.length; i++)
if (a[i - 1].compareTo(a[i]) > 0)
{// a new segment starts at i
numberOfSegments++;
queue.put(new Integer(i));
}
while (numberOfSegments > 1)
{// numberOfSegments is updated by naturalMergePass
naturalMergePass(a, b); // merge from a to b
naturalMergePass(b, a); // merge from b to a
}
}
/** merge pairs of adjacent segments from x to y*/
public static void naturalMergePass(Comparable [] x, Comparable []
y)
{
for (int i = 1; i < numberOfSegments; i += 2)
{// merge two segments
// find the start and end of the two segments
Object a = queue.remove();
int startA = ((Integer) a).intValue();
int startB = ((Integer) queue.remove()).intValue();
Object z = queue.getFrontElement();
int endB = (z == null || ((Integer) z).intValue() == 0) ?
x.length - 1 : ((Integer) z).intValue() - 1;
// merge them
merge(x, y, startA, startB - 1, endB);
// put segment start on queue
queue.put(a);
}
// one segment may remain
if (numberOfSegments % 2 == 1)
{// copy last segment to y
Object a = queue.remove();
int startA = ((Integer) a).intValue();
for (int i = startA; i < x.length; i++)
y[i] = x[i];
queue.put(a);
}
// update number of segments
if (numberOfSegments % 2 == 1)
numberOfSegments = numberOfSegments / 2 + 1;
else
numberOfSegments /= 2;
}
/** merge c[l:m] and c[m + 1:r] to d[l:r] */
public static void merge(Comparable [] c, Comparable [] d,
int l, int m, int r)
{
int i = l, // cursor for first segment
j = m + 1, // cursor for second
k = l; // cursor for result
// merge until i or j exits its segment
while ((i <= m) && (j <= r))
if (c[i].compareTo(c[j]) <= 0)
d[k++] = c[i++];
else
d[k++] = c[j++];
// take care of left overs
if (i > m)
for (int q = j; q <= r; q++)
d[k++] = c[q];
else
for (int q = i; q <= m; q++)
d[k++] = c[q];
}
}
|
Request for Answer Clarification by
aznmattuf-ga
on
04 Aug 2002 19:03 PDT
the above code has not been designed to run with Chain thats what i'm
working on how to code it so it uses ChainNodes
|
Clarification of Answer by
rbnn-ga
on
04 Aug 2002 23:56 PDT
OK, I'm including the code for the ChainWithNaturalMergeSort below.
It can be run in two modes, by setting the variable Debug to "false"
or "true".
If you set it to true, it will print out the segments that are
computed, so that you can verify it is a
natural merge sort.
Some brief notes on the design:
I tried to follow reasonably closely the array code you gave. However,
the queue variable is not
static; it was only static in the array code because there was no
class to be acted upon.
In this case the queue is simply a private instance variable.
I did not use separate x and y variables to do the copying, as this
seemed like some unnecessary work in the
case of Lists.
I haven't put too much in the way of comments here, as I am not sure
exactly what you are looking for, but of course I would
be happy to answer any questions on the code.
Here are some scripts:
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort
unsorted is: []
sorted is: []
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort zzz
unsorted is: [zzz]
sorted is: [zzz]
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort zzz aaa
unsorted is: [zzz, aaa]
sorted is: [aaa, zzz]
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort aaa zzz
unsorted is: [aaa, zzz]
sorted is: [aaa, zzz]
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort a b c
unsorted is: [a, b, c]
sorted is: [a, b, c]
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort c b a
unsorted is: [c, b, a]
sorted is: [a, b, c]
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort c a b
unsorted is: [c, a, b]
sorted is: [a, b, c]
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort b a c
unsorted is: [b, a, c]
sorted is: [a, b, c]
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort o p q r d e f g h i s t u v
w x y z j a b c k l m n
unsorted is: [o, p, q, r, d, e, f, g, h, i, s, t, u, v, w, x, y, z, j,
a, b, c, k, l, m, n]
sorted is: [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s,
t, u, v, w, x, y, z]
/cygdrive/c/stiller/google: # Now with the Debug switch set to on....
/cygdrive/c/stiller/google: java
dataStructures.ChainWithNaturalMergeSort o p q r d e f g h i s t u v
w x y z j a b c k l m n
unsorted is: [o, p, q, r, d, e, f, g, h, i, s, t, u, v, w, x, y, z, j,
a, b, c, k, l, m, n]
Showing segments:
Segment 0 is: [o p q r ]
Segment 1 is: [d e f g h i s t u v w x y z ]
Segment 2 is: [j ]
Segment 3 is: [a b c k l m n ]
Showing segments:
Segment 0 is: [d e f g h i o p q r s t u v w x y z ]
Segment 1 is: [a b c j k l m n ]
Showing segments:
Segment 0 is: [a b c d e f g h i j k l m n o p q r s t u v w x y z ]
sorted is: [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s,
t, u, v, w, x, y, z]
/cygdrive/c/stiller/google:
package dataStructures;
import java.util.Iterator;
public class ChainWithNaturalMergeSort extends Chain
{
private ArrayQueue queue;
private int numberSegments;
public static final boolean Debug=false;
//constructors
public ChainWithNaturalMergeSort(int initialCapacity)
{ super(initialCapacity);}
public ChainWithNaturalMergeSort()
{ super();}
public void naturalMergeSort()
{
int nelements=size(); // number of elements
if (nelements==0||nelements==1)return; // Already sorted
queue=new ArrayQueue(10);
numberSegments=0;
queue.put(firstNode);
ChainNode current=firstNode;
while(true){
ChainNode previous=current;
current=current.next;
if (current==null){queue.put(current);++numberSegments;break;}
if (((Comparable)(current.element)).compareTo(previous.element)<0){
queue.put(current);
numberSegments++;
}
}
while(numberSegments>1){
showSegments();
naturalMergePass();
}
showSegments();
}
public void naturalMergePass(){
Object start=queue.getFrontElement();
while(true){
ChainNode a =(ChainNode)(queue.remove());
if (a==null){queue.put(a);break;}
ChainNode b=(ChainNode)(queue.remove());
if (b==null){queue.put(a);queue.put(b);break;}
ChainNode z=(ChainNode)(queue.getFrontElement());
merge(a,b,z);
queue.put(a);
numberSegments--;
}
}
public void merge(ChainNode start1,ChainNode start2, ChainNode
end){
ChainNode next1=start1;
ChainNode next2=start2;
ChainNode previous=null;
ChainNode first=null;
int iternumber=0;
while(next1!=start2 || next2!=end){
ChainNode current=new ChainNode();
if (previous!=null)
previous.next=current;
else
first=current;
previous=current;
if (next1==start2){
current.element=next2.element;
next2=next2.next;
}
else if (next2==end){
current.element=next1.element;
next1=next1.next;
}
else if (((Comparable)(next1.element)).compareTo(next2.element)<=0)
{current.element=next1.element;next1=next1.next;}
else
{current.element=next2.element;next2=next2.next;}
}
ChainNode next=start1;
while(next!=end){
next.element=first.element;
first=first.next;
next=next.next;}
}
public static void main(String[]argv){
ChainWithNaturalMergeSort chain=new ChainWithNaturalMergeSort();
for (int i=0;i<argv.length;++i)
chain.add(i,argv[i]);
System.out.println("unsorted is: "+chain);
chain.naturalMergeSort();
System.out.println("sorted is: "+chain);
}
public String showQueue(ArrayQueue q){
java.util.ArrayList foo=new java.util.ArrayList();
String result="";
while (!q.isEmpty()) foo.add(q.remove());
for (int i=0;i<foo.size();++i){
result+=foo.get(i)+ " ";
q.put(foo.get(i));
}
return result;
}
public void showSegments(){
if (Debug)System.out.println("Showing segments: ");
if (!Debug)return;
java.util.ArrayList foo=new java.util.ArrayList();
while (!queue.isEmpty()) foo.add(queue.remove());
for (int i=0;i<foo.size()-1;++i){
ChainNode start= (ChainNode)(foo.get(i));
System.out.print("Segment " +i+ " is: ");
System.out.print("[");
ChainNode current=start;
do{
System.out.print(current.element+" ");
current=current.next;
}
while(current!=foo.get(i+1));
System.out.println("]");
}
for (int i=0;i<foo.size();++i) queue.put(foo.get(i));
}
}
|
Request for Answer Clarification by
aznmattuf-ga
on
05 Aug 2002 15:01 PDT
i posted a possible approach, I was wondering if you thought of either
your own way or if this could work. The null pointers are a problem
for me
|
Clarification of Answer by
rbnn-ga
on
05 Aug 2002 17:59 PDT
I'm happy to help with the request for clarification.
To answer your question about where my original code came from, I just
modified the existing code you gave me for NaturalMergeSort on arrays
to work with lists; I just changed "indices" in the array to
"ChainNodes".
Now, you posted some new code and if I understand you correctly you
are curious why your new code gives a NullPointerException, and also
possibly some suggestions/comments on the code.
Well, the simple answer is that your code gives a NullPointerException
because in the lines:
// Traverse the chain, place segments into queue
while (a != null)
I think you mean (c!=null) here , not (a!=null) . This is because you
later call on "c.element" and c could be null here.
One *very* good idea in your code is to destructively modify the
ChainNode next pointers so that the segments are actually stored
naturally as null-terminated lists. This dramatically simplifies
everything, I wish
I'd used that idea, since in my original code I kept having to do a
lot of useless and
error-prone bounds-checking.
Anyway, even after fixing the (a!=null) to c!=null there were a few
minor issues in your code, which I took the liberty of correcting
below.
For instance, it's not a good idea for the numberOfSegments variables
to be static ! In java, this means that their value is the same for
all instances of a class, so that if two clients tried to sort two
different ChainWithNaturalMergeSort instances at the same time, they
would get garbage results. (actually you don't really need any static
methods except for main in the class; it's very common among C/C++
programmers when they switch to java to make too many things static.
So I changed out some of the static declarations to instance ones.
Finally, I changed a couple places were you used iteration to loop
over the linked list to recursion.
I did this in two places.
First, when naturalMergeSort first sets up the queue, you can see I
use a different helper function, setupQueue, that uses recursion.
Second, in the merge function, I use a recursive idea (which is MUCH
simpler thanks to the way segments are true chains terminated by
null).
Often, when dealing with linked lists and so on, it's
simpler to use recursion, not iteration. True, it can sometimes
be a bit faster to use iteration, but I don't think that's an issue
here. Anyway, you will see that the recursive solution is much, much
shorter than the iterative one [80 lines versus 180 lines]; it's
easier to debug; and it ran right
the first time with no NullPointerExceptions, which wouldn't have been
the case with an iterative solution.
Please feel free to continue to ask for more clarifications.
package dataStructures;
import java.util.*;
public class ChainWithNaturalMergeSort extends Chain
{
ArrayQueue queue = new ArrayQueue(10);
int numberOfSegments;
public ChainWithNaturalMergeSort(int initialCapacity)
{
super (initialCapacity);
}
public ChainWithNaturalMergeSort()
{
super();
}
void setupQueue(ChainNode a){
if (a==null) return;
queue.put(a);
++numberOfSegments;
ChainNode next=a.next;
while(true){
if (a.next==null)return;
if ((((Comparable)(a.next.element)).compareTo(a.element))<0){
setupQueue(a.next);
a.next=null;
return;
}
a=a.next;}
}
public void naturalMergeSort()
{
firstNode = naturalMergeSort(firstNode, size);
}
ChainNode naturalMergeSort(ChainNode a, int chainLength)
{
if (chainLength < 2)
return a;
setupQueue(a);
while(numberOfSegments>1)
{
ChainNode mergePrev = ((ChainNode)queue.remove());
// gets first chain of queue
ChainNode mergeNext = ((ChainNode)queue.remove());
// gets second chain off of queue
a = merge(mergePrev, mergeNext); // create chain
numberOfSegments--;
queue.put(a);
}
return (ChainNode)queue.remove();
}
ChainNode merge(ChainNode e, ChainNode f)
{
//if one chain is empty, return other chain
if (e==null)return f;
if (f==null)return e;
if (((Comparable)(e.element)).compareTo(f.element)<=0){
e.next=merge(e.next,f);
return e;}
else
return new ChainNode(f.element,
merge(e,f.next));
}
public static void main(String[] args)
{
ChainWithNaturalMergeSort x = new ChainWithNaturalMergeSort();
for (int i=0;i<args.length;++i)x.add(i,args[i]);
System.out.println("Start x is: "+x);
x.naturalMergeSort();
System.out.println("end x is: "+x);
}
}
|