[Algorithms] Six major sorts Insertion sort Hill sort Selection sort Heap sort Bubble sort Quick sort

All code for this chapter can be accessed here


1. The concept of sorting and its application

1.1 The concept of sorting

Sorting : The so-called sorting is the operation of arranging a string of records in increasing or decreasing order according to the size of one or some of the keywords.
Stability : Assume that there are multiple records with the same keyword in the sequence of records to be sorted. If sorted, the relative order of these records remains unchanged, that is, in the original sequence, r[i]=r[j] , and r[i] is before r[j], and in the sorted sequence, r[i] is still before r[j], the sorting algorithm is said to be stable; otherwise it is called unstable.
Internal sorting : A sorting in which all data elements are placed in memory.
External sorting : Too many data elements cannot be placed in memory at the same time, and the sorting of data cannot be moved between internal and external memory according to the requirements of the sorting process.

1.2 Common sorting algorithms

insert image description here

Second, the implementation of common sorting algorithms

1. Direct insertion sort

  • Basic idea
    Direct insertion sorting is a simple insertion sorting method. Its basic idea is: insert the records to be sorted into an ordered sequence that has been sorted one by one according to the size of its key code value, until all records are inserted At the end, a new ordered sequence is obtained .

<In fact, when we play poker, we use the idea of ​​insertion sort>
insert image description here

  • Step
    Suppose we are sorting in ascending order
    insert image description here
  1. We first consider our first data to be ordered.

  2. Take a data data and traverse the sorted element sequence from back to front

  3. If during the traversal process, it is found that a certain data largeData in the sorted element sequence is greater than data, then we will move the data largeData backward by one unit, and then continue to traverse forward. If a certain data smallerData in the sorted element sequence is smaller than data, then we put the data data behind smallerData, and then there is no need to traverse forward, because the sorted element sequence has gone through the above 2-3 steps The newly arrived data data is also put in the correct position.
    insert image description here
    insert image description here

  4. So our sorting of many data is transformed into a loop of the above 2-3 steps.

Animation presentation

insert image description here

  • Code
//直接插入排序
void InsertSort(int* a,int n)
{
    
    
	//排序 
	for (int i = 0; i + 1 < n; i++)
	{
    
    
		//end是有序数列的最后一个位置的下标
		int end = i;
		int tmp = a[end + 1];
		//end >= 0 防止在--end时数组越界
		while (end >= 0 && tmp < a[end])
		{
    
    
			a[end +1] = a[end];
			--end;
		}
		a[end+1] = tmp;
	}
}

  • Summary of the characteristics of direct insertion sort:
  1. The closer the element set is to the order, the higher the time efficiency of the direct insertion sorting algorithm (because the closer the data is to the order, the smaller the number of backward moves)
  2. Worst case O ( n 2 ) O(n^2)O ( n2 )At this time, the column to be sorted is in reverse order, or close to reverse order.
    In the best case, it isO ( n ) O(n)O ( n ) , at this time the column to be sorted is in ascending order, or close to ascending order
  3. Space complexity: O ( 1 ) O(1)O ( 1 ) , it is a stable sorting algorithm
  4. Stability: Stable

2. Hill sort

The steps of the Hill sorting method are:
first select an integer gap smaller than N (N is the number of arrays), divide all the data into gap groups, and group all the data with a gap distance into the same group, and divide each The elements of the group are sorted directly by insertion. Then take a new gap (the new gap is smaller than the previous gap), and repeat the above grouping and sorting work. When gap=1 is reached, all data are sorted within the same group.

insert image description here

The idea of ​​Hill sorting method is :
Hill sorting first pre-sorts the sequence to be sorted by gap grouping (pre-sorting is always when gap>1), so that the sequence to be sorted is close to order, and then performs an insertion sort on the sequence (When gap=1, it is actually insertion sorting), at this time, the time complexity of insertion sorting is close to O(N)

Code

