Sort

Table of Contents

Overview

According to information theory, no comparison sort can perform better than comparisons in the average case.

  Timesort Merge sort Quicksort Insertion sort Selection sort Smoothsort
Best case Θ(n) Θ(nlogn) Θ(nlogn) Θ(n) Θ(n2) Θ(n)
Average case Θ(nlogn) Θ(nlogn) Θ(nlogn) Θ(n2) Θ(n2) Θ(nlogn)
Worst case Θ(nlogn) Θ(nlogn) Θ(n2) Θ(n2) Θ(n2) Θ(nlogn)
Space complexity Θ(n) Θ(n) Θ(logn) Θ(1) Θ(1) Θ(1)

Note, however, that the space complexity of both Timsort and merge sort can be reduced to at the cost of speed

Comparison of Sorting Algorithms

Merge sort

def mergesort(lst, left=0, right=None):
    if right is None:
        right = len(lst) - 1
    if left >= right:
        return
    middle = (left + right) // 2
    mergesort(lst, left, middle)
    mergesort(lst, middle + 1, right)
    i, end_i, j = left, middle, middle + 1
    while i <= end_i and j <= right:
        if lst[i] <= lst[j]:
            i += 1
            continue
        lst[i], lst[i+1:j+1] = lst[j], lst[i:j]
        i, end_i, j = i + 1, end_i + 1, j + 1

Quicksort

def quicksort(lst, left=0, right=None):
    if right is None:
        right = len(lst) - 1
    l = left
    r = right
    if l <= r:
        mid = (lst[left] + lst[right]) / 2
        while l < r:
            while l <= right and lst[l] < mid:
                l += 1
            while r >= left and lst[r] > mid:
                r -= 1
            if l <= r:
                lst[l], lst[r] = lst[r], lst[l]
                l += 1
                r -= 1
        if left < r:
            quicksort(lst, left, r)
        if l < right:
            quicksort(lst, l, right)

Insertion sort

def insertionsort(lst):
    for i in range(1, len(lst)):
        for j in range(i):
            if lst[i] < lst[j]:
                x = lst.pop(i);
                lst.insert(j, x);

Selection sort

def selectionsort(lst):
    for i in range(len(lst) - 1, -1, -1):
        m = lst.index(max(lst[:i+1]))
        lst[m], lst[i] = lst[i], lst[m]

Smoothsort1

# Possibly replace with a generator that produces Leonardo numbers?
# That would be of limited utility since this is all of them up to 31 bits.
LP = [ 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, 287, 465, 753, 1219, 1973,
       3193, 5167, 8361, 13529, 21891, 35421, 57313, 92735, 150049, 242785,
       392835, 635621, 1028457, 1664079, 2692537, 4356617, 7049155,
       11405773, 18454929, 29860703, 48315633, 78176337, 126491971,
       204668309, 331160281, 535828591, 866988873 ]

# Solution for determining number of trailing zeroes of a number's binary representation.
# Taken from http://www.0xe3.com/text/ntz/ComputingTrailingZerosHOWTO.html
# don't much like the magic numbers, but they really are magic.
MultiplyDeBruijnBitPosition = [ 0,  1, 28,  2, 29, 14, 24, 3,
                                30, 22, 20, 15, 25, 17,  4, 8,
                                31, 27, 13, 23, 21, 19, 16, 7,
                                26, 12, 18,  6, 11,  5, 10, 9]

def trailingzeroes(v):
    return MultiplyDeBruijnBitPosition[(((v & -v) * 0x077CB531L) >> 27) & 0b11111]


def sift(lst, pshift, head):
    while pshift > 1:
        rc = head - 1
        lc = head - 1 - LP[pshift - 2]
        if lst[head] >= lst[lc] and lst[head] >= lst[rc]:
            break
        elif lst[rc] > lst[lc]:
            lst[head], lst[rc] = lst[rc], lst[head]
            head = rc
            pshift -= 2
        else:
            lst[head], lst[lc] = lst[lc], lst[head]
            head = lc
            pshift -= 1


def trinkle(lst, p, pshift, head):
    while p != 1:
        priorHeap = head - LP[pshift]
        if lst[head] >= lst[priorHeap]:
            break
        lst[head], lst[priorHeap] = lst[priorHeap], lst[head]
        head = priorHeap
        trail = trailingzeroes(p & ~1)
        p >>= trail
        pshift += trail
    sift(lst, pshift, head)

def smoothsort(lst):
    """
    case 0: If there are no elements in the heap, add a tree of order 1
    case 1: If the last two heaps have sizes that differ by one, merge them
    case 2: If the last heap has LP 1, add a singleton heap of LP 0
    case 3: add a singleton heap of LP 1
    """
    #case 0:
    p = 1
    pshift = 1
    head = 0

    while head < len(lst) - 1:
        #case 1:
        if (p & 3) == 3:
            sift(lst, pshift, head)
            p >>= 2
            pshift += 2
        else:
            if LP[pshift - 1] >= len(lst) - 1 - head:
                trinkle(lst, p, pshift, head)
            else:
                sift(lst, pshift, head)

            #case 2
            if pshift == 1:
                p <<= 1
                pshift = 0
            else: #case 3
                p <<= pshift - 1
                pshift = 1
        p |= 1
        head += 1
    trinkle(lst, p, pshift, head)
    while pshift != 1 or p != 1:
        if pshift <= 1:
            trail = trailingzeroes(p & ~1)
            p >>= trail
            pshift += trail
        else:
            p <<= 2
            p ^= 7
            pshift -= 2
            #left child
            trinkle(lst, p >> 1, pshift + 1, head - LP[pshift] - 1)
            trinkle(lst, p, pshift, head - 1)
        head -= 1

