Questions are in black, answers in blue.
Here we will use the main idea of Levitin 7.1 to implement a radix sort of strings.
public class StringArray
int n; // length of array
int k; // length of each string
String [] a; // the array of n strings, each must be of length k
public void sortBy (int i)
{// does stable sort on array a by the i'th letter of the strings
public void sort()
{// sorts strings in a lexicographically
sortBy
method, asuming that all the characters in
all the strings in the StringArray
are ASCII (each character is in
the range from 0 through 127 when viewed as a number).
Your method should run in O(n) time, treating 128 as a constant.
It is all right to make a second array of $n$ strings if you like.
Recall that you can cast a character to an int by the
operator ``(int)
''
or just by using the character where an int is called for.
public void sortBy (int i)
{// does stable sort on array a by the i'th letter of the strings
int[] count = new int[128]; // how many with each i'th letter
int[] next = new int[128]; // position for next string with letter
String [] b = new String[n]; // temporary to assemble sorted version
for (int j=0; j < n; j++)
count[a[j].charAt(i)]++;
for (int j=1; j < n; j++)
next[j] = next[j-1] + count[j-1]; // so next[i] is sum of counts < i
for (int j=0; j < n; j++) {
b[next[a[j].charAt(i)] = a[j];
next[a[j].charAt(i)]++;}
a = b;}
This is pretty similar to the pseudocode on Levitin page 249. (Levitin saves a little space by overwriting my "next" into "count".) Given j and z, the z'th element with charAt(i) = j is going into the location given by next[j] when it is moved, and this will be z-1 plus the original value of next[j], or z-1 plus the sum of count[y] for all y less than j. Timing is clearly Θ(n) since the loops execute n, n-1, and n times respectively.
sort
method using {\it radix sort} as follows: By
applying the different sortBy
operations for the various values of
i in the right order, and using the fact that sortBy
is
stable, you can sort the strings completely using O(nk) total time.
Give code for this and argue that your code sorts correctly.
Clearly this is Θ(kn) time because it makes k calls to sortBy.
Why is it correct? We need to show that for any two strings u and v, each
of length k, if u and v both occur in the array a and u is lexicographically
before v, then u will be placed before v in the output. Let u and v be
arbitrary strings meeting these conditions.
Since u is before v, there exists some number i such that u.charAt(i)
comes before v.charAt(i), and u.charAt(j) equals v.charAt(j) for all j less
than i. When the sort algorithm runs sortBy(i), u will be placed before v.
When it then runs sortBy(j) for each j less than i, the j'th characters of
the two strings will be equal, and then u will remain before v because
sortBy is a stable sort and preserves the order of elements that are tied in
its order. So at the end of the run of sort, u is before v as desired. Since
u and v were arbitrary, we conclude that sort sorts correctly.
public void sort()
{// does radix sort of array a
for (int j=k-1; j >= 0; j--)
sortBy(j);}
Last modified 5 November 2003