Data structure experiment quick sorting, bubble sorting algorithm (exchange sorting), using STM32 single-chip test (learning computer comprehensive exam 408 Wu single-chip series)

Both quick sort and bubble sort belong to the category of exchange sort, which means that its basic operation is to exchange two numbers.

Quick sorting, as the name suggests, quick sorting, introduces its own characteristics unobtrusively. And bubble sorting is just like its name, sorting slowly and smoothly like raising fish with bubbles. (joking, hahaha)

When testing the algorithm mentioned in this article, random numbers are used to test the sorting algorithm. For the method of generating random numbers, please refer to

https://blog.csdn.net/Fairchild_1947/article/details/118757154

Let's get down to business and introduce the two kinds of sorting principles and detailed testing process. The order of magnitude of time and space complexity and the comparison of the time and memory space consumed in real operation will be shown at the end of the article.

First, take the exam

Properties of Quick Sort and Bubble Sort
algorithm name time complexity space complexity Is it stable
best case average case worst case
quick sort O(n) O(n²) O(n²) O(1) yes        
Bubble Sort O(nlog2n) O(nlog2n) O(n²) O(log2n) no

When quicksort sorts an ordered sequence, the time complexity will be O(n²), and its space complexity will also reach O(n) due to the recursive operation method. When using quick sort, you need to pay attention that it is very unsuitable for sorting sequences that already have a certain order. (This phenomenon will be actually tested in the later test section)

In the ideal state of quick sorting, the pivot selected each time is the value in the middle of the area. If it continues, its recursive operation process is similar to the balanced binary tree of the sequence, and its space complexity is O(log2n) because the balanced binary tree has a total of log2n layers, so it needs to push the log2n layer during recursion. (The actual memory resource consumption will also be reflected in the subsequent test part)

Now that quick sorting has been mentioned above, please let the awesome quick sorting come on stage first. Quick sorting will select the first value as the pivot element, and move the elements larger than the pivot to the back of the pivot, and the elements smaller than the pivot to the front of the pivot, and finally the pivot will move to its final position after sorting. Afterwards, the pivot is used as the wiring to divide it into two parts, the front and back, and then repeat the above operations for the front and back two parts, select the first element as the pivot, and move the ones that are larger than the pivot to the back of the pivot, and the ones that are smaller than the pivot... In this way, it iterates again and again, and finally all the elements will be placed in their final positions.

The dynamic demonstration of quick sort is as follows:

The red element in the picture is the selected element, the yellow element is the pivot element, the green element is the element smaller than the pivot, the purple element is the element larger than the pivot, and the orange element is the element whose final position has been determined.

 The code is divided into a version using unsigned numbers and a signed number version for the subscript, because the lower part cannot be a negative number, but in the process of sorting, the subscript variable sometimes becomes an amplitude value, such as (i>=0), if i is an unsigned number, it will meet the condition, and an exception will be thrown when the program is running.

Secondly, there are two main differences between the code part and the kingly book:

1. Separately define variables for sentinels instead of in arrays, because this is a very invisible thing, because in general, the data in the array that needs to be sorted is stored from the element with the subscript 0. If you want to use element 0 as a sentinel, the code will lose good portability.

2. The formal parameter is changed from (array head address, array length) to (array head address, subscript to start sorting, subscript to end sorting), so that it can sort any part of the array, instead of rigidly arranging the entire array at a time.

3. The variables are defined at the beginning of the function. Since the C51 compiler cannot support the C99 mode, defining variables in the function will cause an error. In order to meet the requirements of porting this sorting algorithm to the 8051 microcontroller, the code writing is backward compatible with the C51 compiler.

Finally, in terms of function, this function sorts the unsigned 32-bit integer array. If you need to sort other types of data, you can directly modify the array data type.

Subscript unsigned version:

