[Sorting] Bubble sorting and quick sorting (three versions + detailed explanation of non-recursive diagrams)

introduction

In this article, we will continue to introduce sorting algorithms: bubble sorting and quick sorting:

They all belong to exchange sorting, that is, through pairwise comparison and exchange, an element is placed in its correct position, and finally sorting is achieved.

Bubble Sort

In the previous article, the bubble sorting has been introduced in detail: poke my kang detailed explanation of the bubble sorting,
only the code is given here:

//冒泡排序
void bubble_sort(int* nums, int numsSize)
{
    
    
    int i = 0;
    int j = 0;
    int x = 0;
    for (i = 0; i < numsSize - 1; i++)
    {
    
    
        x = 1;
        for (j = 0; j < numsSize - 1 - i; j++)
        {
    
    
            if (nums[j]>nums[j + 1])
            {
    
    
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
                x = 0;
            }
        }
        if (x)
        {
    
    
            break;
        }
    }
}

quick sort

train of thought

The idea of ​​quick sorting is:
each time the starting value of the interval is the reference value, through pairwise comparison and exchange, the elements in the interval that are smaller than the reference value are all on the left, and the elements that are larger than the reference value are on the right;
then Then make the starting position of the interval on the left as the reference value, so that the new interval on the left is smaller than the reference value on the left, and the one greater than the reference value is on the right; iterate until after a certain adjustment, the interval on the left of the reference
value is only For one element, go back to the previous layer, and adjust the interval on the right of the reference value in the interval of the previous layer;
in this way, the original area, that is, the sorting of the entire array can be realized through iteration.

It is not difficult to find that such an iterative idea is very similar to the preorder traversal of a binary tree, and it also has good recursive properties.
We can implement it recursively, and there are three implementation methods: Hoare version, digging method, and forward and backward pointer method:

accomplish

We can do it recursively first:

Hoare version

When the Hoare version is implemented, three parameters are required: the array to be sorted, and the left and right intervals to be sorted (the subscripts of the intervals in the array).

First, when the interval has only one element or does not exist, that is, when left is greater than or equal to right, the recursion terminates; if the
interval exists, the reference value key is the initial value of the interval, and we can record this reference value by recording the subscript (keyi );
the range of the storage interval is convenient for recursion;

Then the while loop: In each loop, right traverses from right to left to find the subscript of the element smaller than the key value; then left traverses from left to right to find the subscript of the element greater than the key value. Then swap the two values ​​of left and right. The loop is terminated until left and right meet;
after the loop ends, the element at the position where left and right meet is exchanged with the element at the keyi position. It is realized that the elements before the key in the interval are all smaller than the key, and the elements after the key are larger than the key. That is, the key is confirmed in the correct position

Finally, recursively pass the parameters of the previous interval and the latter interval of keyi respectively:
insert image description here

 //快速排序hoare版本
void QuickSort1(int* a, int left, int right)
{
    
    
	if (left >= right)
	{
    
    
		return;
	}
	
	int keyi = left;//keyi从最左边开始
	int left2 = left;
	int right2 = right;
	while (left < right)
	{
    
    
		while (left < right && a[right] >= a[keyi])
		{
    
    
			right--;
		}
		while (left < right && a[left] <= a[keyi])
		{
    
    
			left++;
		}
		Swap(a, left, right);
	}
	Swap(a, keyi, left);

	QuickSort1(a, left2, left - 1);
	QuickSort1(a, right + 1, right2);
}

Quick sort optimization

With the above realization, I believe that everyone has a deeper understanding of the idea of ​​quick queuing, so why is quick queuing fast?

Similar to the recursion of a binary tree, in the optimal case: the position of one element is confirmed in the first layer, the position of 2 elements is confirmed in the second layer, the position of 4 elements is confirmed in the third layer...and so on, assuming that there are
n elements need to be sorted, and in the optimal case, logn layers (base 2) are required, and the time complexity is O(nlogn).

However, when this array is already sorted, when the key value selects the leftmost value, it is the smallest (largest) value in the interval. One-level traversal can actually only confirm the position of one element. Arranging the entire array requires n layers, and the time complexity is O(n^2).
insert image description here

