5
\$\begingroup\$

Code is below. I'd like to know if this is a solid/efficient implementation of the in-place quicksort algorithm and if my Python style is good (I know there are no comments for now). Code is also on Github here if you'd like: https://github.com/michaelrbock/data-structs-and-algos/blob/master/Sorts/quicksort.py Thanks!

def quicksort(lst):
    if len(lst) <= 1:
        return lst
    lst, store_index = partition(lst)
    return quicksort(lst[:store_index-1]) + [lst[store_index-1]] + quicksort(lst[store_index:])

def partition(lst):
    if len(lst) % 2 == 0:
        middle = (len(lst) / 2) - 1
    else:
        middle = len(lst) / 2

    pivot_choice = get_median( [lst[0], lst[middle], lst[len(lst)-1]] )

    if pivot_choice == lst[0]:
        PIVOT_INDEX = 0
    elif pivot_choice == lst[middle]:
        PIVOT_INDEX = middle
    elif pivot_choice == lst[len(lst)-1]:
        PIVOT_INDEX = len(lst) - 1

    pivot = lst[PIVOT_INDEX]
    lst[0], lst[PIVOT_INDEX] = lst[PIVOT_INDEX], lst[0]
    i = 1
    for j in range(1, len(lst)):
        if lst[j] < pivot:
            lst[j], lst[i] = lst[i], lst[j]
            i += 1
    lst[0], lst[i-1] = lst[i-1], lst[0]
    return lst, i

def get_median(nums):
    values = sorted(nums)
    if len(values) % 2 == 1:
        return values[(len(values)+1)/2-1]
    else:
        lower = values[len(values)/2-1]
        upper = values[len(values)/2]
    return (lower+upper)/2
\$\endgroup\$
4
  • \$\begingroup\$ Doesn't using sorted in a sorting function defeat the purpose of writing your own sorting function? \$\endgroup\$
    – Jaime
    Commented Jul 27, 2013 at 14:27
  • \$\begingroup\$ yes, yes it would. But in this case, sorted() in only used in the choice of pivot, which is really just some preprocessing outside of the quicksort. Though perhaps some more imaginative implementation would have been more in tune with the problem \$\endgroup\$
    – Nick Burns
    Commented Jul 29, 2013 at 2:17
  • \$\begingroup\$ The in-place sort doesn't work correctly. The returned list is sorted, but the list that you call quicksort on isn't neccessarily. a = [2,99,1,0,3]; print quicksort(a), a prints [0, 1, 2, 3, 99] [0, 1, 2, 99, 3]. \$\endgroup\$
    – flornquake
    Commented Aug 1, 2013 at 23:05
  • \$\begingroup\$ @flornquake, good point thanks. Any suggestions on how to improve this? \$\endgroup\$ Commented Aug 4, 2013 at 23:30

2 Answers 2

1
\$\begingroup\$

Personally I like the style (especially the nice clear function and variable names). As its quicksort most of the code is straightforward enough that comments would probably only add meaning when it comes to the swapping. You should probably try and use four spaces, rather than tabs, for indenting the code (it's just common practice). The only minor stylistic change I would personally make is to uncapitalise PIVOT_INDEX. I know technically it is constant (in that it doesn't change value after it's assigned), but I just prefer to have one clear assignment for any constant values. However, I'm sure other people would disagree.

As for efficiency, you're always going to be bounded by the algorithm itsef, but there are a couple changes you could make. You calculate len(lst) multiple times in partition. Rather than doing that, you could just assign it like list_length = len(lst) once at the start of the partition function. Not a big deal I know. The only other meaningful change I can think of is in your get_median function. As you know that it's always called with three values you could change it to:

def get_median(nums):
    values = sorted(nums)
    return values[1]

If you really want to be fancy you can replace the get_median and the whole if-then-else construct that determines the PIVOT_INDEX with something like:

PIVOT_INDEX = sorted(zip([lst[0], lst[middle], lst[list_length - 1]], [0, middle, list_length]))[1][1]

which will give you the index (0, middle or list_length) of the median value in the same way.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ len is an O(1) operation for Python lists, i.e. you don't calculate the length, you just read it. So there's no significant improvement there. \$\endgroup\$
    – Jaime
    Commented Jul 27, 2013 at 14:25
  • \$\begingroup\$ True. I suppose I worded it badly. I just meant that assigning a value to a variable is easier for the reader/writer of the code than explicitly evaluating it each time (although I suppose with len() its not a big deal). \$\endgroup\$
    – user27741
    Commented Jul 27, 2013 at 14:53
1
\$\begingroup\$

Your style is good.

I had a little improvement for efficiency. Instead of using the condition i % 2 == 0 for checking i as an even number you can instead use i % 2 as a condition for checking i as an odd number. It reduces the number of comparisons and doesn't effect the functionality at all. It might not have the best readability but it improves time-efficiency. A simple comment can offset the readability lost.

Also this seems like Python 2 so instead of using range which returns a list you should use xrange which returns a generator when you are looping. It costs a little time-performance but is usually compensated by the huge improvement in memory-performance.

Also in the median function you can probably eliminate the lower and upper variable by using something like

return (
    values[len(values)/2-1] +
    values[len(values)/2]
    )/2

Hope this helped.

\$\endgroup\$
1
  • \$\begingroup\$ xrange is not only more efficient than range memory-wise, it's also quite a bit faster when all you want to do is loop over a range of numbers. As for the lower and upper variables, I think it is perfectly fine to assign them, even if you only use them once. Giving the values a name makes it immediately obvious what the piece of code is doing (imo it is even more readable than an explanatory comment would be). \$\endgroup\$
    – flornquake
    Commented Aug 1, 2013 at 22:56

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.