[Data Structures and Algorithms] Top Ten Classic Sorting Algorithms


Preface

Sorting algorithm is one of the most basic algorithms in "Data Structures and Algorithms".
Sorting algorithms can be divided into internal sorting and external sorting. Internal sorting is to sort data records in memory, while external sorting is because the sorted data is very large and cannot accommodate all the sorted records at one time. During the sorting process, external memory needs to be accessed. Common sorting algorithms include: insertion sort, Hill sort, selection sort, bubble sort, merge sort, quick sort, heap sort, technical sort, etc.


1. Summary of the top ten common sorting algorithms

The following are the average time complexity and best-case time complexity of common sorting algorithms: bubble sort, selection sort, insertion sort, Hill sort, merge sort, quick sort, heap sort, counting sort, bucket sort, and radix sort. A summary of worst-case time complexity, space complexity, sorting method, and stability.

1. Explanation of terms

  • n: data size
  • k: the number of "buckets"
  • In-Place: Occupies constant memory, does not occupy additional memory
  • Out-Place: takes up extra memory
  • Stability: the order of 2 equal key values ​​after sorting is the same as their order before sorting

Stable sorting algorithms include: bubble sort, insertion sort, merge sort and radix sort.
Unstable sorting algorithms include: selection sort, quick sort, Hill sort, and heap sort.
Insert image description here
Click on the image below for a larger view:
Insert image description here
Insert image description here

2. Time complexity

- Square order O(n2): all kinds of simple sorting, direct insertion, direct selection, bubble sorting;

  • Linear logarithmic order O (nlog2n): quick sort, merge sort, heap sort;
  • O(n1+ζ): ζ is a constant between 0 and 1, Hill sorting;
  • Linear order O(n): cardinality sorting, bucket sorting, box sorting;

2. Sorting algorithm and C language implementation

1. Bubble sorting

Bubble Sort is a simple and intuitive sorting algorithm. Compare each other, the larger one is always ranked last, and then continue to compare with the following numbers until the largest number is ranked last, and then continue to repeat the process in the sequence before the last number until all the numbers are ranked. good.

  • Algorithm steps
    Compare adjacent elements, if the first one is larger than the second one, exchange and arrange the larger one behind.
    The same comparison is performed for each pair of adjacent elements, and the last element will be the largest number.
    Do the same for all elements until the first number is the smallest number.

  • Animation demonstration
    Insert image description here

  • When is it fastest?
    When the input data is already in order.

  • When is it slowest?
    When the input data is in reverse order, all steps need to be moved and exchanged.

  • C language to implement bubble sort

#include <stdio.h>
void bubble_sort(int arr[], int len) {
    
    
        int i, j, temp;
        for (i = 0; i < len - 1; i++)
                for (j = 0; j < len - 1 - i; j++)
                        if (arr[j] > arr[j + 1]) {
    
    
                                temp = arr[j];
                                arr[j] = arr[j + 1];
                                arr[j + 1] = temp;
                        }
}
int main() {
    
    
        int arr[] = {
    
     22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };
        int len = sizeof(arr) / sizeof(arr[0]);
        bubble_sort(arr, len);
        int i;
        for (i = 0; i < len; i++)
                printf("%d ", arr[i]);
        return 0;
}

2. Select sorting

Selection sort is a simple and intuitive sorting algorithm, no matter what data goes in, it has a time complexity of O(n2). So when using it, the smaller the data size, the better.

  • Algorithm steps

First choose to find the smallest (largest) element in the unsorted sequence and store it in the starting position of the sorted sequence.
Then continue to find the smallest (largest) element from the remaining unsorted elements, and then put it at the end of the sorted sequence.
Repeat step two until all elements are sorted.

  • Animation demonstration
    Insert image description here

  • C language implementation of selection sorting

void swap(int *a,int *b) //交換兩個變數
{
    
    
    int temp = *a;
    *a = *b;
    *b = temp;
}
void selection_sort(int arr[], int len) 
{
    
    
    int i,j;

        for (i = 0 ; i < len - 1 ; i++) 
    {
    
    
                int min = i;
                for (j = i + 1; j < len; j++)     //走訪未排序的元素
                        if (arr[j] < arr[min])    //找到目前最小值
                                min = j;    //紀錄最小值
                swap(&arr[min], &arr[i]);    //做交換
        }
}

3. Insertion sort

Although the code implementation of insertion sort is not as simple and crude as bubble sort and selection sort, its principle should be the easiest to understand, because anyone who has played poker should be able to understand it in seconds. Insertion sort is the simplest and most intuitive sorting algorithm. Its working principle is to construct an ordered sequence, relative to unsorted data, scan from back to front in the sorted sequence, find the corresponding position and insert it.

  • Algorithm steps

