Understanding quicksort
Asked Answered
C

2

19

I'm having a hard time understanding quicksort, most of the demonstrations and explanations leave out what actually happens (http://me.dt.in.th/page/Quicksort/ for example).

Wikipedia says:

Pick an element, called a pivot, from the array. Partitioning: reorder the array so that all elements with values less than the pivot come before the pivot, while all elements with values greater than the pivot come after it (equal values can go either way). After this partitioning, the pivot is in its final position. This is called the partition operation. Recursively apply the above steps to the sub-array of elements with smaller values and separately to the sub-array of elements with greater values.

How would that work with an array of 9,1,7,8,8 for example with 7 as the pivot? The 9 needs to move to the right of the pivot, all quicksort implementations are in place operations it seems so we can't add it after the 8,8, so the only option is to swap the 9 with the 7.

Now the array is 7,1,9,8,8. The idea behind quicksort is that now we have to recursively sort the parts to the left and right of the pivot. The pivot is now at position 0 of the array, meaning there's no left part, so we can only sort the right part. This is of no use as 7>1 so the pivot ended up in the wrong place.

In this image 4 is the pivot, then why is 5 going almost all the way to the left? It's bigger than 4! After a lot of swapping it ends up being sorted but I don't understand how that happened.

https://static.mcmap.net/file/mcmap/ZG-AbGLDKwf0bFYvWV3QcRlrZV0lXFlhKmfwXe/wikipedia/commons/thumb/a/af/Quicksort-diagram.svg/400px-Quicksort-diagram.svg.png

Cumming answered 23/9, 2016 at 16:15 Comment(7)
youtube.com/watch?v=ywWBy6J5gz8Quirites
Keep reading. You haven't read the parts of the article that talk about how to perform the partitioning, and you're getting it all wrong.Fermata
Here's a resource for students/teachers that might help : nrich.maths.org/8192 - it also links through to toptal.com/developers/sorting-algorithms which has some beautiful animations of the various sorting algorithms in action.Estremadura
" so the only option is to swap the 9 with the 7." No, another option is to swap 1 and 9 so that you have 1,9,7,8,8, at which point you can swap 7 and 9 to get 1,7,9,8,8. It's not clear what you think the partitioning algorithm is, but your choices don't follow from a correct one.Accelerate
it goes by swaps, like this: 9,1,>7<,8a,8b --> :9,1:,8b,8a,7 --> 1:,:9,8b,8a,7 --> 1,<7>,8b,8a,9 .Nolitta
Marc B: nice video, didnt help me understand it user2357112: 'im getting it all wrong' wow thanks for the useful feedback @Accelerate how does that make sense from an algorithmic viewpoint?Cumming
@Cumming This article with an implementation in Javascript really helped me, I was having the issue as you, no simple explanation - khan4019.github.io/front-end-Interview-Questions/…Babu
I
31

Quicksort

The Quicksort steps are:

  • Pick an element, called a pivot, from the list.
  • Reorder the list so that all elements with values less than the pivot come before the pivot, while all elements with values greater than the pivot come after it (equal values can go either way). After this partitioning, the pivot is in its final position. This is called the partition operation.
  • Recursively sort the sub-list of lesser elements and the sub-list of greater elements. The base case of the recursion are lists of size zero or one, which never need to be sorted.

Lomuto partition scheme

  • This scheme chooses a pivot which is typically the last element in the array.
  • The algorithm maintains the index to put the pivot in variable i and each time it finds an element less than or equal to pivot, this index is incremented and that element would be placed before the pivot.
  • As this scheme is more compact and easy to understand, it is frequently used in introductory material.
  • Is less efficient than Hoare's original scheme.

Partition algorithm (using Lomuto partition scheme)

algorithm partition(A, lo, hi) is
    pivot := A[hi]
    i := lo        // place for swapping
    for j := lo to hi – 1 do
        if A[j] ≤ pivot then
            swap A[i] with A[j]
            i := i + 1
    swap A[i] with A[hi]
    return i

Quicksort algorithm (using Lomuto partition scheme)

algorithm quicksort(A, lo, hi) is
    if lo < hi then
        p := partition(A, lo, hi)
        quicksort(A, lo, p – 1)
        quicksort(A, p + 1, hi)