Obviously, this greatly affects the efficiency of quick sort. To solve this problem, the key value needs to be a moderate value in the interval: the closer the key value is to the median of the interval, the higher the efficiency of quick sorting.

So I thought of the method of taking the middle of the three numbers:
that is, compare the values ​​of the starting element, the middle position element and the end element, and exchange the median of these three numbers to the starting position, so that the starting point is the value The key value is a moderate value to improve efficiency:

void Swap(int* a, int m, int n)//交换
{
    
    
	int temp = a[n];
	a[n] = a[m];
	a[m] = temp;
}
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[right] < a[left])
		{
    
    
			return left;
		}
		else
		{
    
    
			return right;
		}
	}
	else //a[left] >= a[mid]
	{
    
    
		if (a[right] < a[mid])
		{
    
    
			return mid;
		}
		else if (a[left] < a[right])
		{
    
    
			return left;
		}
		else
		{
    
    
			return right;
		}
	}
}

However, this solution still cannot solve the situation that all elements are equal, even if it is selected repeatedly, it cannot take a key value that is not too big or too small. A solution to this situation may be presented in a later article.

pit digging

When the digging method is implemented: the parameter part is the same as the Hoare version.

First, when the interval has only one element or does not exist, that is, when left is greater than or equal to right, the recursion terminates;
then, according to the middle of the three numbers, select a reference value key, put it at the starting position of the interval, and the initial value of the pit It is the subscript of the key;
the range of the storage interval is convenient for recursion;

Then while loop: in each loop, right traverses from right to left to find the subscript of the element smaller than the key value, and then put this element in the pit, the position of the element is the new pit; then left from the left Traverse to the right to find the subscript of the element greater than the key value, and then put this element into the pit, and the position of the element is the new pit. End the loop until left and right meet;
after the loop ends, put the key value into the pit;

Finally, recursively pass the parameters of the previous interval and the latter interval of keyi respectively:
insert image description here

//快速排序挖坑法
void QuickSort2(int* a, int left, int right)
{
    
    
	if (left >= right)
	{
    
    
		return;
	}

	int midi = GetMidNumi(a, left, right);//三数取中,将中间的数放到左位置
	if (a[midi] != a[left])
	{
    
    
		Swap(a, left, midi);
	}

	int key = a[left];//key是最左边的数
	int hole = left;
	int left2 = left;//保存left与right的值
	int right2 = right;
	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;

	QuickSort2(a, left2, left - 1);
	QuickSort2(a, right + 1, right2);
}

forward and backward pointer method

When the front and rear pointer methods are implemented, the parameter list is also the same as the first two;

First, judge the interval; select the selected key from the three numbers, and record the key subscript keyi; store the range of the interval to facilitate recursion;
then, create two variables prev and cur, which respectively represent the two positions before and after: prev is initialized to the starting position of the area , cur is initialized to the next position of prev;

Then the while loop, when the value of the element at the position of cur is less than the key value, prev moves one element backward, and exchanges the element at the position of prev and cur; otherwise, cur moves one element backward, and prev does not move. When cur moves to the end of the area, the loop ends;
after the loop ends, exchange the key value at the keyi position with the value at the prev position, and change keyi to prev; [The
purpose of this process is to make the elements behind prev (not including prev, including cur) are all greater than the key value, and finally exchange the values ​​of prev and keyi, that is, make all the values ​​in the front interval of keyi less than key, and all the values ​​behind are greater than key ]

Finally, recursively pass the parameters of the previous interval and the latter interval of keyi respectively:

insert image description here

 //快速排序前后指针法
void QuickSort3(int* a, int left, int right)
{
    
    
	if (left >= right)
	{
    
    
		return;
	}

	int midi = GetMidNumi(a, left, right);//三数取中,将中间的数放到左位置
	if (a[midi] != a[left])
	{
    
    
		Swap(a, left, midi);
	}

	int keyi = left;//keyi还是从最左边开始
	int prev = keyi;
	int cur = prev + 1;
	while (cur <= right)
	{
    
    
		if (a[cur] < a[keyi])
		{
    
    
			prev++;
			if (a[prev] != a[cur])
			{
    
    
				Swap(a, prev, cur);
			}
		}
		cur++;
	}
	Swap(a, keyi, prev);
	keyi = prev;

	QuickSort3(a, left, keyi - 1);
	QuickSort3(a, keyi + 1, right);
}