//希尔排序   正常一组一组排序
void ShellSort(int* a,int n)
{
    
    
	//定义第一次gap的值  
	int gap = n;
	//多次预排序,gap=1时是直接排序
	while (gap > 1)
	{
    
    
		//减小gap的值直到1
		gap = gap / 3 + 1;
		//多趟排序
		for (int j = 0; j < gap; j++)
		{
    
    
			//单趟排序  i是当前有序序列最后一个位置的下标
			for (int i = j; i + gap < n; i += gap)
			{
    
    
				int end = i;
				//记录一下待插入的数据
				int tmp = a[end + gap];
				//end >= 0 防止在end -=gap 时数组越界
				while (end >= 0 && tmp < a[end])
				{
    
    
					a[end + gap] = a[end];
					end -= gap;
				}
				a[end + gap] = tmp;
			}
		}
	}
}
  • Summary of the characteristics of Hill sorting:
  1. Hill sort is an optimization of direct insertion sort.
  2. When gap > 1, it is pre-sorted, the purpose is to make the array closer to order. When gap == 1, the array is already close to order, so it will be very fast. In this way, the overall optimization effect can be achieved. After we implement it, we can compare performance tests.
  3. The time complexity of Hill sorting is not easy to calculate, because there are many ways to value the gap, which makes it difficult to calculate, so the time of Hill sorting given in many books is not fixed.
  4. Average time complexity : O ( N 1.3 ) O(N^{1.3})O ( N1.3)
  5. Space complexity: O ( 1 ) O(1)O(1)
  6. Stability: Unstable

3. Selection sort

Basic idea :
Select the smallest and largest elements from the data elements to be sorted each time, store them in the start position and end position of the sequence, then move the start position backward by one unit, and move the end position forward by one unit. Until all the data elements to be sorted are exhausted.

Steps :
Traverse the entire array once, select the maximum value and minimum value, put the minimum value in a[0] for the first time, the maximum value a[n-1], and put the minimum value in a[1] for the second time, the maximum value a[n-2], until subscript m >= n − m − 1 m >= nm-1m>=nm1 to end the program.

Dynamic demonstration
(the following figure demonstrates an algorithm that only selects one minimum value at a time, we select the maximum and minimum at one time, which is more efficient than the one in the figure)

insert image description here
Code