Treat the first element of the sequence to be sorted as an ordered sequence, and the second to last elements as an unsorted sequence.
Scans an unsorted sequence from beginning to end, inserting each element found in the sequence at its proper position. (If the element to be inserted is equal to an element in the ordered sequence, the element to be inserted will be inserted after the equal element)

  • Animation demonstration

Insert image description here

  • C language implementation
void insertion_sort(int arr[], int len){
    
    
        int i,j,key;
        for (i=1;i<len;i++){
    
    
                key = arr[i];
                j=i-1;
                while((j>=0) && (arr[j]>key)) {
    
    
                        arr[j+1] = arr[j];
                        j--;
                }
                arr[j+1] = key;
        }
}

4. Hill sorting

Hill sorting, also known as descending incremental sorting algorithm, is a more efficient and improved version of insertion sort. But Hill sort is an unstable sorting algorithm.
Hill sorting is an improved method based on the following two properties of insertion sort:

  • Insertion sort is highly efficient when operating on almost already arranged data, that is, it can achieve the efficiency of linear sorting;
  • But insertion sorting is generally inefficient, because insertion sorting can only move data one bit at a time;

The basic idea of ​​Hill sorting is: first divide the entire sequence of records to be sorted into several sequences for direct insertion sorting (exchange according to the step size). When the records in the entire sequence are "basically in order", then all records Perform direct insertion sort in sequence.

  • Algorithm steps

Take the sequence: {8, 9, 1, 7, 2, 3, 5, 6, 4, 0} for example!

1. The initial step size gap = length/2 = 5, which means that the entire array is divided into 5 groups, namely [8, 3], [9, 5], [1, 6], [7, 4], [2 , 0], perform insertion sort on each group, and get the sequence: {3, 5, 1, 4, 0, 8, 9, 6, 7, 2}, you can see: 3, 5, 4, 0 these small elements have all been mentioned earlier.
Insert image description here

2. Reduce the increment gap = 5/2 = 2. The array is divided into two groups, namely [3, 1, 0, 9, 7], [5, 4, 8, 6, 2]. For these two groups respectively Performing direct insertion sort, you can see that the entire array is even more ordered.
Insert image description here

3. Reduce the increment again, gap = 2/2 = 1. At this time, the entire array is [0, 2, 1, 4, 3, 5, 7, 6, 9, 8]. Perform an insertion sort to achieve it. Sorting of arrays (requires only simple fine-tuning, not extensive move operations).
Insert image description here

  • java language implementation
import java.util.Arrays;

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * 希尔排序
 */
public class ShellSort {
    
    

    public static void main(String[] args) {
    
    
        int[] arr = {
    
    8, 9, 1, 7, 2, 3, 5, 6, 4, 0};
        System.out.println("排序前: " + Arrays.toString(arr));
        shellSort(arr);
        System.out.println("排序后: " + Arrays.toString(arr));
    }

    //希尔排序
    public static void shellSort(int[] arr){
    
    
        //设定步长
        for (int gap = arr.length / 2; gap > 0; gap /= 2){
    
    
            //将数据分为arr.length/gap组,逐个对其所在的组进行插入排序
            for (int i = gap; i < arr.length; i++) {
    
    
                //遍历各组中的所有元素,步长为gap
                int j = i;
                int temp = arr[j]; //记录待插入的值
                while (j - gap >= 0 && temp < arr[j-gap]){
    
    
                    //移动
                    arr[j] = arr[j-gap];
                    j -= gap;
                }
                //找到位置,进行插入
                arr[j] = temp;
            }
            System.out.println(Arrays.toString(arr));
        }
    }
}

5. Merge sort

Merge Sort is an effective and stable sorting algorithm based on merge operations. This algorithm is a very typical application of the divide and conquer method. Merge the already ordered subsequences to obtain a completely ordered sequence; that is, first make each subsequence orderly, and then make the subsequence segments orderly.

  • Algorithm step
    Step1 - Determine the dividing point mid (three commonly used methods): q[l], q[(l+r)/2], q[r], or randomly determine; Step2 - Adjust the column to be
    sorted Into two subsequences left and right, sort left and right recursively;
    Step3 - Merge (combine two into one), Finish!

  • Demo
    Insert image description here
    Insert image description here

  • C implementation