Since this version is the most convenient when it is implemented, this way of writing is the most recommended.

Quick sort non-recursive version

Of course, all recursive implementations can be converted to non-recursive, such as the previous Fibonacci, which can be directly converted to non-recursive.
But if you implement fast sorting directly and non-recursively, there will be problems. That is, when a left area has been sorted, return to the upper level area, and then recurse to the right area. This process needs to know the area range of the upper level. It is obviously impossible to directly convert non-recursive.

So we need a structure to store the range of the upper area. Similar to returning upward after sorting an area, this structure needs to be satisfied. The last stored area is sorted first, and then the sorting of the largest area can be completed layer by layer from the smallest area upward. The data structure of the stack just meets our needs. When the stack is empty, that is, when all regions are sorted.

train of thought

The above idea is only to simulate the process of returning upwards from the smallest part. When an array is to be sorted quickly, the complete process should be recursively down from the largest area to the smallest area, and then return to it, so the complete process should be:

First put the left and right intervals of the largest area into the stack, and then enter the loop:
in the loop, first take out the interval at the top of the stack, sort the interval, and put the key value in the correct position;
then put the interval after keyi with the previous interval Push them into the stack respectively (when popping out the stack, first go out the range on the left);
then take a set of ranges on the top of the stack, sort them, and then push the two areas into the stack until the stacked area is less than or equal to 1 element, then do not enter Stack;
the next cycle, the pop-up area is the right side of the upper-level area;

Loop like this, when the stack is empty, the loop is terminated, and the sorting ends:

accomplish

The outer loop controls the stacking and popping of each layer, and the inner loop controls the sorting in each area (this sorting can use the three methods implemented above, here is the forward and backward pointer method):

insert image description here

// 快速排序 非递归实现(前后指针法)(需要栈辅助实现)
void QuickSortNonR(int* a, int left, int right)
{
    
    
	ST st;
	STInit(&st);
	STPush(&st, left);
	STPush(&st, right);
	while (STEmpty(&st) == 0)
	{
    
    
		int end = STTop(&st);
		STPop(&st);
		int begin= STTop(&st);
		STPop(&st);

		//前后指针法 
		int midi = GetMidNumi(a, begin, end);//三数取中,将中间的数放到左位置
		if (a[midi] != a[begin])
		{
    
    
			Swap(a, begin, midi);
		}
		int keyi = begin;
		int prev = begin;
		int cur = prev + 1;
		while (cur <= end)
		{
    
    
			if (a[cur] < a[keyi])
			{
    
    
				prev++;
				if (a[prev] != a[cur])
				{
    
    
					Swap(a, prev, cur);
				}
			}
			cur++;
		}
		Swap(a, keyi, prev);
		keyi = prev;

		//新的区间入栈(先传右区间再传左区间,取的时候就是先左后右)
		if (keyi + 1 < end)
		{
    
    
			STPush(&st, keyi + 1);
			STPush(&st, end);
		}
		if (keyi - 1 >begin)
		{
    
    
			STPush(&st, begin);
			STPush(&st, keyi - 1);
		}
	}
	STDestroy(&st);
}

In the future, some algorithms that cannot be directly converted into non-recursive algorithms can also be completed with the help of stacks.

Summarize

So far, the three implementations of bubble sort and quick sort and the non-recursive implementation have been introduced.
Next, we will continue to introduce other sorts. Welcome to continue to pay attention.

If you think that I did not introduce a certain part clearly or that there is a problem with a certain part, you are welcome to raise it in the comment area

If this article is helpful to you, I hope it will be connected with one click

Hope to make progress together with you

Guess you like

Origin blog.csdn.net/weixin_73450183/article/details/130264319