Article directory
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.
Click on the image below for a larger view:
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
-
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
-
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
- 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.
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.
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).
- 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
-
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}.
We can select an element at will. If we select the first element of the array, let's call this element the "pivot".
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.
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.
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
-
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...
Small top heap: Ascending order (Asc) 2 from small to large...
- Algorithm steps
to build a large top heap:
Build a small top heap:
- Animation demonstration
- 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)
. 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)
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.
- 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
-
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
- 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
- LSD radix sort animation demonstration
- 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;
}