Sort | quick sort

Posted by varecha on Thu, 17 Feb 2022 10:19:44 +0100

thought

For each division, make the left side smaller than the benchmark and the right side larger than the benchmark, but in disorder, and then divide the left and right sides once again. In this way, the division will continue until the size of the decomposed array is 1 and the division stops. At this time, it is in order

Partition function

It is essential to divide the function in fast scheduling. Its function is to determine a benchmark and then divide it based on this benchmark. There are many ways to divide. The most commonly used is to traverse the boundary with double pointers. The right pointer looks for the one smaller than the benchmark and the left pointer looks for the one larger than the benchmark, and then exchange until the left and right pointers coincide, and then put the benchmark at the final pointer position, so that the left side of the benchmark is small, The right side is big

Double pointers point to the leftmost and rightmost partition functions
int Partition(int* ar, int left, int right)
{
	int tmp = ar[left];
	while (left < right)
	{
		while (right > left && ar[right] > tmp) right--;
		ar[left] = ar[right];
		while (right > left && ar[left] <= tmp) left++;
		ar[right] = ar[left];
	}
	ar[left] = tmp;
	return left;
}
Partition function of one-way approximation

The number between i and j is larger than the benchmark. Scan to the right. When a number smaller than the benchmark is found, the number between i and j is exchanged. This method can be used as a quick sorting of single linked lists

int Partition1(int* ar, int left, int right)
{
	int i = left-1;
	int j = left;
	while (j <= right)
	{
		while (j<=right && ar[j] > ar[left]) j++;
		if (j > right) break;
		swap(ar[++i], ar[j++]);
		/*if (ar[j] <= ar[left])
		{
			++i;
			swap(ar[i], ar[j]);
		}
		++j;*/
	}
	swap(ar[i], ar[left]);
	return i;
}

Sorting function

Recursive version

void Qsort(int* ar, int left,int right)
{
	if (right > left)
	{
		int pos = Partition1(ar, left, right);
		Qsort(ar, left, pos - 1);
		Qsort(ar, pos + 1, right);
	}
}

Non recursive version:
The non recursive implementation needs to use the medium to store the interval to be divided. You can use stack, queue and pair

void Qsort(int* ar, int size)
{
	stack<int> st;
	st.push(size - 1);
	st.push(0);
	
	while (!st.empty())
	{
		int left = st.top(); st.pop();
		int right = st.top(); st.pop();
		int pos = Partition(ar, left, right);
		if (pos > left)
		{
			st.push(pos - 1);
			st.push(left);
		}
		if (right > pos)
		{
			st.push(right);
			st.push(pos + 1);
			
		}
	}
}

BFPRT algorithm

Algorithm idea:
For the problem of finding the k-th smallest element in the array, we already have a good conventional algorithm. In the best case, the time complexity of this algorithm is O (n), but in the worst case, it is O (n^2). In fact, bfprt algorithm is improved on this basis.

Conventional solution:
We randomly select a number in the array as the partition value (number), and then carry out the fast scheduling partition process (put the number less than number on the left of the array, the number equal to number in the middle of the array, and the number greater than number on the right of the array), and then judge the relative relationship between k and the area equal to number. If k is just in the area equal to number, If k is on the left of the area equal to number, we will recursively perform the above process on the left. If k is on the right of the area equal to number, we will recursively perform the above process on the right.

For the conventional solution, we analyze its time complexity:

Recursive function complexity calculation:

T(N)=aT(N/b)+o(N^d);
When log (B, a) > D, the complexity is O (N^log(b,a));
When the complexity is n (d log) = 2;
When log (B, a) < D, the complexity is O (N^d);
N is the number of sample data, that is, the number of elements in the array.
N/b is to divide the N numbers into smaller parts. The number of sample data in each part is generally evenly divided, so b is equal to 2.
A is the number of times a small part is executed after being divided into multiple parts.
d is the time complexity of the remaining operations after the recursive function call is completed.

For the best case: the number selected each time is just in the middle of the array. In the above formula, a is equal to 1 and b is equal to 2. For the partation process, the time complexity is O (n), so d is equal to 1. Therefore, T (n) = T (N/2) + O (n). At this time, log (2, 1) < 1, so the time complexity is O (n).

For the worst case: if the number selected each time is just on the edge of the array, the time complexity is O (N ^ 2)

bfprt algorithm is based on this number. bfprt algorithm can ensure that the selected number is in the middle of the array every time, so the time complexity is O (N).

Two important functions: take the intermediate value function and find the k-th smallest function
To find the median, we have to find the k-th smallest function
The k-th function uses the median to obtain the best division point. It is a recursive function that calls each other, which is a little difficult to understand

void Sort(int* ar, int left, int right)//bubble
{
	//sort
	for (int i = left, n=0 ;i < right; i++,n++)
	{
		for (int j = left; j < right-n; j++)
		{
			if (ar[j] > ar[j + 1])
			{
				swap(&ar[j], &ar[j + 1]);
			}
		}
	}
}

int GetMidIndex(int* ar, int left, int right)
{
	int left1 = left;
	int count = (right - left + 1) / 5;//How many times should the record be cycled
	int res = (right - left + 1) % 5;
	if (right-left+1<=5)
	{
		Sort(ar, left, right);
		return (right + left) / 2;
	}
	int n = left;

	for(int i=0;i<count;i++)
	{
		Sort(ar, left, left + 4);
		swap(&ar[n++], &ar[left + 2]);
		left += 5;
	}
	if (res > 0)
	{
		Sort(ar, right - res + 1, right);//0 1 2 3 4 5 6 7 8 9 10
		swap(&ar[n], &ar[right - res / 2]);//right - res/2
	}
	else count--;
	return (BFPRT(ar, left1, left1 + count, (count / 2)+1));
	
	
}
int Partition(int* ar, int left,int right, int i)//i is the datum coordinate
{

	while (right > left)
	{
		while (left < right && ar[right] > ar[i]) right--;
		while (left < right && ar[left] <= ar[i]) left++;
		swap(&ar[left], &ar[right]);
	}
	swap(&ar[left], &ar[i]);
	return left;

}
int BFPRT(int* ar,int left,int right,int k)//The largest K is the smallest size-k
{
	int mid_index=GetMidIndex(ar, left, right);
	int part_index=Partition(ar, left, right, mid_index);
	int num = part_index - left+1;
	if (num == k)
	{
		return part_index;
	}
	else if (num > k)
	{
		return BFPRT(ar, left, part_index-1, k);
	}
	else
	{
		return BFPRT(ar, part_index+1, right, k - num);
	}
}

Topics: C Algorithm data structure