[C Language] Ten Classic Sorting Algorithms-Gift Demo

Table of contents

0. Algorithm overview

0.1 Algorithm classification

0.2 Algorithm complexity

1. Bubble Sort

1.1 Algorithm description

1.3 Code implementation

2. Selection Sort

2.1 Algorithm description

2.2 Animation presentation

2.3 Code implementation

2.4 Algorithm analysis

3. Insertion Sort

3.1 Algorithm description

3.2 Animation presentation

3.2 Code implementation

3.4 Algorithm analysis

4. Shell Sort

4.1 Algorithm description

4.2 Animation presentation

4.3 Code implementation

4.4 Algorithm analysis

5. Merge Sort

5.1 Algorithm description

5.2 Animation presentation

5.3 Code implementation

5.4 Algorithm analysis

6. Quick Sort

6.1 Algorithm description

6.2 Animation presentation

6.3 Code implementation

7. Heap Sort

7.1 Algorithm description

7.2 Animation presentation

7.3 Code implementation

8. Counting Sort

8.1 Algorithm description

8.2 Animation presentation

8.3 Code implementation

8.4 Algorithm analysis

9. Bucket Sort

9.1 Algorithm description

9.2 Picture presentation

9.3 Code implementation

9.4 Algorithm analysis

10. Radix Sort

10.1 Algorithm description

10.2 Animation presentation

10.3 Code implementation

10.4 Algorithm Analysis


0. Algorithm overview

0.1 Algorithm classification

The ten common sorting algorithms can be divided into two broad categories:

  • Comparison sorting : The relative order of elements is determined by comparison. Since its time complexity cannot exceed O(nlogn), it is also called nonlinear time comparison sorting.
  • Non-comparative sorting : The relative order of elements is not determined by comparison. It can break through the lower bound of comparison-based sorting and run in linear time, so it is also called linear time non-comparative sorting. 

0.2 Algorithm complexity

0.3 Related concepts

  • Stable : If a is originally in front of b, and a=b, a is still in front of b after sorting.
  • Unstable : If a is originally in front of b, and a=b, a may appear behind b after sorting.
  • Time complexity : The total number of operations on sorted data. It reflects the regularity of the number of operations when n changes.
  • Space complexity: refers to the algorithm in the computer

It is a measure of the storage space required for internal execution, and it is also a function of the data size n. 

1. Bubble Sort

Bubble sort is a simple sorting algorithm. It iteratively walks through the array to be sorted, comparing two elements at a time and swapping them if they are in the wrong order. The work of visiting the sequence is repeated until there is no need to exchange, that is to say, the sequence has been sorted. The name of this algorithm comes from the fact that the smaller elements will slowly "float" to the top of the sequence through exchange. 

1.1 Algorithm description

  • Compare adjacent elements. If the first is greater than the second, swap them both;
  • Do the same for each pair of adjacent elements, from the first pair at the beginning to the last pair at the end, so the element at the end should be the largest number;
  • Repeat the above steps for all elements except the last one;
  • Repeat steps 1~3 until sorting is complete.

1.2 Animation presentation

1.3 Code implementation


int *bubbleSort(int arr[], int n)
{
    for(int i =0 ; i< n-1; ++i)
    {
        for(int j = 0; j < n-i-1; ++j)
        {
            if(arr[j] > arr[j+1])
            {
                int tmp = a[j] ;  //交换
                a[j] = a[j+1] ;
                a[j+1] = tmp;
            }
        }
    }
    return arr; 
}

2. Selection Sort

Selection sort (Selection-sort) is a simple and intuitive sorting algorithm. How it works: first find the smallest (largest) element in the unsorted sequence, store it at the beginning of the sorted sequence, then continue to find the smallest (largest) element from the remaining unsorted elements, and then put it in the sorted sequence end of . And so on until all elements are sorted. 

2.1 Algorithm description