int min(int x, int y) {
    
    
    return x < y ? x : y;
}
void merge_sort(int arr[], int len) {
    
    
    int *a = arr;
    int *b = (int *) malloc(len * sizeof(int));
    int seg, start;
    for (seg = 1; seg < len; seg += seg) {
    
    
        for (start = 0; start < len; start += seg * 2) {
    
    
            int low = start, mid = min(start + seg, len), high = min(start + seg * 2, len);
            int k = low;
            int start1 = low, end1 = mid;
            int start2 = mid, end2 = high;
            while (start1 < end1 && start2 < end2)
                b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
            while (start1 < end1)
                b[k++] = a[start1++];
            while (start2 < end2)
                b[k++] = a[start2++];
        }
        int *temp = a;
        a = b;
        b = temp;
    }
    if (a != arr) {
    
    
        int i;
        for (i = 0; i < len; i++)
            b[i] = a[i];
        b = a;
    }
    free(b);
}

6. Quick sort

Quick Sort is an improvement on bubble sort. Proposed by CAR Hoare in 1962, the basic idea is to select a record as the pivot, and after a sorting process, divide the entire sequence into two parts, one part of which has values ​​smaller than the pivot, and the other part has values ​​greater than the pivot. Then continue to sort these two parts so that the entire sequence is in order.

  • Algorithm steps
  • 1. Basic idea:
    For example, for a source array to be sorted arr = {4, 1, 3, 2, 7, 6, 8}.
    Insert image description here
    We can select an element at will. If we select the first element of the array, let's call this element the "pivot".
    Insert image description here

Then put the elements that are greater than or equal to the pivot on the right, and put the elements that are less than or equal to the pivot on the left.
Insert image description here

After adjusting this rule, the elements on the left are all less than or equal to the pivot, and the elements on the right are greater than or equal to the pivot. Obviously, the position of the pivot at this time is an ordered position, that is, the pivot Already in a sorted position.
The pivot divides the array into two halves. The operation of dividing a large array into two small parts through the pivot is also called a partition operation.
Next, we use the same method for the left and right parts through recursion, selecting one main element each time to put it in an ordered position. Of course, the recursion ends when the subarray has only one element or 0 elements.
Insert image description here

Code: quick_sort is a quick sorting algorithm, and the partition function is a splitting operation for an array. There are many methods for splitting operations.

There are many methods for quick sort split operation, the most basic one is listed here.

  • Animation demonstration
    Insert image description here

  • C language implementation of selection sorting

void QuickSort(int array[], int low, int high) {
    
    
    int i = low; 
    int j = high;
    if(i >= j) {
    
    
        return;
    }
 
    int temp = array[low];
    while(i != j) {
    
    
        while(array[j] >= temp && i < j) {
    
    
            j--;
        }
	while(array[i] <= temp && i < j) {
    
    
            i++;
        }
	if(i < j) {
    
    
            swap(array[i], array[j]);
        }
    }
 
    //将基准temp放于自己的位置,(第i个位置)
    swap(array[low], array[i]);
    QuickSort(array, low, i - 1);
    QuickSort(array, i + 1, high);
}

7. Heap sort

Heapsort refers to a sorting algorithm designed using a data structure such as a heap. It stacks a structure that is approximately a complete binary tree and satisfies the properties of stacking: the key value or index of a child node is always smaller than (or larger than) it. the parent node of .
It is divided into two methods:
big top heap: the parent node is greater than or equal to the child node, which belongs to the ascending order algorithm;
small top heap: the parent node is less than or equal to the child node, which belongs to the descending order algorithm;
the average time complexity of heap sorting is O (nlogn)

The heap is generally used to find the top N largest or top N smallest data in a large amount of data.

Large top heap: Descending order from large to small (Desc) 12...
Insert image description here
Small top heap: Ascending order (Asc) 2 from small to large...
Insert image description here

  • Algorithm steps
    to build a large top heap:
    Insert image description here
    Insert image description here
    Insert image description here

Build a small top heap:
Insert image description here
Insert image description here
Insert image description here

  • Animation demonstration

Insert image description here
Insert image description here

  • C language implements selection sorting
    . Big code:
#include <stdio.h>