/* 计算枢轴位置并将枢轴元素移动到最终位置 */
int Partition(uint32_t array[], uint32_t start, uint32_t end)
{
	uint32_t pivot = array[start];						//获取枢轴
	while(start < end){
		while(start < end && array[end]>=pivot) end--;	//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[start] = array[end];						//找到小于枢轴的元素,交换到枢轴左侧
		while(start < end && array[start]<=pivot) start++;//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[end] = array[start];						//找到大于枢轴的元素,交换到枢轴右侧
	}
	array[start] = pivot;
	return start;
}
/* 快速排序算法,无符号整形,无符号下标版本 */
void QuickSort_UINT32(uint32_t array[], uint32_t start, uint32_t end)
{
	uint32_t pivotpos;			//枢轴元素位置信息变量定义
	if(start < end){
		pivotpos = Partition(array, start, end);//获取枢轴元素位置信息
		if(pivotpos > 0){
			QuickSort_UINT32(array, start, pivotpos-1);	//对枢轴左侧元素进行排序,由于下标信息使用无符号数定义,顾需要额外判断是否大于零		
		} else{
			;
		}
		QuickSort_UINT32(array, pivotpos+1, end);//对枢轴右侧元素进行排序
	}
}

Signed version:

/* 计算枢轴位置并将枢轴元素移动到最终位置 */
int Partition_UINT32(uint32_t array[], int32_t start, int32_t end)
{
	uint32_t pivot = array[start];						//获取枢轴
	while(start < end){
		while(start < end && array[end]>=pivot) end--;	//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[start] = array[end];						//找到小于枢轴的元素,交换到枢轴左侧
		while(start < end && array[start]<=pivot) start++;//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[end] = array[start];						//找到大于枢轴的元素,交换到枢轴右侧
	}
	array[start] = pivot;
	return start;
}
/* 快速排序算法,无符号整形,无符号下标版本 */
void QuickSort_UINT32(uint32_t array[], int32_t start, int32_t end)
{
	uint32_t pivotpos;			//枢轴元素位置信息变量定义
	if(start < end){
		pivotpos = Partition_UINT32(array, start, end);//获取枢轴元素位置信息
		QuickSort_UINT32(array, start, pivotpos-1);	//对枢轴左侧元素进行排序,由于下标信息使用无符号数定义,顾需要额外判断是否大于零		
		QuickSort_UINT32(array, pivotpos+1, end);//对枢轴右侧元素进行排序
	}
}

Compared with the unsigned version, the signed version reduces the judgment of whether the pivot element position is zero, and is slightly faster than the unsigned version in actual operation. But the signed version must pay attention to the start and end must use a value greater than 0, otherwise it will lead to negative values ​​as array subscripts and access out of bounds.

Next, test the algorithm

The test contents are respectively: sorting the random number table with length 32 and displaying the sorting result completely to verify the correctness of the algorithm, sorting the random number table with length 1024 in time and space usage, and sorting the ordered number table with length 1024 in time and space usage.

Key points of the test environment section:

1. Time complexity test, at the millisecond level, use the osKernelGetTickCount() (the original function is xTaskGetTickCount()) function provided by the FreeRTOS embedded real-time operating system to obtain the time stamps before and after the sorting algorithm runs, and subtract them to get the time length. The microsecond-level time using timer measurement principle is similar to the millisecond-level test. Clear and open the timer before the algorithm is executed. Read the timer value and close the timer at the end of the algorithm. By separately measuring the millisecond level and the microsecond level, the problem of timer overflow can be avoided.

2. The space complexity test uses the memory maximum water level test function osThreadGetStackSpace() (the original function is uxTaskGetStackHighWaterMar()) provided by the FreeRTOS embedded real-time operating system to test. The test is performed twice before and after the algorithm is run, and the memory usage is obtained.

Use a random number table of length 32 to verify the correctness of the algorithm:

 Algorithm performance test using a random number table with a length of 1024:

If quicksort encounters a partially ordered sequence, it will not be able to divide the area very effectively to approximate a balanced binary tree, which will lead to a deeper recursion depth, and an increase in the number of recursions will increase the use of time and space.

 As mentioned above, quick sorting is not conducive to sorting ordered sequences. Next, we will do a worst-case test. Use bubble sorting to sort the random number table into order, and then use bubble sorting to sort. You can see that quick sorting does require a lot of time and space at this time.

The quick sort test is complete, and it's the turn of the bubble sort