The direct selection and sorting of n records can obtain ordered results through n-1 direct selection and sorting. The specific algorithm is described as follows:

  • Initial state: the unordered area is R[1..n], and the ordered area is empty;
  • When the i-th sorting (i=1,2,3...n-1) starts, the current ordered area and unordered area are R[1..i-1] and R(i..n) respectively. This sorting process selects the record R[k] with the smallest key from the current unordered area, and exchanges it with the first record R in the unordered area, so that R[1..i] and R[i+1 ..n) respectively become a new ordered area with the number of records increased by 1 and a new disordered area with the number of records decreased by 1;
  • The n-1 trip is over, and the array is sorted.

2.2 Animation presentation

  

2.3 Code implementation

int *bubbleSort(int arr[], int n) {
    int len = n;
    int minIndex, temp;
    for (int i = 0; i < len - 1; i++) {
        minIndex = i;
        for (int j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}

 

2.4 Algorithm analysis

One of the most stable sorting algorithms, because no matter what data is entered, it has a time complexity of O(n2), so when it is used, the smaller the data size, the better. The only advantage may be that it does not take up additional memory space. Theoretically speaking, selection sorting may also be the sorting method that most people usually think of when sorting.

3. Insertion Sort

The algorithm description of Insertion-Sort is a simple and intuitive sorting algorithm. It works by constructing an ordered sequence, and for unsorted data, scans from the back to the front in the sorted sequence, finds the corresponding position and inserts it.

3.1 Algorithm description

Generally speaking, insertion sort is implemented on arrays using in-place. The specific algorithm is described as follows:

  1. Starting from the first element, the element can be considered sorted;
  2. Take out the next element and scan from back to front in the sorted element sequence;
  3. If the element (sorted) is larger than the new element, move the element to the next position;
  4. Repeat step 3 until you find the position where the sorted element is less than or equal to the new element;
  5. After inserting a new element at that position;
  6. Repeat steps 2~5.

3.2 Animation presentation

3.2 Code implementation

int *bubbleSort(int arr[], int n) {
    int len = n;
    int preIndex, current;
    for (int i = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while (preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex + 1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex + 1] = current;
    }
    return arr;
}

3.4 Algorithm analysis

In the implementation of insertion sorting, in-place sorting is usually used (that is, sorting that only needs to use O(1) extra space), so in the process of scanning from back to forward, it is necessary to repeatedly move the sorted elements backwards step by step , providing insertion space for the newest element.

4. Shell Sort

Invented by Shell in 1959, the first sorting algorithm to break through O(n2) is an improved version of simple insertion sort. It differs from insertion sort in that it compares elements that are farther away first. Hill sort is also called shrinking incremental sort .

4.1 Algorithm description

First divide the entire record sequence to be sorted into several subsequences for direct insertion sorting respectively. The specific algorithm description:

  • Choose an incremental sequence t1, t2, ..., tk, where ti>tj, tk=1;
  • According to the incremental sequence number k, sort the sequence k times;
  • For each sorting, according to the corresponding increment ti, the column to be sorted is divided into several subsequences of length m, and direct insertion sorting is performed on each sublist respectively. Only when the increment factor is 1, the entire sequence is treated as a table, and the length of the table is the length of the entire sequence.

4.2 Animation presentation

4.3 Code implementation

int *bubbleSort(int arr[], int n) {
    int len = n;
    for (int gap = len / 2; gap > 0; gap = gap / 2) {
        // 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
        for (int i = gap; i < len; i++) {
            int j = i;
            int current = arr[i];
            while (j - gap >= 0 && current < arr[j - gap]) {
                 arr[j] = arr[j - gap];
                 j = j - gap;
            }
            arr[j] = current;
        }
    }
    return arr;
}

4.4 Algorithm analysis

The core of Hill sorting lies in the setting of the interval sequence. The interval sequence can be set in advance, or the interval sequence can be defined dynamically. The algorithm for dynamically defining interval sequences was proposed by Robert Sedgewick, co-author of Algorithms (4th Edition). 

5. Merge Sort

Merge sort is an efficient sorting algorithm based on the merge operation. This algorithm is a very typical application of Divide and Conquer. Combine the ordered subsequences to obtain a completely ordered sequence; that is, first make each subsequence in order, and then make the subsequence segments in order. Merging two sorted lists into one sorted list is called a 2-way merge. 

5.1 Algorithm description

  • Divide an input sequence of length n into two subsequences of length n/2;
  • Use merge sort for the two subsequences respectively;
  • Merges two sorted subsequences into a final sorted sequence.

5.2 Animation presentation

5.3 Code implementation

//递归实现
int *bubbleSort(int arr[], int left,int right) {
    // [left, right)
    if(left < right) {
        int mid = (left + right) / 2;//从中间截开
        merge_sort(arr,left, mid);//把左边沿中间截开
        merge_sort(arr, mid + 1, right);//把右边沿中间截开
        merge(arr, left, right, mid);//合并
    }
    return arr;
}
//接下来这个函数是合并的过程。
 
void merge(int a[],int left,int right,int mid) {
    int s[right - left];//一个新数组用来存储排序好的数组
    int i = left, j = mid + 1;//两个变量分别指向左边和右边两组数的第一个数
    int sor = 0;
    while (i <= mid && j <= right) {
        if (a[i] < a[j]) {//归并的过程
            s[sor++] = a[i++];
        }
        else {
            s[sor++] = a[j++];
        }
    }
    //当一组数已经全部排进去之后,再将另外一组数的全部数字都排进去
    while (i <= mid) s[sor++] = a[i++];
    while (j <= right)  s[sor++] = a[j++];
    
    //合并之后的数组替换原数组
    for (int m = 0; m < right - left; ++m) {
        a[left++] = s[m]; 
    }
}

//迭代实现
int *bubbleSort(int arr[], int left,int right) {
	//对数组arr归并排序迭代实现,len为数组长度,从小到大排序,O(nlog2^n),稳定
	/*核心思想,i表示步长,也就是左右两组各几个元素比较
		,从第一轮左右每组1个开始,每轮步长增大一倍
		,比较后从小到大存入temp,再对剩余元素进行处理
		,最后将排好序的temp返回arr数组
		*/
 
	//分别为步长、temp下标、左边起始下标、左边终点下标、右边起始下标、右边终止下标
	int i,next,left_min,left_max,right_min,right_max;
	int len = right - left + 1;
	//新建一个temp数组,长度等于初始数组长度
	int temp[len];
 
	//每轮比较左右两个步长i长度的区间,每轮后i*=2
	for(i=1; i < len; i *= 2) {
		//从数组0号开始,下一组的起始位置等于上一组的终止位置,如果下一组左边步长都不够就不比了
		for(left_min=0; left_min < len-i; left_min = right_max){
			//右边起始位置=左边终止位置=左边起始加步长i
			right_min = left_max = left_min + i;
			//右边终止位置=右边起始位置加步长i
			right_max = right_min + i;
			next = 0;//temp的下标
 
			if(right_max > len){//如果右边越界
				right_max = len;//右边终止位置最大值只能为len
			}
 
			while(left_min < left_max && right_min < right_max){//左右都没到尽头
				if(arr[left_min] < arr[right_min]){//左小右大,左边存入temp
					temp[next++] = arr[left_min++];
				}else{//右小左大,右边存入temp
					temp[next++] = arr[right_min++];
				}
			}
 
			/*左边还有一组剩余元素,右边已到终止位置
				,说明左边剩余元素最大,将剩余元素移到右边最后
				,如果是右边有剩余,则不需要移了已经在最后*/
			while(left_min < left_max){
				arr[--right_min] = arr[--left_max];
			}
 
			while(next > 0){//把排好序的temp部分返回arr
				 arr[--right_min] = temp[--next];
			}
		}
	}
	return arr;
}

5.4 Algorithm analysis

Merge sort is a stable sorting method. Like selection sort, the performance of merge sort is not affected by the input data, but the performance is much better than selection sort, because it is always O(nlogn) time complexity. The cost is that additional memory space is required.

6. Quick Sort

The basic idea of ​​quick sorting: the records to be sorted are separated into two independent parts by one-pass sorting, and the keywords of one part of the records are smaller than the keywords of the other part, then the two parts of the records can be sorted separately to achieve The whole sequence is in order.

6.1 Algorithm description

Quicksort uses a divide-and-conquer method to divide a string (list) into two sub-strings (sub-lists). The specific algorithm is described as follows:

  • Pick an element from the sequence, called the "pivot" (pivot);
  • Reorder the sequence, all elements smaller than the reference value are placed in front of the reference value, and all elements larger than the reference value are placed behind the reference value (the same number can go to either side). After this partition exits, the benchmark is in the middle of the sequence. This is called a partition operation;
  • Recursively sort subarrays with elements less than the base value and subarrays with elements greater than the base value.

6.2 Animation presentation

6.3 Code implementation

//递归实现
int *bubbleSort(int arr[], int left, int right) {
    int i = left, j = right;
    int pivot = arr[(left + right) / 2];
    int temp;
 
    while (i <= j) {
        while (arr[i] < pivot)
            i++;
        while (arr[j] > pivot)
            j--;
        if (i <= j) {
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            i++;
            j--;
        }
    }
    if (left < j)
        quickSort(arr, left, j);
    if (i < right)
        quickSort(arr, i, right);
    return arr;
}
//迭代实现
int *bubbleSort(int arr[], int left, int right) {
    int s[right-left+1];
    int top = -1;
    s[++top] = left;
    s[++top] = right;
    while (top != -1) {
        int r = s[top--];
        int l = s[top--];
        int pivot = arr[(l + r) / 2];
        int i = l, j = r;
        while (i <= j) {
            while (arr[i] < pivot)
                i++;
            while (arr[j] > pivot)
                j--;
            if (i <= j) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i++;
                j--;
            }
        }
        if (l < j) {
            s[++top] = l;
            s[++top] = j;
        }
        if (i < r) {
            s[++top] = i;
            s[++top] = r;
        }
    }
    return arr;
}

 

7. Heap Sort

Heapsort (Heapsort) refers to a sorting algorithm designed using the data structure of the heap. Stacking is a structure that approximates a complete binary tree, and at the same time satisfies the nature of stacking: that is, the key value or index of a child node is always smaller (or larger) than its parent node.

7.1 Algorithm description

  • Build the initial sequence of keywords to be sorted (R1, R2....Rn) into a large top heap, which is the initial unordered area;
  • Exchange the top element R[1] with the last element R[n], and get a new unordered area (R1, R2,...Rn-1) and a new ordered area (Rn), and satisfy R [1,2...n-1]<=R[n];
  • Since the new heap top R[1] after the exchange may violate the nature of the heap, it is necessary to adjust the current unordered area (R1, R2,...Rn-1) to a new heap, and then combine R[1] with the unordered area again The last element of the region is exchanged to obtain a new disordered region (R1, R2....Rn-2) and a new ordered region (Rn-1, Rn). This process is repeated until the number of elements in the ordered area is n-1, and the entire sorting process is completed.

7.2 Animation presentation

7.3 Code implementation

// 堆排序:(最大堆,有序区)。从堆顶把根卸出来放在有序区之前,再恢复堆。
void max_heapify(int arr[], int start, int end) {
	//建立父节点指标和子节点指标
	int dad = start;
	int son = dad * 2 + 1;
	while (son <= end) { //若子节点在范围内才做比较
		if (son + 1 <= end && arr[son] < arr[son + 1]) //先比较两个子节点指标,选择最大的
			son++;
		if (arr[dad] > arr[son]) //如果父节点大于子节点代表调整完成,直接跳出函数
			return;
		else { //否则交换父子內容再继续子节点与孙节点比較
			swap(arr[dad], arr[son]);
			dad = son;
			son = dad * 2 + 1;
		}
	}
}

int *heap_sort(int arr[], int len) {
	//初始化,i从最后一个父节点开始调整
	for (int i = len / 2 - 1; i >= 0; i--)
		max_heapify(arr, i, len - 1);
	//先将第一个元素和已经排好的元素前一位做交换,再从新调整(刚调整的元素之前的元素),直到排序完成
	for (int i = len - 1; i > 0; i--) {
		swap(arr[0], arr[i]);
		max_heapify(arr, 0, i - 1);
	}
    return arr;
}

8. Counting Sort

Counting sorting is not a sorting algorithm based on comparison. Its core is to convert the input data value into a key and store it in the additional array space. As a sort of linear time complexity, counting sort requires that the input data must be integers with a certain range.

8.1 Algorithm description

  • Find the largest and smallest elements in the array to be sorted;
  • Count the number of occurrences of each element whose value is i in the array, and store it in the i-th item of the array C;
  • Accumulate all counts (starting from the first element in C, each item is added to the previous item);
  • Fill the target array in reverse: put each element i in the C(i)th item of the new array, and subtract 1 from C(i) every time an element is placed.

8.2 Animation presentation

8.3 Code implementation

int *bubbleSort(int arr[], int len) {
    int maxValue = arr[0];
    for (int i = 0; i < len; ++i) {
        maxValue = fmax(maxValue, arr[i]);
    }
    int bucket[maxValue + 1];
    memset(bucket, 0, sizeof(bucket));
    int sortedIndex = 0;
    int arrLen = len;
    int bucketLen = maxValue + 1;
    //计数数组
    for (int i = 0; i < arrLen; ++i) {
        bucket[arr[i]]++;
    }
    //累计数组
    for (int i = 1; i < bucketLen; ++i) {
        bucket[i] += bucket[i-1];
    } 
    int temp[len];
    for (int i = len; i > 0; i--) {
        temp[--bucket[arr[i - 1]]] = arr[i - 1];
    }
    memcpy(arr, temp, sizeof(temp));
    return arr;
}

8.4 Algorithm analysis

Counting sort is a stable sorting algorithm. When the input elements are n integers between 0 and k, the time complexity is O(n+k), the space complexity is also O(n+k), and its sorting speed is faster than any comparison sorting algorithm. Counting sort is an efficient sorting algorithm when k is not very large and the sequences are relatively concentrated.

9. Bucket Sort

Bucket sort is an upgraded version of counting sort. It makes use of the mapping relationship of functions, and the key to high efficiency lies in the determination of this mapping function. The working principle of bucket sort (Bucket sort): Assuming that the input data is uniformly distributed, the data is divided into a limited number of buckets, and each bucket is sorted separately (it is possible to use other sorting algorithms or continue to use recursively) bucket sort).