//选择排序
void SelectSort(int* a, int n)
{
    
    
	//多趟排序
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
    
    
		//先假设 a[0] 位置既是最小值又是最大值
		//mini是最小值的下标,maxi是最大位置的下标
		int mini = begin, maxi = begin;
		//单趟遍历,选出最大值于最小值
		for (int i = begin; i <=end; ++i)
		{
    
    
			if (a[i] > a[maxi])
			{
    
    
				maxi = i;
			}
			else if (a[i] < a[mini])
			{
    
    
				mini = i;
			}
		}
		Swap(&a[begin], &a[mini]);
		if (begin == maxi)
		{
    
    
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

Summary of features of direct selection sort:

  1. Direct selection sort thinking is very easy to understand, but the efficiency is not very good. rarely used in practice
  2. Time complexity: O(N^2)
  3. Space complexity: O(1)
  4. Stability: Unstable

4. Heap sort

Heapsort (Heapsort) refers to a sorting algorithm designed using a stacked tree (heap) data structure, which is a type of selection sort. It selects data through the heap. It should be noted that you need to build a large heap for ascending order, and a small heap for descending order .

See here for details on heap sorting

Summary of the characteristics of heap sorting:

  1. Heap sort uses the heap to select numbers, which is much more efficient.
  2. Time complexity: O(N*logN)
  3. Space complexity: O(1)
  4. Stability: Unstable

5. Bubble sort

Basic idea :
Compare two by two, exchange if the left side is greater than the right side, and sort a number after a trip.

insert image description here
Code

//冒泡排序
int BubbleSort(int*a,int n)
{
    
    
	for (int j = 0; j < n; j++)
	{
    
    
		//end是排序数组的最后一个需要排序元素的下标
		int end = n - 1 - j;
		//定义一个交换标志
		int exchange = 0;
		for (int i = 0; i < end; i++)
		{
    
    
			if (a[i] > a[i + 1])
			{
    
    
				Swap(&a[i], &a[i + 1]);
				exchange = 1;
			}
		}
		// 一趟冒泡过程中,没有发生交换,说明已经有序了,不需要再处理
		if (exchange == 0)
		{
    
    
			break;
		}
	}
}

Summary of the characteristics of bubble sort:

  1. Bubble sort is a very easy to understand sort
  2. Time complexity: O(N^2)
  3. Space complexity: O(1)
  4. Stability: Stable

6. Quick Sort

Basic idea :
any element in the element sequence to be sorted is taken as the reference value, and the set to be sorted is divided into two subsequences according to the sort code. All elements in the left subsequence are less than the reference value, and all elements in the right subsequence are greater than The base value, and then the leftmost subsequence repeats the process until all elements are arranged in the corresponding positions.

6.1 Hall method

steps :

  1. First select a key value as the reference value, usually select the leftmost value of the column to be sorted as the key value.
  2. Define two subscripts, left and right, where left points to the subscript of the leftmost value of the column to be sorted, and right points to the rightmost subscript of the column to be sorted.
  3. If we choose the leftmost as the key value, we have to let the right position judge a[right] < key If it is true, then let right stop temporarily; if it is not true, let right- -, until we find a position a[right ] < key, when found, right will temporarily stop. ( It should be noted that if you choose the leftmost data as the key, you need to go right first; if you choose the rightmost data as the key, you need to go first with begin ).
  4. After the right stops, we need to let the position of left on the left judge a[left] > key. If it is true, we will exchange a[left] with a[right]. If it is not true, we will use left++ until we find a position a [left] > key, exchange with a[right] after finding it.
  5. Then repeat 3-4 until left=right, we exchange the key with a[left] (at this time a[left]=a[right]), at this time the key is in the correct position, and the key divides the array into Two parts. At this time, the left side of the key is smaller than the key, and the right side of the key is larger than the key. A single-pass sorting is completed, and a single-pass sorting completes one piece of data.
  6. We then perform the above steps on the left and right arrays of the last key until each divided cell is in order, and the sorting of the entire array is completed. (Finally, when there is only one data in the left and right sequences, or the left and right sequences do not exist, the cells are already in order)

Dynamic demo
insert image description here
code implementation

//快速排序   hoare法   

void QuickSort(int* a, int begin, int end)
{
    
    
	//当区间只有一个值时,或区间不存在时退出递归
	if (begin >= end)
	{
    
    
		return;
	}
	//left是闭区间最最左边的下标,right是最右边位置的下标
	int left = begin, right = end;
	//选择最左边作为key值
	int keyi = left;
	//一趟排序
	while (left < right)
	{
    
    
		//右边先走,找到a[right]<key的位置
		while (left < right && a[right] >= a[keyi])
		{
    
    
			--right;
		}
		//左边后走,找到a[left]>key的位置
		while (left < right && a[left] <= a[keyi])
		{
    
    
			++left;
		}
		//交换左右两边的值,如果left == right了,也没有必要交换了。
		if (left != right)
		{
    
    
			Swap(&a[left], &a[right]);
		}
	}
	//交换左右两边的值,如果keyi == left了,也没有必要交换了。
	if (keyi != left)
	{
    
    
		Swap(&a[keyi], &a[left]);
	}
	keyi = left;
	//此时区间被分割为[begin,keyi-1] keyi [keyi+1,end]
	//注意传递的是begin,不是0,中间递归时不一定是从0开始的区间,0是一个常量,begin是一个变量。
	QuickSort(a, begin, keyi-1);
	QuickSort(a, keyi+1, end);
}

6.2 Digging method

steps :

  1. First set the leftmost value of the column to be sorted as the key, use a temporary variable to save the value of the key and set the subscript of the position as the first hole.
  2. The right side looks for a value smaller than the key, finds it and puts it into the pit, and at the same time updates the pit position to right.
  3. The left side looks for a value larger than the key, finds it and puts it into the pit, and updates the position of the pit to left.
  4. Repeat steps 2-3 until left==right, then put the key at the meeting position, a[left]=key (or a[right]=key)
  5. At this point, the single-pass sorting is completed. The value on the left of the key is smaller than the key, and the value on the right of the key is larger than the key. The key is in the correct position, and the interval is divided into two parts. One number is sorted in a single pass at a time.
  6. Then perform the above-mentioned single-pass sorting on the divided intervals multiple times, and the entire array will be in order!
    insert image description here
//快速排序  挖坑法
void QuickSort(int* a, int begin, int end)
{
    
    
	//当区间只有一个值时,或区间不存在时退出递归
	if (begin >= end)
	{
    
    
		return;
	}
	int left = begin, right = end;
	int keyi = left;
	//刚开始时keyi的位置作为坑位
	int hole = keyi;

	int key = a[keyi];
	while (left < right)
	{
    
    
		while (left < right && a[right] >= key)
		{
    
    
			--right;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
    
    
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	keyi = hole;
	
	//此时区间被分割为[begin,keyi-1] keyi [keyi+1,end]
	//注意传递的是begin,不是0,中间递归时不一定是从0开始的区间,0是一个常量,begin是一个变量。
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

6.3 Back and forth pointer method

steps :

  1. Initially, we select the leftmost of the data to be sorted as the key, define prev as the leftmost of the data to be sorted, and define cur as the leftmost of the data to be sorted + 1.
  2. Cur looks forward for a value smaller than the key, stops after finding it, and then ++prev exchanges the values ​​of the prev position and the cur position. When the gap between prev and cur is widened, the values ​​between prev and cur are greater than or equal to The value of the key.
  3. Finally, when cur just crosses the boundary, the array traversal is completed, and a[prev] and key are exchanged. At this time, the key is in the correct position, the interval is divided into two parts, and one number is sorted in a single pass at a time.
  4. Then perform the above-mentioned single-pass sorting on the divided intervals multiple times, and the entire array will be in order!

insert image description here

//快速排序  前后指针法
void QuickSort(int* a, int begin, int end)
{
    
    
	//当区间只有一个值时,或区间不存在时退出递归
	if (begin >= end)
	{
    
    
		return;
	}
	int prev = begin , cur = begin + 1;
	int keyi = begin;
	while (prev <= cur && cur <= end)
	{
    
    
		if (a[cur] < a[keyi] && ++prev != cur)
		{
    
    
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}
	Swap(&a[keyi], &a[prev]);
	keyi = prev;
	//此时区间被分割为[begin,keyi-1] keyi [keyi+1,end]
	//注意传递的是begin,不是0,中间递归时不一定是从0开始的区间,0是一个常量,begin是一个变量。
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

Summary of quick sort features:

  1. The overall comprehensive performance and usage scenarios of quick sort are relatively good, so I dare to call it quick sort
  2. Time complexity: O(N*logN)
    insert image description here
  3. Space complexity: O(logN)
  4. Stability: Unstable

7. Quick sort non-recursive

The non-recursive implementation of quick sort depends on the stack . The key data we pass when we recurse the quick sort is the subscript of the range . Only when we get the subscript of the range can we sort correctly.
Therefore, we should use the characteristics of the stack to store the subscript of the interval.

insert image description here

Code implementation :

//单趟排序
int Partion3(int* a ,int begin ,int end)
{
    
    
	int prev = begin , cur = begin + 1;
	int keyi = begin;
	while (prev <= cur && cur <= end)
	{
    
    
		if (a[cur] < a[keyi] && ++prev != cur)
		{
    
    
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}
	Swap(&a[keyi], &a[prev]);
	keyi = prev;
	return keyi;
}
//快速排序的非递归实现
void QuickSortNonR(int* a ,int begin ,int end)
{
    
    
	Stack st;
	StackInit(&st);
	StackPush(&st, begin);
	StackPush(&st, end);

	while (!StackEmpty(&st))
	{
    
    
		int right = StackTop(&st);
		StackPop(&st);
		int left = StackTop(&st);
		StackPop(&st);

		//一趟排序
		int keyi = Partion3(a, left, right);

		//此时区间被划分为[left,keyi-1]keyi[keyi+1,right]
		if (keyi + 1 < right)
		{
    
    
			StackPush(&st, keyi + 1);
			StackPush(&st, right);
		}
		if (left < keyi - 1)
		{
    
    
			StackPush(&st, left);
			StackPush(&st, keyi - 1);
		}
	}
	StackDestroy(&st);
}

Guess you like

Origin blog.csdn.net/qq_65207641/article/details/129318056