【Data structure】-8 kinds of sorting analysis (detailed summary, concise, including code, examples)

Table of contents

1. Overview analysis of 8 sorting methods (with pictures)

    1. Classify by method (comparative order)

2. Detailed analysis of 8 sorting methods

     1. Counting sort

     2. Bubble sort

     3. Selection sort

     4. Insertion sort

     5. Hill sort

     6. Heap sort

     7. Quick sort (recursive and non-recursive writing)

        1. Three sorting methods

         2. Non-recursive writing (analogous layer order traversal is implemented with a queue, here a stack is used)

      8. Merge sort (recursive and non-recursive writing)

          1. Recursive writing

          2. Non-recursive writing (note the classification and discussion of cross-border situations)

Three. Complexity/stability analysis of 8 sorting methods

     1. The concept of stability

     2. Analysis

  1. The reason why the simple selection sort is unstable

  2. Overview of Complexity Analysis


1. Overview analysis of 8 sorting methods (with pictures)

    1. Classify by method (comparative order)

 

* Counting sort : non-comparison sorting

2. Detailed analysis of 8 sorting methods

     1. Counting sort

Note: Counting sort is suitable for sorting integer arrays with a small range and a small range. Not suitable for sorting with scattered ranges or non-integer types, such as: strings, floating point numbers, etc.

step:

1. Find the maximum value of the original array, denoted as range

2. Set up a counting array, traverse the original array O(n), and count the number of occurrences of each data.

3. Traversing the count array O(range)

Counting sorting is divided into: relative mapping type and non-relative mapping type (relative position)

The figure shows:

     2. Bubble sort

Traverse each number in the ordered interval, rotate and exchange from the beginning to the end of the interval , and continuously shrink the interval .

Principle: Keep moving the big/small numbers to the back

Note: In order to improve efficiency, when it is found that there are no pairs of exchanges in a cycle , the cycle can be terminated.

void BubbleSort(int*a,int n)
{
	int i = 0,j=0;
	for (j = 0; j<n; j++)
	{
		bool exchange = false;
		for (i = 0; i < n-j; i++)
		{
			if (a[i + 1] < a[i])
			{
				Swap(&a[i + 1], &a[i]);
				exchange = true;
			}
		}
        //加入判断环节,提前终止,提高效率
		if (exchange == false)
		{
			break;
		}
	}
}

     3. Selection sort

Traverse each number in the ordered interval , find the largest/smallest number after it and replace it with the number after that number .

The design idea of ​​the code is to set the left and right subscripts to traverse from both ends of the array to the middle, filter out the maximum and minimum values ​​mini and maxi in turn, and exchange them with left and right respectively.

Points to note: During the exchange process, the position of left may just be marked by maxi , and the exchange of maxi and right in the next step will be wrong, and right cannot be exchanged with the correct maxi.

Solution: If left and maxi overlap, correct them after swapping

void SelectSort(int* a, int n)
{
	int left = 0;
	int right = n;
	while (left < right)
	{
		int mini = left, maxi = right;
		for (int i =left+1; i <= right; i++)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;//移动下标
			}
			if (a[i] < a[mini])
			{
				mini = i;
			}
		}
		Swap(&a[left], &a[mini]);
		if (left == maxi)
		{
			maxi = mini;
		}
		Swap(&a[right], &a[maxi]);
		left++;
		right--;
	}
}

     4. Insertion sort

Traverse each number in the ordered interval, treat it as a temporary variable tmp, and compare it with the number before it,

Its further optimization is "Hill sorting"

Note: In this algorithm, when tmp is larger/hour than the first number, end will reach the -1 position. Therefore, the marking usage in the figure is used .

