A series of knowledge points — ten sorting algorithms implemented in Python

Time:2021-10-22

Write in front

Sorting is the two most important concepts in search algorithm. We are searching and sorting in most cases. Scientists have made every effort to make sorting and searching faster. This article uses Python to implement the top ten sorting algorithms.

Dry goods

Sorting algorithms can be divided into many categories from different dimensions. From their sorting ideas (which generally determine the magnitude of their time complexity), they can be divided into four categories:

  • Double level cyclic comparison sort: square sort
  • Divide and conquer strategy comparison sort: logarithmic sort
  • Alternative non comparative sorting: linear sorting
  • Other orders of laughing dead people do not pay for their lives: it is difficult to describe the time complexity.
Square sort
  • Bubble sorting

    1. Starting from the first element of the array, compare the current element with the next element. If the current element is greater than the next element, exchange the positions of the two elements.
    2. Then, starting with the second element, repeat the first step until the current element is the last element. At this point, the last element is the largest element. An unordered array is an element other than the last element.
    3. Repeat the above steps for the unordered array until the unordered array is empty.
    def bubble_sort(arr):
        length = len(arr)
        for i in range(length):
            for j in range(length-i-1):
                if arr[j] > arr[j+1]:
                    arr[j], arr[j+1] = arr[j+1], arr[j]
        return arr
  • Select sort

    1. Select the smallest element in the array and exchange positions with the first element in the array
    2. Select the smallest element of the remaining elements in the array except the first element, and exchange positions with the second element in the array.
    3. Repeat the above steps until the currently selected element is the last element in the array.
    def select_sort(arr):
        length = len(arr)
        for i in range(length):
            min_ix = i
            for j in range(i, length):
                if arr[j] < arr[min_ix]:
                    min_ix = j
            arr[min_ix], arr[i] = arr[i], arr[min_ix]
        return arr
  • Insert sort

    1. Starting with the first element of the array, constantly compare the current element with the previous element. If the current element is smaller than the previous element, the current element is inserted in front of the previous element (that is, the two exchange positions)
    2. Starting with the second element, repeat the above steps until all elements go through the above steps.
    def insert_sort(arr):
        length = len(arr)
        for i in range(length):
            for j in range(i, 0, -1):
                if arr[j] < arr[j-1]:
                    arr[j], arr[j-1] = arr[j-1], arr[j]
        return arr
Logarithmic order
  • Shell Sort

    1. Select an incremental value K to place the elements with K as the interval index in the array in the same array.
    2. Reduce the increment value to 1 / 2 of the original increment value, and then repeat step 1.
    3. Until the increment value is 1, use insert sort to sort the partially ordered array.
    def shell_sort(arr):
        n = len(arr)
        gap = int(n/2)
        while gap > 0: 
            for i in range(gap,n): 
                temp = arr[i] 
                j = i 
                while  j >= gap and arr[j-gap] >temp: 
                    arr[j] = arr[j-gap] 
                    j -= gap 
                arr[j] = temp 
            gap = int(gap/2)
        return arr
  • Merge sort

    1. Taking the middle elements of the array as the boundary, the array is divided into two arrays of equal length (may not be equal in length, which is related to the parity of the array length).
    2. Perform step 1 for all arrays
    3. Repeat the above steps until the array is divided into multiple arrays containing a single element.
    4. Merge the above arrays in pairs and sort them. At this time, there are multiple arrays containing ordered two elements (may contain a single element, which is related to the parity of the array length).
    5. Repeat step 4 until all arrays are merged into one array
    def merge(left, right):
        i = j = 0
        res = []
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                res.append(left[i])
                i += 1
            else:
                res.append(right[j])
                j += 1
        if i == len(left):
            res.extend(right[j:])
        else:
            res.extend(left[i:])
        return res
    
    def merge_sort(arr):
        if len(arr) <= 1:
            return arr
        length = len(arr)
        i = int(length / 2)
        left = merge_sort(arr[:i])
        right = merge_sort(arr[i:])
        return merge(left, right)
  • Quick sort

    1. Select an element as the benchmark
    2. Elements larger than the benchmark are used as an array, and elements smaller than or equal to the benchmark are used as an array.
    3. Repeat the above steps for the newly segmented array until the segmented array contains only 1 or 0 elements
    4. Recursively merge the above arrays into ordered arrays by: [elements less than or equal to the benchmark] + [benchmark] + [elements greater than the benchmark]
    def fast_sort(arr):
        if len(arr) <= 1:
            return arr
        pivot = arr.pop()
        left = [i for i in arr if i <= pivot]
        right = [i for i in arr if i > pivot]
        return fast_sort(left) + [pivot] + fast_sort(right)

    The above algorithm requires additional space. If we continuously place elements less than or equal to the benchmark before the benchmark element and elements greater than the benchmark after the benchmark element, we can realize in-situ sorting without additional space.

    def fast_sort_on_extra_spacing(arr):
        l = 0
        h = len(arr)-1
    
        def partition(arr, l, h):
            pivot = arr[h]
            for i in range(l, h):
                if arr[i] <= pivot:
                    arr[l], arr[i] = arr[i], arr[l]
                    l += 1
            arr[h], arr[l] = arr[l], arr[h]
            return l
    
        def fast_sort(arr, l, h):
            if l < h:
                pivot = partition(arr, l, h)
                fast_sort(arr, l, pivot-1)
                fast_sort(arr, pivot+1, h)
            return arr
        return fast_sort(arr, l, h)
  • Heap sort

    1. First, construct a large root heap for the array to be sorted
    2. Swap the first and last elements of the large root heap. At this time, the last element is the largest element, and the array to be sorted is all elements except the last element.
    3. Repeat the above steps for the array to be sorted until there is only one element in the array to be sorted.
    def heapify(arr, n, i):
        # build a max root heap
        max_ix = i
        left_i = 2 * i + 1
        right_i = 2 * i + 2
    
        if left_i < n and arr[max_ix] < arr[left_i]:
            max_ix = left_i
        if right_i < n and arr[max_ix] < arr[right_i]:
            max_ix = right_i
        if max_ix != i:
            arr[max_ix], arr[i] = arr[i], arr[max_ix] 
            heapify(arr, n, max_ix)
    
    def heap_sort(arr):
        for i in range(n-1, -1, -1):
            heapify(arr, n, i)
    
        for i in range(n-1, 0, -1):
            arr[i], arr[0] = arr[0], arr[i]
            heapify(arr, i, 0)
        return arr
