Algorithms and Data Structures in an Object-Oriented Framework (“ADSOOF”)
Sorting Arrays and ArrayLists: Part 2
Quicksort(1) on arrayLists can be programmed in a way very similar to the quicksort on Lisp lists we saw previously: we create two new empty arrayLists, then add all the elements of the original arrayList less than its first element to one of them, all the elements greater than or equal to the first element to the other, sort these two lists, and join the results together with the old first element in the middle. Here is the code for this:
public static ArrayList<Integer> sort(ArrayList<Integer> a) { if(a.size()<=0) return a; ArrayList<Integer> smaller = new ArrayList<Integer>(); ArrayList<Integer> greater = new ArrayList<Integer>(); Integer pivot = a.get(0); for(int i=1; i<a.size(); i++) { Integer n=a.get(i); if(n.compareTo(pivot)<0) smaller.add(n); else greater.add(n); } smaller=sort(smaller); greater=sort(greater); smaller.add(pivot); smaller.addAll(greater); return smaller; }You can find this code in the files QuickSort1.java, QuickSort1a.java, QuickSort2.java from the code folder for this section. These differ only in the front end code provided, the code for the method
sort
is the same in each of them. There is a front end where you type in
the numbers to be sorted, a front end where they are generated randomly and the result is
displayed, and a front end where the numbers are generated randomly and the sorted
arrayList is not displayed, just timing figures for how long it took to do the sorting.
The front end which does not display the result can be used to test the sorting code with
many more numbers than it would be sensible to print.
The code relies on arrayLists being flexible in size, since we do not know in advance
the number of elements which are less than the first element. Note that the code
uses a method from Java's ArrayList<E>
class which has not been
mentioned previously, addAll
. If a
and b
refer to two arrayList objects, then the call a.addAll(b)
destructively changes the object referred to by a
so
that all the elements from the object referred to by b
are added to it, in the order they occurred, after the existing elements.
You can see Java's official documentation for this method
here.
If we wish to code constructive quicksort on arrays rather than arrayLists, we can do so simply as follows. Go through the original array once to find the number of elements less than the pivot. This gives us the size of the array of elements less than the pivot, and by implication, the size of the array of elements greater than or equal to the pivot not including the pivot itself. So we create arrays of these sizes and go back through the original array putting its elements into one or other of the new arrays. We then sort these new arrays, and join the results together in a final array of the same size as the original array. This gives us the following code:
public static Integer[] sort(Integer[] a) { if(a.length==0) return new Integer[0]; else if(a.length==1) { Integer[] b = new Integer[1]; b[0]=a[0]; return b; } else { Integer pivot = a[0]; int count=0; for(int i=0; i<a.length; i++) if(a[i].compareTo(pivot)<0) count++; Integer[] smaller = new Integer[count]; Integer[] greater = new Integer[a.length-count-1]; for(int i=1, j=0, k=0; i<a.length; i++) if(a[i].compareTo(pivot)<0) smaller[j++]=a[i]; else greater[k++]=a[i]; smaller = sort(smaller); greater = sort(greater); Integer[] b = new Integer[a.length]; for(int i=0; i<count; i++) b[i]=smaller[i]; b[count]=pivot; for(int i=0; i<greater.length; i++) b[count+i+1]=greater[i]; return b; } }Note the code for the loop which goes through the original array (referenced by variable name
a
) and copies references
to its elements into either one or other of the new arrays (referenced
by variable names smaller
and greater
). This is
the loop:
for(int i=1, j=0, k=0; i<a.length; i++) if(a[i].compareTo(pivot)<0) smaller[j++]=a[i]; else greater[k++]=a[i];Here,
i
is the index to the original array,
j
is the index to the array smaller
and
k
is the index to the array greater
.
These three int
variables are declared together in
the initialisation part of the loop, but only i
is updated
in the update part of the loop. This is because each time round the loop
we need to consider the next element from the array a
, but
we only update j
if we add that element to the array
smaller
and we only update k
if we add
that element to the array greater
. Remember that j++
means “use the value of j
then increase it by 1”. So
smaller[j++]=a[i]
is equivalent to:
smaller[j]=a[i]; j=j+1;Be careful to recall this is different from:
j=j+1; smaller[j]=a[i];which is the equivalent of
smaller[++j]=a[i]
, and remember
that the ++
operators change the value of an integer variable
permanently, so don't think that smaller[j++]=a[i]
is the same
as smaller[j+1]=a[i]
where there is nothing to change the
value of j
.
You can find code for constructive quicksort of arrays of integers in the form given here in the files QuickSort3.java and QuickSort4.java. The second of these does not print the sorted array, but gives timing figures, so you can use it to check the time taken to sort a very large sized array.
There is an inefficiency in this code, because it compares each element against the pivot twice, first to find the size of the two smaller arrays, and then to decide for each element which array it should go into. Here is a way that could be used to get round this:
public static Integer[] sort(Integer[] a) { if(a.length==0) return new Integer[0]; else if(a.length==1) { Integer[] b = new Integer[1]; b[0]=a[0]; return b; } else { Integer pivot = a[0]; boolean[] greaterthan = new boolean[a.length]; int count=0; for(int i=0; i<a.length; i++) if(a[i].compareTo(pivot)<0) count++; else greaterthan[i]=true; Integer[] smaller = new Integer[count]; Integer[] greater = new Integer[a.length-count-1]; for(int i=1, j=0, k=0; i<a.length; i++) if(greaterthan[i]) greater[j++]=a[i]; else smaller[k++]=a[i]; smaller = sort(smaller); greater = sort(greater); Integer[] b = new Integer[a.length]; for(int i=0; i<count; i++) b[i]=smaller[i]; b[count]=pivot; for(int i=0; i<greater.length; i++) b[count+i+1]=greater[i]; return b; } }Here we create a temporary array of booleans of the size of the array being sorted, and go through the array of elements, in each case where an element is greater than the pivot setting the same position in the array of booleans to
true
.
When a new array of booleans is created, all its positions are initialised to
false
, so the other position remain set to false
. We also do
the counting as before. After that, instead of doing the checking against the pivot
again, whether an element is put into the smaller
or greater
is decided by what value is stored in its position in the array of boolean.
You can find code for this form of constructive quicksort of arrays of integers in the files: QuickSort3a.java and QuickSort4a.java. Again, the second of these can be used to get timing figures for sorting very large arrays. If you compare the time taken to sort an array of a particular size and range with the time taken using QuickSort4.java to sort an array of the same size and range, you should find it is significantly less. Comparing integers is done in one step, so this would be even more of an issue if we were sorting an array of elements which involved some longer calculation to determine whether one is less than another.
A simple way of obtaining destructive quicksort would be to copy the elements from the two sorted arrays in the above code back into the original array rather than into a new array, but otherwise use code similar to the constructive quicksort given above. This would give the code:
public static void sort(Integer[] a) { if(a.length>1) { Integer pivot = a[0]; int count=0; for(int i=0; i<a.length; i++) if(a[i].compareTo(pivot)<0) count++; Integer[] smaller = new Integer[count]; Integer[] greater = new Integer[a.length-count-1]; for(int i=1, j=0, k=0; i<a.length; i++) if(a[i].compareTo(pivot)<0) smaller[j++]=a[i]; else greater[k++]=a[i]; sort(smaller); sort(greater); for(int i=0; i<count; i++) a[i]=smaller[i]; a[count]=pivot; for(int i=0; i<greater.length; i++) a[count+i+1]=greater[i]; } }and you can find this code in the files QuickSort5.java and QuickSort6.java. Although this is destructive, since the array passed as an argument is changed, it does involve the construction of the two temporary arrays indexed by
smaller
and
greater
, and when each of these are sorted further
temporary arrays are created as the algorithms is executed recursively.
Since it is destructive, new arrays are not created in the base case
when the array being sorted is of length 1 or 0.
As previously, it could have been made more efficient by using the technique of having
an array of booleans to avoid checking each element twice.
However, there is another version of quicksort on arrays which does not involve any construction of new arrays at all. Instead it works just by swapping the elements around in the array, as we saw previously with the destructive selection sort. This is referred to as quicksort in place
The algorithm is the same before: divide the array into two portions, one containing all the elements less than the pivot, the other containing all the elements greater than or equal to the pivot, then sort each of these two portions recursively. However, in this case instead of creating new arrays, we move the elements about in the original array until they are in positions where one section of the array is smaller elements and the other section is larger elements. We do not know at the start how many elements are less than the pivot and how many are greater, so the two sections are not necessarily equal in length. The base case is when the section being sorted has 0 or 1 elements in it.
There are several variations on the way this can be coded, but they all work on the principle of starting with two indexes to the portion of the array being sorted, one indexing either end. The lower one is increased to index further up the array and the higher one is decreased to index lower down the array, until the lower one indexes an element greater than or equal to the pivot and the upper one indexes an element less than the pivot. These two elements are then swapped in position. This is repeated until the two indexes have crossed over. At that point, the array has been partioned into a smaller and larger section, and these sections are sorted recursively.
Here is some code to do this:
public static void sort(Integer[] a)
{
sort(a,0,a.length);
}
private static void sort(Integer[] a,int from,int to)
{
if(to>from+1)
{
Integer pivot = a[from];
int low=from+1,high=to-1;
while(low<high)
{
while(low<high&&a[low].compareTo(pivot)<0)
low++;
while(pivot.compareTo(a[high])<0)
high--;
if(low<high)
{
swap(a,high,low);
low++;
high--;
}
}
while(pivot.compareTo(a[high])<0)
high--;
swap(a,from,high);
sort(a,from,high);
sort(a,high+1,to);
}
}
private static void swap(Integer[] a,int pos1,int pos2)
{
Integer temp = a[pos1];
a[pos1]=a[pos2];
a[pos2]=temp;
}
In this version, the first element in the section of the array being sorted is
used as the pivot. Once the high
index has crossed with
the low
index it may need to be moved down further until
it indexes an element less than or equal to the pivot, which thus must be
the last element in the smaller valued section of the array. This element is
swapped with the pivot element, so the pivot element is positioned between the smaller
elements and the larger elements. When the recursive sorts are finished, the whole
array will then be sorted.
The work of the sorting is done in the method sort
which takes three arguments, a reference to the array, the position of the
first element in the section of the array which is currently being sorted,
and the position following the position of the last element in the section of
the array being sorted. Initially this method is called with the two positions
being0
and the length of the whole array. So note that instead
of actually creating separate arrays to sort using a recursive method, we
have a method which sorts a section of one array, and recursive calls
sort smaller sections of the same array.
Let us consider an example. Suppose the whole array being sorted is
[30,25,47,99,8,12,28,63,16,20]Then we take
30
as the pivot, and start with the
low
index at 1
and the high
index at
9
(the index of the last component of the array).
Then the low
index is increased until it has the value 2
which indexes 47
which is greater than 30
,
and the high
index remains at 9
as this indexes
the value 20
, which is less than 30
. Swapping
47
with 20
gives:
[30,25,20,99,8,12,28,63,16,47] low=2 high=9The
low
index is increased by one and indexes the number
99
which is greater than 30
. The high
index is decreased by one and indexes the number 16
which is less
than 30
. These two numbers are swapped round giving:
[30,25,20,16,8,12,28,63,99,47] low=3 high=8Now
low
is increased until it indexes the next element which
is greater than the pivot, which is 63
at position 7
while high
is decreased until it indexes the next element which
is less than the pivot, which is 28
at position 6
.
At this point, high
and low
have crossed over.
[30,25,20,16,8,12,28,63,99,47] low=7 high=6Swapping the pivot with the element indexed by
high
gives:
[28,25,20,16,8,12,30,63,99,47]We can now recursively sort the portion of the array
[28,25,20,16,8,12]
and the portion [63,99,47]
. But there is no need to copy the
numbers into separate arrays to do this, we just call the sort
method with the whole array, 0
and 6
as its
arguments to sort the first part, and with the whole array, 7
and
10
to sort the second part. When both parts are sorted, the
whole array is sorted.
Code for this version of quicksort can be found in the files QuickSort7.java and QuickSort8.java. Code using the same algorithm, but applied to an arrayList rather than an array can be found in the files QuickSort9.java and QuickSort10.java.
The merge sort algorithm, which we saw previously with Lisp lists can also be applied to arrays. Since merge sort works by dividing the original array into two equally sized halves, we can create these half sized arrays immediately and sort them (note they will differ in size by one if the original array is of odd length). It does not matter which elements go into each half, since in merge sort the work of comparing elements is done after the recursive calls when the sorted collections are merged.
Here is code for merge sort of an array of integers:
public static void sort(Integer[] a) { if(a.length>1) { int i,mid = a.length/2; Integer[] half1 = new Integer[mid]; Integer[] half2 = new Integer[a.length-mid]; for(i=0; i<mid; i++) half1[i]=a[i]; for(; i<a.length; i++) half2[i-mid]=a[i]; sort(half1); sort(half2); int j=0, k=0; for(i=0; j<half1.length&&k<half2.length; i++) if(half1[j].compareTo(half2[k])<0) { a[i]=half1[j]; j++; } else { a[i]=half2[k]; k++; } for(; j<half1.length; i++, j++) a[i]=half1[j]; for(; k<half2.length; i++, k++) a[i]=half2[k]; } }The code before the recursive calls
sort(half1); sort(half2);creates two new arrays, and then copies the elements from the first half of the original array into one of them, and copies the elements from the second half into the other. The recursive calls apply the same algorithm on these two arrays. The base case is when the array is of length 0 or 1, so as the recursive calls are guaranteed to be on arrays of smaller size than the original array, the base case will eventually be reached.
The code after the recursive calls
merges the two sorted temporary arrays, referred to by variables
half1
and half2
, back into the space of
the original array referred to by variable a
. It uses
the technique we saw with partioning in constructive quicksort
of an index for each of the individual arrays, again with the
names i
for the main array and j
and k
for the smaller arrays. Again, i
is increased each time
round the loop, and either j
or k
as
appropriate, but not both, is increased each time round the loop.
The merge part looks at the element in the array half1
indexed by j
and the element in the array half2
indexed by k
. It copies whichever of them is smaller
into a[i]
and increases the index of whichever array
it was copied to by one, so next time round the loop the following
element in the array is looked at.
We can consider how this algorithm works on the array we used before,
[30,25,47,99,8,12,28,63,16,20]
. It is divided into two halves,
[30,25,47,99,8]
and [12,28,63,16,20]
.
Sorting these gives [8,25,30,47,99]
and
[12,16,20,28,63]
. Now let us consider merging them. We have:
[_,_,_,_,_,_,_,_,_,_] i=0 [8,25,30,47,99] j=0 [12,16,20,28,63] k=0The first array here actually still stores the numbers in their original order, but that is irrelevant as they will be overwritten in the merger. Here, starting off with
i
, j
and
k
all at 0
, half1[j]
is 8
and half2[k]
is 12
, so half1[j]
is the lower, it is copied into a[i]
and j
is increased by one, giving:
[8,_,_,_,_,_,_,_,_,_] i=1 [8,25,30,47,99] j=1 [12,16,20,28,63] k=0Now
half2[k]
is lower than half1[j]
so it
is copied into a[i]
and k
is increased by one
giving:
[8,12,_,_,_,_,_,_,_,_] i=2 [8,25,30,47,99] j=1 [12,16,20,28,63] k=1And now
half2[k]
is lower than half1[j]
again,
so it is copied into a[i]
and k
is
increased by one again, giving:
[8,12,16,_,_,_,_,_,_,_] i=3 [8,25,30,47,99] j=1 [12,16,20,28,63] k=2Once again,
half2[k]
is lower than half1[j]
,
so the situation becomes:
[8,12,16,20,_,_,_,_,_,_] i=4 [8,25,30,47,99] j=1 [12,16,20,28,63] k=3But now
half1[j]
is lower than half2[k]
, so
the situation becomes:
[8,12,16,20,25,_,_,_,_,_] i=5 [8,25,30,47,99] j=2 [12,16,20,28,63] k=3You can see that by carrying on this work, the original array will be filled with the elements from the other two arrays in sorted order. Actually, it won't quite work like this, it will always be the case that
j
reaches the length of half1
when k
hasn't reached the length of half2
, or the other way round.
Once the all the elements from one of the arrays have been copied into the
original array, any elements left in the other array have to be copied in
after them, that is why we have the additional statements:
for(; j<half1.length; i++, j++) a[i]=half1[j]; for(; k<half2.length; i++, k++) a[i]=half2[k];The body in one or other of these loops will be executed, depending on whether
j
is less than half1.length
or k
is less than half2.length
, to copy
the remaining elements into the original array. The other loop will
only get as far as the loop test which will be false before the body
is executed.
Code for merge sort of arrays of integers, together with support code to give your own integers, generate and print random integers, or generate random integers and sort them but just give timing figures can be found in the files MergeSort1.java, MergeSort3.java and MergeSort5.java. There is also code, with similar supporting code, to merge sort arrays of strings in the files MergeSort2.java, MergeSort4.java and MergeSort6.java. You will need the file WordGen.java to generate random strings.
Merge sort can be applied to arrayLists as well as to arrays. The files MergeSort7.java and MergeSort8.java show merge sort applied to arrayLists.
Note the merge sort code we have given here is destructive, because the merger puts the sorted objects back into the same array or arrayList object they came from originally. If they were instead put into a new array or arrayList, we would obtain constructive merge sort.
When a method call is made, a new environment for that method call is created, the method call is executed in that environment, and then execution returns to the environment it was in previously, which is the method call that made the method call. So as method calls make method calls, a stack of environments is built up. The current environment is at the top of the stack. When a method is called a new environment is added to the top of the stack. When a method call terminates, its environment is taken off the top of the stack.
To show that there is nothing “magic” about recursion, we can look at how a method that is usually expressed recursively can be implemented purely iteratively if, instead of using recursion, there is an explicit stack of the values that would otherwise be in the method call environments. Here is destructive quicksort of an array expressed in that way:
public static void sort(Integer[] a) { ArrayList<Pair> stack = new ArrayList<Pair>(); stack.add(new Pair(0,a.length)); while(stack.size()>0) { Pair p=stack.remove(stack.size()-1); int from=p.from; int to=p.to; if(to>from+1) { Integer pivot = a[from]; int low=from+1,high=to-1; while(low<high) { while(low<high&&a[low].compareTo(pivot)<0) low++; while(pivot.compareTo(a[high])<0) high--; if(low<high) { swap(a,high,low); low++; high--; } } while(pivot.compareTo(a[high])<0) high--; swap(a,from,high); stack.add(new Pair(from,high)); stack.add(new Pair(high+1,to)); } } }The stack is given by an arrayList of
Pair
objects, where a
Pair
just holds two integer values, from
and
to
. Initially the stack is set to hold the pair 0
and a.length
where a
is the array, just as the
initial call to the recursive method in the code
above had these as its arguments.
In the place of the recursive calls in that code, new pairs are added to the
stack. There is a loop which takes the pair off the top of the stack and
executes the equivalent of the recursive method call with the from
and to
values of that pair. When the stack becomes empty it is
the equivalent of all recurisve calls having been made, so the original call
is returned to, and the computation terminates.
The following here is the code for class Pair
:
private static class Pair { int from, to; Pair(int f,int t) { from=f; to=t; } }This can be declared as a nested class, which is the class equivalent of a helper method, that is a class declared only to be used internally by code in another class. Java allows a class to be declared inside another class. Like a method, a class declared inside another class may be declared as
static
meaning it is self-contained. If it is not declared
as static
it is called an inner class. There are
additional issues with inner classes, but a static
nested
class is no different than any other class, except it can only be used directly
in code in the class it is in. Because it can only be used in that limited
way, the usual rule of practice that variables in a class must be declared as
private
and accessed only through public methods does not have to
be applied. So, the variables from
and to
in
Pair
objects used in the sort
code are accessed
directly, this is not a problem since no code in other classes could access
them and change them. If you have code which requires a collection of values
grouped together, such as here where from
-to
pairs
are required, the way to do it is to declare a small nested class like this.
You can find the code for this non-recursive implementation of quicksort in the files
QuickSort11.java and
QuickSort12.java.
Note, you do not need a separate file for the class Pair
since
as it is a nested class its code is inside these files.
QuickSort12.java
has the front-end which gives the time taken, so you can compare it with the
equivalent recursive code in
QuickSort8.java.
People who find recursion difficult to understand often don't use it and
instead write more complicated non-recursive code, making the excuse that
they are doing that “because recursion is less efficient”.
However, if you run these two programs, you will find the recursive
version, if anything, teds to run quicker than the iteratuve version, not slower.
Sorting is such a fundamental task in computing that anyone who deserves the description “Computer Scientist” ought to have a good familiarity with the main sorting algorithms. It also helps us develop an understanding of algorithms and of how code translates algorithms into something the computer can obey to see these sorting algorithms and to see Java code for them. However, in practice as a Java programmer you would not have to write your own code to sort arrays of data. It is such a common operation that the Java library provides code for you which does the job. Not only does this mean you don't have to write your own code, you can also be sure that the developers of the Java language put a lot of thought into the code library they distribute with it. You can therefore be sure that the array sorting methods they provide do not contain any errors and are written to be the most efficient algorithm for sorting (2).
The philosophy of the Java language is to re-use code wherever possible. So it's always a good idea to see if the Java language already supplies code in its code library to fill a need you have, rather than write code yourself. It is not impossible for code supplied from elsewhere to be contain errors. It is however much more likely to be error‑free than code you have written yourself, in particular if you are using it in a standard way to do standard tasks, and it has been provided by a trusted provider. As a novice programmer, you can safely assume that the Java compiler and Java library code will always work correctly - if your code does not work, it will be because of an error or a misassumption you have made, not because of an error in the compiler or library code.
The Java class Arrays
contains a range of static methods for doing common array operations.
It has a static method called sort
which destructively
sorts arrays of any objects. So Arrays.sort(a)
where
a
refers to an array will change the array that a
refers to so that it is sorted. Note that the sorting it does is in the
order given by the compareTo
method of the objects it
is sorting. For example, if str1
and str2
are
of type String
then str1.compareTo(str2)
gives a negative number if str1
comes before str2
alphabetically, and a positive number if str1
comes
after str2
alphabetically. This means that if a
is of type String[]
then Arrays.sort(a)
will sort the array referred to by a
into alphabetical
order. This is not the only possible ordering you might want to apply
to an array of strings. For example, you might want to sort an array of
strings in order of their length rather than alphabetically. We will see
later
how code for sorting can be generalised to allow different orderings
to be specified. The ordering given by an object's compareTo
methods is referred to as the natural ordering.
Even with an array of integers, while you might think the only possible ordering you would ever need is the natural ordering of ascending numerical order, there could be other possibilities, apart from the obvious descending numerical order. Suppose, for example, you were running a competition “guess the number of marbles in a jar”, and you knew the answer was 1037. Then if you had guesses stored in an array, you would want to order those guesses in the order “closest to 1037”. In this ordering, 1042 comes before 1027, but after 1039. Again, this illustrates the importance of being precise in your specification. What you assume is the only possible interpretaton of some vaguely worded specification such as “sort this array” may not be the only possible one. It is always good to write specifications for code that has to be written in such a way as there cannot possibly be any alternative way of interpreting the specification.
In the files
JavaSort1.java and
JavaSort3.java, you
will find code that uses Java's sort
method to
sort an array of randomly generated integers, in the first case
displaying the integers before and after sorting, in the second
displaying only timing figures. In the files
JavaSort2.java and
JavaSort4.java, you
will find code that uses Java's sort
method to
sort an array of randomly generated strings (using the file
WordGen.java to
generate them). The strings are sorted in their default order,
which is alphabetical.
The Java class Collections
is equivalent to the Java class Arrays
but dealing with arrayLists
rather than arrays. Actually, it deals with a range of Collection
types, of which ArrayList
is just one example, but it
is the only example we have seen so far. So if a
is
a variable of type ArrayList<Integer>
or
ArrayList<String>
, or an arrayList of any other
element type so long as that element type may have the method
compareTo
called on it, then Collections.sort(a)
will re-order the contents of the arrayList referred to by a
according to the ordering given by the compareTo
method
of the element type. It has several other useful methods as well, for example
Collections.max(a)
will return the maximum element according
to natural ordering in the arrayList referred to by a
In the files
JavaSort5.java and
JavaSort7.java, you
will find code that uses Java's sort
method to
sort an arrayList of randomly generated integers, in the first case
displaying the integers before and after sorting, in the second
displaying only timing figures. In the files
JavaSort6.java and
JavaSort8.java, you
will find code that uses Java's sort
method to
sort an arrayList of randomly generated strings.
(1) You probably would not notice if it wasn't pointed out here, but “quicksort” is written as one word while “merge sort” is written as two words. The reason for this is that quicksort is an algorithm invented by the computer scientist Tony Hoare, who gave it this one word name in his article describing it in the July 1961 edition of the Communications of the ACM. There is no definite claim for the invention of merge sort, although opinion is that it was first described by the computer scientist John von Neumann. It is often assumed that the sorting algorithm Shell sort (which we have not covered in this module) is so named because “shell” refers to an aspect of the algorithm. This algorithm, like quicksort, had a definite inventor who described it in another Communications of the ACM article, his name is Donald Shell, and the algorithm is named after him.
(2) You cannot be completely sure, however. In fact, for many years, Java's
sorting code (and sorting code in many other places) contained an error.
You can see comment on this error
here.
The problem would only occur when sorting very large arrays, since it comes
about when two array indexes are added in order to get the mid-point, but
their sum is greater than the maximum value that can be held in an
int
variable. In general, library code will have been
thoroughly tested by its distributors, and then by being used by all the
people who have made use of it - that's much more testing than you would be
able to do on code you've written yourself. So while not completely
ruling out the possibility that if your code isn't working it's due to an
error in library code or the compiler, in general you can assume that if
your code isn't working as you expect, even if its failure to work completely
baffles you, it's likely to be something you have written or understood
wrongly, not the library software you're using which is behaving wrongly.
This is particularly so if your programming isn't doing anything very unusual
(and nothing you are asked to do in this module will involve code so unusual
that it will reveal an error in Java that no-one found before).
Last modified: 8 March 2019