Eight major sorting-quick sort (Hall | Hollowing | Front and rear pointers | Non-recursive)

Today we will talk about quick sort among the eight major sortings. The most obvious feature of quick sort is that it is fast. The time complexity is O(N*logN). However, the disadvantage is that if you sort an array in reverse order, the time complexity It is O(N^2), and it does not require our insertion sort, so the features are obvious, but the shortcomings are also obvious, so let’s start today’s study.

The first is the sorting method of our Hall boss. The idea is to sort the big ones on the right and the small ones on the left. Let’s take a look at the following animation.

We can see that there are many pitfalls in Mr. Hall's sorting method. First, we start from the right side and look for the small one. When we find the small one, we stop. Then we start moving from the left side. We find it on the left side and find it. When it's big, we start to swap the left and right, and then we start looking for big on the right, and small on the left. What we still need to pay attention to here is when our sorting will end. We have the conditions that can be seen through the animation. When right and left meet, what we need to do at this time is to exchange the values ​​of key and the place where left and rigth meet.

The only difficulty here is why we must have this value smaller than the key when they meet? ? ?

There are two situations when left and right meet, one is when right meets left, and the other is when left and right meet. Both of these encounters can ensure that the value we encounter is smaller than the key. We can look at it this way, in the first case, the right moves and the left does not move. Our right is small. When the right encounters the left, the position of the left must be smaller than the key. , the last time we exchanged, we changed left to a smaller one, so this ensures that when right and left meet, it is smaller than key. Let's look at the second situation where left meets right, because right It is to find the small value, and then we go to left to find the big value. If we still don't find the big value, the condition is no longer satisfied when we get to right, so when left and right meet, the condition is also smaller than the key.

But this is all because we start looking for the smaller one on the right, and then start looking for the larger one on the left. Without this condition, we cannot establish that when left and right meet, the value is smaller than the key.

code show as below

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
int SortPart1(int* a, int left, int right)
{
	int key = a[left];
	int begin = left;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		while (left < right && a[left] <= key)
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[begin]);
	return left;
}



void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int midi = Midi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int keyi = SortPart3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);

}

This is the idea of ​​our Mr. Hall, but Mr. Hall’s writing method is too correct. You can see that our code is easy to make mistakes. Then let’s take a look at other versions, and then look at other versions. Sometimes we can optimize our code by taking the middle of our three numbers, because our code is the slowest when it is in reverse order, so every time we take a value, if our value is not the largest and smallest every time, it will be solved very well. To solve our problem, the following is the code to get the three numbers.

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

Let’s take a look at the gif of hollowing out.

Let's give the code first, and then look at the code and pictures so that everyone can better understand the hollowing method.

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

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


int SortPart2(int* a, int left, int right)
{
	int hole = left;
	int key = a[hole];
	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;
	return hole;
}


void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int midi = Midi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int keyi = SortPart3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);

}

 The idea of ​​hollowing out is actually very similar to Mr. Hall's idea. We must first save the data of the pit position, because this data is equivalent to being hollowed out and is an empty position. We remember that this hole is a pit position. The subscript, and then the key is the value of this pit. We first look for the small one from the right, find the small one and fill this position into the hole. Then the current right position is the new pit, starting from the left. Find the big one. When we find the big one, it will be the same as on the right. We fill the hole on the right with the value on the left, and then dig a hole on the left. Finally, when left and right meet, it is the end. The end is the final pit, and the final pit is filled with the value we had at the beginning.

The next one is oursThe front and back pointer method

Let’s take a look at our animation first.

For the front and rear pointer method, I think you can write the code by looking at the picture. Just cur and then find the smaller one. When you find the smaller one, pre needs to ++ first and then swap again. Cur will end at the end, so the end condition is cur < = endOur code can be optimized into the following code.​ 

int Midi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[mid] > a[left])
	{
		if (a[right] > a[mid])
		{
			return mid;
		}
		else if (a[right] < a[left])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else//left > mid
	{
		if (a[right] > a[left])
		{
			return left;
		}
		else if (a[right] < a[mid])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}
}
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


int SortPart3(int* a, int left, int right)
{
	int pre = left;
	int cur = left + 1;
	int begin = left;
	int end = right;
	while (cur <= end)
	{
		if (a[cur] < a[begin] && ++pre != cur)
		{
			Swap(&a[cur], &a[pre]);
		}
		cur++;
	}
	Swap(&a[pre], &a[begin]);
	return pre;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int midi = Midi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int keyi = SortPart3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);

}

In this way, our front and rear pointer methods are also completed. Are these three methods recursive? Now let’s talk about our non-recursive method. The non-recursive method first requires a stack. We must first have a stack. Is there a stack in the previous article? You can also use my on-site simplified version of the stack.


#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int STDateType;
typedef struct Stack
{
	STDateType* a;
	int top;
	int capacity;
}ST;


void Init(ST* ps);

void Push(ST* ps, STDateType x);

void Pop(ST* ps);

STDateType Top(ST* ps);

void Dstory(ST* ps);

bool Empty(ST* ps);

int Size(ST* ps);


#include"stack.h"


void Init(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = -1;
}

void Push(ST* ps, STDateType x)
{
	assert(ps);
	if (ps->top + 1 == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDateType* tmp =(STDateType*) realloc(ps->a, sizeof(STDateType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->capacity = newcapacity;
		ps->a = tmp;
	}
	ps->top++;

	ps->a[ps->top] = x;
}


void Pop(ST* ps)
{
	assert(ps);
	assert(ps->top >= 0);
	ps->top--;
}


void Dstory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->capacity = 0;
}

STDateType Top(ST* ps)
{
	assert(ps);
	return ps->a[ps->top];
}


bool Empty(ST* ps)
{
	assert(ps);
	return ps->top == -1;
}




int Size(ST* ps)
{
	assert(ps);
	return ps->top + 1;
}

With this stack, our non-recursive idea is actually a boundary issue. Our stack stores not the value of our array, but the values ​​of our subscripts, begin and end. The value we push at the beginning must be 0. and end, we must also pay attention to the order of push, and then take out left and right. Let's take a look at this picture. An example is this array.

Our code is the one below.

//非递归
void QuickSortNonR(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	ST st;
	Init(&st);
	Push(&st, end);
	Push(&st, begin);
	while (!Empty(&st))
	{

		int left = Top(&st);
		Pop(&st);
		int right = Top(&st);
		Pop(&st);
		int keyi = SortPart3(a, left, right);

		if (left < keyi - 1)
		{
			Push(&st, keyi-1);
			Push(&st, left);
		}
		if (right > keyi + 1)
		{
			Push(&st, right);
			Push(&st, keyi+1);
		}
		
	}
	Dstory(&st);
}

 What we share today is our eight major sorting quick sorting. Quick sorting is a very important sorting. There is also a quick sorting that can sort many of our repeated values, such as a bunch of 222222. We put the OJ question at the back. Speaking of it, the next thing to share is merge sort. See you next time.

Guess you like

Origin blog.csdn.net/2301_76895050/article/details/135044560