9.1 Algorithm description

  • Set a quantitative array as an empty bucket;
  • Traverse the input data and put the data one by one into the corresponding bucket;
  • Sort each bucket that is not empty;
  • Concatenate sorted data from non-empty buckets. 

9.2 Picture presentation

9.3 Code implementation

#define NBUCKET 6  // 桶的数量
//建立桶
struct Node {
    float data;
    struct Node *next;
};
// 桶排序,arr 为待排序序列
float *bubbleSort(float arr[], int len) {
    int i, j;
    struct Node **buckets;
    // 创建所有桶
    buckets = (struct Node **)malloc(sizeof(struct Node *) * NBUCKET);
    // 设置每个桶为空桶
    for (i = 0; i < NBUCKET; ++i) {
        buckets[i] = NULL;
    }
    // 根据规定,将 arr 中的每个元素分散存储到各个桶中
    for (i = 0; i < len; ++i) {
        struct Node *current;
        int pos = arr[i] * 10;  //根据规则,确定元素所在的桶
        //创建存储该元素的存储块,并连接到指定的桶中
        current = (struct Node *)malloc(sizeof(struct Node));
        current->data = arr[i];
        current->next = buckets[pos];
        buckets[pos] = current;
    }
    // 调用自定义的排序算法,对各个桶进行排序
    for (i = 0; i < NBUCKET; ++i) {
        buckets[i] = InsertionSort(buckets[i]);
    }
    // 合并所有桶内的元素
    for (j = 0, i = 0; i < NBUCKET; ++i) {
        struct Node *node;
        node = buckets[i];
        while (node) {
            arr[j++] = node->data;
            node = node->next;
        }
    }
    return arr;
}
// 自定义的排序算法,用于对各个桶内元素进行排序
struct Node *InsertionSort(struct Node *list) {
    struct Node *k, *nodeList;
    if (list == NULL || list->next == NULL) {
        return list;
    }
    nodeList = list;
    k = list->next;
    nodeList->next = NULL;
    while (k != NULL) {
        struct Node *ptr;
        if (nodeList->data > k->data) {
            struct Node *tmp;
            tmp = k;
            k = k->next;
            tmp->next = nodeList;
            nodeList = tmp;
            continue;
        }
        for (ptr = nodeList; ptr->next != 0; ptr = ptr->next) {
            if (ptr->next->data > k->data)
                break;
        }
        if (ptr->next != 0) {
            struct Node *tmp;
            tmp = k;
            k = k->next;
            tmp->next = ptr->next;
            ptr->next = tmp;
            continue;
        }
        else {
            ptr->next = k;
            k = k->next;
            ptr->next->next = 0;
            continue;
        }
    }
    return nodeList;
}