Bubble sorting, as the name suggests, is to complete the sorting by doing bubbling actions. Just like blowing oil droplets into water with a straw at the bottom of the water, the oil droplets are denser than water first, and the oil droplets are smaller, so they float up. In the same way, when air is blown into the bottom of the water through the straw, the air density is lower than that of the water first, and after floating to the surface of the water, it is then compared with the density of the oil, and the density of the air is lower, so it continues to float to the surface of the oil.

It can be seen from this example that the characteristics of bubble sorting are: comparing one by one and constantly rising.

The animation of bubble sorting is as follows:

The green part in the figure is the two elements that are being compared when comparing one by one, and the orange part is the element that determines the final position.

 In terms of specific implementation, bubble sorting is mainly divided into two actions: comparison and floating. Among them, the comparison form is determined, which is directly written in the algorithm function, and the floating action is an action of exchanging two numbers. The specific implementation of the action of exchanging two numbers can be divided into two ways: using the third variable and not using the third variable.

Bubble sort algorithm body code:

void BubbleSort_UINT32(uint32_t array[], uint32_t start, uint32_t end)
{
	uint32_t i,j;
	bool flag;
	for(i=start; i<end; i++){
		flag = false;
		for(j=end; j>i; j--){
			if(array[j-1]>array[j]){
				Swap_UINT32(&array[j-1], &array[j]);    //借助第三变量交换两元素
              //Swap_UINT32_XOR(&array[j-1], &array[j]);//不借助第三变量交换两元素
				flag = true;
			}else{
				;
			}
		}
		if(flag == false){
			return;
		}
	}
}

Swap two-element codes with the help of a third variable:

void Swap_UINT32(uint32_t *a, uint32_t *b)
{
	uint32_t temp;
	temp = *b;
	*b = *a;
	*a = temp;
}

Swap two-element codes without a third variable:

void Swap_UINT32_XOR(uint32_t *a, uint32_t *b)
{
	*a = (*a)^(*b);
	*b = (*a)^(*b);
	*a = (*a)^(*b);
}

Next, test this algorithm.

The test contents include: use two methods of exchanging two elements to sort the random number table of length 32 to verify the correctness of the algorithm, use two methods of exchanging two elements to sort the random number table of length 1024 and measure the time and space used by the algorithm.

The test environment is the same as quick sort.

Bubble sort correctness test:

 Sort a table of random numbers using a third variable that swaps two numbers:

 Sort a table of random numbers without swapping two numbers with a third variable:

Now that the two algorithms for swap sorting have results, the following will compare the time and space complexity with the actual time and space consumed during operation.

1. Time complexity: bubble sorting O(n²), quick sorting O(nlog2n), direct comparison of the complexity can be obtained. For the same data table, the speed ratio of bubble sorting and quick sorting is n/log2n. In the test environment described in this article, an array with a length of 1024 is used. Substituting calculations, bubble sorting should be about 100 times slower than quick sorting. The actual situation is that the bubble sorting is about 200MS, and the quick sorting is about 4.7MS. Although it is not 100 times, but the order of magnitude is the same.

2. Space complexity: bubble sort O(1), quick sort O(log2n). Through the actual test, it can be seen that the bubble sort does not use memory resources when it is running. The amount of memory used by quick sorting is brought into the length of the data table of 1024, using 10 memory. In actual use, since the internal processor of the STM32 microcontroller used in this test platform is Cortex-M3, which is a 32-bit processor without a floating point unit, the breakpoint information, some register values ​​and intermediate variable values ​​saved during recursive calls actually need about 40 words of space, which is in line with the order of magnitude.

To sum up, it can be clearly seen that quick sorting is much faster than bubble sorting, but quick sorting consumes more memory resources, especially when encountering ordered sequences, it will swallow a very huge amount of memory. This makes it necessary to consider the actual situation of the number table when designing a program using the quick sort algorithm, whether it is possible to have an ordered sequence, and secondly, it is necessary to reserve a relatively large memory space for the quick sort algorithm.

Guess you like

Origin blog.csdn.net/Fairchild_1947/article/details/118876734