//升序
void InsertSort(int* a, int n)//a 数组  n 个数
{
	int i = 0;
	for (i = 1; i < n; i++)
	{
		int end = i - 1;
		int tmp = i;
		while (end >= 0)
		{
			if (a[tmp] < a[end])
			{
				//整体后移
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		//填空
		a[end + 1] = a[tmp];
	}
}

     5. Hill sort

It can be understood as pre-sorting on the basis of insertion sorting (grouping and inserting)

Points to note: Graphical aids in understanding the cycle:

void ShellSort(int* a, int n)
{
	//gap==1 插入排序
	//gap>1预先排序
	int gap=n;
	//升序
	while(gap>1)
	{ 
		gap = gap / 2;
		//gap=gap/3+1     确保gap的跳跃到最后为1,
		int i = 0;
		for (i = 0; i < n-gap; i++)
		{
			int end = i;
			int tmp = i+gap;
			while (end >= 0)
			{
				if (a[tmp] < a[end])
				{
					//整体后移
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			//填空
			a[end + gap] = a[tmp];
		}
	}
}

     6. Heap sort

For details, see the blogger's detailed explanation of heaping:

 

     7. Quick sort (recursive and non-recursive writing)

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 sorting code . All elements in the left subsequence are smaller than the reference value , all elements in the right subsequence are greater than the reference value , and then the leftmost subsequence repeats the process until all elements are arranged in the corresponding position.

Points to note: When the quick sort is close to the recursive mode of the binary (binary tree), the efficiency is the highest. Therefore, the " three-number center " optimization code is introduced:

int GetMidNumi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}

        1. Three sorting methods

  1. Exchange method

   1. Make the key on the left, and go first on the right - make sure that the meeting position is smaller than the key

     ps: [Go to the right first to find a number smaller than key, then its stop position must be less than or equal to key]

   2. Since the position where the left and right meet must be smaller than the key, replace the left with the meeting position

Graphic:

 code:

2. Digging method

 1. First put the first data on the left in the temporary variable key , and form a pit in place

  2. Move to the right first, find a number smaller than the key, put it in the pit, and generate a new pit on the spot

  3. When left and right meet, fill the key into the last pit

3. Back and forth pointer method (play interval)

  1. The first number on the left is set as key, prev (delay pointer), cur (real-time pointer)

  2. cur starts to move to the right, find a value smaller than key prev and cur move at the same time

  3. Find a value larger than the key and only move cur - ensure that there is an interval larger than the key between prev and cur

 4. When a value smaller than key is found, exchange the value of the next position of prev (the interval larger than key ) and the value of cur positionthe value larger than key is flipped to the right of the interval, and the value smaller than key is flipped to the left of the interval.

 Graphic:

         2. Non-recursive writing (analogous layer order traversal is implemented with a queue , here a stack is used )

Reasons for learning: The essence of recursion is to constantly open up space. When there are too many recursive layers, stack overflow may occur . Therefore, the introduction of non-recursive writing

Implementation principle: The recursive writing method is essentially to continuously open up space downwards, and return and return the space when the termination condition is reached. Instead of using recursive writing, the subscript can be directly divided on the original array

1. Enter the tail mark, enter the head mark

2. After marking begin and end, delete the head and calculate the keyi

3. At this point, the original array is divided into [begin, keyi-1] keyi [keyi+1, end].

Perform the same operation ( push and pop ) on the existing intervals respectively .

Graphic:

PS: Number representation, can be regarded as the number of layers of recursion. And actually there is no recursion. 

void quicksortnonr(int*a,int left,int right)
{
	ST st;
	StackInit(&st);
	StackPush(&st, right);//表示end的先入栈
	StackPush(&st, left);
	while (!StackEmpty(&st))
	{
		int begin = StackTop(&st);
		StackPop(&st);
		int end = StackTop(&st);
		StackPop(&st);
        //得出keyi
		int keyi = Partsort3(a, begin, end);//三数取中
		//【begin,keyi-1】keyi【keyi+1,end】
		if (keyi + 1 < end)
		{
			StackPush(&st, end);//表示end的先入栈
			StackPush(&st, keyi+1);
		}
		if (keyi -1 >begin)
		{
			StackPush(&st, keyi - 1);//表示end的先入栈
			StackPush(&st, begin);
		}
	}
	StackDestroy(&st);
}

      8. Merge sort (recursive and non-recursive writing)

          1. Recursive writing

The principle of merging: compare two ordered arrays and insert them into a new space.

PS: After combining recursion, it can be subdivided until only two numbers are combined to form an ordered array, and a new ordered sequence is synthesized in pairs, and copied to a new space (to avoid covering the original array). The positional relationship of the new space should correspond to the original array

Image icon:

Note: In order to improve efficiency, the middle number is used for division 

Graphic:

void MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;
	MergeSort(a,begin,mid,tmp);
	MergeSort(a,mid+1,end,tmp);
	//拷贝回与原数组a相对应的位置
	memcpy(a + begin,tmp + begin,sizeof(int) * (end - begin + 1));
}

The logic of recursive implementation: post-order traversal

PS: For post-order traversal, you can check the blogger's related blog: (62 messages) The use of binary tree (recursion) (traversal method) (concise. Contains code, exercises)_YYDsis' Blog - CSDN Blog

          2. Non-recursive writing (note the classification and discussion of cross-border situations)

Analysis: It is the same as the non-recursive algorithm of quick sort. When there are too many recursions, it may cause stack overflow. On the basis of the original array, you can directly merge the arrays within the range corresponding to the subscript and copy them back to the original array.

Image icon:

Note: Sometimes the selection of gap will cross the boundary!

Analysis: In essence, it is to continuously select [begin1, end1] [begin2, end2]

Note: the following analysis is to discuss the space corresponding to the subscript before merging !

1. When begin1 and end2 are combined to form new begin1 and end1, if end1 is critical (begin2 is out of bounds)/end1 is out of bounds, stop merging

2. When end1 is out of bounds, correct end1 

Image icon: 

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail\n");
		return;
	}

	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			// [begin1,end1][begin2, end2]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;//((i+gap)+(gap-1))
			//printf("[%d,%d][%d,%d] ", begin1, end1,begin2,end2);
            
            //分类讨论情况
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;//修正end2区间
			}

			printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);

			int j = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}

			// 归并一部门拷贝一部分
			memcpy(a+i, tmp+i, sizeof(int) *(end2-i+1));
		}
		printf("\n");
		gap *= 2;
	}
	free(tmp);
}

Three. Complexity/stability analysis of 8 sorting methods

     1. The concept of stability

Assuming that there are multiple records with the same keyword in the record sequence to be sorted, if the relative order of these records remains unchanged after sorting , the algorithm is said to be stable.

     2. Analysis

   * Counting sorting is special, time complexity O(n)/O(range), space complexity O(n)

  1. The reason why the simple selection sort is unstable

Special case: when the number to be replaced is on the same side of two identical numbers

  2. Overview of Complexity Analysis

 1. Hill sorting is based on direct insertion sorting with preprocessing. It is more complicated, and the conclusion is tentative.

2. Direct insertion sorting is to compare each number with all previous numbers. In any case, it must be fetched first, so the best case is the ordered case, which is n, and the worst case is equivalent to traversing an array, n^2.

3. Quick sort When keyi can take the intermediate value every time, it is close to the binary tree, nlogn. When keyi takes the leftmost/rightmost value every time, it is equivalent to traversing an array, n^2.

4. Merge sort, close to binary tree, nlogn. Since the new space of tmp is required to accommodate the new space after merging, the space complexity is n

5. Heap sorting is divided into two parts: heap adjustment (up and down) and heap sorting with deletion ideas. According to mathematical calculations, the complexity of the latter is nlogn, that is, the overall heap sorting is nlogn.

Guess you like

Origin blog.csdn.net/YYDsis/article/details/130110495