7
\$\begingroup\$

In order to complete one of my assignment I needed to include a quicksort so I was wondering if this is correct and acceptable.

The code:

 private ArrayList<String> sort(ArrayList<String> ar, int lo, int hi){
 if (lo < hi){
 int splitPoint = partition(ar, lo, hi);
 sort(ar, lo, splitPoint);
 sort(ar, splitPoint +1, hi);
 }
 return ar;
 }
 private int partition(ArrayList<String> ar, int lo, int hi){
 String pivot = ar.get(lo);
 lo--;
 hi++;
 while (true){
 lo++;
 while (lo<hi && ar.get(lo).compareTo(pivot) < 0){
 lo++;
 }
 hi--;
 while (hi>lo && ar.get(hi).compareTo(pivot) >= 0){
 hi--;
 }
 if (lo<hi){
 swap(ar, lo, hi);
 }else {
 return hi;
 }
 }
 }

The additional swap method:

 private ArrayList<String> swap(ArrayList<String> ar, int a, int b){
 String temp = ar.get(a);
 ar.set(a, ar.get(b));
 ar.set(b, temp);
 return ar;
 }

If this is incorrectly done can you please give me some pointers and tell me why. Otherwise I would gladly accept any suggestions you have.

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Feb 7, 2014 at 23:46
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Have you tested your code anything? \$\endgroup\$ Commented Feb 8, 2014 at 0:00
  • \$\begingroup\$ @SimonAndréForsberg yes I did and it seems to work. \$\endgroup\$ Commented Feb 8, 2014 at 0:38

2 Answers 2

4
\$\begingroup\$

Those are pure functions that don't access any instance variables. You can therefore make them static.

You could use generics more generically. Your code should work with any List, as long as it supports an efficient .get(i) for arbitrary i. The elements of the list can be any kind of Comparable object.

The .sort() function should have a void return type to avoid giving the false impression that it returns a copy of the original array.

private static <E extends Comparable<E>, L extends List<E> & RandomAccess>
void sort(L ar, int lo, int hi) {
 if (lo < hi) {
 int splitPoint = partition(ar, lo, hi);
 sort(ar, lo, splitPoint);
 sort(ar, splitPoint +1, hi);
 }
}
private static <E extends Comparable<E>, L extends List<E> & RandomAccess>
int partition(L ar, int lo, int hi){
 E pivot = ar.get(lo);
 lo--;
 hi++;
 while (true){
 lo++;
 while (lo<hi && ar.get(lo).compareTo(pivot) < 0){
 lo++;
 }
 hi--;
 while (hi>lo && ar.get(hi).compareTo(pivot) >= 0){
 hi--;
 }
 if (lo<hi){
 swap(ar, lo, hi);
 }else {
 return hi;
 }
 }
}
private static <E extends Comparable<E>, L extends List<E> & RandomAccess>
void swap(L ar, int a, int b){
 E temp = ar.get(a);
 ar.set(a, ar.get(b));
 ar.set(b, temp);
}
answered Feb 8, 2014 at 1:02
\$\endgroup\$
3
  • \$\begingroup\$ I am not a fan of exposing the RandomAccess interface in the generics. In this case, as example code, it sort of makes sense, but, in a real implementation, the signature should just be private static <E extends Comparable<E>> void sort(List<E> ar, int lo, int hi) {... and then, inside the sort method, it should choose between two alternative algorithms depending on whether the input list is RandomAccess or not. The RandomAccess interface leads to confusion for most Java programmers (who have never heard of it). \$\endgroup\$ Commented Feb 8, 2014 at 1:50
  • \$\begingroup\$ @rolfl True, but then you would be getting into creeping expansion of feature scope. My point was that you could get that flexibility for free just by changing the method signatures. \$\endgroup\$ Commented Feb 8, 2014 at 3:48
  • \$\begingroup\$ It all seems reasonable points to me, but as of now I can't relate to what you said as I am still to learn 'generics' in my AP class. \$\endgroup\$ Commented Feb 8, 2014 at 13:56
2
\$\begingroup\$

I agree with the handy generics use from previous answer and I would like to add something more that I noticed.

In your partition method I thin you can remove the following code

private int partition(ArrayList<String> ar, int lo, int hi){
 String pivot = ar.get(lo);
 lo--;//remove
 hi++;//remove
 while (true){
 lo++;//remove
 while (lo<hi && ar.get(lo).compareTo(pivot) < 0){
 lo++;
 }
 hi--;//remove
 while (hi>lo && ar.get(hi).compareTo(pivot) >= 0){
 hi--;
 }
 if (lo<hi){
 swap(ar, lo, hi);
 }else {
 return hi;
 }
 }
}

I the l-- and l++ right after you can get rid of because this path always happens. regarding the h++ and h-- I had to look into it longer but if you think line by line what the code does I am pretty sure those two can be removed as well.

Efficiency:

Another important point regarding the efficiency (since you asked that as well) is the selection of the pivot. Quick sort has complexity from O(n.logn) to O(n^2). And that depends on the selection of the pivot. Lets say you try to sort the array that is sorted other way around. Your pivot selection would result in O(n^2) complexity because in every level of recursion one half is of a size of 1 and other is n - 1. The best complexity would be if both the half are the same at each level, which you cannot achieve for any array.

Statistically speaking if you select the pivot randomly every time you end up with asymptotic time complexity O(nlogn) in most of the cases. That is why Quicksort is said to have average time complexity O(nlogn).

Last addition about the static method. That was a good point in the previous answer but just bear in mind that static methods cannot be overriden and are hard to mock in testing. So if you would like to do some general design of your classes to let's say have one super class "Sort" with method sort and underneath having QuickSort, Buble, Merge Sort... you would probably like to stay with non-static sorting method.

answered Feb 8, 2014 at 21:43
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.