Three optimizations of bubble sorting in C language and the analysis and Simulation of qsort function

Posted by verdrm on Mon, 04 Oct 2021 05:50:25 +0200

Bubble sorting

When we first mention bubble sorting, we always ask what is bubble sorting?
In short, it is to repeatedly visit the element column to be sorted, compare two adjacent elements in turn, and exchange them if the order (e.g. from large to small and from Z to A) is wrong. The work of visiting elements is repeated until no adjacent elements need to be exchanged, that is, the element column 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 (in ascending or descending order) through exchange, just as the bubbles of carbon dioxide in carbonated drinks will eventually float to the top, so it is called "bubble sorting".

Normal version

Let's sort the integer data first. For example, we need to arrange the eight numbers 5 6 1 3 4 2 7 8 to make them into an ordered array.
The picture analysis is as follows:

The code is as follows:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

void BubbleSort(int* p, int sz)
{
	int i = 0, j = 0;
	for (int i = 0; i < sz - 1; i++) //The total number of times compared is the number of elements minus 1
	{
		//Control the number of comparisons in each process
		//After each trip, the last element of the next comparison is the one in front
		for (int j = 0; j < sz - 1 - i; j++) 
		{
			//If the front element is larger than the rear element, it will be exchanged
			if (p[j] > p[j + 1])
			{
				int tmp = p[j];
				p[j] = p[j + 1];
				p[j + 1] = tmp;
			}
		}
	}
}

void Print(int* p, int sz) //Print elements within an array
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
	printf("\n");
}

int main()
{
   //Validate with two arrays
	int arr[] = { 5,6,1,3,4,2,7,8 };
	int arr2[] = { 10,9,8,7,6,5,4,3,2,1 };
	//Find the number of elements in the array as sz
	int sz = sizeof(arr) / sizeof(arr[0]);
	int sz2 = sizeof(arr2) / sizeof(arr2[0]);
	//Sort array arr
	BubbleSort(arr, sz);  //Ordinary bubble sorting
	Print(arr, sz);  //Print function
	
    //Sort array arr2
    BubbleSort(arr2, sz2);
	Print(arr, sz);
	return 0;
}

The program execution results are as follows:

Optimize external circulation

Please look at the order of the third and last round first:

I don't know if you have found a problem. The above has been orderly since the fourth trip, but our program still needs to run once. We can start from this place to optimize.
The idea is as follows:
We can set a variable flag equal to 0 in each trip; If the program verifies that the previous number is larger than the following number, it indicates that the array is not ordered, and set the variable flag to 1. If no number is larger than the following number in one trip, and the variable flag is still 0 without any modification, it indicates that the array has been ordered.
Finally, we judge whether the variable flag is 0 after each trip; If the flag is 1, it means that the array has not been sorted, and the program still continues to sort the next time; If the flag is 0, it indicates that the array has been sorted. Do not perform the next sorting and exit. So as to reduce the number of trips.
The code is as follows:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//Optimize external circulation 1
void BubbleSort1(int* p, int sz)
{
	int i = 0; 
	int j = 0;
	for (i = 0; i < sz - 1; i++)  // Control times
	{
		//The variable flag must be placed at the beginning of each trip, so that the flag will be set to 0 for each trip
		int flag = 0;
		for (j = 0; j < sz - 1 - i; j++)  //Control the number of comparisons per trip
		{
			if (p[j] > p[j + 1])
			{
				//It is found that the previous number is larger than the following number, and the array has no order
				//Set flag to 1
				int tmp = p[j];
				p[j] = p[j + 1];
				p[j + 1] = tmp;
				flag = 1;
			}
		}
		// Judge whether the flag changes after each trip
		if (flag == 0)
		{
			//The flag has not changed. The array has been ordered. Just exit sorting
			return;
		}
	}
}

void Print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
	printf("\n");
}

int main()
{
   //Validate with two arrays
	int arr[] = { 5,6,1,3,4,2,7,8 };
	int arr2[] = { 10,9,8,7,6,5,4,3,2,1 };
	//Find the number of elements in the array as sz
	int sz = sizeof(arr) / sizeof(arr[0]);
	int sz2 = sizeof(arr2) / sizeof(arr2[0]);
	//Sort array arr
	BubbleSort1(arr, sz);  //Optimize bubble sorting for version 1
	Print(arr, sz);  //Print function
	
    //Sort array arr2
    BubbleSort1(arr2, sz2);
	Print(arr, sz);
	return 0;
}

