Sorting (fast sorting and merging)

Posted by cac_azure03 on Fri, 26 Nov 2021 21:56:00 +0100

Quick sort

basic thought

1. Find a benchmark number at random
2. Put all numbers less than him on the left and all large numbers on the right
3. Finally, repeat the above operations until the left and right pointers of each part meet

As shown in GIF, an unordered array 3 5 8 1 2 9 4 7 6
Take the right endpoint as the reference number, and then the left pointer starts scanning from the left. When it encounters a number > = 6, it stops. At this time, the right pointer starts scanning. When it encounters a number < = 6, it stops. The positions of the two numbers do not conform to the second basic idea, so it is exchanged. Then the left pointer starts scanning again. When the left and right pointers meet, it is the first traversal

The array is divided into two parts and the above operation is repeated

code

int op[10010];//Open array space

void quick_sort(int op[], int l, int r) {
	if (l >= r)//End flag of recursion
		return ;
	int key = op[(l + r) / 2];//Reference number
	int i = l - 1, j = r + 1;//Left and right pointers (why - 1 and + 1)
	while (i < j) {
		do
			i++;
		while (op[i] < key);//Left pointer scan
		do
			j--;
		while (op[j] > key);//Right pointer scan
		if (i < j) {//Digital switching
			int t = op[i];
			op[i] = op[j];
			op[j] = t;
		}
	}
	quick_sort(op, l, j);//recursion
	quick_sort(op, j + 1, r);
}

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		scanf("%d", &op[i]);
		
	quick_sort(op, 0, n - 1);
	
	for (int i = 0; i < n; i++)
		printf("%d ", op[i]);
		
	return 0;
}

Time complexity

The time complexity of quick sort is O(nlogn)
Borrow here Spy ion Explanation of
Quick sort divides the array interval into two parts each time. In the most ideal case, that is, the selected benchmark number is exactly the median of this interval, then each segmentation can be divided into two intervals of the same size, and then recursion

  • In the first layer of recursion, n numbers are divided into 2 intervals, and each interval has n/2 numbers
  • In the second layer of recursion, n numbers are divided into 4 intervals, and each interval has n/4 numbers
  • In the third layer of recursion, n numbers are divided into 8 intervals, and each interval has n/8 numbers
    .........
  • In the log n layer of recursion, n numbers are divided into n intervals with 1 number in each interval
    A total of logn level recursions are performed, and each recursion is O(n), so the time complexity of fast sorting is O(nlogn). However, when the array itself is an ordered sequence, the time complexity immediately becomes O(n^2), so fast sorting is unstable

matters needing attention

  • Why + 1, - 1?

  • Why do; while; Instead of while

  • Why use the middle value of the array as the benchmark instead of the endpoint

  • Why check the relative position of the pointer before switching
    1. Question 1 and question 2 are the same question. This is because in order to prevent that the values of the numbers i and j are equal, and the numbers i and j are still equal after the exchange, resulting in program crash, you have to add and subtract each time

    2. Question 3 is the same as question 4. Taking the intermediate value as the benchmark is to prevent boundary problems

When you select the left endpoint as the benchmark, quick_sort(op, l, j),quick_sort(op, j + 1, r); Recursion here cannot take i as the boundary of recursion
Accordingly, when selecting the right endpoint as the benchmark, j cannot be used as the recursive boundary

It is because there will be unilateral dead circulation, such as 2 1 3 5 4

Template question

acwing785
Luogu P1177

Merge sort

basic thought

1. The interval to be sorted is infinitely divided into indivisible sub intervals
2. Then merge all sub intervals in pairs, and complete the sorting operation in the merging process. Finally, the merged new areas are ordered sequences

Compared with fast scheduling, merge scheduling does not have so many boundary problems, and it is very simple. Moreover, the time complexity is O(nlogn), and there will be no situation with the most O(n^2) as fast scheduling

code

#include <stdio.h>
int op[10010];
int temp[10010];

void merge_sort(int op[], int l, int r) {
	if (l >= r)
		return ;
	int mid = (l + r ) / 2;
	merge_sort(op, l, mid), merge_sort(op, mid + 1, r);//Recursive first
	int k = 0, i = l, j = mid + 1;
	while (i <= mid && j <= r) {//Post sort
		if (op[i] <= op[j])
			temp[k++] = op[i++];
		else
			temp[k++] = op[j++];
	}
	while (i <= mid)//Add extra numbers to the array
		temp[k++] = op[i++];
	while (j <= r)
		temp[k++] = op[j++];
	for (i = l, j = 0; i <= r; i++, j++)
		op[i] = temp[j];
}

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		scanf("%d", &op[i]);
	merge_sort(op, 0, n - 1);
	for (int i = 0; i < n; i++)
		printf("%d ", op[i]);
	return 0;
}

Time complexity

  • In the first layer of recursion, n numbers are divided into 2 intervals, and each interval has n/2 numbers
  • In the second layer of recursion, n numbers are divided into 4 intervals, and each interval has n/4 numbers
  • In the third layer of recursion, n numbers are divided into 8 intervals, and each interval has n/8 numbers
    .........
  • In the log n layer of recursion, n numbers are divided into n intervals with 1 number in each interval
    A total of logn level recursions are performed, and each recursion is O(n), so the time complexity of merging and sorting is O(nlogn)

Template question

acwing787