< Back

Lecture 17 - Quick Sort


As usual, create a directory to hold today's activities:

$ mkdir ~/cs170/labs/lab17 
$ cd ~/cs170/labs/lab17 

How to Recurse

Recursion is probably still a little bit hazy in your minds. That's a little understandable, because it is quite a weird concept. But I think part of the issue is that you are still a little cloudy on how function calls really work, as far as the python interpreter is concerned. Let's cover functions, and how that relates to recursion today as we go over merge sort.


Lab Activity 1
Quick Sort

Quick sort earned its name by being an incredibly quick sorting algorithm. The idea is roughly the same as merge sort: Split the list into two halves and recursively call the same procedure. The difference is when the actual work of sorting is taking place.

In merge sort, the work is being done AFTER the list has been split up into its smaller pieces. In quick sort, the work is being done BEFORE the list is split up. The process of "sorting" is actually creating the two "halves" of the list. By finding the partition element's correct location in the list, you just have to worry about sorting the list before the partition element, and the list after the partition element.

Details

In the file sorting.py file, create two functions: quick_sort_copy(a_list) and partition(a_list). Both of these functions take a list of comparable elements. quick_sort_copy(a_list) should return a sorted copy of a_list, while partition should return three values: The partition element, a list of all of the items that are less than or equal to the partition element, and a list of all of the items that are greater than the partition element.

partition uses the last element of the list as the partition element. It iterates through the rest of the list, appending items into either a left list, or a right list depending on how it compares to the partition element. We will arbitrarily say that all of the elements equal to the partition element will go into the half of the list less than the partition element.

quick_sort_copy is very simple: It calls partition, then recursively calls quick_sort_copy on the left and right halves of the list.

Example

>>> my_list = [5, 4, 3, 2, 1]
>>> partition_return = partition(my_list)
>>> print(partition_return)
(1, [], [5, 4, 3, 2]
>>> quick_sort_return = quick_sort(my_list)
>>> print(quick_sort_return)
[1, 2, 3, 4, 5]

Hint

  • Your base case for quick sort is when the length of the list is one. A list of length one is already in sorted order.

  • Partition needs to create two lists, which I will call left and right. as you go through the elements of the incoming list, simply append to the correct list.

  • When you are joining your recursive calls, you need to append the partition element to the end of the left list. Then join the two lists together.

Lab Activity 2
Quick Sort

The above version of quick sort is pretty fast, but it uses a lot of extra memory. On Monday I made you write the in place version of Merge sort first. I think that caused too much confusion. However, I think it's still important for you to write the in place version of quick sort as well.

Details

In the file sorting.py file, create two functions: quick_sort(a_list, start, end) and partition(a_list, start, end). Both of these functions take a list of comparable elements and two integers in the range [0, len(a_list)]. Neither of these functions should return a list, they should simply modify the order of the elements in place. Partition, however, should return the index of where it placed the partition element.

partition uses the last element of the list as the partition element. It iterates through the rest of the list, performing a swap operation when it finds an item that is not in the correct half of the list. We will arbitrarily say that all of the elements equal to the partition element will go into the half of the list less than the partition element.

quick_sort is very simple: It calls partition, then recursively calls quick_sort on the left and right halves of the list.

How does Quick Sort compare to Merge Sort? Are either the best we can do?

Example

>>> my_list = [5, 4, 3, 2, 1]
>>> partition_return = partition(my_list, 0, len(my_list))
0
>>> print(my_list)
[1, 4, 3, 2, 5]
>>> quick_sort_return = quick_sort(my_list, 0, len(my_list))
>>> print(my_list)
[1, 2, 3, 4, 5]
>>> print(quick_sort_return)
None

Hint

  • Your base case for quick sort is when the length of the list is one. A list of length one is already in sorted order.

  • Notice that the end index for my examples above are non-inclusive: You specify the index that is one past the end of the sub list. I do this because it allows for easy specification of the range of your for loop for partition. However, the book doesn't do it this way. You may choose which one makes more sense for you.

  • Remember, the pseudo code in the text book is based off a 1's based index scheme for lists. You need to modify it slightly to work with a 0's based index.



Submission

When you have finished, create a tar file of your lab16 directory. To create a tar file, execute the following commands:

cd ~/cs170/labs
tar czvf lab17.tgz lab17/

To submit your activity, go to inquire.roanoke.edu. You should see an available assignment called Lab Assignment 17. Make sure you include a header listing the authors of the file.


In-Class Notes