9.4 Algorithm analysis

Bucket sorting uses linear time O(n) in the best case. The time complexity of bucket sorting depends on the time complexity of sorting data between buckets, because the time complexity of other parts is O(n). Obviously, the smaller the buckets are divided, the less data there is between each bucket, and the less time it takes to sort. But the corresponding space consumption will increase. 

10. Radix Sort

Cardinality sorting is to sort according to the low order first, and then collect; then sort according to the high order, and then collect; and so on, until the highest order. Sometimes some attributes are prioritized, first sorted by low priority, and then sorted by high priority. The final order is that the one with the higher priority comes first, and the one with the same high priority and the lower priority comes first.

10.1 Algorithm description

  • Obtain the maximum number in the array and obtain the number of digits;
  • arr is the original array, each bit is taken from the lowest bit to form a radix array;
  • Perform counting sort on radix (using the feature that counting sort is suitable for small range numbers);

10.2 Animation presentation

10.3 Code implementation

int *bubbleSort(int arr[], int len) {
	//max为数组中最大值
	int max = arr[0];
	int base = 1;
	//找出数组中的最大值
	for (int i = 0; i < n; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
	}
	//循环结束max就是数组最大值
	//临时存放数组元素的空间
	int tmp[n];
	//循环次数为最大数的位数
	while (max / base > 0)
	{
		//定义十个桶,桶里面装的不是数据本身,而是每一轮排序对应(十、白、千...)位的个数
		//统计每个桶里面装几个数
		int bucket[10] = { 0 };
		for (int i = 0; i < n; i++)
		{
			//arr[i] / base % 10可以取到个位、十位、百位对应的数字
			bucket[arr[i] / base % 10]++;
		}
		//循环结束就已经统计好了本轮每个桶里面应该装几个数
		//将桶里面的元素依次累加起来,就可以知道元素存放在临时数组中的位置
		for (int i = 1; i < 10; i++)
		{
			bucket[i] += bucket[i - 1];
		}
		//循环结束现在桶中就存放的是每个元素应该存放到临时数组的位置
		//开始放数到临时数组tmp
		for (int i = n - 1; i >= 0; i--)
		{
			tmp[bucket[arr[i] / base % 10] - 1] = arr[i];
			bucket[arr[i] / base % 10]--;
		}
		//把临时数组里面的数拷贝回去
		for (int i = 0; i < n; i++)
		{
			arr[i] = tmp[i];
		}
		base *= 10;
	}
	return arr;
}

10.4 Algorithm Analysis

Radix sorting is based on sorting separately and collecting separately, so it is stable. However, the performance of radix sorting is slightly worse than that of bucket sorting. Each keyword bucket allocation requires O(n) time complexity, and obtaining a new keyword sequence after allocation requires O(n) time complexity. If the data to be sorted can be divided into d keywords, the time complexity of radix sorting will be O(d*2n). Of course, d is much smaller than n, so it is basically linear.

The space complexity of radix sort is O(n+k), where k is the number of buckets. Generally speaking, n>>k, so the extra space needs about n or so.

Guess you like

Origin blog.csdn.net/m0_64560763/article/details/131413308