Linear ranking

This sorting method is only applicable to the case where the array elements are all integers.

  • Count sort

    1. Find the largest element in the array to be sorted and construct a count array with a length of this element value.
    2. Traverse the array elements to be sorted, take the current element as the index, and add the corresponding value in the count array by 1
    3. At this time, the index in the count array is the element in the array to be sorted, and the value is the number of occurrences. Concatenate the indexes of all elements with non-zero values in the count array according to their occurrence times.
    def count_sort(arr):
        min_ix, max_ix = min(arr), max(arr)
        bucket = [0 for _ in range(max_ix+1)]
        for i in arr:
            bucket[i] += 1
        return sum([[i] * bucket[i] for i in range(len(bucket)) if bucket[i] != 0], [])
  • Bucket sorting

    1. Set a fixed number of buckets (this is a technical job)
    2. Put the elements in the array to be sorted into the corresponding bucket (the correspondence is also a technical work, and the following example uses integer division)
    3. Concatenate the elements in a non empty bucket.
    def bucket_sort(arr):
        min_ix, max_ix = min(arr), max(arr)
        bucket_range = (max_ix - min_ix) / len(arr)
        # +1 avoid for that max_ix - min_ix will raise a IndexError
        temp_bucket = [[] for i in range(len(arr) + 1)]
        for i in arr:
            temp_bucket[int((i-min_ix)//bucket_range)].append(i)
        return sum(temp_bucket, [])
  • Cardinality sort

    1. Find the number of bits of the largest element in the array to be sorted. Make up this number of digits for all elements by filling 0 in front.
    2. Multiple rounds of array sorting are performed from the lowest to the highest.
    def radix_sort(arr):
        max_value = max(arr)
        num_digits = len(str(max_value))
        for i in range(num_digits):
            bucket = [[] for _ in range(10)]
            for j in arr:
                bucket[j//(10**i)%10].append(j)
            arr = [j for i in bucket for j in i]
        return arr
Laughing is not worth your life
  • Sleep sorting

    Let multiple processes (threads) sleep the elements in the array to be sorted for a long time. For the process (thread) that wakes up first, the corresponding elements are appended to the result array.

  • Monkey sorting

    Keep sorting randomly, and then check whether all elements are in order. If you are European emperor, you can try this sorting algorithm, which is likely to be done at one time.

Summary of complexity, stability and generality of sorting algorithm
algorithm Average time complexity Optimal time complexity Worst time complexity Spatial complexity Sort in place Is it stable Is it universal
Bubble sorting O(n2) O(n) O(n2) O(1) yes yes yes
Select sort O(n2) O(n2) O(n2) O(1) yes no yes
Insert sort O(n2) O(n) O(n2) O(1) yes yes yes
Shell Sort O(n logn) O(n log2n) O(n log2n) O(1) yes no yes
Merge sort O(n logn) O(n logn) O(n logn) O(n) no yes yes
Quick sort O(n logn) O(n logn) O(n2) O(n logn) yes no yes
Heap sort O(n logn) O(n logn) O(n logn) O(1) yes no yes
Count sort O(n+k) O(n+k) O(n+k) O(k) no yes no
Bucket sorting O(n+k) O(n+k) O(n2) O(n+k) no yes no
Cardinality sort O(n*k) O(n*k) O(n*k) O(n+k) no yes no
Write at the end

Sorting algorithm is the core of algorithm learning. Mastering sorting algorithm and its idea is the basis of learning other algorithms. I hope you can master it. Welcome to personal blog:Yao Shaomin’s blog