Hoare partition scheme

  • Uses two indices that start at the ends of the array being partitioned, then move toward each other, until they detect an inversion: a pair of elements, one greater than the pivot, one smaller, that are in the wrong order relative to each other. The inverted elements are then swapped.

  • There are many variants of this algorithm, for example, selecting pivot from A[hi] instead of A[lo]

partition algorithm (using Hoare partition scheme)

algorithm partition(A, lo, hi) is
    pivot := A[lo]
    i := lo – 1
    j := hi + 1
    loop forever
        do
            i := i + 1
        while A[i] < pivot

        do
            j := j – 1
        while A[j] > pivot

        if i >= j then
            return j

        swap A[i] with A[j]

quicksort algorithm(using Hoare partition scheme)

algorithm quicksort(A, lo, hi) is
    if lo < hi then
        p := partition(A, lo, hi)
        quicksort(A, lo, p)
        quicksort(A, p + 1, hi)

Hoare partition scheme vs Lomuto partition scheme

The pivot selection

  • The execution speed of the algorithm depends largely on how this mechanism is implemented, poor implementation can assume that the algorithm is run at a slow speed.

  • The choice of pivot determines partitions the data list, therefore, this is the most critical part of the implementation of the Quicksort algorithm. It is important to try that selecting the pivot left and right partitions have an identical size as much as possible.

Best and worst case

Worst case

The most unbalanced partition occurs when the pivot divides the list into two sublists of sizes _0 and n − 1. This may occur if the pivot happens to be the smallest or largest element in the list, or in some implementations when all the elements are equal.

Imgur

Best Case In the most balanced case, each time we perform a partition we divide the list into two nearly equal pieces. This means each recursive call processes a list of half the size.

Imgur

Formal analysis

  • Worst-case analysis = O(n²)
  • Best-case analysis = O(n) factor
  • Average-case analysis = O(n log n)

Examples source

Using additional memory

def quicksort(array):
    less = []
    equal = []
    greater = []
    if len(array) > 1:
        pivot = array[0]
        for x in array:
            if x < pivot:
                less.append(x)
            if x == pivot:
                equal.append(x)
            if x > pivot:
                greater.append(x)
        return sort(less)+equal+sort(greater)  
    else: 
        return array

Usage:

quicksort([12,4,5,6,7,3,1,15])

Without additional memory

def partition(array, begin, end):
    pivot = begin
    for i in xrange(begin+1, end+1):
        if array[i] <= array[begin]:
            pivot += 1
            array[i], array[pivot] = array[pivot], array[i]
    array[pivot], array[begin] = array[begin], array[pivot]
    return pivot

def quicksort(array, begin=0, end=None):
    if end is None:
        end = len(array) - 1
    if begin >= end:
        return
    pivot = partition(array, begin, end)
    quicksort(array, begin, pivot-1)
    quicksort(array, pivot+1, end)

Usage:

quicksort([97, 200, 100, 101, 211, 107])

In your example Imgur

Debug Lomuto partition Imgur

References:
Interstellar answered 23/9, 2016 at 21:7 Comment(3)
The algorithm maintains the index to put the pivot in variable i and each time it finds an element less than or equal to pivot, this index is incremented and that element would be placed before the pivot. what does that even mean? and why?Cumming
I added debug Lomuto partition in the publication, see thisInterstellar
What do you mean debug, is there are a bug somewhere? Also you messed up the index and the bigger/smaller than signs a few times, and you didn't explain why all this is going on, how it makes sense. I'm thinking that this version is about detecting inversions and swapping them, then swapping the pivot with a value that is sure to belong in the right half. But I'm not sure how swapping it with the first found candidate for inversion makes sense, 9933334 with 4 as pivot would end up with the 9s in the middle. So I think Lomuto wouldn't work here and Hoare would, thoughts?Cumming
A
1

Some day I found this jewel, which animates the different Sorting Algorhitms which helped me a lot in understanding them! But this is just a graphical explanation, the poster prior to me (@Hydex), already answered in a academically way ;-)

Audie answered 24/9, 2016 at 20:43 Comment(1)
looks very hip but the algorithm explanation is sparseCumming

© 2022 - 2024 — McMap. All rights reserved.