Optimize internal circulation

Let's take a look at the original array sorting process:

You have found a problem, that is, in the sorting process, the last segment is already orderly. We don't need to sort again. We just need to sort to the place where the last element is exchanged.
The idea is as follows:
We set a variable max to record the subscript of the last element exchange. Therefore, in the next sorting, we only need to sort to the variable max and add the optimization of the number of times analyzed above.
The code is as follows:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

void BubbleSort2(int* p, int sz)
{
	int i = 0, j = 0;
	int pos = sz - 1;  //The limit of the first sort is given to the last element first
	int max = 0;   //To save the subscript of the last exchange
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 0; 
		for (j = 0; j < pos; j++)
		{
			if (p[j] > p[j + 1])
			{
				int tmp = p[j];
				p[j] = p[j + 1];
				p[j + 1] = tmp;
				flag = 1;
				//Each exchange saves the subscript to max
				max = j; 
			  //You can't assign j to pos directly here,
			  //The subscript of the last element exchange cannot be determined here
			  //Wait until the end of each trip
			}
		}
		// Assign the saved subscript to pos after each trip
		//You can't assign j to pos directly here,
		//Because the subscript j of the last exchange will continue to change
		pos = max;
		if (flag == 0)
		{
			//The flag has not changed. The array has been ordered. Just exit sorting
			return;
		}
	}
}

void Print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
	printf("\n");
}

int main()
{
   //Validate with two arrays
	int arr[] = { 5,6,1,3,4,2,7,8 };
	int arr2[] = { 10,9,8,7,6,5,4,3,2,1 };
	//Find the number of elements in the array as sz
	int sz = sizeof(arr) / sizeof(arr[0]);
	int sz2 = sizeof(arr2) / sizeof(arr2[0]);
	//Sort array arr
	BubbleSort2(arr, sz);  //Optimize bubble sorting for version 2
	Print(arr, sz);  //Print function
	
    //Sort array arr2
    BubbleSort2(arr2, sz2);
	Print(arr, sz);
	return 0;
}

Bidirectional Bubble Sort

We sometimes encounter special examples in the process of sorting, such as sorting: 9 1 2 3 4 5 6 0. The maximum number of 9 is on the left, while the minimum number of 0 changes on the right. If you still optimize according to the above method, you can't save many steps. We need another way.
The idea is as follows:
At this time, we will think that if we change the position of the maximum number 9 and the minimum number 0, we can also traverse from right to left since the array can be traversed from left to right. Move the maximum number from left to right to the right; Move the smallest number to the left from right to left. We set two variables left and right. When left is less than right, the sorting will be executed, otherwise it will exit. Then add the previous two optimized exit methods.

The picture analysis is as follows:

The code is as follows:

//Final version optimization
void BubbleSort3(int* p, int sz)
{
	int left = 0;  //The starting subscript on the left is 0
	int right = sz - 1;   //The starting subscript on the right is the last element
	int max = 0;    
	int min = 0;
	while (left < right)
	{
		int i = 0;
		int flag = 0;
		// Sort from left to right
		for (i = left; i < right; i++)
		{
			if (p[i] > p[i + 1])
			{
				int tmp = p[i];
				p[i] = p[i + 1];
				p[i + 1] = tmp;
				flag = 1;
				// Save exchanged Subscripts
				max = i;  
			}
		}
		//Assign the subscript of the last exchange to right
		right = max;
		if (flag == 0)
		{
			return ;
		}

		// Sort from right to left
		flag = 0;  //To reset the flag to 0
		for (i = right; i > left; i--)
		{
			if (p[i] < p[i - 1])
			{
				int tmp = p[i];
				p[i] = p[i - 1];
				p[i - 1] = tmp;
				flag = 1;
				min = i;
			}
		}
		//Assign the subscript of the last exchange to right
		left = min;
		if (flag == 0)
		{
			return ;
		}
	}
}

qsort function

Did you find that the bubble sorting in the previous analysis can only sort integers, but it can't be realized when we want to sort strings or character types. So what is the way to solve it?
We have to mention the qsort function, which can sort any type. Here is its usage and resolution.

Analysis and use