Timesort2


bitonicsort3

import math
ASCENDING = True
DESCENDING = False

def compare(lst, i, j, dir):
    if(dir == (lst[i] > lst[j])):
        lst[i], lst[j] = lst[j], lst[i]

def merge(lst, l, n, dir):
    if n > 1:
        k = n / 2
        for i in range(l, l + k):
            compare(lst, i, i + k, dir)
        merge(lst, l, k, dir)
        merge(lst, l + k, k, dir)

def _bitonicsort(lst, l, n, dir):
    if n > 1:
        k = n / 2
        _bitonicsort(lst, l, k, ASCENDING)
        _bitonicsort(lst, l + k, k, DESCENDING)
        merge(lst, l, n , dir)

def bitonicsort(lst):
    # Length of list must be 2**x, where x is an integer.
    assert math.modf(math.log(len(lst), 2))[0] == 0
    _bitonicsort(lst, 0, len(lst), ASCENDING)

bubblesort

def bubblesort(lst):
    bound = len(lst) - 1
    while 1:
        t = 0
        for j in range(bound):
            if lst[j] > lst[j + 1]:
                lst[j + 1], lst[j] = lst[j], lst[j + 1]
                t = j
        if t == 0:
            break
        bound = t

cocktailsort

def cocktailsort(lst):
    begin, end = 0, len(lst) - 1
    finished = False
    while not finished:
        finished = True
        for i in xrange(begin, end):
            if lst[i] > lst[i + 1]:
                lst[i], lst[i + 1] = lst[i + 1], lst[i]
                finished = False
        if finished:
            break
        end -= 1
        for i in reversed(xrange(begin,end)):
            if lst[i] > lst[i + 1]:
                lst[i], lst[i + 1] = lst[i + 1], lst[i]
                finished = False
        begin += 1

cmobsort

def combsort(lst):
    gap = len(lst)
    swap = False
    while 1:
        gap = int(gap / 1.25)
        swap = False
        for i in xrange(len(lst) - gap):
            if lst[i] > lst[i + gap]:
                lst[i], lst[i + gap] = lst[i + gap], lst[i]
                swap = True
        if not swap and gap <= 1:
            break

cyclesort4


gnomesort

def gnomesort(lst):
    i = 0
    while i < len(lst):
        if i == 0 or lst[i] > lst[i - 1]:
            i += 1
        else:
            lst[i], lst[i - 1] = lst[i - 1], lst[i]
            i -= 1

heapsort

def sift(lst, start, count):
    root = start
    while (root * 2) + 1 < count:
        child = root * 2 + 1
        if child < count - 1 and lst[child] < lst[child + 1]:
            child += 1
        if lst[root] < lst[child]:
            lst[root], lst[child] = lst[child], lst[root]
            root = child
        else:
            return

def heapsort(lst):
    start = len(lst) / 2 - 1
    end = len(lst) - 1
    while start >= 0:
        sift(lst, start, len(lst))
        start -= 1
    while end > 0:
        lst[end], lst[0] = lst[0], lst[end]
        sift(lst, 0, end)
        end -= 1

oddevensort

def oddevensort(lst, nloops=2):
    finished = False
    while not finished:
        finished = True
        for n in xrange(nloops):
            for i in xrange(n, len(lst) - 1, nloops):
                if lst[i] > lst[i + 1]:
                    lst[i], lst[i + 1] = lst[i + 1], lst[i]
                    finished = False

radixsort

from itertools import chain
def radixsort(lst):
    is_sorted = lambda l: all([a < b for a,b in zip(l[:-1], l[1:])])
    shift = 1
    zeros = []
    ones = []
    while not is_sorted(lst):
        orig = lst[:]
        while len(orig) != 0:
            item = orig.pop(0)
            if (item & shift) == 0:
                zeros.append(item)
            else:
                ones.append(item)

            for i, item in enumerate(chain(zeros, orig, ones)):
                lst[i] = item
            if is_sorted(lst):
                return
        shift = shift << 1
        zeros = []
        ones = []

shellsort

def shellsort(lst):
    gaps = [5 , 3, 1]
    for gap in gaps:
        for i in xrange(gap, len(lst)):
            temp = lst[i]
            j = i;
            while j>= gap and lst[j - gap] > temp:
                lst[j] = lst[j - gap]
                j -= gap
            lst[j]= temp

stoogesort

def stoogesort(lst, i=0, j=None):
    if j is None:
        j = len(lst) - 1
    if lst[j] < lst[i]:
        lst[j], lst[i] = lst[i], lst[j]
    if j - i > 1:
        t = (j - i + 1) // 3
        stoogesort(lst, i, j - t)
        stoogesort(lst, i + t, j)
        stoogesort(lst, i, j - t)

Footnotes:

Author: Shi Shougang

Created: 2015-03-05 Thu 23:21

Emacs 24.3.1 (Org mode 8.2.10)

Validate