void swap(int arr[], int i, int j){
    
    
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

void heapify(int tree[], int n, int i){
    
    
if (i >= n){
    
    
return;
}

int c1 = 2 * i + 1;
int c2 = 2 * i + 2;
int max = i;
if (c1 < n && tree[c1] > tree[max]){
    
    
max = c1;
}
if (c2 < n && tree[c2] > tree[max]){
    
    
max = c2;
}
if (max != i){
    
    
swap(tree, max ,i);
heapify(tree, n, max);
}
}

void build_heap(int tree[], int n){
    
    
int last_node = n - 1;
int parent = (last_node - 1) / 2;
for (int i = parent; i >= 0; i--){
    
    
heapify(tree, n, i);
}
}

void heap_sort(int tree[], int n){
    
    
build_heap(tree, n);
for (int i = n - 1; i >= 0; i--){
    
    
swap(tree, i, 0);
heapify(tree, i, 0);
}
}

int main(){
    
    
int tree[] = {
    
    6, 10, 3, 9, 5, 12, 7, 2, 8};
int n = 9;

build_heap(tree, 9);
// heap_sort(tree, 9);

for(int i = 0; i < n; i++){
    
    
printf("%d\n",tree[i]);
}
return 0;
}

Big top heap results: From large to small, the results are in descending order (Desc)
Insert image description here
. Small top heap code:

#include <stdio.h>

void swap(int arr[], int i, int j){
    
    
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

void heapify(int tree[], int n, int i){
    
    
if (i >= n){
    
    
return;
}

int c1 = 2 * i + 1;
int c2 = 2 * i + 2;
int max = i;
if (c1 < n && tree[c1] > tree[max]){
    
    
max = c1;
}
if (c2 < n && tree[c2] > tree[max]){
    
    
max = c2;
}
if (max != i){
    
    
swap(tree, max ,i);
heapify(tree, n, max);
}
}

void build_heap(int tree[], int n){
    
    
int last_node = n - 1;
int parent = (last_node - 1) / 2;
for (int i = parent; i >= 0; i--){
    
    
heapify(tree, n, i);
}
}

void heap_sort(int tree[], int n){
    
    
build_heap(tree, n);
for (int i = n - 1; i >= 0; i--){
    
    
swap(tree, i, 0);
heapify(tree, i, 0);
}
}

int main(){
    
    
int tree[] = {
    
    6, 10, 3, 9, 5, 12, 7, 2, 8};
int n = 9;

// build_heap(tree, 9);
heap_sort(tree, 9);

for(int i = 0; i < n; i++){
    
    
printf("%d\n",tree[i]);
}
return 0;
}

Small top heap result: ascending order from small to large (Asc)
Insert image description here

8. Counting sorting

The core of counting sort is to convert the input data values ​​into keys and store them in the additionally opened array space. As a linear time complexity sort, counting sort requires that the input data must be integers within a certain range.

  1. Characteristics of counting sort
    When the input elements are n integers between 0 and k, its running time is Θ(n + k). Counting sort is not comparison sort, and sorting is faster than any comparison sort algorithm.

Since the length of the array C used for counting depends on the range of the data in the array to be sorted (equal to the difference between the maximum value and the minimum value of the array to be sorted plus 1), this makes counting sorting require a large amount of data for arrays with a large data range. time and memory. For example: counting sort is the best algorithm for sorting numbers between 0 and 100, but it is not suitable for sorting names in alphabetical order. However, counting sort can be used to sort arrays with large data ranges using the algorithm used in radix sort.

To understand it in a layman's terms, for example, there are 10 people of different ages, and the statistics show that 8 people are younger than A, then A's age is ranked 9th. Using this method, the position of each other person can be obtained, and the ranking is completed. Order. Of course, special processing is required when ages are repeated (to ensure stability), which is why the target array is filled in the end and the statistics of each number are subtracted by 1.

  • Algorithm steps
    (1) Find the largest and smallest elements in the array to be sorted
    (2) Count the number of occurrences of each element with value i in the array, and store it in the i-th item of array C
    (3) Accumulate all counts (Starting from the first element in C, each item is added to the previous item)
    (4) Fill the target array in reverse: Place each element i in the C(i)th item of the new array, and place each element i element, subtract 1 from C(i)

  • Animation demonstration
    Insert image description here

  • C language implementation of selection sorting

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void print_arr(int *arr, int n) {
    
    
        int i;
        printf("%d", arr[0]);
        for (i = 1; i < n; i++)
                printf(" %d", arr[i]);
        printf("\n");
}

void counting_sort(int *ini_arr, int *sorted_arr, int n) {
    
    
        int *count_arr = (int *) malloc(sizeof(int) * 100);
        int i, j, k;
        for (k = 0; k < 100; k++)
                count_arr[k] = 0;
        for (i = 0; i < n; i++)
                count_arr[ini_arr[i]]++;
        for (k = 1; k < 100; k++)
                count_arr[k] += count_arr[k - 1];
        for (j = n; j > 0; j--)
                sorted_arr[--count_arr[ini_arr[j - 1]]] = ini_arr[j - 1];
        free(count_arr);
}

int main(int argc, char **argv) {
    
    
        int n = 10;
        int i;
        int *arr = (int *) malloc(sizeof(int) * n);
        int *sorted_arr = (int *) malloc(sizeof(int) * n);
        srand(time(0));
        for (i = 0; i < n; i++)
                arr[i] = rand() % 100;
        printf("ini_array: ");
        print_arr(arr, n);
        counting_sort(arr, sorted_arr, n);
        printf("sorted_array: ");
        print_arr(sorted_arr, n);
        free(arr);
        free(sorted_arr);
        return 0;
}

9. Bucket sorting

Insert image description here

  • C++ language to implement selection sorting
#include<iterator>
#include<iostream>
#include<vector>
using namespace std;
const int BUCKET_NUM = 10;

struct ListNode{
    
    
        explicit ListNode(int i=0):mData(i),mNext(NULL){
    
    }
        ListNode* mNext;
        int mData;
};

ListNode* insert(ListNode* head,int val){
    
    
        ListNode dummyNode;
        ListNode *newNode = new ListNode(val);
        ListNode *pre,*curr;
        dummyNode.mNext = head;
        pre = &dummyNode;
        curr = head;
        while(NULL!=curr && curr->mData<=val){
    
    
                pre = curr;
                curr = curr->mNext;
        }
        newNode->mNext = curr;
        pre->mNext = newNode;
        return dummyNode.mNext;
}


ListNode* Merge(ListNode *head1,ListNode *head2){
    
    
        ListNode dummyNode;
        ListNode *dummy = &dummyNode;
        while(NULL!=head1 && NULL!=head2){
    
    
                if(head1->mData <= head2->mData){
    
    
                        dummy->mNext = head1;
                        head1 = head1->mNext;
                }else{
    
    
                        dummy->mNext = head2;
                        head2 = head2->mNext;
                }
                dummy = dummy->mNext;
        }
        if(NULL!=head1) dummy->mNext = head1;
        if(NULL!=head2) dummy->mNext = head2;
        
        return dummyNode.mNext;
}

void BucketSort(int n,int arr[]){
    
    
        vector<ListNode*> buckets(BUCKET_NUM,(ListNode*)(0));
        for(int i=0;i<n;++i){
    
    
                int index = arr[i]/BUCKET_NUM;
                ListNode *head = buckets.at(index);
                buckets.at(index) = insert(head,arr[i]);
        }
        ListNode *head = buckets.at(0);
        for(int i=1;i<BUCKET_NUM;++i){
    
    
                head = Merge(head,buckets.at(i));
        }
        for(int i=0;i<n;++i){
    
    
                arr[i] = head->mData;
                head = head->mNext;
        }
}

10. Radix sorting

- **Algorithm Steps**

  • LSD radix sort animation demonstration

Insert image description here

  • C language implementation of selection sorting
#include<stdio.h>
#define MAX 20
//#define SHOWPASS
#define BASE 10

void print(int *a, int n) {
    
    
  int i;
  for (i = 0; i < n; i++) {
    
    
    printf("%d\t", a[i]);
  }
}

void radixsort(int *a, int n) {
    
    
  int i, b[MAX], m = a[0], exp = 1;

  for (i = 1; i < n; i++) {
    
    
    if (a[i] > m) {
    
    
      m = a[i];
    }
  }

  while (m / exp > 0) {
    
    
    int bucket[BASE] = {
    
     0 };

    for (i = 0; i < n; i++) {
    
    
      bucket[(a[i] / exp) % BASE]++;
    }

    for (i = 1; i < BASE; i++) {
    
    
      bucket[i] += bucket[i - 1];
    }

    for (i = n - 1; i >= 0; i--) {
    
    
      b[--bucket[(a[i] / exp) % BASE]] = a[i];
    }

    for (i = 0; i < n; i++) {
    
    
      a[i] = b[i];
    }

    exp *= BASE;

#ifdef SHOWPASS
    printf("\nPASS   : ");
    print(a, n);
#endif
  }
}

int main() {
    
    
  int arr[MAX];
  int i, n;

  printf("Enter total elements (n <= %d) : ", MAX);
  scanf("%d", &n);
  n = n < MAX ? n : MAX;

  printf("Enter %d Elements : ", n);
  for (i = 0; i < n; i++) {
    
    
    scanf("%d", &arr[i]);
  }

  printf("\nARRAY  : ");
  print(&arr[0], n);

  radixsort(&arr[0], n);

  printf("\nSORTED : ");
  print(&arr[0], n);
  printf("\n");

  return 0;
}

Summarize

Guess you like

Origin blog.csdn.net/qq_38628970/article/details/132661451