It is worth noting that the last parameter is the function pointer type. It requires us to write a function to compare the two elements, and then the qsort function will automatically sort for us.
Use examples:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//A function that compares the size of an integer type
int compar1(const void* a, const void* b)
{
	if ((*(int*)a - *(int*)b) > 0)
	{
		return 1;
	}
	else if ((*(int*)a - *(int*)b) == 0)
	{
		return 0;
	}
	else
	{
		return -1;
	}
	//return ( *(int*)a - *(int*)b );
	//This may cause overflow
}

//A function that compares the size of character types
int compar2(const void* a, const void* b)
{
	if ((*(char*)a - *(char*)b) > 0)
	{
		return 1;
	}
	else if ((*(char*)a - *(char*)b) == 0)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}

int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1 };
	char arr2[] = "gfedcba";
	int sz = sizeof(arr) / sizeof(arr[0]);
	int sz2 = strlen(arr2);

	//Sort integer types
	qsort(arr, sz, sizeof(arr[0]), compar1); 
	//Sort character types
	qsort(arr2, sz2, sizeof(arr2[0]), compar2);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	printf("%s\n", arr2);
	return 0;
}

The program execution results are as follows:

Analog implementation of qsort function

Through the above examples, we have a general understanding of the characteristics of qsort function. Then, combined with the bubble sorting we analyzed earlier, can we use bubble sorting to simulate the implementation of qsort function?

The analysis is as follows:
We have two difficulties in implementing this function: first, how to find my next element? Because the pointer to the first element of the array is of type void, the step size of addition and subtraction cannot be determined.
The second difficulty is that even if two elements are determined, because the pointer of the first element of the array is of void type, it cannot be dereferenced, so it cannot be exchanged between elements.
So is there a solution?
Let's take a look at the four parameters of the qsort function. The third parameter is characteristic. What's the use of passing the size of the elements in the array? This parameter is the key to implementing the qsort function.

We know that among all pointer types, the pace of char * pointer is the smallest, and one byte is skipped every time we add one. Therefore, when we know the size of the element, that is, the width, we forcibly convert the first element pointer to char * type, multiply the width by a variable j in the loop body, and then add the first element pointer, This allows you to find any element in the array.
We know the width of the element. When two elements are exchanged, we can exchange byte by byte. The number of exchanges is the width of the element. In this way, the two difficulties are solved.

The code implementation is as follows:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct people
{
	char name[10];
	int age;
	char fun[10];
	double grade;
};


//A function that compares the size of an integer type
int compar1(const void* a, const void* b)
{
	if (((struct people*)a)->age - ((struct people*)b)->age > 0)
	{
		return 1;
	}
	else if (((struct people*)a)->age - ((struct people*)b)->age == 0)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}
int compar3(const void* a, const void* b)
{
	return strcmp(((struct people*)a)->name, ((struct people*)b)->name);
}

void Swap(char* str1, char* str2, int width)
{
	//Swap elements, byte by byte
	//The number of exchanges is the width
	while (width--)
	{
		char tmp = *(str1);
		*(str1) = *(str2);
		*(str2) = tmp;
		str1++;
		str2++;
	}
}

void my_qsort(void* base, int sz, int width, int compar(const void* a, const void* b))
{
	//Implement qsort by bubble sorting
	int i = 0, j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//Pass the address of the element to be compared to the comparison function compare
			if (compar((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				//Exchange element
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
				flag = 1;
			}
		}
		if (flag == 0)
		{
			return;
		}
	}
}

void Print(struct people* pc,int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", pc[i].name);
		printf("%d ", pc[i].age);
		printf("%s ", pc[i].fun);
		printf("%f ", pc[i].grade);
		printf("              ");
	}
	printf("\n\n");
}


int main()
{
	struct people arr[] =
	{ {"zhangsan",22,"add",3.14},{"lisi",18,"sub",6.67},{"wangw",28,"ghe",8.84} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//Sort string size
	printf("Sort string size:\n");
	my_qsort(arr, sz, sizeof(arr[0]), compar3);
	Print(arr, sz);

	// Sort integer sizes
	printf("Sort integer sizes:\n");
	my_qsort(arr, sz, sizeof(arr[0]), compar1);
	Print(arr, sz);
	return 0;
}

Note: the qsort function can help us realize many types of sorting, but we should write a function that can compare the size of two elements and pass the function address to qsort.
Program execution results:

Topics: C Algorithm